Skip to content

Commit 0e42f97

Browse files
authored
Tidy src/solution.jl (#582)
1 parent 946d18d commit 0e42f97

File tree

2 files changed

+72
-87
lines changed

2 files changed

+72
-87
lines changed

src/solution.jl

Lines changed: 52 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,88 @@
11
function add_variables!(model, var::AbstractVariable)
2-
var.id_hash == objectid(:constant) &&
3-
error("Internal error: constant used as variable")
4-
return if sign(var) == ComplexSign()
2+
if sign(var) == ComplexSign()
53
L = MOI.add_variables(model, length(var))
64
R = MOI.add_variables(model, length(var))
75
return (L, R)
8-
else
9-
return MOI.add_variables(model, length(var))
106
end
7+
return MOI.add_variables(model, length(var))
118
end
129

1310
"""
14-
solve!(problem::Problem, optimizer_factory;
11+
solve!(
12+
problem::Problem,
13+
optimizer_factory;
1514
silent_solver = false,
1615
)
1716
18-
Solves the problem, populating `problem.optval` with the optimal value,
19-
as well as the values of the variables (accessed by [`evaluate`](@ref))
20-
and constraint duals (accessed by `cons.dual`), where applicable.
17+
Solves the problem, populating `problem.optval` with the optimal value, as well
18+
as the values of the variables (accessed by [`evaluate`](@ref)) and constraint
19+
duals (accessed by `cons.dual`), where applicable.
20+
2121
Optional keyword arguments:
22-
* `silent_solver`: whether the solver should be silent (and not emit output or logs) during the solution process.
22+
23+
* `silent_solver`: whether the solver should be silent (and not emit output or
24+
logs) during the solution process.
2325
"""
2426
function solve!(p::Problem, optimizer_factory; silent_solver = false)
25-
context = Context(p, optimizer_factory)
26-
model = context.model
27-
28-
if silent_solver
29-
MOI.set(model, MOI.Silent(), true)
30-
end
31-
32-
# check DCPness
3327
if problem_vexity(p) in (ConcaveVexity(), NotDcp())
3428
throw(DCPViolationError())
3529
end
36-
30+
context = Context(p, optimizer_factory)
31+
if silent_solver
32+
MOI.set(context.model, MOI.Silent(), true)
33+
end
3734
if context.detected_infeasible_during_formulation[]
3835
p.status = MOI.INFEASIBLE
3936
else
40-
MOI.optimize!(model)
41-
p.status = MOI.get(model, MOI.TerminationStatus())
37+
MOI.optimize!(context.model)
38+
p.status = MOI.get(context.model, MOI.TerminationStatus())
4239
end
43-
44-
p.model = model
45-
40+
p.model = context.model
4641
if p.status != MOI.OPTIMAL
4742
@warn "Problem wasn't solved optimally" status = p.status
4843
end
49-
50-
dual_status = MOI.get(model, MOI.DualStatus())
51-
primal_status = MOI.get(model, MOI.PrimalStatus())
52-
53-
var_to_indices = context.var_id_to_moi_indices
54-
id_to_variables = context.id_to_variables
55-
56-
if primal_status != MOI.NO_SOLUTION
57-
for (id, var_indices) in var_to_indices
58-
var = id_to_variables[id]
59-
vexity(var) == ConstVexity() && continue
60-
if var_indices isa Tuple
61-
vectorized_value_re =
62-
MOI.get(model, MOI.VariablePrimal(), var_indices[1])
63-
vectorized_value_im =
64-
MOI.get(model, MOI.VariablePrimal(), var_indices[2])
65-
set_value!(
66-
var,
67-
unpackvec(vectorized_value_re, size(var), false) +
68-
im * unpackvec(vectorized_value_im, size(var), false),
69-
)
70-
else
71-
vectorized_value =
72-
MOI.get(model, MOI.VariablePrimal(), var_indices)
73-
set_value!(
74-
var,
75-
unpackvec(
76-
vectorized_value,
77-
size(var),
78-
iscomplex(sign(var)),
79-
),
80-
)
81-
end
82-
end
83-
else
84-
for (id, var_indices) in var_to_indices
85-
var = id_to_variables[id]
86-
vexity(var) == ConstVexity() && continue
44+
primal_status = MOI.get(context.model, MOI.PrimalStatus())
45+
for (id, indices) in context.var_id_to_moi_indices
46+
var = context.id_to_variables[id]
47+
if vexity(var) == ConstVexity()
48+
continue # Fixed variable
49+
elseif primal_status == MOI.NO_SOLUTION
8750
set_value!(var, nothing)
51+
elseif indices isa Tuple # Complex-valued variable
52+
primal_re = MOI.get(p.model, MOI.VariablePrimal(), indices[1])
53+
primal_im = MOI.get(p.model, MOI.VariablePrimal(), indices[2])
54+
set_value!(
55+
var,
56+
_unpack_vector(primal_re, size(var)) +
57+
im * _unpack_vector(primal_im, size(var)),
58+
)
59+
else
60+
@assert !iscomplex(sign(var))
61+
primal = MOI.get(p.model, MOI.VariablePrimal(), indices)
62+
set_value!(var, _unpack_vector(primal, size(var)))
8863
end
8964
end
90-
91-
if dual_status != MOI.NO_SOLUTION
92-
for (constr, MOI_constr_indices) in pairs(context.constr_to_moi_inds)
93-
populate_dual!(model, constr, MOI_constr_indices)
94-
end
95-
else
96-
# Empty duals
97-
for constr in keys(context.constr_to_moi_inds)
98-
constr.dual = nothing
65+
dual_status = MOI.get(context.model, MOI.DualStatus())
66+
for (c, indices) in context.constr_to_moi_inds
67+
if dual_status == MOI.NO_SOLUTION
68+
c.dual = nothing
69+
else
70+
populate_dual!(context.model, c, indices)
9971
end
10072
end
101-
102-
return nothing
73+
return
10374
end
10475

105-
function populate_dual!(model::MOI.ModelLike, constr::Constraint, ::Nothing)
106-
constr.dual = nothing
107-
return nothing
76+
function populate_dual!(::MOI.ModelLike, c::Constraint, ::Nothing)
77+
# If the Constraint does not support `populate_dual!` it must return
78+
# `nothing` from `_add_constraint!`.
79+
c.dual = nothing
80+
return
10881
end
10982

110-
# This is type unstable!
111-
function unpackvec(v::AbstractVector, size::Tuple{Int,Int}, iscomplex::Bool)
112-
if iscomplex && length(v) == 2
113-
return v[1] + im * v[2]
114-
elseif iscomplex
115-
l = length(v) ÷ 2
116-
# use views?
117-
return reshape(v[1:l], size) + im * reshape(v[l+1:end], size)
118-
elseif length(v) == 1
83+
function _unpack_vector(v::AbstractVector, size::Tuple{Int,Int})
84+
if length(v) == 1
11985
return v[]
120-
else
121-
return reshape(v, size)
12286
end
87+
return reshape(v, size)
12388
end

test/test_utilities.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,26 @@ function test_tree_interface()
11361136
return
11371137
end
11381138

1139+
function test_multiple_constraint_dual()
1140+
x = Variable()
1141+
c = x >= 1
1142+
p = minimize(x, [c, c])
1143+
solve!(p, SCS.Optimizer)
1144+
@test isapprox(c.dual, 1.0; atol = 1e-5)
1145+
return
1146+
end
1147+
1148+
function test_fixed_variable_value()
1149+
x = Variable()
1150+
y = Variable()
1151+
fix!(x, 2.0)
1152+
p = minimize(y, x + y >= 1)
1153+
solve!(p, SCS.Optimizer)
1154+
@test isapprox(x.value, 2.0; atol = 1e-5)
1155+
@test isapprox(y.value, -1.0; atol = 1e-5)
1156+
return
1157+
end
1158+
11391159
function test_scalar_fn_constant_objective()
11401160
x = Variable()
11411161
p = minimize(2.1, [x >= 1])

0 commit comments

Comments
 (0)