Skip to content

Commit

Permalink
Polynomial optimization meta-solver based on algebraic system solver (#…
Browse files Browse the repository at this point in the history
…80)

* Polynomial optimization meta-solver based on algebraic system solver

* Add tests

* Add bridge linquad -> poly

* Add LinearAlgebra to test/Project

* Create Bridges module

* Format

* Renames

* Add Objective bridge

* Add JuMP test

* Add support for nonlinear objective

* Add support for nonlinear constraints

* Format

* Fixes for MOI tests

* Fixes for MOI tests

* Add MOI tests

* Fix dual sign for MAX with eq

* Correct handling of invalid model

* Implement substitute_variables and constant

* Remvoe ZerosBridge

* Implement bridge modification

* Typo fix

* Clarify failing tests

* Fixes

* Format fixes

* Run tests against HomotopyContinuation

* AlgebraicKKT -> KKT

* AlgebraicKKT -> KKT

* AlgebraicKKT -> KKT

* AlgebraicKKT -> KKT

* Format fix

* Exclude HomotopyContinuation for 32 bit

* Typo fix

* Avoid import for non-64 bit

* Format fix
  • Loading branch information
blegat authored Dec 16, 2022
1 parent 0767705 commit df4d9e5
Show file tree
Hide file tree
Showing 45 changed files with 2,552 additions and 611 deletions.
8 changes: 8 additions & 0 deletions .JuliaFormatter.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Configuration file for JuliaFormatter.jl
# For more information, see: https://domluna.github.io/JuliaFormatter.jl/stable/config/

always_for_in = true
always_use_return = true
margin = 80
remove_extra_newlines = true
short_to_long_function_def = true
31 changes: 31 additions & 0 deletions .github/workflows/format_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: format-check
on:
push:
branches:
- master
- release-*
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: julia-actions/setup-julia@latest
with:
version: '1'
- uses: actions/checkout@v1
- name: Format check
shell: julia --color=yes {0}
run: |
using Pkg
Pkg.add(PackageSpec(name="JuliaFormatter", version="1"))
using JuliaFormatter
format("src", verbose=true)
format("test", verbose=true)
out = String(read(Cmd(`git diff`)))
if isempty(out)
exit(0)
end
@error "Some files have not been formatted !!!"
write(stdout, out)
exit(1)
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ repo = "https://github.com/jump-dev/PolyJuMP.jl.git"
version = "0.6.3"

[deps]
DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
Expand Down
8 changes: 8 additions & 0 deletions src/Bridges/Bridges.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Bridges

# Constraint bridges
include("Constraint/Constraint.jl")
# Objective bridges
include("Objective/Objective.jl")

end # module
21 changes: 21 additions & 0 deletions src/Bridges/Constraint/Constraint.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Constraint

using MultivariatePolynomials
const MP = MultivariatePolynomials
import MultivariateBases
const MB = MultivariateBases
import SemialgebraicSets
const SS = SemialgebraicSets
import MultivariateMoments
const MM = MultivariateMoments
import MathOptInterface
const MOI = MathOptInterface

using PolyJuMP

include("zero_polynomial.jl")
include("zero_polynomial_in_algebraic_set.jl")
include("plus_minus.jl")
include("to_polynomial.jl")

end # module
93 changes: 93 additions & 0 deletions src/Bridges/Constraint/plus_minus.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
struct PlusMinusBridge{
T,
F<:MOI.AbstractVectorFunction,
G<:MOI.AbstractVectorFunction,
ST<:MOI.AbstractVectorSet,
} <: MOI.Bridges.Constraint.AbstractBridge
plus::MOI.ConstraintIndex{F,ST}
minus::MOI.ConstraintIndex{G,ST}
end

function MOI.Bridges.Constraint.bridge_constraint(
::Type{PlusMinusBridge{T,F,G,ST}},
model::MOI.ModelLike,
f::MOI.AbstractVectorFunction,
set::PolyJuMP.PlusMinusSet{ST},
) where {T,F,G,ST}
plus = MOI.add_constraint(model, f, set.set)
minus = MOI.add_constraint(model, MOI.Utilities.operate(-, T, f), set.set)
return PlusMinusBridge{T,F,G,ST}(plus, minus)
end

function MOI.supports_constraint(
::Type{PlusMinusBridge{T}},
::Type{<:MOI.AbstractVectorFunction},
::Type{<:PolyJuMP.PlusMinusSet},
) where {T}
return true
end
function MOI.Bridges.added_constrained_variable_types(::Type{<:PlusMinusBridge})
return Tuple{DataType}[]
end
function MOI.Bridges.added_constraint_types(
::Type{<:PlusMinusBridge{T,F,F,ST}},
) where {T,F,ST}
return [(F, ST)]
end
function MOI.Bridges.added_constraint_types(
::Type{<:PlusMinusBridge{T,F,G,ST}},
) where {T,F,G,ST}
return [(F, ST), (G, ST)]
end
function MOI.Bridges.Constraint.concrete_bridge_type(
::Type{<:PlusMinusBridge{T}},
F::Type{<:MOI.AbstractVectorFunction},
::Type{<:PolyJuMP.PlusMinusSet{ST}},
) where {T,ST}
G = MOI.Utilities.promote_operation(-, T, F)
return PlusMinusBridge{T,F,G,ST}
end

# Attributes, Bridge acting as an model
function MOI.get(
::PlusMinusBridge{T,F,F,ST},
::MOI.NumberOfConstraints{F,ST},
) where {T,F,ST}
return 2
end
function MOI.get(
::PlusMinusBridge{T,F,G,ST},
::MOI.NumberOfConstraints{F,ST},
) where {T,F,G,ST}
return 1
end
function MOI.get(
::PlusMinusBridge{T,F,G,ST},
::MOI.NumberOfConstraints{G,ST},
) where {T,F,G,ST}
return 1
end
function MOI.get(
bridge::PlusMinusBridge{T,F,F,ST},
::MOI.ListOfConstraintIndices{F,ST},
) where {T,F,ST}
return [bridge.plus, bridge.minus]
end
function MOI.get(
bridge::PlusMinusBridge{T,F,G,ST},
::MOI.ListOfConstraintIndices{F,ST},
) where {T,F,G,ST}
return [bridge.plus]
end
function MOI.get(
bridge::PlusMinusBridge{T,F,G,ST},
::MOI.ListOfConstraintIndices{G,ST},
) where {T,F,G,ST}
return [bridge.minus]
end

# Indices
function MOI.delete(model::MOI.ModelLike, bridge::PlusMinusBridge)
MOI.delete(model, bridge.plus)
return MOI.delete(model, bridge.minus)
end
99 changes: 99 additions & 0 deletions src/Bridges/Constraint/to_polynomial.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import DynamicPolynomials

const VarType = DynamicPolynomials.PolyVar{true}
const PolyType{T} = DynamicPolynomials.Polynomial{true,T}
const FuncType{T} = PolyJuMP.ScalarPolynomialFunction{T,PolyType{T}}

"""
ToPolynomialBridge{T,S} <: Bridges.Constraint.AbstractBridge
`ToPolynomialBridge` implements the following reformulations:
* ``f(x) \\in S`` into ``p(x) \\in S`` where ``f(x)`` is a scalar function
and ``p(x)`` is a polynomial.
## Source node
`ToPolynomialBridge` supports:
* `F` in `S` where `F` is a `MOI.AbstractScalarFunction` for which
`convert(::Type{PolyJuMP.ScalarPolynomialFunction}, ::Type{F})`. That is for
instance the case for `MOI.VariableIndex`, `MOI.ScalarAffineFunction` and
`MOI.ScalarQuadraticFunction`.
## Target nodes
`ToPolynomialBridge` creates:
* [`PolyJuMP.ScalarPolynomialFunction{T}`](@ref) in `S`
"""
struct ToPolynomialBridge{T,S} <:
MOI.Bridges.Constraint.AbstractFunctionConversionBridge{FuncType{T},S}
constraint::MOI.ConstraintIndex{FuncType{T},S}
end

function MOI.Bridges.Constraint.bridge_constraint(
::Type{ToPolynomialBridge{T,S}},
model,
f::MOI.AbstractScalarFunction,
s::S,
) where {T,S}
constraint = MOI.add_constraint(model, convert(FuncType{T}, f), s)
return ToPolynomialBridge{T,S}(constraint)
end

function MOI.supports_constraint(
::Type{ToPolynomialBridge{T}},
::Type{<:MOI.AbstractScalarFunction},
::Type{<:MOI.AbstractScalarSet},
) where {T}
return true
end

function MOI.Bridges.added_constrained_variable_types(
::Type{<:ToPolynomialBridge},
)
return Tuple{Type}[]
end

function MOI.Bridges.added_constraint_types(
::Type{ToPolynomialBridge{T,S}},
) where {T,S}
return Tuple{Type,Type}[(FuncType{T}, S)]
end

