-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(#63) Initial implementation of the Best-Worst method
- Loading branch information
Showing
7 changed files
with
139 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -187,4 +187,9 @@ JMcDM.cilos | |
## IDOCRIW | ||
```@docs | ||
JMcDM.idocriw | ||
``` | ||
|
||
## Best-Worst | ||
```@docs | ||
JMcDM.bestworst | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
module BestWorstMethod | ||
|
||
export bestworst | ||
export BestWorstResult | ||
|
||
|
||
using ..JuMP, ..Ipopt | ||
|
||
struct BestWorstResult | ||
ε::Float64 | ||
weights::Vector | ||
end | ||
|
||
|
||
""" | ||
bestworst(pref_to_best::Vector{Int}, pref_to_worst::Vector{Int})::BestWorstResult | ||
The Best-Worst Method is a method for deriving weights from preference information. | ||
The method is based on the idea that the best alternative should be as close as possible to the best alternative | ||
and as far as possible from the worst alternative. | ||
In `pref_to_best`, the best alternative should have the highest preference value of 1. | ||
Similarly, in `pref_to_worst`, the worst alternative should have the highest preference value of 1. | ||
# Arguments | ||
- `pref_to_best::Vector{Int}`: Vector of preferences for the best alternative. | ||
- `pref_to_worst::Vector{Int}`: Vector of preferences for the worst alternative. | ||
# Returns | ||
- `BestWorstResult`: A struct with the following fields: | ||
- `ε::Float64`: The value of epsilon. | ||
- `weights::Vector`: The weights of the alternatives. | ||
# Example | ||
```julia | ||
using JMcDM | ||
pref_to_best = [8, 2, 1] | ||
pref_to_worst = [1, 5, 8] | ||
result = bestworst(pref_to_best, pref_to_worst) | ||
``` | ||
# References | ||
- Rezaei, Jafar. "Best-worst multi-criteria decision-making method." Omega 53 (2015): 49-57. | ||
!!! warning "Dependencies" | ||
This method is enabled when the JuMP and Ipopt packages are installed and loaded. | ||
Please first load the JuMP and Ipopt packages before using this method. | ||
The method is not available in the JMcDM module until the JuMP and Ipopt packages are loaded. | ||
""" | ||
function bestworst(pref_to_best::Vector{Int}, pref_to_worst::Vector{Int})::BestWorstResult | ||
|
||
n = length(pref_to_best) | ||
|
||
if n != length(pref_to_worst) | ||
throw(ArgumentError("The lengths of the preference vectors are not equal.")) | ||
end | ||
|
||
best_index = argmin(pref_to_best) | ||
|
||
if pref_to_best[best_index] != 1 | ||
throw(ArgumentError("The best index must have the highest preference value of 1.")) | ||
end | ||
|
||
worst_index = argmin(pref_to_worst) | ||
|
||
if pref_to_worst[worst_index] != 1 | ||
throw(ArgumentError("The worst index must have the highest preference value of 1.")) | ||
end | ||
|
||
model = Model(Ipopt.Optimizer) | ||
|
||
set_silent(model) | ||
|
||
@variable(model, ε >= 0) | ||
|
||
@variable(model, w[1:n] >= 0) | ||
|
||
@objective(model, Min, ε) | ||
|
||
indices = collect(1:n) | ||
|
||
bestindices = indices[indices.!=best_index] | ||
|
||
worstindices = indices[indices.!=worst_index] | ||
|
||
for i in bestindices | ||
@constraint(model, abs(w[best_index] / w[i] - pref_to_best[i]) <= ε) | ||
end | ||
|
||
for i in worstindices | ||
@constraint(model, abs(w[i] / w[worst_index] - pref_to_worst[i]) <= ε) | ||
end | ||
|
||
@constraint(model, sum(w) == 1) | ||
|
||
optimize!(model) | ||
|
||
result = BestWorstResult(value(ε), value.(w)) | ||
|
||
return result | ||
end | ||
|
||
end # module |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
@testset "Best-Worst" verbose = true begin | ||
|
||
@testset "Basic Example in the Original Paper" begin | ||
|
||
eps = 0.01 | ||
|
||
pref_to_best = [8, 2, 1] | ||
|
||
pref_to_worst = [1, 5, 8] | ||
|
||
result = bestworst(pref_to_best, pref_to_worst) | ||
|
||
@test isapprox(result.ε, 0.26, atol = eps) | ||
|
||
@test isapprox(result.weights, [0.071, 0.338, 0.589], atol = eps) | ||
end | ||
|
||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters