Skip to content

Commit ce775a6

Browse files
committed
Refactor into GenericConstraint
1 parent 1f4a7bd commit ce775a6

File tree

4 files changed

+93
-147
lines changed

4 files changed

+93
-147
lines changed
Lines changed: 26 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,40 @@
1-
mutable struct GreaterThanConstraint <: Constraint
2-
lhs::AbstractExpr
3-
rhs::AbstractExpr
4-
size::Tuple{Int,Int}
5-
dual::Union{Value,Nothing}
1+
head(io::IO, ::MOI.Nonnegatives) = print(io, "nonneg")
62

7-
function GreaterThanConstraint(lhs::AbstractExpr, rhs::AbstractExpr)
8-
if sign(lhs) == ComplexSign() || sign(rhs) == ComplexSign()
9-
error(
10-
"Cannot create inequality constraint between expressions of sign $(sign(lhs)) and $(sign(rhs))",
11-
)
12-
end
13-
if lhs.size == rhs.size || lhs.size == (1, 1)
14-
sz = rhs.size
15-
if lhs.size == (1, 1) && rhs.size != (1, 1)
16-
lhs = lhs * ones(rhs.size)
17-
end
18-
elseif rhs.size == (1, 1)
19-
sz = lhs.size
20-
if rhs.size == (1, 1) && lhs.size != (1, 1)
21-
rhs = rhs * ones(lhs.size)
22-
end
23-
else
24-
error(
25-
"Cannot create inequality constraint between expressions of size $(lhs.size) and $(rhs.size)",
26-
)
27-
end
28-
return new(lhs, rhs, sz, nothing)
29-
end
3+
function is_feasible(f, ::MOI.Nonnegatives, tol)
4+
return all(f .>= tol)
305
end
316

32-
head(io::IO, ::GreaterThanConstraint) = print(io, "")
33-
34-
function vexity(c::GreaterThanConstraint)
35-
vex = -vexity(c.lhs) + (vexity(c.rhs))
36-
if vex == ConcaveVexity()
7+
function vexity(vex, ::MOI.Nonnegatives)
8+
if vex == ConvexVexity()
379
return NotDcp()
3810
end
3911
return vex
4012
end
4113

42-
function _add_constraint!(
43-
context::Context{T},
44-
c::GreaterThanConstraint,
45-
) where {T}
46-
f = conic_form!(context, c.lhs - c.rhs)
47-
if f isa AbstractVector
48-
if !all(f .>= -CONSTANT_CONSTRAINT_TOL[])
49-
@warn "Constant constraint is violated"
50-
context.detected_infeasible_during_formulation[] = true
14+
function Base.:>=(lhs::AbstractExpr, rhs::AbstractExpr)
15+
if sign(lhs) == ComplexSign() || sign(rhs) == ComplexSign()
16+
error(
17+
"Cannot create inequality constraint between expressions of sign $(sign(lhs)) and $(sign(rhs))",
18+
)
19+
end
20+
if lhs.size == rhs.size || lhs.size == (1, 1)
21+
sz = rhs.size
22+
if lhs.size == (1, 1) && rhs.size != (1, 1)
23+
lhs = lhs * ones(rhs.size)
5124
end
52-
return
25+
elseif rhs.size == (1, 1)
26+
sz = lhs.size
27+
if rhs.size == (1, 1) && lhs.size != (1, 1)
28+
rhs = rhs * ones(lhs.size)
29+
end
30+
else
31+
error(
32+
"Cannot create inequality constraint between expressions of size $(lhs.size) and $(rhs.size)",
33+
)
5334
end
54-
set = MOI.Nonnegatives(MOI.output_dimension(f))
55-
context.constr_to_moi_inds[c] = MOI_add_constraint(context.model, f, set)
56-
return
35+
return GenericConstraint{MOI.Nonnegatives}(lhs - rhs)
5736
end
5837

59-
Base.:>=(lhs::AbstractExpr, rhs::AbstractExpr) = GreaterThanConstraint(lhs, rhs)
60-
6138
Base.:>=(lhs::AbstractExpr, rhs::Value) = >=(lhs, constant(rhs))
6239

63-
Base.:>=(lhs::Value, rhs::AbstractExpr) = >=(constant(lhs), rhs)
64-
65-
function populate_dual!(model::MOI.ModelLike, c::GreaterThanConstraint, indices)
66-
ret = MOI.get(model, MOI.ConstraintDual(), indices)
67-
c.dual = output(reshape(ret, c.size))
68-
return
69-
end
40+
Base.:>=(lhs::Value, rhs::AbstractExpr) = >=(constant(lhs), rhs)