function MOI.Bridges.Constraint.concrete_bridge_type(
::Type{<:ToPolynomialBridge{T}},
::Type{<:MOI.AbstractScalarFunction},
::Type{S},
) where {T,S<:MOI.AbstractScalarSet}
return ToPolynomialBridge{T,S}
end

function MOI.get(
::ToPolynomialBridge{T,S},
::MOI.NumberOfConstraints{FuncType{T},S},
)::Int64 where {T,S}
return 1
end

function MOI.get(
b::ToPolynomialBridge{T,S},
::MOI.ListOfConstraintIndices{FuncType{T},S},
) where {T,S}
return [b.constraint]
end

function MOI.delete(model::MOI.ModelLike, b::ToPolynomialBridge)
MOI.delete(model, b.constraint)
return
end

function MOI.modify(
model::MOI.ModelLike,
b::ToPolynomialBridge,
change::MOI.AbstractFunctionModification,
)
MOI.modify(model, b.constraint, change)
return
end
95 changes: 95 additions & 0 deletions src/Bridges/Constraint/zero_polynomial.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
struct ZeroPolynomialBridge{
T,
F<:MOI.AbstractVectorFunction,
MT<:AbstractMonomial,
MVT<:AbstractVector{MT},
} <: MOI.Bridges.Constraint.AbstractBridge
zero_constraint::MOI.ConstraintIndex{F,MOI.Zeros}
monomials::MVT
end

function MOI.Bridges.Constraint.bridge_constraint(
::Type{ZeroPolynomialBridge{T,F,MT,MVT}},
model::MOI.ModelLike,
f::MOI.AbstractVectorFunction,
s::PolyJuMP.ZeroPolynomialSet{SS.FullSpace,<:MB.MonomialBasis},
) where {T,F,MT,MVT}
@assert MOI.output_dimension(f) == length(s.monomials)
zero_constraint =
MOI.add_constraint(model, f, MOI.Zeros(length(s.monomials)))
return ZeroPolynomialBridge{T,F,MT,MVT}(zero_constraint, s.monomials)
end

function MOI.supports_constraint(
::Type{<:ZeroPolynomialBridge{T}},
::Type{<:MOI.AbstractVectorFunction},
::Type{<:PolyJuMP.ZeroPolynomialSet{SS.FullSpace}},
) where {T}
return true
end
function MOI.Bridges.added_constrained_variable_types(
::Type{<:ZeroPolynomialBridge},
)
return Tuple{DataType}[]
end
function MOI.Bridges.added_constraint_types(
::Type{<:ZeroPolynomialBridge{T,F}},
) where {T,F}
return [(F, MOI.Zeros)]
end
function MOI.Bridges.Constraint.concrete_bridge_type(
::Type{<:ZeroPolynomialBridge{T}},
F::Type{<:MOI.AbstractVectorFunction},
::Type{
<:PolyJuMP.ZeroPolynomialSet{SS.FullSpace,<:MB.MonomialBasis,MT,MVT},
},
) where {T,MT,MVT}
return ZeroPolynomialBridge{T,F,MT,MVT}
end

# Attributes, Bridge acting as an model
function MOI.get(
::ZeroPolynomialBridge{T,F},
::MOI.NumberOfConstraints{F,MOI.Zeros},
) where {T,F}
return 1
end
function MOI.get(
b::ZeroPolynomialBridge{T,F},
::MOI.ListOfConstraintIndices{F,MOI.Zeros},
) where {T,F}
return [b.zero_constraint]
end

# Indices
function MOI.delete(model::MOI.ModelLike, bridge::ZeroPolynomialBridge)
return MOI.delete(model, bridge.zero_constraint)
end

# Attributes, Bridge acting as a constraint
function MOI.get(
model::MOI.ModelLike,
::MOI.ConstraintSet,
bridge::ZeroPolynomialBridge{T,F,MT,MVT},
) where {T,F,MT,MVT}
return PolyJuMP.ZeroPolynomialSet(
SS.FullSpace(),
MB.MonomialBasis{MT,MVT},
bridge.monomials,
)
end
function MOI.get(
model::MOI.ModelLike,
attr::Union{MOI.ConstraintPrimal,MOI.ConstraintDual},
bridge::ZeroPolynomialBridge,
)
return MOI.get(model, attr, bridge.zero_constraint)
end
function MOI.get(
model::MOI.ModelLike,
attr::PolyJuMP.MomentsAttribute,
bridge::ZeroPolynomialBridge,
)
values = MOI.get(model, MOI.ConstraintDual(attr.N), bridge)
return MM.measure(values, bridge.monomials)
end
Loading

0 comments on commit df4d9e5

Please sign in to comment.