Skip to content

Commit ba95bea

Browse files
committed
Add polynomial SAGE
1 parent 5ce4558 commit ba95bea

File tree

5 files changed

+148
-42
lines changed

5 files changed

+148
-42
lines changed

src/RelativeEntropy/RelativeEntropy.jl

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,43 @@ import MathOptInterface as MOI
66
import JuMP
77
import PolyJuMP
88

9+
abstract type AbstractAGECone <: MOI.AbstractVectorSet end
10+
11+
"""
12+
struct SignomialSAGECone <: MOI.AbstractVectorSet
13+
α::Matrix{Int}
14+
end
15+
16+
**S**ums of **A**M/**G**M **E**xponential for signomials.
17+
"""
18+
struct SignomialSAGECone <: AbstractAGECone
19+
α::Matrix{Int}
20+
end
21+
922
"""
10-
struct SAGECone <: MOI.AbstractVectorSet
23+
struct PolynomialSAGECone <: MOI.AbstractVectorSet
1124
α::Matrix{Int}
1225
end
1326
14-
**S**ums of **A**M/**G**M **E**xponential.
27+
**S**ums of **A**M/**G**M **E**xponential for polynomials.
1528
"""
16-
struct SAGECone <: MOI.AbstractVectorSet
29+
struct PolynomialSAGECone <: AbstractAGECone
1730
α::Matrix{Int}
1831
end
19-
MOI.dimension(set::SAGECone) = size(set.α, 1)
20-
Base.copy(set::SAGECone) = set
2132

22-
struct SAGESet <: PolyJuMP.PolynomialSet end
23-
JuMP.reshape_set(::SAGECone, ::PolyJuMP.PolynomialShape) = SAGESet()
33+
struct SignomialAGECone <: AbstractAGECone
34+
α::Matrix{Int}
35+
k::Int
36+
end
37+
38+
struct PolynomialAGECone <: AbstractAGECone
39+
α::Matrix{Int}
40+
k::Int
41+
end
42+
43+
MOI.dimension(set::AbstractAGECone) = size(set.α, 1)
44+
Base.copy(set::AbstractAGECone) = set
45+
2446
function _exponents_matrix(monos)
2547
α = Matrix{Int}(undef, length(monos), MP.nvariables(monos))
2648
for (i, mono) in enumerate(monos)
@@ -31,35 +53,51 @@ function _exponents_matrix(monos)
3153
end
3254
return α
3355
end
34-
JuMP.moi_set(::SAGESet, monos) = SAGECone(_exponents_matrix(monos))
3556

36-
struct AGECone <: MOI.AbstractVectorSet
37-
α::Matrix{Int}
38-
k::Int
57+
struct SignomialSAGESet <: PolyJuMP.PolynomialSet end
58+
JuMP.reshape_set(::SignomialSAGECone, ::PolyJuMP.PolynomialShape) = SignomialSAGESet()
59+
JuMP.moi_set(::SignomialSAGESet, monos) = SignomialSAGECone(_exponents_matrix(monos))
60+
61+
struct PolynomialSAGESet <: PolyJuMP.PolynomialSet end
62+
JuMP.reshape_set(::PolynomialSAGECone, ::PolyJuMP.PolynomialShape) = PolynomialSAGESet()
63+
JuMP.moi_set(::PolynomialSAGESet, monos) = PolynomialSAGECone(_exponents_matrix(monos))
64+
65+
struct SignomialAGESet{MT<:MP.AbstractMonomial} <: PolyJuMP.PolynomialSet
66+
monomial::MT
67+
end
68+
function JuMP.reshape_set(set::SignomialAGECone, shape::PolyJuMP.PolynomialShape)
69+
return SignomialAGESet(shape.monomials[set.k])
70+
end
71+
function JuMP.moi_set(set::SignomialAGESet, monos)
72+
k = findfirst(isequal(set.monomial), monos)
73+
return SignomialAGECone(_exponents_matrix(monos), k)
3974
end
40-
MOI.dimension(set::AGECone) = size(set.α, 1)
41-
Base.copy(set::AGECone) = set
4275

43-
struct AGESet{MT<:MP.AbstractMonomial} <: PolyJuMP.PolynomialSet
76+
struct PolynomialAGESet{MT<:MP.AbstractMonomial} <: PolyJuMP.PolynomialSet
4477
monomial::MT
4578
end
46-
function JuMP.reshape_set(set::AGECone, shape::PolyJuMP.PolynomialShape)
47-
return AGESet(shape.monomials[set.k])
79+
function JuMP.reshape_set(set::PolynomialAGECone, shape::PolyJuMP.PolynomialShape)
80+
return PolynomialAGESet(shape.monomials[set.k])
4881
end
49-
function JuMP.moi_set(set::AGESet, monos)
82+
function JuMP.moi_set(set::PolynomialAGESet, monos)
5083
k = findfirst(isequal(set.monomial), monos)
51-
return AGECone(_exponents_matrix(monos), k)
84+
return PolynomialAGECone(_exponents_matrix(monos), k)
5285
end
5386

5487
function setdefaults!(data::PolyJuMP.Data)
55-
PolyJuMP.setdefault!(data, PolyJuMP.NonNegPoly, SAGESet)
88+
PolyJuMP.setdefault!(data, PolyJuMP.NonNegPoly, PolynomialSAGESet)
5689
return
5790
end
5891

5992
function JuMP.build_constraint(
6093
_error::Function,
6194
p,
62-
set::Union{SAGESet,AGESet};
95+
set::Union{
96+
SignomialSAGESet,
97+
PolynomialSAGESet,
98+
SignomialAGESet,
99+
PolynomialAGESet,
100+
};
63101
kws...,
64102
)
65103
coefs = PolyJuMP.non_constant_coefficients(p)
@@ -77,7 +115,7 @@ include("bridges/sage.jl")
77115

78116
function PolyJuMP.bridges(
79117
F::Type{<:MOI.AbstractVectorFunction},
80-
::Type{<:SAGECone},
118+
::Type{<:SignomialSAGECone},
81119
)
82120
return [(SAGEBridge, PolyJuMP._coef_type(F))]
83121
end
@@ -86,9 +124,19 @@ include("bridges/age.jl")
86124

87125
function PolyJuMP.bridges(
88126
F::Type{<:MOI.AbstractVectorFunction},
89-
::Type{<:AGECone},
127+
::Type{<:SignomialAGECone},
90128
)
91129
return [(AGEBridge, PolyJuMP._coef_type(F))]
92130
end
93131

132+
include("bridges/signomial.jl")
133+
134+
function PolyJuMP.bridges(
135+
F::Type{<:MOI.AbstractVectorFunction},
136+
::Type{<:Union{PolynomialSAGECone,PolynomialAGECone}},
137+
)
138+
return [(SignomialBridge, PolyJuMP._coef_type(F))]
139+
end
140+
141+
94142
end

