Skip to content

Commit a679f64

Browse files
blegatodow
andauthored
Refactor some constraint into GenericConstraint (#590)
* Refactor Constraint * Refactor into GenericConstraint * up * Fix tests * Fixes * Fix char * Fixes * Fix format * Apply suggestions from code review * Update * Relocate * Update * Add support for Zeros * Add SecondOrderCone support * Update * Update changelog.md --------- Co-authored-by: Oscar Dowson <[email protected]> Co-authored-by: odow <[email protected]>
1 parent a55c0a4 commit a679f64

11 files changed

+314
-393
lines changed

docs/src/changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ changes.
2727
dual variable associated with such constraints is now reversed in sign**.
2828
(Following the convention in MathOptInterface, the dual of `a <= b` is
2929
always negative, regardless of optimization sense.) (#593)
30+
* The structs `LessThanConstraint`, `GreaterThanConstraint`, `EqualToConstraint`
31+
`SecondOrderConeConstraint` and `PostiveSemidefinteConeConstraint` have
32+
been replaced by `GenericConstraint{S}` where `S<:MOI.AbstractSet` (#590)
3033
* The syntaxes `dot(*)`, `dot(/)` and `dot(^)` have been removed in favor of
3134
explicit broadcasting (`x .* y`, `x ./ y`, and `x .^ y`). These were (mild)
3235
type piracy. In addition, `vecdot(x,y)` has been removed. Call

src/atoms/second_order_cone/QolElemAtom.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@ function new_conic_form!(context::Context{T}, q::QolElemAtom) where {T}
3030
x, y = q.children
3131
t = Variable(x.size)
3232
for i in 1:length(x)
33-
add_constraint!(
34-
context,
35-
SecondOrderConeConstraint(y[i] + t[i], y[i] - t[i], 2 * x[i]),
36-
)
33+
f = vcat(y[i] + t[i], y[i] - t[i], 2 * x[i])
34+
add_constraint!(context, GenericConstraint{MOI.SecondOrderCone}(f))
3735
end
3836
add_constraint!(context, y >= 0)
3937
return conic_form!(context, t)

src/atoms/second_order_cone/QuadOverLinAtom.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ end
3030
function new_conic_form!(context::Context, q::QuadOverLinAtom)
3131
t = Variable()
3232
x, y = q.children
33-
add_constraint!(context, SecondOrderConeConstraint(y + t, y - t, 2 * x))
33+
f = vcat(y + t, y - t, 2 * x)
34+
add_constraint!(context, GenericConstraint{MOI.SecondOrderCone}(f))
3435
add_constraint!(context, y >= 0)
3536
return conic_form!(context, t)
3637
end

src/constraints/EqualToConstraint.jl

Lines changed: 0 additions & 68 deletions
This file was deleted.

src/constraints/GenericConstraint.jl

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
mutable struct GenericConstraint{S<:MOI.AbstractSet} <: Constraint
2+
child::AbstractExpr
3+
set::S
4+
dual::Union{Value,Nothing}
5+
6+
function GenericConstraint(child::AbstractExpr, set::MOI.AbstractSet)
7+
return new{typeof(set)}(child, set, nothing)
8+
end
9+
end
10+
11+
function GenericConstraint{S}(child::AbstractExpr) where {S<:MOI.AbstractSet}
12+
return GenericConstraint(child, set_with_size(S, size(child)))
13+
end
14+
15+
head(io::IO, c::GenericConstraint) = head(io, c.set)
16+
17+
# A default fallback that skips the feasibiltiy check.
18+
is_feasible(f, ::MOI.AbstractSet, tol) = true
19+
20+
AbstractTrees.children(c::GenericConstraint) = (c.child,)
21+
22+
vexity(c::GenericConstraint) = vexity(vexity(c.child), c.set)
23+
24+
function _add_constraint!(context::Context, c::GenericConstraint)
25+
if vexity(c.child) == ConstVexity()
26+
x = evaluate(c.child)
27+
if !is_feasible(x, c.set, CONSTANT_CONSTRAINT_TOL[])
28+
context.detected_infeasible_during_formulation[] = true
29+
end
30+
return
31+
end
32+
f = conic_form!(context, c.child)
33+
context.constr_to_moi_inds[c] = MOI_add_constraint(context.model, f, c.set)
34+
return
35+
end
36+
37+
function populate_dual!(model::MOI.ModelLike, c::GenericConstraint, indices)
38+
ret = MOI.get(model, MOI.ConstraintDual(), indices)
39+
c.dual = output(reshape(ret, c.child.size))
40+
return
41+
end
42+
43+
function populate_dual!(
44+
model::MOI.ModelLike,
45+
c::GenericConstraint,
46+
indices::NTuple{2},
47+
)
48+
re = MOI.get(model, MOI.ConstraintDual(), indices[1])
49+
imag = MOI.get(model, MOI.ConstraintDual(), indices[2])
50+
c.dual = output(reshape(re + im * imag, c.child.size))
51+
return
52+
end
53+
54+
function _promote_size(lhs::AbstractExpr, rhs::AbstractExpr)
55+
if lhs.size == rhs.size || lhs.size == (1, 1)
56+
sz = rhs.size
57+
if lhs.size == (1, 1) && rhs.size != (1, 1)
58+
lhs = lhs * ones(rhs.size)
59+
end
60+
elseif rhs.size == (1, 1)
61+
sz = lhs.size
62+
if rhs.size == (1, 1) && lhs.size != (1, 1)
63+
rhs = rhs * ones(lhs.size)
64+
end
65+
else
66+
error(
67+
"Cannot create constraint between expressions of size " *
68+
"$(lhs.size) and $(rhs.size)",
69+
)
70+
end
71+
return lhs, rhs
72+
end
73+
74+
# ==============================================================================
75+
# Nonnegatives
76+
# ==============================================================================
77+
78+
function set_with_size(::Type{MOI.Nonnegatives}, sz::Tuple{Int,Int})
79+
return MOI.Nonnegatives(prod(sz))
80+
end
81+
82+
head(io::IO, ::MOI.Nonnegatives) = print(io, "")
83+
84+
is_feasible(f, ::MOI.Nonnegatives, tol) = all(f .>= -tol)
85+
86+
function vexity(vex, ::MOI.Nonnegatives)
87+
if vex == ConvexVexity()
88+
return NotDcp()
89+
elseif vex == ConcaveVexity()
90+
return ConvexVexity()
91+
end
92+
return vex
93+
end
94+
95+
function Base.:>=(lhs::AbstractExpr, rhs::AbstractExpr)
96+
if sign(lhs) == ComplexSign() || sign(rhs) == ComplexSign()
97+
error(
98+
"Cannot create constraint between expressions of sign " *
99+
"$(sign(lhs)) and $(sign(rhs))",
100+
)
101+
end
102+
lhs, rhs = _promote_size(lhs, rhs)
103+
return GenericConstraint{MOI.Nonnegatives}(lhs - rhs)
104+
end
105+
106+
Base.:>=(lhs::AbstractExpr, rhs::Value) = >=(lhs, constant(rhs))
107+
108+
Base.:>=(lhs::Value, rhs::AbstractExpr) = >=(constant(lhs), rhs)
109+
110+
# ==============================================================================
111+
# Nonnpositives
112+
# ==============================================================================
113+
114+
function set_with_size(::Type{MOI.Nonpositives}, sz::Tuple{Int,Int})
115+
return MOI.Nonpositives(prod(sz))
116+
end
117+
118+
head(io::IO, ::MOI.Nonpositives) = print(io, "")
119+
120+
is_feasible(f, ::MOI.Nonpositives, tol) = all(f .<= tol)
121+
122+
function vexity(vex, ::MOI.Nonpositives)
123+
if vex == ConcaveVexity()
124+
return NotDcp()
125+
end
126+
return vex
127+
end
128+
129+
function Base.:<=(lhs::AbstractExpr, rhs::AbstractExpr)
130+
if sign(lhs) == ComplexSign() || sign(rhs) == ComplexSign()
131+
error(
132+
"Cannot create constraint between expressions of sign " *
133+
"$(sign(lhs)) and $(sign(rhs))",
134+
)
135+
end
136+
lhs, rhs = _promote_size(lhs, rhs)
137+
return GenericConstraint{MOI.Nonpositives}(lhs - rhs)
138+
end
139+
140+
Base.:<=(lhs::AbstractExpr, rhs::Value) = <=(lhs, constant(rhs))
141+
142+
Base.:<=(lhs::Value, rhs::AbstractExpr) = <=(constant(lhs), rhs)
143+
144+
# ==============================================================================
145+
# Zeros
146+
# ==============================================================================
147+
148+
function set_with_size(::Type{MOI.Zeros}, sz::Tuple{Int,Int})
149+
return MOI.Zeros(prod(sz))
150+
end
151+
152+
head(io::IO, ::MOI.Zeros) = print(io, "==")
153+
154+
is_feasible(f, ::MOI.Zeros, tol) = all(abs.(f) .<= tol)
155+
156+
function vexity(vex, ::MOI.Zeros)
157+
if vex == ConvexVexity() || vex == ConcaveVexity()
158+
return NotDcp()
159+
end
160+
return vex
161+
end
162+
163+
function Base.:(==)(lhs::AbstractExpr, rhs::AbstractExpr)
164+
lhs, rhs = _promote_size(lhs, rhs)
165+
return GenericConstraint{MOI.Zeros}(lhs - rhs)
166+
end
167+
168+
Base.:(==)(lhs::AbstractExpr, rhs::Value) = ==(lhs, constant(rhs))
169+
170+
Base.:(==)(lhs::Value, rhs::AbstractExpr) = ==(constant(lhs), rhs)
171+
172+
# ==============================================================================
173+
# PositiveSemidefiniteConeSquare
174+
# ==============================================================================
175+
176+
function set_with_size(
177+
::Type{MOI.PositiveSemidefiniteConeSquare},
178+
sz::Tuple{Int,Int},
179+
)
180+
if sz[1] != sz[2]
181+
error("Positive semidefinite expressions must be square")
182+
end
183+
return MOI.PositiveSemidefiniteConeSquare(sz[1])
184+
end
185+
186+
head(io::IO, ::MOI.PositiveSemidefiniteConeSquare) = print(io, "sdp")
187+
188+
function vexity(vex, ::MOI.PositiveSemidefiniteConeSquare)
189+
if !(vex in (AffineVexity(), ConstVexity()))
190+
return NotDcp()
191+
end
192+
return AffineVexity()
193+
end
194+
195+
function is_feasible(x, ::MOI.PositiveSemidefiniteConeSquare, tol)
196+
return x transpose(x) && LinearAlgebra.eigmin(x) >= -tol
197+
end
198+
199+
function LinearAlgebra.isposdef(x::AbstractExpr)
200+
if iscomplex(x)
201+
return GenericConstraint{MOI.PositiveSemidefiniteConeSquare}(
202+
[real(x) -imag(x); imag(x) real(x)],
203+
)
204+
end
205+
return GenericConstraint{MOI.PositiveSemidefiniteConeSquare}(x)
206+
end
207+
208+
(x::AbstractExpr, y::AbstractExpr) = isposdef(x - y)
209+
210+
function (x::AbstractExpr, y::Value)
211+
if all(y .== 0)
212+
return isposdef(x)
213+
end
214+
return isposdef(x - constant(y))
215+
end
216+
217+
function (x::Value, y::AbstractExpr)
218+
if all(x .== 0)
219+
return isposdef(-y)
220+
end
221+
return isposdef(constant(x) - y)
222+
end
223+
224+
(x::AbstractExpr, y::AbstractExpr) = (y, x)
225+
226+
(x::Value, y::AbstractExpr) = (y, x)
227+
228+
(x::AbstractExpr, y::Value) = (y, x)
229+
230+
# ==============================================================================
231+
# SecondOrderCone
232+
# ==============================================================================
233+
234+
function set_with_size(::Type{MOI.SecondOrderCone}, sz::Tuple{Int,Int})
235+
return MOI.SecondOrderCone(prod(sz))
236+
end
237+
238+
head(io::IO, ::MOI.SecondOrderCone) = print(io, "soc")
239+
240+
function vexity(vex, ::MOI.SecondOrderCone)
241+
if !(vex == ConstVexity() || vex == AffineVexity())
242+
return NotDcp()
243+
end
244+
return ConvexVexity()
245+
end

0 commit comments

Comments
 (0)