Skip to content

Commit be44219

Browse files
blibliboeLaar
andauthored
35 min max variables (#41)
* Updated the generate parralel to return state aka all solutions from metaheuristics * Updated generate alternatives to make multiple MGA techniques possible * Update optimization specifically to also allow for multiple MGa techniques * Added the max-distance method to the MGA methods, this was already implemented * Added the max-distance method to the MGA methods, this was already implemented * Changed it so that max distance is not neccesarily the standard when testing * Added Min/Max Variables as a method * Included min max * Updated the test files and small bug --------- Co-authored-by: Laar <luuk.vandelaar@tno.nl>
1 parent edba438 commit be44219

File tree

4 files changed

+199
-4
lines changed

4 files changed

+199
-4
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
export Min_Max_Variables_update!, Min_Max_Variables_initial!
2+
3+
4+
"""
5+
Min_Max_Variables_initial!(
6+
model::JuMP.Model,
7+
variables::AbstractArray{T,N},
8+
fixed_variables::Vector{VariableRef};
9+
weights::Vector{Float64} = zeros(length(variables)),
10+
metric::Distances.SemiMetric = SqEuclidean(),
11+
) where {T<:Union{VariableRef,AffExpr}, N}
12+
Initialize the objective of a JuMP model using the Min/Max Variables method to generate alternative solutions.
13+
This function sets a new objective that minimizes the weighted sum of the decision variables, where weights are randomized between -1, 0 and 1. Fixed variables are locked at their optimal values.
14+
# Arguments
15+
- `model::JuMP.Model`: a solved JuMP model whose objective is to be redefined for alternative generation.
16+
- `variables::AbstractArray{T,N}`: the variables involved in the objective, typically a vector or matrix of `VariableRef`s or `AffExpr`s.
17+
- `fixed_variables::Vector{VariableRef}`: variables to be fixed at their current values to avoid changes in alternatives.
18+
- `weights::Vector{Float64}`: optional vector of weights for each variable; will be internally overwritten based on variable values.
19+
- `metric::Distances.SemiMetric`: unused in this method (included for consistency with other alternative generation methods).
20+
# Behavior
21+
- Variables are randomly minimized or maximized.
22+
- Fixed variables are frozen at their optimal values using `fix(...)`.
23+
- The objective is set to minimize the weighted sum of the variables, encouraging sparsity or deviation from the original.
24+
"""
25+
function Min_Max_Variables_initial!(
26+
model::JuMP.Model,
27+
variables::AbstractArray{T,N},
28+
fixed_variables::Vector{VariableRef};
29+
weights::Vector{Float64} = zeros(length(variables)),
30+
metric::Distances.SemiMetric = SqEuclidean(),
31+
) where {T<:Union{VariableRef,AffExpr},N}
32+
# new objective function consist of the n variables in variables
33+
for (i, v) in enumerate(variables)
34+
weights[i] = rand([-1, 0, 1])
35+
end
36+
# Fix the variables that are fixed
37+
fix.(fixed_variables, value.(fixed_variables), force = true)
38+
39+
# update these variables based on their sign
40+
objective_function = [v * weights[i] for (i, v) in enumerate(variables)]
41+
42+
# Update objective by adding the distance between variables and the previous optimal solution.
43+
@objective(model, Min, sum(objective_function))
44+
end
45+
46+
"""
47+
Min_Max_Variables_update!(
48+
model::JuMP.Model,
49+
variables::AbstractArray{T,N};
50+
weights::Vector{Float64} = zeros(length(variables)),
51+
metric::Distances.SemiMetric = SqEuclidean(),
52+
) where {T<:Union{VariableRef,AffExpr}, N}
53+
Update the objective of a JuMP model using the Min/Max Variables method to generate the next alternative solution.
54+
Update the weights randomly between -1, 0 and 1.
55+
# Arguments
56+
- `model::JuMP.Model`: the JuMP model to be updated.
57+
- `variables::AbstractArray{T,N}`: the decision variables involved in the updated objective.
58+
- `weights::Vector{Float64}`: optional vector of weights; will be overwritten based on current variable values.
59+
- `metric::Distances.SemiMetric`: unused in this method (included for interface consistency).
60+
# Behavior
61+
- Variables are randomly minimized or maximized.
62+
- A new objective is set: minimize the weighted sum of the variables.
63+
- This function does not re-fix any variables; it is typically called iteratively after `Min_Max_Variables_initial!`.
64+
"""
65+
function Min_Max_Variables_update!(
66+
model::JuMP.Model,
67+
variables::AbstractArray{T,N};
68+
weights::Vector{Float64} = zeros(length(variables)),
69+
metric::Distances.SemiMetric = SqEuclidean(),
70+
) where {T<:Union{VariableRef,AffExpr},N}
71+
# new objective function consist of the n variables in variables
72+
for (i, v) in enumerate(variables)
73+
weights[i] = rand([-1, 0, 1])
74+
end
75+
76+
# update these variables based on their sign
77+
objective_function = [v * weights[i] for (i, v) in enumerate(variables)]
78+
79+
# Update objective by adding the distance between variables and the previous optimal solution.
80+
@objective(model, Min, sum(objective_function))
81+
end

src/NearOptimalAlternatives.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@ using DataStructures
1111
include("MGA-Methods/Max-Distance.jl")
1212
include("MGA-Methods/HSJ.jl")
1313
include("MGA-Methods/Spores.jl")
14+
include("MGA-Methods/Min-Max-Variables.jl")
1415

1516
include("results.jl")
1617
include("alternative-optimisation.jl")
1718
include("generate-alternatives.jl")
1819
include("alternative-metaheuristics.jl")
1920

20-
21-
2221
end

src/alternative-optimisation.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ It is used to dynamically select the appropriate function based on the method sp
77
const METHOD_DISPATCH_UPDATE = Dict{Symbol,Function}(
88
:HSJ => HSJ_update!,
99
:Spores => Spores_update!,
10-
# :Min_Max_Variables => MM_update!,
10+
:Min_Max_Variables => Min_Max_Variables_update!,
1111
# :Random_Vector => RV_update!,
1212
# :Directionally_Weighted_Variables => DW_update!,
1313
:Max_Distance => Dist_update!,
@@ -17,7 +17,7 @@ const METHOD_DISPATCH_UPDATE = Dict{Symbol,Function}(
1717
const METHOD_DISPATCH_INITIAL = Dict{Symbol,Function}(
1818
:HSJ => HSJ_initial!,
1919
:Spores => Spores_initial!,
20-
# :Min_Max_Variables => MM_update!,
20+
:Min_Max_Variables => Min_Max_Variables_initial!,
2121
# :Random_Vector => RV_update!,
2222
# :Directionally_Weighted_Variables => DW_update!,
2323
:Max_Distance => Dist_initial!,

test/test-Min-Max-Variables.jl

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
@testset "Test generate alternatives with Min/Max Variables as modeling_method." begin
2+
@testset "Test regular run with one alternative." begin
3+
optimizer = Ipopt.Optimizer
4+
model = JuMP.Model(optimizer)
5+
6+
# Initialise simple `square` JuMP model
7+
@variable(model, 0 x_1 1)
8+
@variable(model, 0 x_2 1)
9+
@objective(model, Max, x_1 + x_2)
10+
JuMP.optimize!(model)
11+
12+
results = NearOptimalAlternatives.generate_alternatives!(
13+
model,
14+
0.1,
15+
all_variables(model),
16+
1;
17+
modeling_method = :Min_Max_Variables,
18+
)
19+
20+
# Test that `results` contains one solution with 2 variables, and an objective value between 1.8 and 2.0.
21+
@test length(results.solutions) == 1 &&
22+
length(results.solutions[1]) == 2 &&
23+
length(results.objective_values) == 1 &&
24+
(
25+
results.objective_values[1] 1.8 ||
26+
isapprox(results.objective_values[1], 1.8)
27+
) &&
28+
(
29+
results.objective_values[1] 2.0 ||
30+
isapprox(results.objective_values[1], 2.0)
31+
)
32+
end
33+
34+
@testset "Test regular run with one alternative with one fixed variable." begin
35+
optimizer = Ipopt.Optimizer
36+
model = JuMP.Model(optimizer)
37+
38+
# Initialise simple `square` JuMP model
39+
@variable(model, 0 x_1 1)
40+
@variable(model, 0 x_2 1)
41+
@objective(model, Max, x_1 + x_2)
42+
JuMP.optimize!(model)
43+
44+
results = NearOptimalAlternatives.generate_alternatives!(
45+
model,
46+
0.1,
47+
all_variables(model),
48+
1;
49+
fixed_variables = [x_2],
50+
modeling_method = :Min_Max_Variables,
51+
)
52+
53+
# Test that `results` contains one solution with 2 variables, and an objective value between 1.8 and 2.0. Also, `x_2` should remain around 1.0 and `x_1` should be between 0.8 and 1.0.
54+
@test length(results.solutions) == 1 &&
55+
length(results.solutions[1]) == 2 &&
56+
length(results.objective_values) == 1 &&
57+
(
58+
results.objective_values[1] 1.8 ||
59+
isapprox(results.objective_values[1], 1.8)
60+
) &&
61+
(
62+
results.objective_values[1] 2.0 ||
63+
isapprox(results.objective_values[1], 2.0)
64+
) &&
65+
(
66+
results.solutions[1][x_1] 0.8 ||
67+
isapprox(results.solutions[1][x_1], 0.8)
68+
) &&
69+
(
70+
results.solutions[1][x_1] 1.0 ||
71+
isapprox(results.solutions[1][x_1], 1.0)
72+
) &&
73+
isapprox(results.solutions[1][x_2], 1.0)
74+
end
75+
76+
@testset "Test regular run with two alternatives." begin
77+
optimizer = Ipopt.Optimizer
78+
model = JuMP.Model(optimizer)
79+
80+
# Initialise simple `square` JuMP model
81+
@variable(model, 0 x_1 1)
82+
@variable(model, 0 x_2 1)
83+
@objective(model, Max, x_1 + x_2)
84+
JuMP.optimize!(model)
85+
86+
results = NearOptimalAlternatives.generate_alternatives!(
87+
model,
88+
0.1,
89+
all_variables(model),
90+
2;
91+
modeling_method = :Min_Max_Variables,
92+
)
93+
94+
# Test that `results` contains 2 solutions with two variables each, where the objective values of both solutions are between 1.8 and 2.0.
95+
@test length(results.solutions) == 2 &&
96+
length(results.solutions[2]) == 2 &&
97+
length(results.objective_values) == 2 &&
98+
(
99+
results.objective_values[1] 1.8 ||
100+
isapprox(results.objective_values[1], 1.8)
101+
) &&
102+
(
103+
results.objective_values[1] 2.0 ||
104+
isapprox(results.objective_values[1], 2.0)
105+
) &&
106+
(
107+
results.objective_values[2] 1.8 ||
108+
isapprox(results.objective_values[2], 1.8)
109+
) &&
110+
(
111+
results.objective_values[2] 2.0 ||
112+
isapprox(results.objective_values[2], 2.0)
113+
)
114+
end
115+
end

0 commit comments

Comments
 (0)