Skip to content

Commit 6738219

Browse files
committed
Update and add test
1 parent 12f04e5 commit 6738219

File tree

2 files changed

+78
-55
lines changed

2 files changed

+78
-55
lines changed

ext/MultiObjectiveAlgorithmsPolyhedraExt.jl

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,6 @@ function _halfspaces(IPS::Vector{Vector{Float64}})
1515
return [(-H_i.a, -H_i.β) for H_i in H]
1616
end
1717

18-
function _compute_anchors(model::MOA.Optimizer)
19-
anchors = Dict{Vector{Float64},Dict{MOI.VariableIndex,Float64}}()
20-
n = MOI.output_dimension(model.f)
21-
scalars = MOI.Utilities.scalarize(model.f)
22-
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
23-
yI, yUB = zeros(n), zeros(n)
24-
for (i, f_i) in enumerate(scalars)
25-
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f_i)}(), f_i)
26-
MOI.optimize!(model.inner)
27-
# status check
28-
X, Y = MOA._compute_point(model, variables, model.f)
29-
model.ideal_point[i] = Y[i]
30-
yI[i] = Y[i]
31-
anchors[Y] = X
32-
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
33-
MOI.optimize!(model.inner)
34-
# status check
35-
_, Y = MOA._compute_point(model, variables, f_i)
36-
yUB[i] = Y
37-
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
38-
end
39-
40-
return yI, yUB, anchors
41-
end
42-
4318
function _distance(w̄, b̄, OPS, model)
4419
n = MOI.output_dimension(model.f)
4520
optimizer = typeof(model.inner.optimizer)
@@ -65,7 +40,6 @@ end
6540

