Skip to content

Commit be91d02

Browse files
authored
Various fixes to KirlikSayin (#108)
1 parent ed0f348 commit be91d02

File tree

2 files changed

+67
-34
lines changed

2 files changed

+67
-34
lines changed

src/algorithms/KirlikSayin.jl

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ function optimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
9090
model.ideal_point .*= -1
9191
return status, solutions
9292
end
93+
@assert sense == MOI.MIN_SENSE
9394
solutions = SolutionPoint[]
9495
# Problem with p objectives.
9596
# Set k = 1, meaning the nondominated points will get projected
@@ -99,68 +100,62 @@ function optimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
99100
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
100101
n = MOI.output_dimension(model.f)
101102
yI, yN = zeros(n), zeros(n)
102-
δ = sense == MOI.MIN_SENSE ? -1 : 1
103+
# This tolerance is really important!
104+
δ = 1.0
103105
scalars = MOI.Utilities.scalarize(model.f)
104106
# Ideal and Nadir point estimation
105107
for (i, f_i) in enumerate(scalars)
108+
# Ideal point
106109
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f_i)}(), f_i)
107-
MOI.set(model.inner, MOI.ObjectiveSense(), sense)
108110
MOI.optimize!(model.inner)
109111
status = MOI.get(model.inner, MOI.TerminationStatus())
110112
if !_is_scalar_status_optimal(status)
111113
return status, nothing
112114
end
113115
_, Y = _compute_point(model, variables, f_i)
114-
yI[i] = Y + 1
115-
model.ideal_point[i] = Y
116-
MOI.set(
117-
model.inner,
118-
MOI.ObjectiveSense(),
119-
sense == MOI.MIN_SENSE ? MOI.MAX_SENSE : MOI.MIN_SENSE,
120-
)
116+
model.ideal_point[i] = yI[i] = Y
117+
# Nadir point
118+
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
121119
MOI.optimize!(model.inner)
122120
status = MOI.get(model.inner, MOI.TerminationStatus())
123121
if !_is_scalar_status_optimal(status)
124-
_warn_on_nonfinite_anti_ideal(algorithm, sense, i)
122+
# Repair ObjectiveSense before exiting
123+
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
124+
_warn_on_nonfinite_anti_ideal(algorithm, MOI.MIN_SENSE, i)
125125
return status, nothing
126126
end
127127
_, Y = _compute_point(model, variables, f_i)
128-
yN[i] = Y
128+
yN[i] = Y + δ
129+
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
129130
end
130-
# Reset the sense after modifying it.
131-
MOI.set(model.inner, MOI.ObjectiveSense(), sense)
132131
L = [_Rectangle(_project(yI, k), _project(yN, k))]
133-
SetType = ifelse(
134-
sense == MOI.MIN_SENSE,
135-
MOI.LessThan{Float64},
136-
MOI.GreaterThan{Float64},
137-
)
138132
status = MOI.OPTIMAL
139133
while !isempty(L)
140134
if _time_limit_exceeded(model, start_time)
141-
status = MOI.TIME_LIMIT
142-
break
135+
return MOI.TIME_LIMIT, solutions
143136
end
144-
Rᵢ = L[argmax([_volume(Rᵢ, _project(yI, k)) for Rᵢ in L])]
145-
lᵢ, uᵢ = Rᵢ.l, Rᵢ.u
137+
max_volume_index = argmax([_volume(Rᵢ, _project(yI, k)) for Rᵢ in L])
138+
uᵢ = L[max_volume_index].u
146139
# Solving the first stage model: P_k(ε)
147-
# Set ε := uᵢ
148-
ε = insert!(copy(uᵢ), k, 0.0)
149-
ε_constraints = Any[]
140+
# minimize: f_1(x)
141+
# s.t.: f_i(x) <= u_i - δ
142+
@assert k == 1
150143
MOI.set(
151144
model.inner,
152145
MOI.ObjectiveFunction{typeof(scalars[k])}(),
153146
scalars[k],
154147
)
148+
ε_constraints = Any[]
155149
for (i, f_i) in enumerate(scalars)
156-
if i != k
157-
ci = MOI.Utilities.normalize_and_add_constraint(
158-
model.inner,
159-
f_i,
160-
SetType(ε[i] + δ),
161-
)
162-
push!(ε_constraints, ci)
150+
if i == k
151+
continue
163152
end
153+
ci = MOI.Utilities.normalize_and_add_constraint(
154+
model.inner,
155+
f_i,
156+
MOI.LessThan{Float64}(uᵢ[i-1] - δ),
157+
)
158+
push!(ε_constraints, ci)
164159
end
165160
MOI.optimize!(model.inner)
166161
if !_is_scalar_status_optimal(model)
@@ -171,7 +166,7 @@ function optimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
171166
zₖ = MOI.get(model.inner, MOI.ObjectiveValue())
172167
# Solving the second stage model: Q_k(ε, zₖ)
173168
# Set objective sum(model.f)
174-
sum_f = sum(1.0 * s for s in scalars)
169+
sum_f = MOI.Utilities.operate(+, Float64, scalars...)
175170
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(sum_f)}(), sum_f)
176171
# Constraint to eliminate weak dominance
177172
zₖ_constraint = MOI.Utilities.normalize_and_add_constraint(

test/algorithms/KirlikSayin.jl

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ function test_infeasible()
511511
MOI.add_constraint.(model, x, MOI.GreaterThan(0.0))
512512
MOI.add_constraint(model, 1.0 * x[1] + 1.0 * x[2], MOI.LessThan(-1.0))
513513
f = MOI.Utilities.operate(vcat, Float64, 1.0 .* x...)
514+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
514515
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
515516
MOI.optimize!(model)
516517
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
@@ -603,7 +604,44 @@ function test_vector_of_variables_objective()
603604
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
604605
MOI.add_constraint(model, sum(1.0 * xi for xi in x), MOI.GreaterThan(1.0))
605606
MOI.optimize!(model)
606-
MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
607+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
608+
return
609+
end
610+
611+
function test_issue_105()
612+
cost = [100.0, 120.0, 150.0, 110.0, 200.0, 170.0]
613+
time = [8.0, 3.0, 4.0, 2.0, 5.0, 4.0]
614+
capacity = [10.0, 8.0]
615+
demand = [5.0, 8.0, 5.0]
616+
m, n = 2, 3
617+
model = MOI.instantiate(; with_bridge_type = Float64) do
618+
return MOA.Optimizer(HiGHS.Optimizer)
619+
end
620+
MOI.set(model, MOA.Algorithm(), MOA.KirlikSayin())
621+
MOI.set(model, MOI.Silent(), true)
622+
x = MOI.add_variables(model, m * n)
623+
MOI.add_constraint.(model, x, MOI.GreaterThan(0.0))
624+
MOI.add_constraint.(model, x, MOI.Integer())
625+
X = reshape(x, m, n)
626+
for i in 1:m
627+
f_i = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, X[i, :]), 0.0)
628+
MOI.add_constraint(model, f_i, MOI.LessThan(capacity[i]))
629+
end
630+
for j in 1:n
631+
f_j = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, X[:, j]), 0.0)
632+
MOI.add_constraint(model, f_j, MOI.EqualTo(demand[j]))
633+
end
634+
f = MOI.Utilities.operate(
635+
vcat,
636+
Float64,
637+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(cost, x), 0.0),
638+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(time, x), 0.0),
639+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, x), 0.0),
640+
)
641+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
642+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
643+
MOI.optimize!(model)
644+
@test MOI.get(model, MOI.ResultCount()) == 6
607645
return
608646
end
609647

0 commit comments

Comments
 (0)