src/constraints/LessThanConstraint.jl

Lines changed: 25 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,40 @@
1-
mutable struct LessThanConstraint <: Constraint
2-
lhs::AbstractExpr
3-
rhs::AbstractExpr
4-
size::Tuple{Int,Int}
5-
dual::Union{Value,Nothing}
1+
head(io::IO, ::MOI.Nonpositives) = print(io, "nonpos")
62

7-
function LessThanConstraint(lhs::AbstractExpr, rhs::AbstractExpr)
8-
if sign(lhs) == ComplexSign() || sign(rhs) == ComplexSign()
9-
error(
10-
"Cannot create inequality constraint between expressions of sign $(sign(lhs)) and $(sign(rhs))",
11-
)
12-
end
13-
if lhs.size == rhs.size || lhs.size == (1, 1)
14-
sz = rhs.size
15-
if lhs.size == (1, 1) && rhs.size != (1, 1)
16-
lhs = lhs * ones(rhs.size)
17-
end
18-
elseif rhs.size == (1, 1)
19-
sz = lhs.size
20-
if rhs.size == (1, 1) && lhs.size != (1, 1)
21-
rhs = rhs * ones(lhs.size)
22-
end
23-
else
24-
error(
25-
"Cannot create inequality constraint between expressions of size $(lhs.size) and $(rhs.size)",
26-
)
27-
end
28-
return new(lhs, rhs, sz, nothing)
29-
end
3+
function is_feasible(f, ::MOI.Nonpositives, tol)
4+
return all(f .<= -tol)
305
end
316

32-
head(io::IO, ::LessThanConstraint) = print(io, "")
33-
34-
function vexity(c::LessThanConstraint)
35-
vex = vexity(c.lhs) + (-vexity(c.rhs))
7+
function vexity(vex, ::MOI.Nonpositives)
368
if vex == ConcaveVexity()
379
return NotDcp()
3810
end
3911
return vex
4012
end
4113

42-
function _add_constraint!(context::Context{T}, lt::LessThanConstraint) where {T}
43-
f = conic_form!(context, lt.rhs - lt.lhs)
44-
if f isa AbstractVector
45-
# a trivial constraint without variables like `5 <= 0`
46-
if !all(f .>= -CONSTANT_CONSTRAINT_TOL[])
47-
@warn "Constant constraint is violated"
48-
context.detected_infeasible_during_formulation[] = true
14+
function Base.:<=(lhs::AbstractExpr, rhs::AbstractExpr)
15+
if sign(lhs) == ComplexSign() || sign(rhs) == ComplexSign()
16+
error(
17+
"Cannot create inequality constraint between expressions of sign $(sign(lhs)) and $(sign(rhs))",
18+
)
19+
end
20+
if lhs.size == rhs.size || lhs.size == (1, 1)
21+
sz = rhs.size
22+
if lhs.size == (1, 1) && rhs.size != (1, 1)
23+
lhs = lhs * ones(rhs.size)
4924
end
50-
return
25+
elseif rhs.size == (1, 1)
26+
sz = lhs.size
27+
if rhs.size == (1, 1) && lhs.size != (1, 1)
28+
rhs = rhs * ones(lhs.size)
29+
end
30+
else
31+
error(
32+
"Cannot create inequality constraint between expressions of size $(lhs.size) and $(rhs.size)",
33+
)
5134
end
52-
set = MOI.Nonnegatives(MOI.output_dimension(f))
53-
context.constr_to_moi_inds[lt] = MOI_add_constraint(context.model, f, set)
54-
return
35+
return GenericConstraint{MOI.Nonpositives}(lhs - rhs)
5536
end
5637

57-
Base.:<=(lhs::AbstractExpr, rhs::AbstractExpr) = LessThanConstraint(lhs, rhs)
58-
5938
Base.:<=(lhs::AbstractExpr, rhs::Value) = <=(lhs, constant(rhs))
6039

61-
Base.:<=(lhs::Value, rhs::AbstractExpr) = <=(constant(lhs), rhs)
62-
63-
function populate_dual!(model::MOI.ModelLike, c::LessThanConstraint, indices)
64-
# FIXME(odow): this dual is the 'wrong' sign, because Convex implements
65-
# rhs - lhs in Nonnegatives for a LessThanConstraint, instead of
66-
# lhs - rhs in Nonpositives.
67-
# Should we fix this?
68-
ret = MOI.get(model, MOI.ConstraintDual(), indices)
69-
c.dual = output(reshape(ret, c.size))
70-
return
71-
end
40+
Base.:<=(lhs::Value, rhs::AbstractExpr) = <=(constant(lhs), rhs)