6641
function _select_next_halfspace(H, OPS, model)
6742
distances = [_distance(w, b, OPS, model) for (w, b) in H]
68-
@info "Distances: $(Dict(zip(H, distances)))"
6943
index = argmax(distances)
7044
w, b = H[index]
7145
return distances[index], w, b
@@ -75,24 +49,45 @@ function MOA.minimize_multiobjective!(
7549
algorithm::MOA.Sandwiching,
7650
model::MOA.Optimizer,
7751
)
78-
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
79-
ε = algorithm.precision
52+
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
8053
start_time = time()
8154
solutions = Dict{Vector{Float64},Dict{MOI.VariableIndex,Float64}}()
8255
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
8356
n = MOI.output_dimension(model.f)
8457
scalars = MOI.Utilities.scalarize(model.f)
85-
yI, yUB, anchors = _compute_anchors(model)
86-
merge!(solutions, anchors)
87-
@info "yI: $(yI)"
88-
@info "yUB: $(yUB)"
89-
IPS = [yUB, keys(anchors)...]
58+
status = MOI.OPTIMAL
9059
OPS = Tuple{Vector{Float64},Float64}[]
91-
for i in 1:n
60+
anchors = Dict{Vector{Float64},Dict{MOI.VariableIndex,Float64}}()
61+
yI, yUB = zeros(n), zeros(n)
62+
for (i, f_i) in enumerate(scalars)
63+
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f_i)}(), f_i)
64+
MOI.optimize!(model.inner)
65+
status = MOI.get(model.inner, MOI.TerminationStatus())
66+
if !MOA._is_scalar_status_optimal(model)
67+
return status, nothing
68+
end
69+
X, Y = MOA._compute_point(model, variables, model.f)
70+
model.ideal_point[i] = Y[i]
71+
yI[i] = Y[i]
72+
anchors[Y] = X
73+
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
74+
MOI.optimize!(model.inner)
75+
status = MOI.get(model.inner, MOI.TerminationStatus())
76+
if !MOA._is_scalar_status_optimal(model)
77+
MOA._warn_on_nonfinite_anti_ideal(algorithm, MOI.MIN_SENSE, i)
78+
return status, nothing
79+
end
80+
_, Y = MOA._compute_point(model, variables, f_i)
81+
yUB[i] = Y
82+
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
9283
e_i = Float64.(1:n .== i)
9384
push!(OPS, (e_i, yI[i])) # e_i' * y >= yI_i
9485
push!(OPS, (-e_i, -yUB[i])) # -e_i' * y >= -yUB_i ⟹ e_i' * y <= yUB_i
9586
end
87+
@info "yI: $(yI)"
88+
@info "yUB: $(yUB)"
89+
IPS = [yUB, keys(anchors)...]
90+
merge!(solutions, anchors)
9691
@info "IPS: $(IPS)"
9792
@info "OPS: $(OPS)"
9893
u = MOI.add_variables(model.inner, n)
@@ -110,19 +105,28 @@ function MOA.minimize_multiobjective!(
110105
H = _halfspaces(IPS)
111106
count = 0
112107
while !isempty(H)
108+
if MOA._time_limit_exceeded(model, start_time)
109+
status = MOI.TIME_LIMIT
110+
break
111+
end
113112
count += 1
114113
@info "-- Iteration #$(count) --"
115114
@info "HalfSpaces: $(H)"
116115
δ, w, b = _select_next_halfspace(H, OPS, model)
117116
@info "Selected halfspace: w: $(w), b: $(b)"
118117
@info "δ: $(δ)"
119-
if δ - 1e-3 <= ε # added some convergence tolerance
118+
if δ - 1e-3 <= algorithm.precision # added some convergence tolerance
120119
break
121120
end
122121
# would not terminate when precision is set to 0
123122
new_f = sum(w[i] * (scalars[i] + u[i]) for i in 1:n) # w' * (f(x) + u)
124123
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(new_f)}(), new_f)
125124
MOI.optimize!(model.inner)
125+
status = MOI.get(model.inner, MOI.TerminationStatus())
126+
if !MOA._is_scalar_status_optimal(model)
127+
MOA._warn_on_nonfinite_anti_ideal(algorithm, MOI.MIN_SENSE, i)
128+
return status, nothing
129+
end
126130
β̄ = MOI.get(model.inner, MOI.ObjectiveValue())
127131
@info "β̄: $(β̄)"
128132
X, Y = MOA._compute_point(model, variables, model.f)
@@ -134,9 +138,6 @@ function MOA.minimize_multiobjective!(
134138
@info "IPS: $(IPS)"
135139
@info "OPS: $(OPS)"
136140
H = _halfspaces(IPS)
137-
if count == 10
138-
break
139-
end
140141
end
141142
MOI.delete.(model.inner, f_constraints)
142143
MOI.delete.(model.inner, u_constraints)

test/algorithms/Sandwiching.jl

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,9 @@ function run_tests()
2323
return
2424
end
2525

26-
# From International Doctoral School Algorithmic Decision Theory: MCDA and MOO
27-
# Lecture 2: Multiobjective Linear Programming
28-
# Matthias Ehrgott
29-
# Department of Engineering Science, The University of Auckland, New Zealand
30-
# Laboratoire d’Informatique de Nantes Atlantique, CNRS, Universit´e de Nantes, France
31-
function test_molp()
32-
C = Float64[3 1; -1 -2]
26+
function _test_molp(C, A, b, results, sense)
3327
p = size(C, 1)
34-
A = Float64[0 1; 3 -1]
3528
m, n = size(A)
36-
b = Float64[3, 6]
3729
model = MOA.Optimizer(HiGHS.Optimizer)
3830
MOI.set(model, MOA.Algorithm(), MOA.Sandwiching(0.0))
3931
MOI.set(model, MOI.Silent(), true)
@@ -56,20 +48,15 @@ function test_molp()
5648
],
5749
zeros(p),
5850
)
59-
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
51+
MOI.set(model, MOI.ObjectiveSense(), sense)
6052
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
6153
MOI.optimize!(model)
6254
N = MOI.get(model, MOI.ResultCount())
63-
solutions = reverse([
55+
solutions = sort([
6456
MOI.get(model, MOI.VariablePrimal(i), x) =>
6557
MOI.get(model, MOI.ObjectiveValue(i)) for i in 1:N
6658
])
67-
results = reverse([
68-
[0.0, 0.0] => [0.0, 0.0],
69-
[0.0, 3.0] => [3.0, -6.0],
70-
[3.0, 3.0] => [12.0, -9.0],
71-
])
72-
@test length(solutions) == length(results)
59+
@test N == length(results)
7360
for (sol, res) in zip(solutions, results)
7461
x_sol, y_sol = sol
7562
x_res, y_res = res
@@ -79,6 +66,41 @@ function test_molp()
7966
return
8067
end
8168

69+
# From International Doctoral School Algorithmic Decision Theory: MCDA and MOO
70+
# Lecture 2: Multiobjective Linear Programming
71+
# Matthias Ehrgott
72+
# Department of Engineering Science, The University of Auckland, New Zealand
73+
# Laboratoire d’Informatique de Nantes Atlantique, CNRS, Universit´e de Nantes, France
74+
function test_molp_1()
75+
C = Float64[3 1; -1 -2]
76+
A = Float64[0 1; 3 -1]
77+
b = Float64[3, 6]
78+
results = sort([
79+
[0.0, 0.0] => [0.0, 0.0],
80+
[0.0, 3.0] => [3.0, -6.0],
81+
[3.0, 3.0] => [12.0, -9.0],
82+
])
83+
sense = MOI.MIN_SENSE
84+
return _test_molp(C, A, b, results, sense)
85+
end
86+
87+
# From Civil and Environmental Systems Engineering
88+
# Chapter 5 Exercise 5.A.3 A graphical Interpretation of Noninferiority
89+
function test_molp_2()
90+
C = Float64[3 -2; -1 2]
91+
A = Float64[-4 -8; 3 -6; 4 -2; 1 0; -1 3; -2 4; -6 3]
92+
b = Float64[-8, 6, 14, 6, 15, 18, 9]
93+
results = sort([
94+
[1.0, 5.0] => [-7.0, 9.0], # not sure about this
95+
[3.0, 6.0] => [-3.0, 9.0],
96+
[4.0, 1.0] => [10.0, -2.0],
97+
[6.0, 5.0] => [8.0, 4.0],
98+
[6.0, 7.0] => [4.0, 8.0],
99+
])
100+
sense = MOI.MAX_SENSE
101+
return _test_molp(C, A, b, results, sense)
102+
end
103+
82104
end # TestSandwiching
83105

84106
TestSandwiching.run_tests()

0 commit comments

Comments
 (0)