src/RelativeEntropy/bridges/age.jl

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ SIAM Journal on Optimization 26.2 (2016): 1147-1173.
3939
"Signomial and polynomial optimization via relative entropy and partial dualization."
4040
Mathematical Programming Computation 13 (2021): 257-295.
4141
https://arxiv.org/pdf/1907.00814.pdf
42-
43-
4442
"""
4543
struct AGEBridge{T,F,G,H} <: MOI.Bridges.Constraint.AbstractBridge
4644
k::Int
@@ -51,8 +49,8 @@ end
5149
function MOI.Bridges.Constraint.bridge_constraint(
5250
::Type{AGEBridge{T,F,G,H}},
5351
model,
54-
func::G,
55-
set::AGECone,
52+
func::H,
53+
set::SignomialAGECone,
5654
) where {T,F,G,H}
5755
m = size(set.α, 1)
5856
ν = MOI.add_variables(model, m - 1)
@@ -86,7 +84,7 @@ end
8684
function MOI.supports_constraint(
8785
::Type{<:AGEBridge{T}},
8886
::Type{<:MOI.AbstractVectorFunction},
89-
::Type{<:AGECone},
87+
::Type{<:SignomialAGECone},
9088
) where {T}
9189
return true
9290
end
@@ -104,7 +102,7 @@ end
104102
function MOI.Bridges.Constraint.concrete_bridge_type(
105103
::Type{<:AGEBridge{T}},
106104
H::Type{<:MOI.AbstractVectorFunction},
107-
::Type{<:AGECone},
105+
::Type{<:SignomialAGECone},
108106
) where {T}
109107
S = MOI.Utilities.scalar_type(H)
110108
F = MOI.Utilities.promote_operation(
Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
struct SAGEBridge{T,F,G} <: MOI.Bridges.Constraint.AbstractBridge
22
ν::Matrix{MOI.VariableIndex}
3-
age_constraints::Vector{MOI.ConstraintIndex{MOI.VectorOfVariables,AGECone}}
3+
age_constraints::Vector{MOI.ConstraintIndex{MOI.VectorOfVariables,SignomialAGECone}}
44
equality_constraints::Vector{MOI.ConstraintIndex{F,MOI.EqualTo{T}}}
55
end
66

77
function MOI.Bridges.Constraint.bridge_constraint(
88
::Type{SAGEBridge{T,F,G}},
99
model,
1010
func::G,
11-
set::SAGECone,
11+
set::SignomialSAGECone,
1212
) where {T,F,G}
1313
m = size(set.α, 1)
14-
ν = Vector{Vector{MOI.VariableIndex}}(undef, m)
14+
ν = Matrix{MOI.VariableIndex}(undef, m, m)
1515
age_constraints =
16-
Vector{MOI.ConstraintIndex{MOI.VectorOfVariables,AGECone}}(undef, m)
16+
Vector{MOI.ConstraintIndex{MOI.VectorOfVariables,SignomialAGECone}}(undef, m)
1717
for k in 1:m
18-
ν[i], age_constraints[i] =
19-
MOI.add_constrained_variables(model, AGECone(set.α, k))
18+
ν[k, :], age_constraints[k] =
19+
MOI.add_constrained_variables(model, SignomialAGECone(set.α, k))
2020
end
2121
scalars = MOI.Utilities.eachscalar(func)
2222
n = size(set.α, 2)
@@ -25,9 +25,9 @@ function MOI.Bridges.Constraint.bridge_constraint(
2525
MOI.ScalarAffineTerm.(one(T), ν[:, i]),
2626
zero(T),
2727
)
28-
return MOI.add_constraint(
28+
return MOI.Utilities.normalize_and_add_constraint(
2929
model,
30-
MA.sub!(f, scalars[i]),
30+
MA.sub!!(f, scalars[i]),
3131
MOI.EqualTo(zero(T)),
3232
)
3333
end
@@ -37,13 +37,13 @@ end
3737
function MOI.supports_constraint(
3838
::Type{<:SAGEBridge{T}},
3939
::Type{<:MOI.AbstractVectorFunction},
40-
::Type{<:SAGECone},
40+
::Type{<:SignomialSAGECone},
4141
) where {T}
4242
return true
4343
end
4444

4545
function MOI.Bridges.added_constrained_variable_types(::Type{<:SAGEBridge})
46-
return Tuple{Type}[AGECone]
46+
return Tuple{Type}[(SignomialAGECone,)]
4747
end
4848

4949
function MOI.Bridges.added_constraint_types(
@@ -55,7 +55,7 @@ end
5555
function MOI.Bridges.Constraint.concrete_bridge_type(
5656
::Type{<:SAGEBridge{T}},
5757
G::Type{<:MOI.AbstractVectorFunction},
58-
::Type{<:SAGECone},
58+
::Type{<:SignomialSAGECone},
5959
) where {T}
6060
S = MOI.Utilities.scalar_type(G)
6161
F = MOI.Utilities.promote_operation(
@@ -64,5 +64,5 @@ function MOI.Bridges.Constraint.concrete_bridge_type(
6464
MOI.ScalarAffineFunction{Float64},
6565
S,
6666
)
67-
return AGEBridge{T,F,G}
67+
return SAGEBridge{T,F,G}
6868
end
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
struct SignomialBridge{T,S,P,F} <: MOI.Bridges.Constraint.AbstractBridge
2+
constraints::Vector{MOI.ConstraintIndex{F,S}}
3+
end
4+
5+
_signomial(set::PolynomialSAGECone) = SignomialSAGECone(set.α)
6+
_signomial(::Type{PolynomialSAGECone}) = SignomialSAGECone
7+
_signomial(set::PolynomialAGECone) = SignomialAGECone(set.α, set.k)
8+
_signomial(::Type{PolynomialAGECone}) = SignomialAGECone
9+
10+
function MOI.Bridges.Constraint.bridge_constraint(
11+
::Type{SignomialBridge{T,S,P,F}},
12+
model,
13+
func::F,
14+
set,
15+
) where {T,S,P,F}
16+
g = MOI.scalarize(func)
17+
for i in eachindex(g)
18+
if isodd(sum(set.α[i, :]))
19+
vi = MOI.add_variable(model)
20+
# vi ≤ -|g[i]|
21+
MOI.add_constraint(model, one(T) * vi - g[i], MOI.LessThan(zero(T)))
22+
MOI.add_constraint(model, one(T) * vi + g[i], MOI.LessThan(zero(T)))
23+
g[i] = vi
24+
end
25+
end
26+
constraint = MOI.add_constraint(model, g, _signomial(set))
27+
return SignomialBridge{T,S,P,F}(constraint)
28+
end
29+
30+
function MOI.supports_constraint(
31+
::Type{<:SignomialBridge{T}},
32+
::Type{<:MOI.AbstractVectorFunction},
33+
::Type{<:Union{PolynomialSAGECone,PolynomialAGECone}},
34+
) where {T}
35+
return true
36+
end
37+
38+
function MOI.Bridges.added_constrained_variable_types(::Type{<:SignomialBridge})
39+
return Tuple{Type}[(MOI.Reals,)]
40+
end
41+
42+
function MOI.Bridges.added_constraint_types(
43+
::Type{<:SignomialBridge{T,S,P,F}},
44+
) where {T,S,P,F}
45+
return [(F, S)]
46+
end
47+
48+
function MOI.Bridges.Constraint.concrete_bridge_type(
49+
::Type{<:SignomialBridge{T}},
50+
F::Type{<:MOI.AbstractVectorFunction},
51+
P::Type{<:Union{PolynomialSAGECone,PolynomialAGECone}},
52+
) where {T}
53+
S = _signomial(P)
54+
return SignomialBridge{T,S,P,F}
55+
end

test/relative_entropy.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,21 @@ import ECOS
1111
using JuMP
1212
using PolyJuMP
1313

14-
function test_motzkin(x, y, T, solver)
14+
function _test_motzkin(x, y, T, solver, set)
1515
model = Model(solver)
1616
PolyJuMP.setpolymodule!(model, PolyJuMP.RelativeEntropy)
1717
motzkin = x^4 * y^2 + x^2 * y^4 + one(T) - 3x^2 * y^2
18-
@constraint(model, motzkin in PolyJuMP.RelativeEntropy.AGESet(x^2 * y^2))
18+
@constraint(model, motzkin in set)
1919
optimize!(model)
2020
@test termination_status(model) == MOI.OPTIMAL
2121
@test primal_status(model) == MOI.FEASIBLE_POINT
2222
end
2323

24+
function test_motzkin(x, y, T, solver)
25+
_test_motzkin(x, y, T, solver, PolyJuMP.RelativeEntropy.SignomialAGESet(x^2 * y^2))
26+
_test_motzkin(x, y, T, solver, PolyJuMP.RelativeEntropy.SignomialSAGESet())
27+
end
28+
2429
import ECOS
2530
const SOLVERS =
2631
[optimizer_with_attributes(ECOS.Optimizer, MOI.Silent() => true)]

0 commit comments

Comments
 (0)