src/constraints/PositiveSemidefiniteConeConstraint.jl

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,28 @@
1-
const PositiveSemidefiniteConeConstraint = Constraint{MOI.PositiveSemidefiniteConeTriangle}
21
function set_with_size(::Type{MOI.PositiveSemidefiniteConeTriangle}, sz::Tuple{Int,Int})
32
if sz[1] != sz[2]
43
error("Positive semidefinite expressions must be square")
54
end
65
return MOI.PositiveSemidefiniteConeTriangle(sz[1])
76
end
87

9-
head(io::IO, ::PositiveSemidefiniteConeConstraint) = print(io, "sdp")
8+
head(io::IO, ::MOI.PositiveSemidefiniteConeTriangle) = print(io, "sdp")
109

11-
AbstractTrees.children(c::PositiveSemidefiniteConeConstraint) = (c.child,)
12-
13-
function vexity(c::PositiveSemidefiniteConeConstraint)
14-
if !(vexity(c.child) in (AffineVexity(), ConstVexity()))
10+
function vexity(vex, ::MOI.PositiveSemidefiniteConeTriangle)
11+
if !(vex in (AffineVexity(), ConstVexity()))
1512
return NotDcp()
1613
end
1714
return AffineVexity()
1815
end
1916

20-
function _add_constraint!(
21-
context::Context,
22-
c::PositiveSemidefiniteConeConstraint,
23-
)
24-
if vexity(c.child) == ConstVexity()
25-
x = evaluate(c.child)
26-
tol = CONSTANT_CONSTRAINT_TOL[]
27-
if !(x transpose(x))
28-
@warn "constant SDP constraint is violated"
29-
context.detected_infeasible_during_formulation[] = true
30-
elseif evaluate(LinearAlgebra.eigmin(c.child)) < -tol
31-
@warn "constant SDP constraint is violated"
32-
context.detected_infeasible_during_formulation[] = true
33-
end
34-
return
17+
function is_feasible(x, ::MOI.PositiveSemidefiniteConeTriangle, tol)
18+
if !(x transpose(x))
19+
@warn "constant SDP constraint is violated"
20+
return false
21+
elseif evaluate(LinearAlgebra.eigmin(c.child)) < -tol
22+
@warn "constant SDP constraint is violated"
23+
return false
3524
end
36-
f = conic_form!(context, c.child)
37-
set = MOI.PositiveSemidefiniteConeSquare(c.size[1])
38-
context.constr_to_moi_inds[c] = MOI_add_constraint(context.model, f, set)
39-
return
40-
end
41-
42-
function populate_dual!(
43-
model::MOI.ModelLike,
44-
c::PositiveSemidefiniteConeConstraint,
45-
indices,
46-
)
47-
dual = MOI.get(model, MOI.ConstraintDual(), indices)
48-
c.dual = output(reshape(dual, c.size))
49-
return
25+
return true
5026
end
5127

5228
function LinearAlgebra.isposdef(x::AbstractExpr)

src/expressions.jl

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828

2929
abstract type AbstractExpr end
3030

31-
struct Constraint{S<:MOI.AbstractSet}
31+
abstract type Constraint end
32+
33+
struct GenericConstraint{S<:MOI.AbstractSet}
3234
child::AbstractExpr
3335
set::S
3436
dual::ValueOrNothing
@@ -40,6 +42,34 @@ struct Constraint{S<:MOI.AbstractSet}
4042
end
4143
end
4244

45+
head(io::IO, c::GenericConstraint) = head(io, c.set)
46+
47+
AbstractTrees.children(c::GenericConstraint) = (c.child,)
48+
49+
function _add_constraint!(
50+
context::Context,
51+
c::GenericConstraint,
52+
)
53+
if vexity(c.child) == ConstVexity()
54+
x = evaluate(c.child)
55+
if !is_feasible(x, c.set, CONSTANT_CONSTRAINT_TOL[])
56+
context.detected_infeasible_during_formulation[] = true
57+
end
58+
return
59+
end
60+
f = conic_form!(context, c.child)
61+
set = MOI.PositiveSemidefiniteConeSquare(c.size[1])
62+
context.constr_to_moi_inds[c] = MOI_add_constraint(context.model, f, set)
63+
return
64+
end
65+
66+
67+
function populate_dual!(model::MOI.ModelLike, c::GenericConstraint, indices)
68+
ret = MOI.get(model, MOI.ConstraintDual(), indices)
69+
c.dual = output(reshape(ret, c.size))
70+
return
71+
end
72+
4373
const Value = Union{Number,AbstractArray}
4474

4575
# We commandeer `==` to create a constraint.

0 commit comments

Comments
 (0)