From f8480812a1aa9c343594423aae9ebb880b7ddf1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 14 May 2024 12:08:56 +0200 Subject: [PATCH 01/84] Use Variable.KernelBridge by default --- docs/src/tutorials/Extension/certificate.jl | 4 +- src/Bridges/Bridges.jl | 26 ++ src/Bridges/Constraint/Constraint.jl | 15 - src/Bridges/Constraint/sos_polynomial.jl | 266 +++--------------- .../sos_polynomial_in_semialgebraic_set.jl | 2 +- src/Bridges/Variable/kernel.jl | 23 +- src/Certificate/Sparsity/ideal.jl | 5 +- src/Certificate/Sparsity/preorder.jl | 5 +- src/Certificate/Symmetry/wedderburn.jl | 2 +- src/Certificate/ideal.jl | 18 +- src/Certificate/preorder.jl | 4 +- src/attributes.jl | 87 ++++-- src/constraints.jl | 25 ++ test/Project.toml | 1 + test/certificate.jl | 4 +- test/constraint.jl | 2 +- 16 files changed, 203 insertions(+), 286 deletions(-) diff --git a/docs/src/tutorials/Extension/certificate.jl b/docs/src/tutorials/Extension/certificate.jl index 1009dee95..a745d1f55 100644 --- a/docs/src/tutorials/Extension/certificate.jl +++ b/docs/src/tutorials/Extension/certificate.jl @@ -78,8 +78,8 @@ function SOSC.multiplier_basis(certificate::Schmüdgen, index::SOSC.PreorderInde q = SOSC.generator(certificate, index, domain) return SOSC.maxdegree_gram_basis(certificate.basis, variables(domain), SOSC.multiplier_maxdegree(certificate.maxdegree, q)) end -function SOSC.multiplier_basis_type(::Type{Schmüdgen{IC, CT, BT}}) where {IC, CT, BT} - return BT +function SOSC.multiplier_basis_type(::Type{Schmüdgen{IC, CT, BT}}, ::Type{M}) where {IC,CT,BT,M} + return MB.similar_type(BT, M) end function SOSC.generator(::Schmüdgen, index::SOSC.PreorderIndex, domain::SOSC.WithVariables) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index b1fde005c..035dd3dfe 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -1,6 +1,32 @@ module Bridges +import MathOptInterface as MOI +import SumOfSquares as SOS + include("Variable/Variable.jl") include("Constraint/Constraint.jl") +function MOI.get( + model::MOI.ModelLike, + attr::Union{SOS.GramMatrixAttribute,SOS.MomentMatrixAttribute,SOS.SOSDecompositionAttribute}, + bridge::MOI.Bridges.Constraint.VectorSlackBridge, +) + return MOI.get(model, attr, bridge.slack_in_set) +end + +# TODO bridges should redirect to `MOI.get_fallback` as well so that +# we can just use `Union{MOI.ConstraintIndex,MOI.Bridges.AbstractBridge}` in the `get_fallback` in `attributes.jl` +function MOI.get( + model::MOI.ModelLike, + attr::SOS.SOSDecompositionAttribute, + bridge::Union{ + Variable.KernelBridge, + Constraint.ImageBridge, + Constraint.SOSPolynomialInSemialgebraicSetBridge, + }, +) + gram = MOI.get(model, SOS.GramMatrixAttribute(attr.result_index), bridge) + return SOS.SOSDecomposition(gram, attr.ranktol, attr.dec) +end + end diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 967e4c973..511a8a835 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -26,19 +26,4 @@ include("image.jl") include("sos_polynomial.jl") include("sos_polynomial_in_semialgebraic_set.jl") -# TODO bridges should redirect to `MOI.get_fallback` as well so that -# we can just use `Union{MOI.ConstraintIndex,MOI.Bridges.AbstractBridge}` in the `get_fallback` in `attributes.jl` -function MOI.get( - model::MOI.ModelLike, - attr::SOS.SOSDecompositionAttribute, - bridge::Union{ - ImageBridge, - SOSPolynomialBridge, - SOSPolynomialInSemialgebraicSetBridge, - }, -) - gram = MOI.get(model, SOS.GramMatrixAttribute(attr.result_index), bridge) - return SOS.SOSDecomposition(gram, attr.ranktol, attr.dec) -end - end diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 99e66d02a..74788f739 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -2,78 +2,54 @@ struct SOSPolynomialBridge{ T, F<:MOI.AbstractVectorFunction, DT<:SemialgebraicSets.AbstractSemialgebraicSet, - UMCT<:Union{ - Vector{<:MOI.ConstraintIndex{MOI.VectorOfVariables}}, - MOI.ConstraintIndex{MOI.VectorOfVariables}, - }, - UMST, - MCT, - GB<:Union{ - Vector{<:Vector{<:MB.AbstractPolynomialBasis}}, # Symmetry - Vector{<:MB.AbstractPolynomialBasis}, # Sparsity - MB.AbstractPolynomialBasis, # No reduction - }, - ZB<:MB.AbstractPolynomialBasis, + M, + G<:MB.AbstractPolynomialBasis, CT<:SOS.Certificate.AbstractIdealCertificate, MT<:MP.AbstractMonomial, MVT<:AbstractVector{MT}, -} <: MOI.Bridges.Constraint.AbstractBridge - Q::Union{Vector{Vector{MOI.VariableIndex}},Vector{MOI.VariableIndex}} # Vector{Vector{MOI.VariableIndex}} for sparse SOS - cQ::UMCT - gram_basis::GB - zero_constraint::MOI.ConstraintIndex{ - F, - PolyJuMP.ZeroPolynomialSet{DT,ZB,MT,MVT}, - } - domain::DT - monomials::MVT - certificate::CT + W<:MP.AbstractTerm{T}, +} <: MOI.Bridges.Constraint.SetMapBridge{T,SOS.WeightedSOSCone{M,MB.MonomialBasis{MT,MVT},G,W},SOS.SOSPolynomialSet{DT,MT,MVT,CT},F,F} + constraint::MOI.ConstraintIndex{F,SOS.WeightedSOSCone{M,MB.MonomialBasis{MT,MVT},G,W}} + set::SOS.SOSPolynomialSet{DT,MT,MVT,CT} end function MOI.Bridges.Constraint.bridge_constraint( - ::Type{SOSPolynomialBridge{T,F,DT,UMCT,UMST,MCT,GB,ZB,CT,MT,MVT}}, + ::Type{SOSPolynomialBridge{T,F,DT,M,G,CT,MT,MVT,W}}, model::MOI.ModelLike, - f::MOI.AbstractVectorFunction, - s::SOS.SOSPolynomialSet{<:SemialgebraicSets.AbstractAlgebraicSet}, -) where {T,F,DT,UMCT,UMST,MCT,GB,ZB,CT,MT,MVT} - @assert MOI.output_dimension(f) == length(s.monomials) + func::MOI.AbstractVectorFunction, + set::SOS.SOSPolynomialSet{<:SemialgebraicSets.AbstractAlgebraicSet}, +) where {T,F,DT,M,G,CT,MT,MVT,W} + @assert MOI.output_dimension(func) == length(set.monomials) # MOI does not modify the coefficients of the functions so we can modify `p`. # without altering `f`. # The monomials may be copied by MA however so we need to copy it. - p = MP.polynomial(MOI.Utilities.scalarize(f), copy(s.monomials)) + p = MP.polynomial(MOI.Utilities.scalarize(func), copy(set.monomials)) # As `*(::MOI.ScalarAffineFunction{T}, ::S)` is only defined if `S == T`, we # need to call `similar`. This is critical since `T` is # `Float64` when used with JuMP and the coefficient type is often `Int` if # `set.domain.V` is `FullSpace` or `FixedPolynomialSet`. # FIXME convert needed because the coefficient type of `r` is `Any` otherwise if `domain` is `AlgebraicSet` r = SOS.Certificate.reduced_polynomial( - s.certificate, + set.certificate, p, - MP.similar(s.domain, T), + MP.similar(set.domain, T), ) gram_basis = SOS.Certificate.gram_basis( - s.certificate, - SOS.Certificate.with_variables(r, s.domain), + set.certificate, + SOS.Certificate.with_variables(r, set.domain), ) - g, Q, cQ = SOS.add_gram_matrix(model, MCT, gram_basis, T) - # MOI does not modify the coefficients of the functions so we can modify `r`. - # without altering `f`. - q = MA.operate!!(-, r, g) - set = PolyJuMP.ZeroPolynomialSet( - s.domain, - SOS.Certificate.zero_basis(s.certificate), - MP.monomials(q), + constraint = MOI.add_constraint( + model, + func, + SOS.WeightedSOSCone{M}( + MB.MonomialBasis(set.monomials), + [gram_basis], + [MP.term(one(T), MP.constant_monomial(p))], + ), ) - coefs = MOI.Utilities.vectorize(MP.coefficients(q)) - zero_constraint = MOI.add_constraint(model, coefs, set) - return SOSPolynomialBridge{T,F,DT,UMCT,UMST,MCT,GB,ZB,CT,MT,MVT}( - Q, - cQ, - gram_basis, - zero_constraint, - s.domain, - s.monomials, - s.certificate, + return SOSPolynomialBridge{T,F,DT,M,G,CT,MT,MVT,W}( + constraint, + set, ) end @@ -84,16 +60,7 @@ function MOI.supports_constraint( ) where {T} return MOI.Utilities.is_coefficient_type(F, T) end -function MOI.Bridges.added_constrained_variable_types( - ::Type{<:SOSPolynomialBridge{T,F,DT,UMCT,UMST,MCT}}, -) where {T,F,DT,UMCT,UMST,MCT} - return constrained_variable_types(MCT) -end -function MOI.Bridges.added_constraint_types( - ::Type{SOSPolynomialBridge{T,F,DT,UMCT,UMST,MCT,GB,ZB,CT,MT,MVT}}, -) where {T,F,DT,UMCT,UMST,MCT,GB,ZB,CT,MT,MVT} - return Tuple{Type,Type}[(F, PolyJuMP.ZeroPolynomialSet{DT,ZB,MT,MVT})] -end + function MOI.Bridges.Constraint.concrete_bridge_type( ::Type{<:SOSPolynomialBridge{T}}, F::Type{<:MOI.AbstractVectorFunction}, @@ -101,83 +68,10 @@ function MOI.Bridges.Constraint.concrete_bridge_type( ) where {T,DT<:SemialgebraicSets.AbstractAlgebraicSet,MT,MVT,CT} # promotes VectorOfVariables into VectorAffineFunction, it should be enough # for most use cases - G = MOI.Utilities.promote_operation(-, T, F, MOI.VectorOfVariables) - MCT = SOS.matrix_cone_type(CT) - UMCT = union_constraint_types(MCT) - UMST = union_set_types(MCT) - GB = SOS.Certificate.gram_basis_type(CT) - ZB = SOS.Certificate.zero_basis_type(CT) - return SOSPolynomialBridge{T,G,DT,UMCT,UMST,MCT,GB,ZB,CT,MT,MVT} -end - -# Attributes, Bridge acting as an model -_num_variables(Q::Vector{MOI.VariableIndex}) = length(Q) -function _num_variables(Q::Vector{Vector{MOI.VariableIndex}}) - return mapreduce(length, +, Q, init = 0) -end -function MOI.get(bridge::SOSPolynomialBridge, ::MOI.NumberOfVariables) - return _num_variables(bridge.Q) -end -_list_variables(Q::Vector{MOI.VariableIndex}) = Q -_list_variables(Q::Vector{Vector{MOI.VariableIndex}}) = Iterators.flatten(Q) -function MOI.get(bridge::SOSPolynomialBridge, ::MOI.ListOfVariableIndices) - return _list_variables(bridge.Q) -end -_num_constraints(cQ::Vector, ::Type{C}) where {C} = count(ci -> ci isa C, cQ) -_num_constraints(cQ::C, ::Type{C}) where {C} = 1 -_num_constraints(cQ, ::Type) = 0 -function MOI.get( - bridge::SOSPolynomialBridge{T,F,DT,UMCT,UMST}, - ::MOI.NumberOfConstraints{MOI.VectorOfVariables,S}, -) where {T,F,DT,UMCT,UMST,S<:UMST} - return _num_constraints( - bridge.cQ, - MOI.ConstraintIndex{MOI.VectorOfVariables,S}, - ) -end -_list_constraints(cQ::Vector, ::Type{C}) where {C} = filter(ci -> ci isa C, cQ) -_list_constraints(cQ::C, ::Type{C}) where {C} = [cQ] -_list_constraints(cQ, C::Type) = C[] -function MOI.get( - bridge::SOSPolynomialBridge{T,F,DT,UMCT,UMST}, - ::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,S}, -) where {T,F,DT,UMCT,UMST,S<:UMST} - return _list_constraints( - bridge.cQ, - MOI.ConstraintIndex{MOI.VectorOfVariables,S}, - ) -end -function MOI.get( - ::SOSPolynomialBridge{T,F,DT,UMCT,UMST,MCT,GB,ZB,CT,MT,MVT}, - ::MOI.NumberOfConstraints{F,PolyJuMP.ZeroPolynomialSet{DT,ZB,MT,MVT}}, -) where {T,F,DT,UMCT,UMST,MCT,GB,ZB,CT,MT,MVT} - return 1 -end -function MOI.get( - b::SOSPolynomialBridge{T,F,DT,UMCT,UMST,MCT,GB,ZB,CT,MT,MVT}, - ::MOI.ListOfConstraintIndices{F,PolyJuMP.ZeroPolynomialSet{DT,ZB,MT,MVT}}, -) where {T,F,DT,UMCT,UMST,MCT,GB,ZB,CT,MT,MVT} - return [b.zero_constraint] -end - -# Indices -function _delete_variables(model, Q::Vector{MOI.VariableIndex}) - if !isempty(Q) - # FIXME Since there is not variables in the list, we cannot - # identify the `EmptyBridge` to delete - MOI.delete(model, Q) - end -end -function _delete_variables(model, Qs::Vector{Vector{MOI.VariableIndex}}) - for Q in Qs - _delete_variables(model, Q) - end -end -function MOI.delete(model::MOI.ModelLike, bridge::SOSPolynomialBridge) - # First delete the constraints in which the Gram matrix appears - MOI.delete(model, bridge.zero_constraint) - # Now we delete the Gram matrix - return _delete_variables(model, bridge.Q) + M = SOS.matrix_cone_type(CT) + G = SOS.Certificate.gram_basis_type(CT, MT) + W = MP.term_type(MT, T) + return SOSPolynomialBridge{T,F,DT,M,G,CT,MT,MVT,W} end # Attributes, Bridge acting as a constraint @@ -186,48 +80,15 @@ function MOI.get( ::MOI.ConstraintSet, bridge::SOSPolynomialBridge, ) - return SOS.SOSPolynomialSet( - bridge.domain, - bridge.monomials, - bridge.certificate, - ) -end -function MOI.get(::MOI.ModelLike, ::MOI.ConstraintPrimal, ::SOSPolynomialBridge) - throw(SOS.ValueNotSupported()) + return bridge.set end -function MOI.get( - model::MOI.ModelLike, - attr::MOI.ConstraintDual, - bridge::SOSPolynomialBridge{T}, -) where {T} - dual = MOI.get(model, attr, bridge.zero_constraint) - set = MOI.get(model, MOI.ConstraintSet(), bridge.zero_constraint) - μ = MultivariateMoments.measure(dual, set.monomials) - function reduced(mono) - p = MP.polynomial(mono, T) - domain = similar(bridge.domain, T) - return SOS.Certificate.reduced_polynomial(bridge.certificate, p, domain) - end - return [dot(reduced(mono), μ) for mono in bridge.monomials] -end -function MOI.get( - model::MOI.ModelLike, - attr::MOI.ConstraintDual, - bridge::SOSPolynomialBridge{ - T, - <:MOI.AbstractVectorFunction, - SemialgebraicSets.FullSpace, - }, -) where {T} - return MOI.get(model, attr, bridge.zero_constraint) -end function MOI.get( model::MOI.ModelLike, attr::PolyJuMP.MomentsAttribute, bridge::SOSPolynomialBridge, ) - return MOI.get(model, attr, bridge.zero_constraint) + return MOI.get(model, attr, bridge.constraint) end function MOI.get( @@ -237,56 +98,19 @@ function MOI.get( ) return bridge.gram_basis end -function _gram( - f::Function, - Q::Vector{MOI.VariableIndex}, - gram_basis, - T::Type, - MCT, -) - return SOS.build_gram_matrix(convert(Vector{T}, f(Q)), gram_basis, MCT, T) -end -function _gram( - f::Function, - Qs::Vector{Vector{MOI.VariableIndex}}, - gram_bases, - T::Type, - MCT, -) - return SOS.build_gram_matrix(gram_bases, MCT, T) do i - return convert(Vector{T}, f(Qs[i])) - end -end -function MOI.get( - model::MOI.ModelLike, - attr::SOS.GramMatrixAttribute, - bridge::SOSPolynomialBridge{T,F,DT,UMCT,UMST,MCT}, -) where {T,F,DT,UMCT,UMST,MCT} - return _gram( - Q -> MOI.get(model, MOI.VariablePrimal(attr.result_index), Q), - bridge.Q, - bridge.gram_basis, - T::Type, - MCT, - ) -end + function MOI.get( model::MOI.ModelLike, - attr::SOS.MomentMatrixAttribute, + attr::Union{SOS.GramMatrixAttribute,SOS.MomentMatrixAttribute,SOS.SOSDecompositionAttribute}, bridge::SOSPolynomialBridge, ) - if bridge.cQ isa Vector{<:MOI.ConstraintIndex} - return SOS.build_moment_matrix(bridge.gram_basis) do i - return MOI.get( - model, - MOI.ConstraintDual(attr.result_index), - bridge.cQ[i], - ) - end - else - return SOS.build_moment_matrix( - MOI.get(model, MOI.ConstraintDual(attr.result_index), bridge.cQ), - bridge.gram_basis, - ) - end + SOS.check_multiplier_index_bounds(attr, 0:0) + return MOI.get( + model, + typeof(attr)( + multiplier_index = attr.multiplier_index + 1, + result_index = attr.result_index, + ), + bridge.constraint, + ) end diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index 8b99b0b77..62c42ecd1 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -142,7 +142,7 @@ function MOI.Bridges.Constraint.concrete_bridge_type( # for most use cases G = MOI.Utilities.promote_operation(-, T, F, MOI.VectorOfVariables) MCT = SOS.matrix_cone_type(CT) - B = Certificate.multiplier_basis_type(CT) + B = Certificate.multiplier_basis_type(CT, MT) UMCT = union_constraint_types(MCT) UMST = union_set_types(MCT) IC = Certificate.ideal_certificate(CT) diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index 52bdb7224..e2b881d86 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -6,7 +6,7 @@ struct KernelBridge{T,M} <: MOI.Bridges.Variable.AbstractBridge end function MOI.Bridges.Variable.bridge_constrained_variable( - ::Type{KernelBridge{T}}, + ::Type{KernelBridge{T,M}}, model::MOI.ModelLike, set::SOS.WeightedSOSCone{M}, ) where {T,M} @@ -40,6 +40,13 @@ function MOI.Bridges.added_constraint_types(::Type{<:KernelBridge}) return Tuple{Type,Type}[] end +function MOI.Bridges.Variable.concrete_bridge_type( + ::Type{<:KernelBridge{T}}, + ::Type{<:SOS.WeightedSOSCone{M}}, +) where {T,M} + return KernelBridge{T,M} +end + # Attributes, Bridge acting as a model function MOI.get(bridge::KernelBridge, ::MOI.NumberOfVariables) return sum(length, bridge.variables) @@ -108,6 +115,14 @@ function MOI.get( end end +function MOI.get( + model::MOI.ModelLike, + attr::SOS.GramMatrixAttribute, + bridge::KernelBridge, +) + SOS.check_multiplier_index_bounds(attr, eachindex(bridge.constraints)) +end + function MOI.Bridges.bridged_function( bridge::KernelBridge, i::MOI.Bridges.IndexInVector, @@ -116,10 +131,8 @@ function MOI.Bridges.bridged_function( end function MOI.Bridges.Variable.unbridged_map( - bridge::KernelBridge{T}, - coefs::Vector{MOI.VariableIndex}, + ::KernelBridge{T}, + ::Vector{MOI.VariableIndex}, ) where {T} - F = MOI.ScalarAffineFunction{T} - map = Pair{MOI.VariableIndex,F}[] return nothing end diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index 8c3940182..baa093985 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -75,8 +75,9 @@ function SumOfSquares.Certificate.gram_basis(certificate::Ideal, poly) end function SumOfSquares.Certificate.gram_basis_type( ::Type{Ideal{S,C}}, -) where {S,C} - return Vector{<:SumOfSquares.Certificate.gram_basis_type(C)} + ::Type{M}, +) where {S,C,M} + return SumOfSquares.Certificate.gram_basis_type(C, M) end function SumOfSquares.Certificate.reduced_polynomial( certificate::Ideal, diff --git a/src/Certificate/Sparsity/preorder.jl b/src/Certificate/Sparsity/preorder.jl index 4ea91c51d..e0e590f5b 100644 --- a/src/Certificate/Sparsity/preorder.jl +++ b/src/Certificate/Sparsity/preorder.jl @@ -63,8 +63,9 @@ function SumOfSquares.Certificate.multiplier_basis( end function SumOfSquares.Certificate.multiplier_basis_type( ::Type{Preorder{S,C}}, -) where {S,C} - return Vector{SumOfSquares.Certificate.multiplier_basis_type(C)} + ::Type{M}, +) where {S,C,M} + return Vector{SumOfSquares.Certificate.multiplier_basis_type(C, M)} end function SumOfSquares.Certificate.generator( diff --git a/src/Certificate/Symmetry/wedderburn.jl b/src/Certificate/Symmetry/wedderburn.jl index 71adffce5..048c9f3d8 100644 --- a/src/Certificate/Symmetry/wedderburn.jl +++ b/src/Certificate/Symmetry/wedderburn.jl @@ -86,7 +86,7 @@ end function SumOfSquares.matrix_cone_type(::Type{<:Ideal{C}}) where {C} return SumOfSquares.matrix_cone_type(C) end -function SumOfSquares.Certificate.gram_basis_type(::Type{<:Ideal}) +function SumOfSquares.Certificate.gram_basis_type(::Type{<:Ideal}, ::Type{M}) where {M} return Vector{Vector{MB.FixedPolynomialBasis}} end SumOfSquares.Certificate.zero_basis_type(::Type{<:Ideal}) = MB.MonomialBasis diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index dd191699a..22fe0ac8a 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -47,8 +47,8 @@ function gram_basis(certificate::MaxDegree, poly) certificate.maxdegree, ) end -function gram_basis_type(::Type{MaxDegree{CT,BT}}) where {CT,BT} - return BT +function gram_basis_type(::Type{MaxDegree{CT,BT}}, ::Type{M}) where {CT,BT,M} + return MB.similar_type(BT, M) end """ @@ -73,8 +73,8 @@ end function gram_basis(certificate::FixedBasis, poly) return certificate.basis end -function gram_basis_type(::Type{FixedBasis{CT,BT}}) where {CT,BT} - return BT +function gram_basis_type(::Type{FixedBasis{CT,BT}}, ::Type{M}) where {CT,BT,M} + return MB.similar_type(BT, M) end """ @@ -117,8 +117,9 @@ function gram_basis(certificate::Newton{CT,B}, poly) where {CT,B} monomials_half_newton_polytope(MP.monomials(poly), certificate.newton), ) end -function gram_basis_type(::Type{<:Newton{CT,BT}}) where {CT,BT} - return BT + +function gram_basis_type(::Type{<:Newton{CT,BT}}, ::Type{M}) where {CT,BT,M} + return MB.similar_type(BT, M) end """ @@ -148,8 +149,9 @@ end function gram_basis(certificate::Remainder, poly) return gram_basis(certificate.gram_certificate, poly) end -function gram_basis_type(::Type{Remainder{GCT}}) where {GCT} - return gram_basis_type(GCT) + +function gram_basis_type(::Type{Remainder{GCT}}, ::Type{M}) where {GCT,M} + return gram_basis_type(GCT, M) end cone(certificate::Remainder) = cone(certificate.gram_certificate) diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index 66b2e6ddf..d818d1049 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -139,8 +139,8 @@ function multiplier_basis( ) return domain.bases[index.value] end -function multiplier_basis_type(::Type{<:Putinar{MC}}) where {MC} - return gram_basis_type(MC) +function multiplier_basis_type(::Type{<:Putinar{MC}}, ::Type{M}) where {MC,M} + return gram_basis_type(MC, M) end function generator( diff --git a/src/attributes.jl b/src/attributes.jl index 6db694345..7b73fd6a7 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -7,20 +7,28 @@ A constraint attribute for the basis indexing the struct CertificateBasis <: MOI.AbstractConstraintAttribute end """ - GramMatrixAttribute(result_index) - GramMatrixAttribute() + GramMatrixAttribute( + multiplier_index::Int = 0, + result_index::Int = 1, + ) -A constraint attribute for the [`GramMatrix`](@ref) of a constraint, that is, -the positive semidefinite matrix `Q` indexed by the monomials in the vector `X` -such that ``X^\\top Q X`` is the sum-of-squares certificate of the constraint. +A constraint attribute for the [`GramMatrix`](@ref) of the `multiplier_index`th +Sum-of-Squares polynomial ``s_i(x)`` where ``i`` is `multiplier_index` of the certificate: +```math +p(x) = s_0(x) + w_1(x) s_1(x) + \\cdots + w_m(x) s_m(x) +``` +The gram matrix of a Sum-of-Squares polynomial ``s_i(x)`` is the +the positive semidefinite matrix ``Q`` such that ``s_i(x) = b_i(x)^\\top Q b_i(x)`` +where ``b_i(x)`` is the gram basis. """ -struct GramMatrixAttribute <: MOI.AbstractConstraintAttribute - result_index::Int +@kwdef struct GramMatrixAttribute <: MOI.AbstractConstraintAttribute + multiplier_index::Int = 0 + result_index::Int = 1 end -GramMatrixAttribute() = GramMatrixAttribute(1) """ - struct SOSDecompositionAttribute + @kwdef struct SOSDecompositionAttribute + multiplier_index::Int = 0 ranktol::Real dec::MultivariateMoments.LowRankLDLTAlgorithm result_index::Int @@ -31,16 +39,11 @@ By default, it is computed using `SOSDecomposition(gram, ranktol, dec)` where `gram` is the value of the [`GramMatrixAttribute`](@ref). """ -struct SOSDecompositionAttribute <: MOI.AbstractConstraintAttribute +@kwdef struct SOSDecompositionAttribute <: MOI.AbstractConstraintAttribute + multiplier_index::Int = 0 ranktol::Real dec::MultivariateMoments.LowRankLDLTAlgorithm - result_index::Int -end -function SOSDecompositionAttribute( - ranktol::Real, - dec::MultivariateMoments.LowRankLDLTAlgorithm, -) - return SOSDecompositionAttribute(ranktol, dec, 1) + result_index::Int = 1 end function MOI.get_fallback( @@ -53,18 +56,54 @@ function MOI.get_fallback( end """ - MomentMatrixAttribute(N) - MomentMatrixAttribute() + MomentMatrixAttribute( + multiplier_index::Int = 0, + result_index::Int = 1, + ) -A constraint attribute fot the `MomentMatrix` of a constraint. +A constraint attribute for the `MomentMatrix` of the `multiplier_index`th +Sum-of-Squares polynomial ``s_i(x)`` where ``i`` is `multiplier_index` of the certificate: +```math +p(x) = s_0(x) + w_1(x) s_1(x) + \\cdots + w_m(x) s_m(x) +``` +It corresponds to the dual of the Sum-of-Squares constraint for the constraint +for ``s_i(x)`` to be a Sum-of-Squares. """ -struct MomentMatrixAttribute <: MOI.AbstractConstraintAttribute - result_index::Int +@kwdef struct MomentMatrixAttribute <: MOI.AbstractConstraintAttribute + multiplier_index::Int = 0 + result_index::Int = 1 +end + +""" + struct MultiplierIndexBoundsError{AttrType} <: Exception + attr::AttrType + range::UnitRange{Int} + end + +An error indicating that the requested attribute `attr` could not be retrieved, +because the multiplier index is out of the range of valid indices. +""" +struct MultiplierIndexBoundsError{AttrType} <: Exception + attr::AttrType + range::UnitRange{Int} +end + +function check_multiplier_index_bounds(attr, range) + if !(attr.multiplier_index in range) + throw(MultiplierIndexBoundsError(attr, range)) + end +end + +function Base.showerror(io::IO, err::MultiplierIndexBoundsError) + return print( + io, + "Multiplier index of attribute $(err.attr) out of bounds. The index " * + "must be in the range $(err.range).", + ) end -MomentMatrixAttribute() = MomentMatrixAttribute(1) """ - LagrangianMultipliers(N) + LagrangianMultipliers(result_index::Int) LagrangianMultipliers() A constraint attribute fot the `LagrangianMultipliers` associated to the diff --git a/src/constraints.jl b/src/constraints.jl index 7613a64c9..34dcf2223 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -306,12 +306,37 @@ function PolyJuMP.bridges( return [(Bridges.Constraint.ScaledDiagonallyDominantBridge, Float64)] end +function _bridge_coefficient_type( + ::Type{<:WeightedSOSCone{M}}, +) where {M} + return _complex(Float64, M) +end + function _bridge_coefficient_type( ::Type{SOSPolynomialSet{S,M,MV,C}}, ) where {S,M,MV,C} return _complex(Float64, matrix_cone_type(C)) end +function PolyJuMP.bridges( + S::Type{<:WeightedSOSCone}, +) + return Tuple{Type,Type}[( + Bridges.Variable.KernelBridge, + _bridge_coefficient_type(S), + )] +end + +function PolyJuMP.bridges( + F::Type{<:MOI.AbstractVectorFunction}, + ::Type{<:WeightedSOSCone}, +) # Needed so that `KernelBridge` is added as well + return Tuple{Type,Type}[( + MOI.Bridges.Constraint.VectorSlackBridge, + PolyJuMP._coef_type(F), + )] +end + function PolyJuMP.bridges( ::Type{<:MOI.AbstractVectorFunction}, S::Type{<:SOSPolynomialSet{<:AbstractAlgebraicSet}}, diff --git a/test/Project.toml b/test/Project.toml index 492f4adbb..6c0c1d8cb 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -3,6 +3,7 @@ Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" MultivariateBases = "be282fd4-ad43-11e9-1d11-8bd9d7e43378" MultivariateMoments = "f4abf1af-0426-5881-a0da-e2f168889b5e" MultivariatePolynomials = "102ac46a-7ee4-5c85-9060-abc95bfdeaa3" diff --git a/test/certificate.jl b/test/certificate.jl index fc764140a..2c85883e5 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -204,7 +204,7 @@ function certificate_api(certificate::Certificate.AbstractIdealCertificate) MP.AbstractPolynomial _basis_check( Certificate.gram_basis(certificate, poly), - Certificate.gram_basis_type(typeof(certificate)), + Certificate.gram_basis_type(typeof(certificate), MP.monomial_type(x)), ) zbasis = Certificate.zero_basis(certificate) @test zbasis <: MB.AbstractPolynomialBasis @@ -220,7 +220,7 @@ function certificate_api(certificate::Certificate.AbstractPreorderCertificate) for idx in Certificate.preorder_indices(certificate, processed) _basis_check( Certificate.multiplier_basis(certificate, idx, processed), - Certificate.multiplier_basis_type(typeof(certificate)), + Certificate.multiplier_basis_type(typeof(certificate), MP.monomial_type(x)), ) @test Certificate.generator(certificate, idx, processed) isa MP.AbstractPolynomial diff --git a/test/constraint.jl b/test/constraint.jl index 521b188ce..5d2ff0ba7 100644 --- a/test/constraint.jl +++ b/test/constraint.jl @@ -67,6 +67,6 @@ end @constraint(model, p in cone) @test SumOfSquares.Bridges.Constraint.SOSPolynomialBridge{ComplexF64} in model.bridge_types - @test PolyJuMP.Bridges.Constraint.ZeroPolynomialBridge{ComplexF64} in + @test SumOfSquares.Bridges.Variable.KernelBridge{ComplexF64} in model.bridge_types end From 26f5702edcbd5ccf9cc197b7530c55837ebf4533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 14 May 2024 12:17:49 +0200 Subject: [PATCH 02/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 2 ++ src/Bridges/Variable/kernel.jl | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 74788f739..3a22566eb 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -74,6 +74,8 @@ function MOI.Bridges.Constraint.concrete_bridge_type( return SOSPolynomialBridge{T,F,DT,M,G,CT,MT,MVT,W} end +MOI.Bridges.inverse_map_function(::Type{<:SOSPolynomialBridge}, f) = f + # Attributes, Bridge acting as a constraint function MOI.get( ::MOI.ModelLike, diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index e2b881d86..b7bc28609 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -110,17 +110,23 @@ function MOI.get( bridge::KernelBridge, i::MOI.Bridges.IndexInVector, ) - return MOI.Utilities.eval_variable(bridge.affine[i.value]) do - return vi -> MOI.get(model, MOI.VariablePrimal(attr.result_index), vi) + return MOI.Utilities.eval_variables(bridge.affine[i.value]) do vi + return MOI.get(model, MOI.VariablePrimal(attr.result_index), vi) end end function MOI.get( model::MOI.ModelLike, attr::SOS.GramMatrixAttribute, - bridge::KernelBridge, -) + bridge::KernelBridge{T,M}, +) where {T,M} SOS.check_multiplier_index_bounds(attr, eachindex(bridge.constraints)) + return SOS.build_gram_matrix( + convert(Vector{T}, MOI.get(model, MOI.VariablePrimal(), bridge.variables[attr.multiplier_index])), + bridge.set.gram_bases[attr.multiplier_index], + M, + T, + ) end function MOI.Bridges.bridged_function( From 6c96f5dfe399c4c6e01d0fecf8485f4eb567ab3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 14 May 2024 12:44:57 +0200 Subject: [PATCH 03/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 6 +++- src/Bridges/Variable/kernel.jl | 7 ++-- src/attributes.jl | 42 +++++++++++++----------- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 3a22566eb..2cb93a473 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -75,6 +75,7 @@ function MOI.Bridges.Constraint.concrete_bridge_type( end MOI.Bridges.inverse_map_function(::Type{<:SOSPolynomialBridge}, f) = f +MOI.Bridges.adjoint_map_function(::Type{<:SOSPolynomialBridge}, f) = f # Attributes, Bridge acting as a constraint function MOI.get( @@ -90,7 +91,10 @@ function MOI.get( attr::PolyJuMP.MomentsAttribute, bridge::SOSPolynomialBridge, ) - return MOI.get(model, attr, bridge.constraint) + return MultivariateMoments.Measure( + MOI.get(model, MOI.ConstraintDual(attr.result_index), bridge.constraint), + bridge.set.monomials, + ) end function MOI.get( diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index b7bc28609..f315fd715 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -115,14 +115,17 @@ function MOI.get( end end +_attr(attr::SOS.GramMatrixAttribute) = MOI.ConstraintPrimal(attr.result_index) +_attr(attr::SOS.MomentMatrixAttribute) = MOI.ConstraintDual(attr.result_index) + function MOI.get( model::MOI.ModelLike, - attr::SOS.GramMatrixAttribute, + attr::Union{SOS.GramMatrixAttribute,SOS.MomentMatrixAttribute}, bridge::KernelBridge{T,M}, ) where {T,M} SOS.check_multiplier_index_bounds(attr, eachindex(bridge.constraints)) return SOS.build_gram_matrix( - convert(Vector{T}, MOI.get(model, MOI.VariablePrimal(), bridge.variables[attr.multiplier_index])), + convert(Vector{T}, MOI.get(model, _attr(attr), bridge.constraints[attr.multiplier_index])), bridge.set.gram_bases[attr.multiplier_index], M, T, diff --git a/src/attributes.jl b/src/attributes.jl index 7b73fd6a7..2f82af601 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -117,32 +117,34 @@ struct LagrangianMultipliers <: MOI.AbstractConstraintAttribute end LagrangianMultipliers() = LagrangianMultipliers(1) +const _Attributes = Union{ + CertificateBasis, + GramMatrixAttribute, + SOSDecompositionAttribute, + MomentMatrixAttribute, + LagrangianMultipliers, +} + # Needs to declare it set by optimize that it is not queried in the Caching -# optimize, even of `CertificateBasis` which is set befor optimize. -function MOI.is_set_by_optimize( - ::Union{ - CertificateBasis, - GramMatrixAttribute, - SOSDecompositionAttribute, - MomentMatrixAttribute, - LagrangianMultipliers, - }, -) - return true -end +# optimize, even of `CertificateBasis` which is set before optimize. +MOI.is_set_by_optimize(::_Attributes) = true # If a variable is bridged, the `VectorOfVariables`-in-`SOSPolynomialSet` is # bridged by `MOI.Bridges.Constraint.VectorFunctionizeBridge` and it has # to pass the constraint to the SOS bridge. -function MOI.Bridges.Constraint.invariant_under_function_conversion( - ::Union{ - CertificateBasis, - GramMatrixAttribute, - MomentMatrixAttribute, - LagrangianMultipliers, +MOI.Bridges.Constraint.invariant_under_function_conversion(::_Attributes) = true + +# They do not contain any variable so `substitute_variables` would be the identity. +# We cannot just implement `substitute_variables` since it some variables cannot +# be unbridged. +function MOI.Bridges.unbridged_function( + ::MOI.Bridges.AbstractBridgeOptimizer, + value::Union{ + GramMatrix{T}, + MultivariateMoments.Measure{T}, }, -) - return true +) where {T<:Number} + return value end # This is type piracy but we tolerate it. From 8b1a17f54441be48d04be5582af3572d5a33213f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 22 May 2024 23:50:33 +0200 Subject: [PATCH 04/84] Update to StarAlgebras --- .github/workflows/ci.yml | 5 ++++- .github/workflows/documentation.yml | 5 +++++ Project.toml | 3 ++- docs/src/constraints.md | 4 ++-- docs/src/tutorials/Extension/certificate.jl | 4 ++-- src/Bridges/Constraint/image.jl | 7 +++---- src/Bridges/Constraint/sos_polynomial.jl | 6 +++--- src/Certificate/Certificate.jl | 13 +++++++------ src/Certificate/Sparsity/ideal.jl | 4 ++-- src/Certificate/Sparsity/monomial.jl | 12 ++++++------ src/Certificate/Symmetry/wedderburn.jl | 17 +++-------------- src/Certificate/ideal.jl | 14 +++++++------- src/Certificate/newton_polytope.jl | 6 +++--- src/SumOfSquares.jl | 2 ++ src/attributes.jl | 2 +- src/build_matrix.jl | 12 ++++++------ src/constraints.jl | 21 +++++++++------------ src/gram_matrix.jl | 20 ++++++++++---------- src/variables.jl | 2 +- test/Project.toml | 2 ++ test/certificate.jl | 4 ++-- 21 files changed, 82 insertions(+), 83 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49738d965..d13c1438f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,8 +37,11 @@ jobs: run: | using Pkg Pkg.add([ - PackageSpec(name="MultivariateBases", rev="master"), PackageSpec(name="MathOptInterface", rev="master"), + PackageSpec(name="StarAlgebras", rev="mk/non_monomial_basis"), + PackageSpec(name="MultivariateBases", rev="master"), + PackageSpec(name="MultivariateMoments", rev="master"), + PackageSpec(name="PolyJuMP", rev="master"), ]) - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index f0fa77399..3c35574dd 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -22,6 +22,11 @@ jobs: run: | using Pkg Pkg.add([ + PackageSpec(name="MathOptInterface", rev="master"), + PackageSpec(name="StarAlgebras", rev="mk/non_monomial_basis"), + PackageSpec(name="MultivariateBases", rev="master"), + PackageSpec(name="MultivariateMoments", rev="master"), + PackageSpec(name="PolyJuMP", rev="master"), PackageSpec(path=pwd()), ]) Pkg.instantiate() diff --git a/Project.toml b/Project.toml index 0343c6ac6..989d9c1ff 100644 --- a/Project.toml +++ b/Project.toml @@ -16,6 +16,7 @@ PolyJuMP = "ddf597a6-d67e-5340-b84c-e37d84115374" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SemialgebraicSets = "8e049039-38e8-557d-ae3a-bc521ccf6204" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +StarAlgebras = "0c0c59c1-dc5f-42e9-9a8b-b5dc384a6cd1" SymbolicWedderburn = "858aa9a9-4c7c-4c62-b466-2421203962a2" [compat] @@ -29,5 +30,5 @@ MutableArithmetics = "1" PolyJuMP = "0.7" Reexport = "0.2, 1.0" SemialgebraicSets = "0.3" -SymbolicWedderburn = "0.3" +SymbolicWedderburn = "0.4" julia = "1.6" diff --git a/docs/src/constraints.md b/docs/src/constraints.md index 354bd1d0e..4a0900b07 100644 --- a/docs/src/constraints.md +++ b/docs/src/constraints.md @@ -153,7 +153,7 @@ where `α` and `β` are JuMP decision variables and `x` and `y` are polynomial variables. Since the polynomial is a quadratic form, the sum-of-squares certificate is also a quadratic form (see [Blekherman2012; Section~3.3.4](@cite)). Hence the default polynomial basis used for the [Nonnegative polynomial variables] -certificate is `MonomialBasis([x, y])`, that is, we search for a positive +certificate is `MultivariateBases.SubBasis{MultivariateBases.Monomial}([x, y])`, that is, we search for a positive semidefinite matrix `Q` such that ```math \alpha x^2 + \beta y^2 - (\alpha - \beta) x y = X^\top Q X @@ -164,7 +164,7 @@ As the polynomial space is determined by the polynomial being constrained, only the basis *type* needs to be given. For instance, to use the scaled monomial basis in the example above, use ```jldoctest constraint-xy -julia> @constraint(model, α * x^2 + β * y^2 ≥ (α - β) * x * y, basis = ScaledMonomialBasis) +julia> @constraint(model, α * x^2 + β * y^2 ≥ (α - β) * x * y, basis = ScaledMonomial) (β)y² + (-α + β)xy + (α)x² is SOS ``` diff --git a/docs/src/tutorials/Extension/certificate.jl b/docs/src/tutorials/Extension/certificate.jl index a745d1f55..a405193f9 100644 --- a/docs/src/tutorials/Extension/certificate.jl +++ b/docs/src/tutorials/Extension/certificate.jl @@ -53,10 +53,10 @@ solution_summary(model) import MultivariateBases as MB const SOS = SumOfSquares const SOSC = SOS.Certificate -struct Schmüdgen{IC <: SOSC.AbstractIdealCertificate, CT <: SOS.SOSLikeCone, BT <: SOS.AbstractPolynomialBasis} <: SOSC.AbstractPreorderCertificate +struct Schmüdgen{IC<:SOSC.AbstractIdealCertificate,CT<:SOS.SOSLikeCone,B<:SA.AbstractBasis} <: SOSC.AbstractPreorderCertificate ideal_certificate::IC cone::CT - basis::Type{BT} + basis::B maxdegree::Int end diff --git a/src/Bridges/Constraint/image.jl b/src/Bridges/Constraint/image.jl index 0a012fabe..edd6cdfe1 100644 --- a/src/Bridges/Constraint/image.jl +++ b/src/Bridges/Constraint/image.jl @@ -119,9 +119,8 @@ function MOI.Bridges.Constraint.bridge_constraint( MOI.Utilities.operate_output_index!(-, T, k, f, var) else found[mono] = k - t = MP.searchsortedfirst(set.basis.monomials, mono) - if t in eachindex(set.basis.monomials) && - set.basis.monomials[t] == mono + t = MB.monomial_index(set.basis, mono) + if !isnothing(t) first[t] = k if is_diag MOI.Utilities.operate_output_index!( @@ -168,7 +167,7 @@ end function MOI.supports_constraint( ::Type{ImageBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, - ::Type{<:SOS.WeightedSOSCone{M,<:MB.MonomialBasis,<:MB.MonomialBasis}}, + ::Type{<:SOS.WeightedSOSCone{M,<:MB.SubBasis{MB.Monomial},<:MB.SubBasis{MB.Monomial}}}, ) where {T,M} return true end diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 2cb93a473..5e2c96c8e 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -8,8 +8,8 @@ struct SOSPolynomialBridge{ MT<:MP.AbstractMonomial, MVT<:AbstractVector{MT}, W<:MP.AbstractTerm{T}, -} <: MOI.Bridges.Constraint.SetMapBridge{T,SOS.WeightedSOSCone{M,MB.MonomialBasis{MT,MVT},G,W},SOS.SOSPolynomialSet{DT,MT,MVT,CT},F,F} - constraint::MOI.ConstraintIndex{F,SOS.WeightedSOSCone{M,MB.MonomialBasis{MT,MVT},G,W}} +} <: MOI.Bridges.Constraint.SetMapBridge{T,SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W},SOS.SOSPolynomialSet{DT,MT,MVT,CT},F,F} + constraint::MOI.ConstraintIndex{F,SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W}} set::SOS.SOSPolynomialSet{DT,MT,MVT,CT} end @@ -42,7 +42,7 @@ function MOI.Bridges.Constraint.bridge_constraint( model, func, SOS.WeightedSOSCone{M}( - MB.MonomialBasis(set.monomials), + MB.SubBasis{MB.Monomial}(set.monomials), [gram_basis], [MP.term(one(T), MP.constant_monomial(p))], ), diff --git a/src/Certificate/Certificate.jl b/src/Certificate/Certificate.jl index cf9b879fc..4112fc27f 100644 --- a/src/Certificate/Certificate.jl +++ b/src/Certificate/Certificate.jl @@ -1,6 +1,7 @@ module Certificate import MutableArithmetics as MA +import StarAlgebras as SA import MultivariatePolynomials as MP import MultivariateBases as MB using SemialgebraicSets @@ -45,7 +46,7 @@ function multiplier_basis_type end abstract type AbstractCertificate end function maxdegree_gram_basis( - B::Type{<:MB.AbstractMonomialBasis}, + ::MB.FullBasis{MB.Monomial}, bounds::DegreeBounds, ) variables = MP.variables(bounds.variablewise_maxdegree) @@ -53,15 +54,15 @@ function maxdegree_gram_basis( return MP.divides(bounds.variablewise_mindegree, mono) && MP.divides(mono, bounds.variablewise_maxdegree) end - return B(MP.monomials(variables, bounds.mindegree:bounds.maxdegree, filter)) + return MB.SubBasis{MB.Monomial}(MP.monomials(variables, bounds.mindegree:bounds.maxdegree, filter)) end -function maxdegree_gram_basis(B::Type, bounds::DegreeBounds) +function maxdegree_gram_basis(basis::SA.AbstractBasis, bounds::DegreeBounds) # TODO use bounds here too variables = MP.variables(bounds.variablewise_maxdegree) - return maxdegree_gram_basis(B, variables, bounds.maxdegree) + return maxdegree_gram_basis(basis, variables, bounds.maxdegree) end -function maxdegree_gram_basis(B::Type, variables, maxdegree::Int) - return MB.maxdegree_basis(B, variables, fld(maxdegree, 2)) +function maxdegree_gram_basis(basis::SA.AbstractBasis, variables, maxdegree::Int) + return MB.maxdegree_basis(basis, variables, fld(maxdegree, 2)) end include("ideal.jl") diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index baa093985..9d6225c4c 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -52,9 +52,9 @@ end function sparsity( monos, sp::Union{SignSymmetry,Monomial}, - gram_basis::MB.MonomialBasis, + gram_basis::MB.SubBasis{MB.Monomial}, ) - return MB.MonomialBasis.(sparsity(monos, sp, gram_basis.monomials)) + return MB.SubBasis{MB.Monomial}.(sparsity(monos, sp, gram_basis.monomials)) end function sparsity( poly::MP.AbstractPolynomial, diff --git a/src/Certificate/Sparsity/monomial.jl b/src/Certificate/Sparsity/monomial.jl index ed0d88030..f16f8fdd3 100644 --- a/src/Certificate/Sparsity/monomial.jl +++ b/src/Certificate/Sparsity/monomial.jl @@ -183,14 +183,14 @@ function sparsity( return _monomial_vector(cliques) end # This also checks that it is indeed a monomial basis -_monos(basis::MB.MonomialBasis) = basis.monomials +_monos(basis::MB.SubBasis{MB.Monomial}) = basis.monomials function _gram_monos( vars, - certificate::SumOfSquares.Certificate.MaxDegree{CT,MB.MonomialBasis}, -) where {CT} + certificate::SumOfSquares.Certificate.MaxDegree, +) return _monos( SumOfSquares.Certificate.maxdegree_gram_basis( - MB.MonomialBasis, + certificate.basis, vars, certificate.maxdegree, ), @@ -255,6 +255,6 @@ function sparsity( ) cliques, multiplier_cliques = sparsity(MP.monomials(poly), sp, gram_monos, multiplier_generator_monos) - return MB.MonomialBasis.(cliques), - [MB.MonomialBasis.(clique) for clique in multiplier_cliques] + return MB.SubBasis{MB.Monomial}.(cliques), + [MB.SubBasis{MB.Monomial}.(clique) for clique in multiplier_cliques] end diff --git a/src/Certificate/Symmetry/wedderburn.jl b/src/Certificate/Symmetry/wedderburn.jl index 048c9f3d8..4533d76f1 100644 --- a/src/Certificate/Symmetry/wedderburn.jl +++ b/src/Certificate/Symmetry/wedderburn.jl @@ -12,7 +12,7 @@ end function SymbolicWedderburn.ExtensionHomomorphism( action::SymbolicWedderburn.Action, - basis::MB.MonomialBasis, + basis::MB.SubBasis{MB.Monomial}, ) monos = collect(basis.monomials) return SymbolicWedderburn.ExtensionHomomorphism(Int, action, monos) @@ -53,17 +53,6 @@ function SymbolicWedderburn.action( ]) end -# TODO Move it to MultivariateBases -function MP.polynomial_type( - ::Type{<:MB.AbstractPolynomialVectorBasis{PT}}, - T::Type, -) where {PT} - C = MP.coefficient_type(PT) - U = MA.promote_operation(*, C, T) - V = MA.promote_operation(+, U, U) - return MP.polynomial_type(PT, V) -end - """ struct Symmetry.Ideal{C,GT,AT<:SymbolicWedderburn.Action} <: SumOfSquares.Certificate.AbstractIdealCertificate pattern::Symmetry.Pattern{GT,AT} @@ -89,8 +78,8 @@ end function SumOfSquares.Certificate.gram_basis_type(::Type{<:Ideal}, ::Type{M}) where {M} return Vector{Vector{MB.FixedPolynomialBasis}} end -SumOfSquares.Certificate.zero_basis_type(::Type{<:Ideal}) = MB.MonomialBasis -SumOfSquares.Certificate.zero_basis(::Ideal) = MB.MonomialBasis +SumOfSquares.Certificate.zero_basis_type(::Type{<:Ideal}) = MB.Monomial +SumOfSquares.Certificate.zero_basis(::Ideal) = MB.Monomial function SumOfSquares.Certificate.reduced_polynomial( certificate::Ideal, poly, diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 22fe0ac8a..84ba6fbf0 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -4,7 +4,7 @@ abstract type AbstractIdealCertificate <: AbstractCertificate end -abstract type SimpleIdealCertificate{CT,BT} <: AbstractIdealCertificate end +abstract type SimpleIdealCertificate{C,B} <: AbstractIdealCertificate end reduced_polynomial(::SimpleIdealCertificate, poly, domain) = poly cone(certificate::SimpleIdealCertificate) = certificate.cone @@ -15,9 +15,9 @@ function SumOfSquares.matrix_cone_type( end # TODO return something else when `PolyJuMP` support other bases. -zero_basis(::SimpleIdealCertificate) = MB.MonomialBasis -function zero_basis_type(::Type{<:SimpleIdealCertificate{CT,BT}}) where {CT,BT} - return MB.MonomialBasis +zero_basis(certificate::SimpleIdealCertificate) = certificate.basis # FIXME not used yet +function zero_basis_type(::Type{<:SimpleIdealCertificate{C,B}}) where {C,B} + return B end """ @@ -34,10 +34,10 @@ such that `h_i(x) = 0`. The polynomial `σ(x)` is search over `cone` with a basis of type `basis` such that the degree of `σ(x)` does not exceed `maxdegree`. """ -struct MaxDegree{CT<:SumOfSquares.SOSLikeCone,BT<:MB.AbstractPolynomialBasis} <: - SimpleIdealCertificate{CT,BT} +struct MaxDegree{CT<:SumOfSquares.SOSLikeCone,B<:SA.AbstractBasis} <: + SimpleIdealCertificate{CT,B} cone::CT - basis::Type{BT} + basis::B maxdegree::Int end function gram_basis(certificate::MaxDegree, poly) diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index c6a45bc92..51c0ff790 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -505,7 +505,7 @@ function multiplier_basis(g::MP.AbstractPolynomialLike, bounds::DegreeBounds) else halved = _half(shifted) end - basis = MB.MonomialBasis + basis = MB.FullBasis{MB.Monomial,MP.monomial_type(g)}() if isnothing(halved) # TODO add `MB.empty_basis` to API return MB.maxdegree_basis( @@ -541,13 +541,13 @@ function half_newton_polytope( bases = [multiplier_basis(g, bounds).monomials for g in gs] push!( bases, - maxdegree_gram_basis(MB.MonomialBasis, _half(bounds)).monomials, + maxdegree_gram_basis(MB.FullBasis{MB.Monomial,MP.monomial_type(p)}(), _half(bounds)).monomials, ) gs = copy(gs) push!(gs, one(eltype(gs))) filtered_bases = post_filter(p, gs, bases) # The last one will be recomputed by the ideal certificate - return MB.MonomialBasis.(filtered_bases[1:(end-1)]) + return MB.SubBasis{MB.Monomial}.(filtered_bases[1:(end-1)]) end struct SignCount unknown::Int diff --git a/src/SumOfSquares.jl b/src/SumOfSquares.jl index 141b6cfca..9a35c35a2 100644 --- a/src/SumOfSquares.jl +++ b/src/SumOfSquares.jl @@ -5,12 +5,14 @@ using LinearAlgebra import Reexport import MutableArithmetics as MA +import StarAlgebras as SA # MultivariatePolynomials extension import MultivariatePolynomials as MP const _APL = MP.AbstractPolynomialLike Reexport.@reexport using MultivariateBases +const MB = MultivariateBases # @set assumes that `SemialgebraicSets` is defined Reexport.@reexport using SemialgebraicSets Reexport.@reexport using MultivariateMoments diff --git a/src/attributes.jl b/src/attributes.jl index 2f82af601..fbea73789 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -141,7 +141,7 @@ function MOI.Bridges.unbridged_function( ::MOI.Bridges.AbstractBridgeOptimizer, value::Union{ GramMatrix{T}, - MultivariateMoments.Measure{T}, + MultivariateMoments.MomentVector{T}, }, ) where {T<:Number} return value diff --git a/src/build_matrix.jl b/src/build_matrix.jl index 9b6b7be8a..67ae9346c 100644 --- a/src/build_matrix.jl +++ b/src/build_matrix.jl @@ -30,7 +30,7 @@ end # Need these two methods to avoid ambiguity function build_gram_matrix( q::Vector, - basis::AbstractPolynomialBasis, + basis::SA.ExplicitBasis, matrix_cone_type, T::Type, ) @@ -71,7 +71,7 @@ end function build_matrix( Q::Function, - bases::Vector{<:AbstractPolynomialBasis}, + bases::Vector{<:SA.ExplicitBasis}, f::Function, ) return map(eachindex(bases)) do i @@ -80,7 +80,7 @@ function build_matrix( end function build_matrix( Q::Function, - bases::Vector{<:Vector{<:AbstractPolynomialBasis}}, + bases::Vector{<:Vector{<:SA.ExplicitBasis}}, f::Function, ) return [ @@ -117,7 +117,7 @@ end function add_gram_matrix( model::MOI.ModelLike, matrix_cone_type::Type, - basis::AbstractPolynomialBasis, + basis::SA.ExplicitBasis, T::Type, ) Q, cQ = MOI.add_constrained_variables( @@ -127,7 +127,7 @@ function add_gram_matrix( q = build_gram_matrix(Q, basis, matrix_cone_type, T) return q, Q, cQ end -_first(b::AbstractPolynomialBasis) = b +_first(b::SA.ExplicitBasis) = b _first(b::Vector) = first(b) function add_gram_matrix( model::MOI.ModelLike, @@ -158,6 +158,6 @@ function add_gram_matrix( return g, Qs, cQs end -function build_moment_matrix(q::Vector, basis::AbstractPolynomialBasis) +function build_moment_matrix(q::Vector, basis::SA.ExplicitBasis) return MomentMatrix(MultivariateMoments.SymMatrix(q, length(basis)), basis) end diff --git a/src/constraints.jl b/src/constraints.jl index 34dcf2223..04836d9bf 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -99,7 +99,7 @@ end function default_ideal_certificate( ::AbstractAlgebraicSet, ::Certificate.Sparsity.NoPattern, - basis::AbstractPolynomialBasis, + basis::SA.AbstractBasis, cone, args..., ) @@ -246,7 +246,7 @@ function JuMP.moi_set( cone::SOSLikeCone, monos::AbstractVector{<:MP.AbstractMonomial}; domain::AbstractSemialgebraicSet = FullSpace(), - basis = MonomialBasis, + basis = MB.Monomial, newton_polytope::Union{Nothing,Tuple} = tuple(), maxdegree::Union{Nothing,Int} = default_maxdegree(monos, domain), sparsity::Certificate.Sparsity.Pattern = Certificate.Sparsity.NoPattern(), @@ -272,10 +272,7 @@ function JuMP.moi_set( newton_polytope, ), ) - # For terms, `monomials` is `OneOrZeroElementVector` - # so we convert it with `monomial_vector` - # Later, we'll use `MP.MonomialBasis` which is going to do that anyway - return SOSPolynomialSet(domain, MP.monomial_vector(monos), certificate) + return SOSPolynomialSet(domain, MB.SubBasis{MB.Monomial}(monos), certificate) end function PolyJuMP.bridges( @@ -372,14 +369,14 @@ _promote_coef_type(::Type{V}, ::Type) where {V<:JuMP.AbstractVariableRef} = V _promote_coef_type(::Type{F}, ::Type{T}) where {F,T} = promote_type(F, T) function JuMP.build_constraint(_error::Function, p, cone::SOSLikeCone; kws...) - monos = MP.monomials(p) - set = JuMP.moi_set(cone, monos; kws...) + basis = MB.SubBasis{MB.Monomial}(MP.monomials(p)) + set = JuMP.moi_set(cone, basis; kws...) _coefs = PolyJuMP.non_constant_coefficients(p) # If a polynomial with real coefficients is used with the Hermitian SOS # cone, we want to promote the coefficients to complex T = _bridge_coefficient_type(typeof(set)) coefs = convert(Vector{_promote_coef_type(eltype(_coefs), T)}, _coefs) - shape = PolyJuMP.PolynomialShape(monos) + shape = PolyJuMP.PolynomialShape(basis) return PolyJuMP.bridgeable( JuMP.VectorConstraint(coefs, set, shape), JuMP.moi_function_type(typeof(coefs)), @@ -466,13 +463,13 @@ end certificate_monomials(cref::JuMP.ConstraintRef) Return the monomials of [`certificate_basis`](@ref). If the basis if not -`MultivariateBases.AbstractMonomialBasis`, an error is thrown. +`MultivariateBases.SubBasis`, an error is thrown. """ function certificate_monomials(cref::JuMP.ConstraintRef) return basis_monomials(certificate_basis(cref)) end -basis_monomials(basis::AbstractMonomialBasis) = basis.monomials -function basis_monomials(basis::AbstractPolynomialBasis) +basis_monomials(basis::MB.SubBasis) = basis.monomials +function basis_monomials(basis::SA.AbstractBasis) return error( "`certificate_monomials` is not supported with `$(typeof(basis))`, use `certificate_basis` instead.", ) diff --git a/src/gram_matrix.jl b/src/gram_matrix.jl index 19e10b1e4..035f14157 100644 --- a/src/gram_matrix.jl +++ b/src/gram_matrix.jl @@ -53,13 +53,13 @@ end function GramMatrix{T,B,U}( Q::AbstractMatrix{T}, basis::B, -) where {T,B<:AbstractPolynomialBasis,U} +) where {T,B<:SA.ExplicitBasis,U} return GramMatrix{T,B,U,typeof(Q)}(Q, basis) end function GramMatrix{T,B}( Q::AbstractMatrix{T}, basis::B, -) where {T,B<:AbstractPolynomialBasis} +) where {T,B<:SA.ExplicitBasis} return GramMatrix{T,B,_promote_sum(T)}(Q, basis) end function GramMatrix( @@ -68,7 +68,7 @@ function GramMatrix( MultivariateMoments.SymMatrix{T}, MultivariateMoments.VectorizedHermitianMatrix{T}, }, - basis::AbstractPolynomialBasis, + basis::SA.ExplicitBasis, ) where {T} return GramMatrix{T,typeof(basis)}(Q, basis) end @@ -102,7 +102,7 @@ MultivariateMoments.value_matrix(p::GramMatrix{T}) where {T} = p.Q function GramMatrix{T}( f::Function, - basis::AbstractPolynomialBasis, + basis::SA.ExplicitBasis, σ = 1:length(basis), ) where {T} return GramMatrix{T,typeof(basis)}( @@ -112,19 +112,19 @@ function GramMatrix{T}( end function GramMatrix{T}(f::Function, monos::AbstractVector) where {T} σ, sorted_monos = MP.sort_monomial_vector(monos) - return GramMatrix{T}(f, MonomialBasis(sorted_monos), σ) + return GramMatrix{T}(f, MB.SubBasis{MB.Monomial}(sorted_monos), σ) end function GramMatrix( Q::AbstractMatrix{T}, - basis::AbstractPolynomialBasis, + basis::SA.ExplicitBasis, σ = 1:length(basis), ) where {T} return GramMatrix{T}((i, j) -> Q[σ[i], σ[j]], basis) end function GramMatrix(Q::AbstractMatrix, monos::AbstractVector) σ, sorted_monos = MP.sort_monomial_vector(monos) - return GramMatrix(Q, MonomialBasis(sorted_monos), σ) + return GramMatrix(Q, MB.SubBasis{MB.Monomial}(sorted_monos), σ) end #function Base.convert{T, PT <: AbstractPolynomial{T}}(::Type{PT}, p::GramMatrix) @@ -141,11 +141,11 @@ end function change_basis( p::GramMatrix{T,B}, ::Type{B}, -) where {T,B<:AbstractPolynomialBasis} +) where {T,B<:SA.ExplicitBasis} return p end -function change_basis(p::GramMatrix, B::Type{<:AbstractPolynomialBasis}) - return GramMatrix(MultivariateBases.change_basis(p.Q, p.basis, B)...) +function change_basis(p::GramMatrix, basis::SA.AbstractBasis) + return GramMatrix(MultivariateBases.change_basis(p.Q, p.basis, basis)...) end """ diff --git a/src/variables.jl b/src/variables.jl index 82d468705..5a670e7ee 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -19,7 +19,7 @@ for poly in (:DSOSPoly, :SDSOSPoly, :SOSPoly) struct $poly{PB<:AbstractPolynomialBasis} <: PolyJuMP.AbstractPoly polynomial_basis::PB end - $poly(x::AbstractVector{<:_APL}) = $poly(MonomialBasis(x)) + $poly(x::AbstractVector{<:_APL}) = $poly(MB.SubBasis{MB.Monomial}(x)) end end diff --git a/test/Project.toml b/test/Project.toml index 6c0c1d8cb..c5370ac0f 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -10,7 +10,9 @@ MultivariatePolynomials = "102ac46a-7ee4-5c85-9060-abc95bfdeaa3" PolyJuMP = "ddf597a6-d67e-5340-b84c-e37d84115374" SemialgebraicSets = "8e049039-38e8-557d-ae3a-bc521ccf6204" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +StarAlgebras = "0c0c59c1-dc5f-42e9-9a8b-b5dc384a6cd1" SumOfSquares = "4b9e565b-77fc-50a5-a571-1244f986bda1" +SymbolicWedderburn = "858aa9a9-4c7c-4c62-b466-2421203962a2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] diff --git a/test/certificate.jl b/test/certificate.jl index 2c85883e5..5752db3d3 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -167,7 +167,7 @@ function _certificate_api(certificate::Certificate.AbstractCertificate) end function _basis_check_each(basis::MB.AbstractPolynomialBasis, basis_type) @test basis isa basis_type - if basis isa MB.AbstractMonomialBasis + if basis isa MB.SubBasis # This fails if `basis` is `Vector{<:Monomial}` instead of `MonomialVector` # for DynamicPolynomials. This is important as # `polynomial(::AbstractMatrix, ::MonomialVector, ::Type)` is implemented but @@ -233,7 +233,7 @@ end @testset "API" begin @polyvar x cone = SumOfSquares.SOSCone() - BT = MB.MonomialBasis + BT = MB.SubBasis{MB.Monomial} maxdegree = 2 function _test(certificate::Certificate.AbstractIdealCertificate) certificate_api(certificate) From 88a340af73a50e31c760f24a3ccb0056543cb1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 23 May 2024 11:03:14 +0200 Subject: [PATCH 05/84] Fixes --- .github/workflows/ci.yml | 1 + .github/workflows/documentation.yml | 1 + .github/workflows/examples.yml | 6 +++ src/Certificate/Sparsity/preorder.jl | 2 +- src/Certificate/ideal.jl | 64 ++++++++++++++-------------- src/constraints.jl | 19 ++++++++- test/certificate.jl | 31 +++++++------- 7 files changed, 75 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d13c1438f..9f11cabce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,7 @@ jobs: PackageSpec(name="MathOptInterface", rev="master"), PackageSpec(name="StarAlgebras", rev="mk/non_monomial_basis"), PackageSpec(name="MultivariateBases", rev="master"), + PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), PackageSpec(name="PolyJuMP", rev="master"), ]) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 3c35574dd..a460aa460 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -25,6 +25,7 @@ jobs: PackageSpec(name="MathOptInterface", rev="master"), PackageSpec(name="StarAlgebras", rev="mk/non_monomial_basis"), PackageSpec(name="MultivariateBases", rev="master"), + PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), PackageSpec(name="PolyJuMP", rev="master"), PackageSpec(path=pwd()), diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 8e5b6bdf6..29db92889 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -17,6 +17,12 @@ jobs: run: | using Pkg Pkg.add([ + PackageSpec(name="MathOptInterface", rev="master"), + PackageSpec(name="StarAlgebras", rev="mk/non_monomial_basis"), + PackageSpec(name="MultivariateBases", rev="master"), + PackageSpec(name="SemialgebraicSets", rev="master"), + PackageSpec(name="MultivariateMoments", rev="master"), + PackageSpec(name="PolyJuMP", rev="master"), PackageSpec(path=pwd()), ]) Pkg.instantiate() diff --git a/src/Certificate/Sparsity/preorder.jl b/src/Certificate/Sparsity/preorder.jl index e0e590f5b..db42821b9 100644 --- a/src/Certificate/Sparsity/preorder.jl +++ b/src/Certificate/Sparsity/preorder.jl @@ -65,7 +65,7 @@ function SumOfSquares.Certificate.multiplier_basis_type( ::Type{Preorder{S,C}}, ::Type{M}, ) where {S,C,M} - return Vector{SumOfSquares.Certificate.multiplier_basis_type(C, M)} + return SumOfSquares.Certificate.multiplier_basis_type(C, M) end function SumOfSquares.Certificate.generator( diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 84ba6fbf0..528563539 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -21,9 +21,9 @@ function zero_basis_type(::Type{<:SimpleIdealCertificate{C,B}}) where {C,B} end """ - struct MaxDegree{CT <: SumOfSquares.SOSLikeCone, BT <: MB.AbstractPolynomialBasis} <: SimpleIdealCertificate{CT, BT} - cone::CT - basis::Type{BT} + struct MaxDegree{C<:SumOfSquares.SOSLikeCone,B<:SA.AbstractBasis} <: SimpleIdealCertificate{C,B} + cone::C + basis::B maxdegree::Int end @@ -34,9 +34,9 @@ such that `h_i(x) = 0`. The polynomial `σ(x)` is search over `cone` with a basis of type `basis` such that the degree of `σ(x)` does not exceed `maxdegree`. """ -struct MaxDegree{CT<:SumOfSquares.SOSLikeCone,B<:SA.AbstractBasis} <: - SimpleIdealCertificate{CT,B} - cone::CT +struct MaxDegree{C<:SumOfSquares.SOSLikeCone,B<:SA.AbstractBasis} <: + SimpleIdealCertificate{C,B} + cone::C basis::B maxdegree::Int end @@ -47,12 +47,12 @@ function gram_basis(certificate::MaxDegree, poly) certificate.maxdegree, ) end -function gram_basis_type(::Type{MaxDegree{CT,BT}}, ::Type{M}) where {CT,BT,M} - return MB.similar_type(BT, M) +function gram_basis_type(::Type{MaxDegree{C,B}}, ::Type{M}) where {C,B,M} # TODO remove `M` it's not needed anymore + return MB.explicit_basis_type(B) end """ - struct FixedBasis{CT <: SumOfSquares.SOSLikeCone, BT <: MB.AbstractPolynomialBasis} <: SimpleIdealCertificate{CT, BT} + struct FixedBasis{C<:SumOfSquares.SOSLikeCone,B<:SA.ExplicitBasis} <: SimpleIdealCertificate{C,B} cone::CT basis::BT end @@ -64,24 +64,26 @@ such that `h_i(x) = 0`. The polynomial `σ(x)` is search over `cone` with basis `basis`. """ struct FixedBasis{ - CT<:SumOfSquares.SOSLikeCone, - BT<:MB.AbstractPolynomialBasis, -} <: SimpleIdealCertificate{CT,BT} - cone::CT - basis::BT + C<:SumOfSquares.SOSLikeCone, + B<:SA.ExplicitBasis, +} <: SimpleIdealCertificate{C,B} + cone::C + basis::B end -function gram_basis(certificate::FixedBasis, poly) +function gram_basis(certificate::FixedBasis, _) return certificate.basis end -function gram_basis_type(::Type{FixedBasis{CT,BT}}, ::Type{M}) where {CT,BT,M} - return MB.similar_type(BT, M) -end +gram_basis_type(::Type{FixedBasis{C,B}}, ::Type) where {C,B} = B """ - struct Newton{CT <: SumOfSquares.SOSLikeCone, BT <: MB.AbstractPolynomialBasis, NPT <: Tuple} <: SimpleIdealCertificate{CT, BT} - cone::CT - basis::Type{BT} - variable_groups::NPT + struct Newton{ + C<:SumOfSquares.SOSLikeCone, + B<:SA.AbstractBasis, + N<:AbstractNewtonPolytopeApproximation, + } <: SimpleIdealCertificate{C,B} + cone::C + basis::B + newton::N end The `Newton` certificate ensures the nonnegativity of `p(x)` for all `x` such that @@ -94,12 +96,12 @@ If `variable_groups = tuple()` then it falls back to the classical Newton polyto with all variables in the same part. """ struct Newton{ - CT<:SumOfSquares.SOSLikeCone, - BT<:MB.AbstractPolynomialBasis, + C<:SumOfSquares.SOSLikeCone, + B<:SA.AbstractBasis, N<:AbstractNewtonPolytopeApproximation, -} <: SimpleIdealCertificate{CT,BT} - cone::CT - basis::Type{BT} +} <: SimpleIdealCertificate{C,B} + cone::C + basis::B newton::N end @@ -111,15 +113,15 @@ function Newton(cone, basis, variable_groups::Tuple) ) end -function gram_basis(certificate::Newton{CT,B}, poly) where {CT,B} +function gram_basis(certificate::Newton, poly) return MB.basis_covering_monomials( - B, + certificate.basis, monomials_half_newton_polytope(MP.monomials(poly), certificate.newton), ) end -function gram_basis_type(::Type{<:Newton{CT,BT}}, ::Type{M}) where {CT,BT,M} - return MB.similar_type(BT, M) +function gram_basis_type(::Type{<:Newton{C,B}}, ::Type{M}) where {C,B,M} + return MB.explicit_basis_type(B) end """ diff --git a/src/constraints.jl b/src/constraints.jl index 04836d9bf..cbcbc896c 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -242,6 +242,21 @@ function default_maxdegree(monos, domain) return max(MP.maxdegree(monos), _maxdegree(domain)) end +function _promote_monomial_type(::Type{M1}, ::Type{D}) where {M1,D} + M2 = MP.monomial_type(D) + if isnothing(M2) + return M1 + else + return promote_type(M1, M2) + end +end + +_concrete_basis(basis::SA.AbstractBasis, _, _) = basis + +function _concrete_basis(::Type{B}, ::AbstractVector{M}, ::D) where {B<:MB.AbstractMonomialIndexed,M,D} + return MB.FullBasis{B,_promote_monomial_type(M, D)}() +end + function JuMP.moi_set( cone::SOSLikeCone, monos::AbstractVector{<:MP.AbstractMonomial}; @@ -257,7 +272,7 @@ function JuMP.moi_set( newton_of_remainder, symmetry, sparsity, - basis, + _concrete_basis(basis, monos, domain), cone, maxdegree, newton_polytope, @@ -267,7 +282,7 @@ function JuMP.moi_set( sparsity, ideal_certificate, cone, - basis, + _concrete_basis(basis, monos, domain), maxdegree, newton_polytope, ), diff --git a/test/certificate.jl b/test/certificate.jl index 5752db3d3..2b439e747 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -1,5 +1,5 @@ +import StarAlgebras as SA import MultivariatePolynomials as MP - import MultivariateBases as MB @testset "_merge_sorted" begin @@ -165,7 +165,7 @@ function _certificate_api(certificate::Certificate.AbstractCertificate) @test SumOfSquares.matrix_cone_type(typeof(certificate)) <: MOI.AbstractVectorSet end -function _basis_check_each(basis::MB.AbstractPolynomialBasis, basis_type) +function _basis_check_each(basis::SA.ExplicitBasis, basis_type) @test basis isa basis_type if basis isa MB.SubBasis # This fails if `basis` is `Vector{<:Monomial}` instead of `MonomialVector` @@ -181,14 +181,14 @@ function _basis_check_each(basis::MB.AbstractPolynomialBasis, basis_type) end end function _basis_check(basis, basis_type) - @test basis isa MB.AbstractPolynomialBasis || - basis isa Vector{<:MB.AbstractPolynomialBasis} + @test basis isa SA.ExplicitBasis || + basis isa Vector{<:SA.ExplicitBasis} if basis isa Vector # FIXME `basis_type` is `Vector{MB.MonomialBasis}` instead of `Vector{MB.MonomialBasis{...}}` # Once this is fixed, we should check # @test basis isa basis_type for b in basis - _basis_check_each(b, eltype(basis_type)) + _basis_check_each(b, basis_type) end else _basis_check_each(basis, basis_type) @@ -207,8 +207,8 @@ function certificate_api(certificate::Certificate.AbstractIdealCertificate) Certificate.gram_basis_type(typeof(certificate), MP.monomial_type(x)), ) zbasis = Certificate.zero_basis(certificate) - @test zbasis <: MB.AbstractPolynomialBasis - @test zbasis == Certificate.zero_basis_type(typeof(certificate)) + @test zbasis isa SA.AbstractBasis + @test zbasis isa Certificate.zero_basis_type(typeof(certificate)) end function certificate_api(certificate::Certificate.AbstractPreorderCertificate) @@ -233,7 +233,8 @@ end @testset "API" begin @polyvar x cone = SumOfSquares.SOSCone() - BT = MB.SubBasis{MB.Monomial} + B = MB.Monomial + full_basis = MB.FullBasis{B,MP.monomial_type(x)}() maxdegree = 2 function _test(certificate::Certificate.AbstractIdealCertificate) certificate_api(certificate) @@ -245,7 +246,7 @@ end mult_cert = mult_cert.gram_certificate end if mult_cert isa Certificate.FixedBasis # FIXME not supported yet - mult_cert = Certificate.MaxDegree(cone, BT, maxdegree) + mult_cert = Certificate.MaxDegree(cone, full_basis, maxdegree) end preorder = Certificate.Putinar(mult_cert, certificate, maxdegree) certificate_api(preorder) @@ -257,18 +258,18 @@ end certificate_api(Certificate.Sparsity.Preorder(sparsity, preorder)) end end - basis = BT(monomial_vector([x^2, x])) - @testset "$(typeof(certificate))" for certificate in [ - Certificate.MaxDegree(cone, BT, maxdegree), + basis = MB.SubBasis{B}(monomial_vector([x^2, x])) + @testset "$(nameof(typeof(certificate)))" for certificate in [ + Certificate.MaxDegree(cone, full_basis, maxdegree), Certificate.FixedBasis(cone, basis), - Certificate.Newton(cone, BT, tuple()), + Certificate.Newton(cone, full_basis, tuple()), ] _test(certificate) _test(Certificate.Remainder(certificate)) if certificate isa Certificate.MaxDegree _test(Certificate.Sparsity.Ideal(Sparsity.Variable(), certificate)) end - @testset "$(typeof(sparsity))" for sparsity in [ + @testset "$(nameof(typeof(sparsity)))" for sparsity in [ SignSymmetry(), Sparsity.Monomial(ChordalCompletion(), 1), ] @@ -295,7 +296,7 @@ function test_putinar_ijk(i, j, k, default::Bool, post_filter::Bool = default) if post_filter newton = Certificate.NewtonFilter(newton) end - cert = Certificate.Newton(SOSCone(), MB.MonomialBasis, newton) + cert = Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,MP.monomial_type(x * y)}(), newton) certificate = Certificate.Putinar(cert, cert, max(2i, 2j + 1, 2k + 1)) end processed = Certificate.preprocessed_domain(certificate, domain, poly) From f0e56be77d4756013a70ba2be21ff8e0bcc3cc91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 23 May 2024 11:12:39 +0200 Subject: [PATCH 06/84] Use basis, not monomials in SOSPolynomialSet --- bench/sos_polynomial.jl | 2 +- src/Bridges/Constraint/sos_polynomial.jl | 6 ++-- .../sos_polynomial_in_semialgebraic_set.jl | 13 ++++---- src/constraints.jl | 6 ++-- src/sets.jl | 30 +++++++++---------- test/Tests/quadratic.jl | 3 +- test/Tests/quartic_constant.jl | 3 +- test/Tests/term.jl | 3 +- test/Tests/term_fixed.jl | 3 +- test/Tests/univariate_sum.jl | 3 +- 10 files changed, 32 insertions(+), 40 deletions(-) diff --git a/bench/sos_polynomial.jl b/bench/sos_polynomial.jl index 030e50aba..6a49c0ab4 100644 --- a/bench/sos_polynomial.jl +++ b/bench/sos_polynomial.jl @@ -5,7 +5,7 @@ function sos_polynomial(n) @polyvar x monos = monomials(x, 0:2n) set = SumOfSquares.SOSPolynomialSet( - SumOfSquares.FullSpace(), monos, + SumOfSquares.FullSpace(), MB.SubBasis{MB.Monomial}(monos), SumOfSquares.Certificate.Remainder( SumOfSquares.SOSCone(), SumOfSquares.MonomialBasis, diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 5e2c96c8e..ed9974484 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -8,9 +8,9 @@ struct SOSPolynomialBridge{ MT<:MP.AbstractMonomial, MVT<:AbstractVector{MT}, W<:MP.AbstractTerm{T}, -} <: MOI.Bridges.Constraint.SetMapBridge{T,SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W},SOS.SOSPolynomialSet{DT,MT,MVT,CT},F,F} +} <: MOI.Bridges.Constraint.SetMapBridge{T,SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W},SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT},F,F} constraint::MOI.ConstraintIndex{F,SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W}} - set::SOS.SOSPolynomialSet{DT,MT,MVT,CT} + set::SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT} end function MOI.Bridges.Constraint.bridge_constraint( @@ -64,7 +64,7 @@ end function MOI.Bridges.Constraint.concrete_bridge_type( ::Type{<:SOSPolynomialBridge{T}}, F::Type{<:MOI.AbstractVectorFunction}, - ::Type{<:SOS.SOSPolynomialSet{DT,MT,MVT,CT}}, + ::Type{SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}}, ) where {T,DT<:SemialgebraicSets.AbstractAlgebraicSet,MT,MVT,CT} # promotes VectorOfVariables into VectorAffineFunction, it should be enough # for most use cases diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index 62c42ecd1..af9563156 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -30,7 +30,7 @@ struct SOSPolynomialInSemialgebraicSetBridge{ Union{Vector{MOI.VariableIndex},Vector{Vector{MOI.VariableIndex}}}, } lagrangian_constraints::Vector{UMCT} - constraint::MOI.ConstraintIndex{F,SOS.SOSPolynomialSet{DT,MT,MVT,CT}} + constraint::MOI.ConstraintIndex{F,SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}} monomials::MVT end @@ -77,7 +77,7 @@ function MOI.Bridges.Constraint.bridge_constraint( # For terms, `monomials` is `OneOrZeroElementVector` # so we convert it with `monomial_vector` # Later, we'll use `MP.MonomialBasis` which is going to do that anyway - MP.monomial_vector(MP.monomials(p)), + MB.SubBasis{MB.Monomial}(MP.monomial_vector(MP.monomials(p))), Certificate.ideal_certificate(set.certificate), ) constraint = MOI.add_constraint( @@ -123,7 +123,7 @@ function MOI.Bridges.added_constraint_types( SOSPolynomialInSemialgebraicSetBridge{T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT}, }, ) where {T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT} - return [(F, SOS.SOSPolynomialSet{DT,MT,MVT,CT})] + return [(F, SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT})] end function MOI.Bridges.Constraint.concrete_bridge_type( ::Type{<:SOSPolynomialInSemialgebraicSetBridge{T}}, @@ -131,8 +131,7 @@ function MOI.Bridges.Constraint.concrete_bridge_type( ::Type{ <:SOS.SOSPolynomialSet{ SemialgebraicSets.BasicSemialgebraicSet{S,PS,AT}, - MT, - MVT, + MB.SubBasis{MB.Monomial,MT,MVT}, CT, }, }, @@ -198,13 +197,13 @@ function MOI.get( end function MOI.get( ::SOSPolynomialInSemialgebraicSetBridge{T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT}, - ::MOI.NumberOfConstraints{F,SOS.SOSPolynomialSet{DT,MT,MVT,CT}}, + ::MOI.NumberOfConstraints{F,SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}}, ) where {T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT} return 1 end function MOI.get( b::SOSPolynomialInSemialgebraicSetBridge{T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT}, - ::MOI.ListOfConstraintIndices{F,SOS.SOSPolynomialSet{DT,MT,MVT,CT}}, + ::MOI.ListOfConstraintIndices{F,SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}}, ) where {T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT} return [b.constraint] end diff --git a/src/constraints.jl b/src/constraints.jl index cbcbc896c..76f15bb6d 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -99,7 +99,7 @@ end function default_ideal_certificate( ::AbstractAlgebraicSet, ::Certificate.Sparsity.NoPattern, - basis::SA.AbstractBasis, + basis::SA.ExplicitBasis, cone, args..., ) @@ -325,8 +325,8 @@ function _bridge_coefficient_type( end function _bridge_coefficient_type( - ::Type{SOSPolynomialSet{S,M,MV,C}}, -) where {S,M,MV,C} + ::Type{SOSPolynomialSet{D,B,C}}, +) where {D,B,C} return _complex(Float64, matrix_cone_type(C)) end diff --git a/src/sets.jl b/src/sets.jl index 83b549ac1..eb1f8153d 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -155,28 +155,26 @@ end """ struct SOSPolynomialSet{ - DT<:AbstractSemialgebraicSet, - MT<:MP.AbstractMonomial, - MVT<:AbstractVector{MT}, - CT<:Certificate.AbstractCertificate, + D<:AbstractSemialgebraicSet, + B<:SA.ExplicitBasis, + C<:Certificate.AbstractCertificate, } <: MOI.AbstractVectorSet - domain::DT - monomials::MVT - certificate::CT + domain::D + basis::B + certificate::C end -The sum-of-squares cone is the set of vectors of coefficients `a` for monomials `monomials` +The Sum-of-Squares cone is the set of vectors of coefficients `a` for `basis` for which the polynomial is a sum-of-squares `certificate` over `domain`. """ struct SOSPolynomialSet{ - DT<:AbstractSemialgebraicSet, - MT<:MP.AbstractMonomial, - MVT<:AbstractVector{MT}, - CT<:Certificate.AbstractCertificate, + D<:AbstractSemialgebraicSet, + B<:SA.ExplicitBasis, + C<:Certificate.AbstractCertificate, } <: MOI.AbstractVectorSet - domain::DT - monomials::MVT - certificate::CT + domain::D + basis::B + certificate::C end -MOI.dimension(set::SOSPolynomialSet) = length(set.monomials) +MOI.dimension(set::SOSPolynomialSet) = length(set.basis) Base.copy(set::SOSPolynomialSet) = set diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index e74e70e83..6c6840664 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -85,8 +85,7 @@ function quadratic_test( } S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, - monomial_type(x), - monomial_vector_type(x), + MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Newton{typeof(cone),basis,N}, } @test list_of_constraint_types(model) == [(Vector{AffExpr}, S)] diff --git a/test/Tests/quartic_constant.jl b/test/Tests/quartic_constant.jl index fc2b8f69b..ed840cad6 100644 --- a/test/Tests/quartic_constant.jl +++ b/test/Tests/quartic_constant.jl @@ -37,8 +37,7 @@ function quartic_constant_test( S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, - monomial_type(x), - monomial_vector_type(x), + MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.FixedBasis{typeof(cone),typeof(p.basis)}, } @test list_of_constraint_types(model) == [(Vector{AffExpr}, S)] diff --git a/test/Tests/term.jl b/test/Tests/term.jl index 3ca578c4e..2fd4f54f0 100644 --- a/test/Tests/term.jl +++ b/test/Tests/term.jl @@ -50,8 +50,7 @@ function term_test( } S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, - monomial_type(x), - monomial_vector_type(x), + MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Newton{typeof(cone),MonomialBasis,N}, } @test list_of_constraint_types(model) == [(Vector{VariableRef}, S)] diff --git a/test/Tests/term_fixed.jl b/test/Tests/term_fixed.jl index a1716083c..e54bc4c75 100644 --- a/test/Tests/term_fixed.jl +++ b/test/Tests/term_fixed.jl @@ -51,8 +51,7 @@ function term_fixed_test( } S = SumOfSquares.SOSPolynomialSet{ typeof(set), - monomial_type(x), - monomial_vector_type(x), + MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Remainder{ SumOfSquares.Certificate.Newton{typeof(cone),MonomialBasis,N}, }, diff --git a/test/Tests/univariate_sum.jl b/test/Tests/univariate_sum.jl index cc5504175..44faf502a 100644 --- a/test/Tests/univariate_sum.jl +++ b/test/Tests/univariate_sum.jl @@ -36,8 +36,7 @@ function univariate_sum_test( S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, - monomial_type(x), - monomial_vector_type(x), + MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Sparsity.Ideal{ Sparsity.Variable, SumOfSquares.Certificate.MaxDegree{typeof(cone),MonomialBasis}, From 2d940253056a8cb93f6893040e2cba87ac4dfd5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 23 May 2024 11:28:13 +0200 Subject: [PATCH 07/84] Fixes --- .github/workflows/ci.yml | 3 ++- .github/workflows/documentation.yml | 3 ++- .github/workflows/examples.yml | 3 ++- src/variables.jl | 10 ++++---- test/Bridges/Constraint/image.jl | 4 +-- test/Bridges/Variable/kernel.jl | 4 +-- test/Tests/quadratic.jl | 32 +++++++++++------------ test/gram_matrix.jl | 2 +- test/sparsity.jl | 39 +++++++++++++---------------- 9 files changed, 50 insertions(+), 50 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f11cabce..275f94505 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,8 @@ jobs: using Pkg Pkg.add([ PackageSpec(name="MathOptInterface", rev="master"), - PackageSpec(name="StarAlgebras", rev="mk/non_monomial_basis"), + PackageSpec(name="StarAlgebras", rev="bl/eachindex"), + PackageSpec(name="SymbolicWedderburn", rev="bl/supp"), PackageSpec(name="MultivariateBases", rev="master"), PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index a460aa460..bc900bc39 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -23,7 +23,8 @@ jobs: using Pkg Pkg.add([ PackageSpec(name="MathOptInterface", rev="master"), - PackageSpec(name="StarAlgebras", rev="mk/non_monomial_basis"), + PackageSpec(name="StarAlgebras", rev="bl/eachindex"), + PackageSpec(name="SymbolicWedderburn", rev="bl/supp"), PackageSpec(name="MultivariateBases", rev="master"), PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 29db92889..611db339f 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -18,7 +18,8 @@ jobs: using Pkg Pkg.add([ PackageSpec(name="MathOptInterface", rev="master"), - PackageSpec(name="StarAlgebras", rev="mk/non_monomial_basis"), + PackageSpec(name="StarAlgebras", rev="bl/eachindex"), + PackageSpec(name="SymbolicWedderburn", rev="bl/supp"), PackageSpec(name="MultivariateBases", rev="master"), PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), diff --git a/src/variables.jl b/src/variables.jl index 5a670e7ee..c9a62b7de 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -16,10 +16,10 @@ end for poly in (:DSOSPoly, :SDSOSPoly, :SOSPoly) @eval begin - struct $poly{PB<:AbstractPolynomialBasis} <: PolyJuMP.AbstractPoly - polynomial_basis::PB + struct $poly{B<:SA.ExplicitBasis} <: PolyJuMP.AbstractPoly + basis::B end - $poly(x::AbstractVector{<:_APL}) = $poly(MB.SubBasis{MB.Monomial}(x)) + $poly(monos::AbstractVector{<:_APL}) = $poly(MB.SubBasis{MB.Monomial}(monos)) end end @@ -55,7 +55,7 @@ function JuMP.add_variable( ::String = "", ) MCT = matrix_cone_type(v.p) - set = matrix_cone(MCT, length(v.p.polynomial_basis)) + set = matrix_cone(MCT, length(v.p.basis)) # FIXME There is no variable bridge mechanism yet: # https://github.com/jump-dev/MathOptInterface.jl/issues/710 # so there is no equivalent to `BridgeableConstraint`. @@ -72,7 +72,7 @@ function JuMP.add_variable( Q = moi_add_variable(backend(model), set, v.binary, v.integer) return build_gram_matrix( JuMP.VariableRef[JuMP.VariableRef(model, vi) for vi in Q], - v.p.polynomial_basis, + v.p.basis, MCT, Float64, ) diff --git a/test/Bridges/Constraint/image.jl b/test/Bridges/Constraint/image.jl index ae41983cf..c963376c1 100644 --- a/test/Bridges/Constraint/image.jl +++ b/test/Bridges/Constraint/image.jl @@ -31,8 +31,8 @@ function test_runtests() SumOfSquares.WeightedSOSCone{ MOI.PositiveSemidefiniteConeTriangle, }( - MonomialBasis([y^4, x^2 * y^2, x^3 * y, x^4]), - [MonomialBasis([y^2, x * y, x^2])], + MB.SubBasis{MB.Monomial}([y^4, x^2 * y^2, x^3 * y, x^4]), + [MB.SubBasis{MB.Monomial}([y^2, x * y, x^2])], [one(T) * x^0 * y^0], ), ) diff --git a/test/Bridges/Variable/kernel.jl b/test/Bridges/Variable/kernel.jl index 96f07ce0e..0f458875e 100644 --- a/test/Bridges/Variable/kernel.jl +++ b/test/Bridges/Variable/kernel.jl @@ -26,8 +26,8 @@ function test_runtests() SumOfSquares.WeightedSOSCone{ MOI.PositiveSemidefiniteConeTriangle, }( - MonomialBasis([x^4, x^3 * y, x^2 * y^2, y^4]), - [MonomialBasis([x^2, y^2, x * y])], + MB.SubBasis{MB.Monomial}([x^4, x^3 * y, x^2 * y^2, y^4]), + [MB.SubBasis{MB.Monomial}([x^2, y^2, x * y])], [1.0 * x^0 * y^0], ), ) diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index 6c6840664..eac9427bd 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -110,11 +110,11 @@ function quadratic_test( ) end function sos_univariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, SOSCone(), MonomialBasis, false) + return quadratic_test(optimizer, config, SOSCone(), MB.Monomial, false) end sd_tests["sos_univariate_quadratic"] = sos_univariate_quadratic_test function sos_bivariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, SOSCone(), MonomialBasis, true) + return quadratic_test(optimizer, config, SOSCone(), MB.Monomial, true) end sd_tests["sos_bivariate_quadratic"] = sos_bivariate_quadratic_test function sos_scaled_univariate_quadratic_test(optimizer, config) @@ -122,7 +122,7 @@ function sos_scaled_univariate_quadratic_test(optimizer, config) optimizer, config, SOSCone(), - ScaledMonomialBasis, + MB.ScaledMonomial, false, ) end @@ -133,7 +133,7 @@ function sos_scaled_bivariate_quadratic_test(optimizer, config) optimizer, config, SOSCone(), - ScaledMonomialBasis, + MB.ScaledMonomial, true, ) end @@ -148,11 +148,11 @@ end sd_tests["sos_scaled_bivariate_quadratic"] = sos_scaled_bivariate_quadratic_test function sdsos_univariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, SDSOSCone(), MonomialBasis, false) + return quadratic_test(optimizer, config, SDSOSCone(), MB.Monomial, false) end soc_tests["sdsos_univariate_quadratic"] = sdsos_univariate_quadratic_test function sdsos_bivariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, SDSOSCone(), MonomialBasis, true) + return quadratic_test(optimizer, config, SDSOSCone(), MB.Monomial, true) end soc_tests["sdsos_bivariate_quadratic"] = sdsos_bivariate_quadratic_test function sdsos_scaled_univariate_quadratic_test(optimizer, config) @@ -160,7 +160,7 @@ function sdsos_scaled_univariate_quadratic_test(optimizer, config) optimizer, config, SDSOSCone(), - ScaledMonomialBasis, + MB.ScaledMonomial, false, ) end @@ -171,29 +171,29 @@ function sdsos_scaled_bivariate_quadratic_test(optimizer, config) optimizer, config, SDSOSCone(), - ScaledMonomialBasis, + MB.ScaledMonomial, true, ) end soc_tests["sdsos_scaled_bivariate_quadratic"] = sdsos_scaled_bivariate_quadratic_test function sdsos_cheby_univariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, SDSOSCone(), ChebyshevBasis, false) + return quadratic_test(optimizer, config, SDSOSCone(), MB.Chebyshev, false) end soc_tests["sdsos_cheby_univariate_quadratic"] = sdsos_cheby_univariate_quadratic_test function sdsos_cheby_bivariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, SDSOSCone(), ChebyshevBasis, true) + return quadratic_test(optimizer, config, SDSOSCone(), MB.Chebyshev, true) end soc_tests["sdsos_scaled_bivariate_quadratic"] = sdsos_scaled_bivariate_quadratic_test function dsos_univariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, DSOSCone(), MonomialBasis, false) + return quadratic_test(optimizer, config, DSOSCone(), MB.Monomial, false) end linear_tests["dsos_univariate_quadratic"] = dsos_univariate_quadratic_test function dsos_bivariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, DSOSCone(), MonomialBasis, true) + return quadratic_test(optimizer, config, DSOSCone(), MB.Monomial, true) end linear_tests["dsos_bivariate_quadratic"] = dsos_bivariate_quadratic_test function dsos_scaled_univariate_quadratic_test(optimizer, config) @@ -201,7 +201,7 @@ function dsos_scaled_univariate_quadratic_test(optimizer, config) optimizer, config, DSOSCone(), - ScaledMonomialBasis, + MB.ScaledMonomial, false, ) end @@ -212,19 +212,19 @@ function dsos_scaled_bivariate_quadratic_test(optimizer, config) optimizer, config, DSOSCone(), - ScaledMonomialBasis, + MB.ScaledMonomial, true, ) end linear_tests["dsos_scaled_bivariate_quadratic"] = dsos_scaled_bivariate_quadratic_test function dsos_cheby_univariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, DSOSCone(), ChebyshevBasis, false) + return quadratic_test(optimizer, config, DSOSCone(), MB.Chebyshev, false) end linear_tests["dsos_cheby_univariate_quadratic"] = dsos_cheby_univariate_quadratic_test function dsos_cheby_bivariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, DSOSCone(), ChebyshevBasis, true) + return quadratic_test(optimizer, config, DSOSCone(), MB.Chebyshev, true) end linear_tests["dsos_cheby_bivariate_quadratic"] = dsos_cheby_bivariate_quadratic_test diff --git a/test/gram_matrix.jl b/test/gram_matrix.jl index 3edc44003..d4df7351b 100644 --- a/test/gram_matrix.jl +++ b/test/gram_matrix.jl @@ -195,7 +195,7 @@ using LinearAlgebra, Test, SumOfSquares v = MOI.VariableIndex.(1:3) w = MOI.VariableIndex.(1:4) @polyvar x y - basis = MonomialBasis(monomials([x, y], 1)) + basis = MB.SubBasis{MB.Monomial}(monomials([x, y], 1)) @testset "$T" for T in [Float64, Int, BigFloat] #@test_throws DimensionMismatch SumOfSquares.build_gram_matrix(w, basis, T, MOI.PositiveSemidefiniteConeTriangle) g = SumOfSquares.build_gram_matrix( diff --git a/test/sparsity.jl b/test/sparsity.jl index 252a9c799..eb29f73cf 100644 --- a/test/sparsity.jl +++ b/test/sparsity.jl @@ -28,7 +28,7 @@ function xor_complement_test() return end -function set_monos(bases::Vector{<:MB.MonomialBasis}) +function set_monos(bases::Vector{<:MB.SubBasis}) return Set([basis.monomials for basis in bases]) end @@ -40,9 +40,9 @@ Examples of [MWL19]. [WML19] Wang, Jie, Victor Magron, and Jean-Bernard Lasserre. "TSSOS: A Moment-SOS hierarchy that exploits term sparsity." arXiv preprint arXiv:1912.08899 (2019). """ function wml19() - certificate = Certificate.Newton(SOSCone(), MB.MonomialBasis, tuple()) + @polyvar x[1:3] + certificate = Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x))}(), tuple()) @testset "Example 4.2" begin - @polyvar x[1:3] f = 1 + x[1]^4 + x[2]^4 + x[3]^4 + prod(x) + x[2] expected_1_false = Set( monomial_vector.([ @@ -126,15 +126,14 @@ function wml19() end @testset "Example 5.4 $(typeof(ideal_certificate))" for ideal_certificate in [ - Certificate.MaxDegree(SOSCone(), MB.MonomialBasis, 4), - Certificate.Newton(SOSCone(), MB.MonomialBasis, tuple()), + Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), 4), + Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), tuple()), ] preorder_certificate = Certificate.Putinar( - Certificate.MaxDegree(SOSCone(), MB.MonomialBasis, 4), + Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), 4), ideal_certificate, 4, ) - @polyvar x[1:2] f = x[1]^4 + x[2]^4 + x[1] * x[2] K = @set 1 - 2x[1]^2 - x[2]^2 >= 0 @testset "$completion $k $use_all_monomials" for completion in [ @@ -195,7 +194,6 @@ function wml19() end end @testset "Example 6.7" begin - @polyvar x[1:2] f = 1 + x[1]^2 * x[2]^4 + x[1]^4 * x[2]^2 + x[1]^4 * x[2]^4 - x[1] * x[2]^2 - 3x[1]^2 * x[2]^2 @@ -251,9 +249,9 @@ Examples of [MWL19]. [L09] Lofberg, Johan. "Pre-and post-processing sum-of-squares programs in practice." IEEE transactions on automatic control 54.5 (2009): 1007-1011. """ function l09() - certificate = Certificate.Newton(SOSCone(), MB.MonomialBasis, tuple()) + @polyvar x[1:3] + certificate = Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), tuple()) @testset "Example 1 and 2" begin - @polyvar x[1:2] f = 1 + x[1]^4 * x[2]^2 + x[1]^2 * x[2]^4 newt = Certificate.NewtonDegreeBounds(tuple()) @test Certificate.monomials_half_newton_polytope(monomials(f), newt) == @@ -292,7 +290,6 @@ function l09() ) == expected end @testset "Example 3 and 4" begin - @polyvar x[1:3] f = 1 + x[1]^4 + x[1] * x[2] + x[2]^4 + x[3]^2 @testset "$k $use_all_monomials" for k in 0:2, use_all_monomials in [false, true] @@ -371,9 +368,9 @@ function l09() end end function square_domain(ideal_certificate, d) - mult_cert = Certificate.MaxDegree(SOSCone(), MB.MonomialBasis, d) - preorder_certificate = Certificate.Putinar(mult_cert, ideal_certificate, d) @polyvar x y + mult_cert = Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(x * y)}(), d) + preorder_certificate = Certificate.Putinar(mult_cert, ideal_certificate(typeof(x * y)), d) f = x^2 * y^4 + x^4 * y^2 - 3 * x^2 * y * 2 + 1 K = @set(1 - x^2 >= 0 && 1 - y^2 >= 0) @testset "Square domain $k $use_all_monomials" for k in 0:4, @@ -481,7 +478,7 @@ end function sum_square(n) @testset "Sum square" begin @polyvar x[1:(2n)] - certificate = Certificate.Newton(SOSCone(), MB.MonomialBasis, tuple()) + certificate = Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x))}(), tuple()) f = sum((x[1:2:(2n-1)] .- x[2:2:(2n)]) .^ 2) expected = Set( monomial_vector.([ @@ -492,7 +489,7 @@ function sum_square(n) Certificate.Sparsity.sparsity( f, Sparsity.Variable(), - Certificate.MaxDegree(SOSCone(), MB.MonomialBasis, 2), + Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x))}(), 2), ), ) == expected expected = Set(monomial_vector.([[x[(2i-1)], x[2i]] for i in 1:n])) @@ -505,7 +502,7 @@ function drop_monomials() @testset "Drop monomials" begin @polyvar x f = polynomial(x^2) - certificate = Certificate.MaxDegree(SOSCone(), MB.MonomialBasis, 2) + certificate = Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), 2) @testset "$k $use_all_monomials" for k in 0:2, use_all_monomials in [false, true] # The monomial `1˘ is dropped as it is useless. @@ -528,11 +525,11 @@ function drop_monomials() ) == expected end @testset "$(typeof(ideal_certificate))" for ideal_certificate in [ - Certificate.MaxDegree(SOSCone(), MB.MonomialBasis, 4), - Certificate.Newton(SOSCone(), MB.MonomialBasis, tuple()), + Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), 4), + Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), tuple()), ] preorder_certificate = Certificate.Putinar( - Certificate.MaxDegree(SOSCone(), MB.MonomialBasis, 3), + Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), 3), ideal_certificate, 3, ) @@ -589,8 +586,8 @@ end xor_complement_test() wml19() l09() - square_domain(Certificate.MaxDegree(SOSCone(), MB.MonomialBasis, 6), 6) - square_domain(Certificate.Newton(SOSCone(), MB.MonomialBasis, tuple()), 6) + square_domain(M -> Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,M}(), 6), 6) + square_domain(M -> Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,M}(), tuple()), 6) sum_square(8) @test Certificate.Sparsity.appropriate_type(32) == Int64 sum_square(32) From 666ba8e895836da3492bd6d0e3ee7b0cba92d026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 23 May 2024 11:28:19 +0200 Subject: [PATCH 08/84] Fix format --- src/Bridges/Bridges.jl | 6 +- src/Bridges/Constraint/image.jl | 8 +- src/Bridges/Constraint/sos_polynomial.jl | 30 +++++-- .../sos_polynomial_in_semialgebraic_set.jl | 15 +++- src/Bridges/Variable/kernel.jl | 9 +- src/Certificate/Certificate.jl | 15 ++-- src/Certificate/Sparsity/monomial.jl | 5 +- src/Certificate/Symmetry/wedderburn.jl | 5 +- src/Certificate/ideal.jl | 6 +- src/Certificate/newton_polytope.jl | 5 +- src/attributes.jl | 5 +- src/constraints.jl | 24 ++--- src/variables.jl | 3 +- test/Tests/quadratic.jl | 8 +- test/certificate.jl | 14 ++- test/sparsity.jl | 89 +++++++++++++++---- 16 files changed, 175 insertions(+), 72 deletions(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 035dd3dfe..6adadb339 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -8,7 +8,11 @@ include("Constraint/Constraint.jl") function MOI.get( model::MOI.ModelLike, - attr::Union{SOS.GramMatrixAttribute,SOS.MomentMatrixAttribute,SOS.SOSDecompositionAttribute}, + attr::Union{ + SOS.GramMatrixAttribute, + SOS.MomentMatrixAttribute, + SOS.SOSDecompositionAttribute, + }, bridge::MOI.Bridges.Constraint.VectorSlackBridge, ) return MOI.get(model, attr, bridge.slack_in_set) diff --git a/src/Bridges/Constraint/image.jl b/src/Bridges/Constraint/image.jl index edd6cdfe1..1c36252de 100644 --- a/src/Bridges/Constraint/image.jl +++ b/src/Bridges/Constraint/image.jl @@ -167,7 +167,13 @@ end function MOI.supports_constraint( ::Type{ImageBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, - ::Type{<:SOS.WeightedSOSCone{M,<:MB.SubBasis{MB.Monomial},<:MB.SubBasis{MB.Monomial}}}, + ::Type{ + <:SOS.WeightedSOSCone{ + M, + <:MB.SubBasis{MB.Monomial}, + <:MB.SubBasis{MB.Monomial}, + }, + }, ) where {T,M} return true end diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index ed9974484..e90deea34 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -8,8 +8,17 @@ struct SOSPolynomialBridge{ MT<:MP.AbstractMonomial, MVT<:AbstractVector{MT}, W<:MP.AbstractTerm{T}, -} <: MOI.Bridges.Constraint.SetMapBridge{T,SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W},SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT},F,F} - constraint::MOI.ConstraintIndex{F,SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W}} +} <: MOI.Bridges.Constraint.SetMapBridge{ + T, + SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W}, + SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}, + F, + F, +} + constraint::MOI.ConstraintIndex{ + F, + SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W}, + } set::SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT} end @@ -47,10 +56,7 @@ function MOI.Bridges.Constraint.bridge_constraint( [MP.term(one(T), MP.constant_monomial(p))], ), ) - return SOSPolynomialBridge{T,F,DT,M,G,CT,MT,MVT,W}( - constraint, - set, - ) + return SOSPolynomialBridge{T,F,DT,M,G,CT,MT,MVT,W}(constraint, set) end function MOI.supports_constraint( @@ -92,7 +98,11 @@ function MOI.get( bridge::SOSPolynomialBridge, ) return MultivariateMoments.Measure( - MOI.get(model, MOI.ConstraintDual(attr.result_index), bridge.constraint), + MOI.get( + model, + MOI.ConstraintDual(attr.result_index), + bridge.constraint, + ), bridge.set.monomials, ) end @@ -107,7 +117,11 @@ end function MOI.get( model::MOI.ModelLike, - attr::Union{SOS.GramMatrixAttribute,SOS.MomentMatrixAttribute,SOS.SOSDecompositionAttribute}, + attr::Union{ + SOS.GramMatrixAttribute, + SOS.MomentMatrixAttribute, + SOS.SOSDecompositionAttribute, + }, bridge::SOSPolynomialBridge, ) SOS.check_multiplier_index_bounds(attr, 0:0) diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index af9563156..e3045f86d 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -30,7 +30,10 @@ struct SOSPolynomialInSemialgebraicSetBridge{ Union{Vector{MOI.VariableIndex},Vector{Vector{MOI.VariableIndex}}}, } lagrangian_constraints::Vector{UMCT} - constraint::MOI.ConstraintIndex{F,SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}} + constraint::MOI.ConstraintIndex{ + F, + SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}, + } monomials::MVT end @@ -197,13 +200,19 @@ function MOI.get( end function MOI.get( ::SOSPolynomialInSemialgebraicSetBridge{T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT}, - ::MOI.NumberOfConstraints{F,SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}}, + ::MOI.NumberOfConstraints{ + F, + SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}, + }, ) where {T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT} return 1 end function MOI.get( b::SOSPolynomialInSemialgebraicSetBridge{T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT}, - ::MOI.ListOfConstraintIndices{F,SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}}, + ::MOI.ListOfConstraintIndices{ + F, + SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}, + }, ) where {T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT} return [b.constraint] end diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index f315fd715..1d09d5751 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -125,7 +125,14 @@ function MOI.get( ) where {T,M} SOS.check_multiplier_index_bounds(attr, eachindex(bridge.constraints)) return SOS.build_gram_matrix( - convert(Vector{T}, MOI.get(model, _attr(attr), bridge.constraints[attr.multiplier_index])), + convert( + Vector{T}, + MOI.get( + model, + _attr(attr), + bridge.constraints[attr.multiplier_index], + ), + ), bridge.set.gram_bases[attr.multiplier_index], M, T, diff --git a/src/Certificate/Certificate.jl b/src/Certificate/Certificate.jl index 4112fc27f..a56d579de 100644 --- a/src/Certificate/Certificate.jl +++ b/src/Certificate/Certificate.jl @@ -45,23 +45,26 @@ function multiplier_basis_type end abstract type AbstractCertificate end -function maxdegree_gram_basis( - ::MB.FullBasis{MB.Monomial}, - bounds::DegreeBounds, -) +function maxdegree_gram_basis(::MB.FullBasis{MB.Monomial}, bounds::DegreeBounds) variables = MP.variables(bounds.variablewise_maxdegree) function filter(mono) return MP.divides(bounds.variablewise_mindegree, mono) && MP.divides(mono, bounds.variablewise_maxdegree) end - return MB.SubBasis{MB.Monomial}(MP.monomials(variables, bounds.mindegree:bounds.maxdegree, filter)) + return MB.SubBasis{MB.Monomial}( + MP.monomials(variables, bounds.mindegree:bounds.maxdegree, filter), + ) end function maxdegree_gram_basis(basis::SA.AbstractBasis, bounds::DegreeBounds) # TODO use bounds here too variables = MP.variables(bounds.variablewise_maxdegree) return maxdegree_gram_basis(basis, variables, bounds.maxdegree) end -function maxdegree_gram_basis(basis::SA.AbstractBasis, variables, maxdegree::Int) +function maxdegree_gram_basis( + basis::SA.AbstractBasis, + variables, + maxdegree::Int, +) return MB.maxdegree_basis(basis, variables, fld(maxdegree, 2)) end diff --git a/src/Certificate/Sparsity/monomial.jl b/src/Certificate/Sparsity/monomial.jl index f16f8fdd3..1dc05facf 100644 --- a/src/Certificate/Sparsity/monomial.jl +++ b/src/Certificate/Sparsity/monomial.jl @@ -184,10 +184,7 @@ function sparsity( end # This also checks that it is indeed a monomial basis _monos(basis::MB.SubBasis{MB.Monomial}) = basis.monomials -function _gram_monos( - vars, - certificate::SumOfSquares.Certificate.MaxDegree, -) +function _gram_monos(vars, certificate::SumOfSquares.Certificate.MaxDegree) return _monos( SumOfSquares.Certificate.maxdegree_gram_basis( certificate.basis, diff --git a/src/Certificate/Symmetry/wedderburn.jl b/src/Certificate/Symmetry/wedderburn.jl index 4533d76f1..db353a523 100644 --- a/src/Certificate/Symmetry/wedderburn.jl +++ b/src/Certificate/Symmetry/wedderburn.jl @@ -75,7 +75,10 @@ end function SumOfSquares.matrix_cone_type(::Type{<:Ideal{C}}) where {C} return SumOfSquares.matrix_cone_type(C) end -function SumOfSquares.Certificate.gram_basis_type(::Type{<:Ideal}, ::Type{M}) where {M} +function SumOfSquares.Certificate.gram_basis_type( + ::Type{<:Ideal}, + ::Type{M}, +) where {M} return Vector{Vector{MB.FixedPolynomialBasis}} end SumOfSquares.Certificate.zero_basis_type(::Type{<:Ideal}) = MB.Monomial diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 528563539..268065d10 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -63,10 +63,8 @@ such that `p(x) - σ(x)` is guaranteed to be zero for all `x` such that `h_i(x) = 0`. The polynomial `σ(x)` is search over `cone` with basis `basis`. """ -struct FixedBasis{ - C<:SumOfSquares.SOSLikeCone, - B<:SA.ExplicitBasis, -} <: SimpleIdealCertificate{C,B} +struct FixedBasis{C<:SumOfSquares.SOSLikeCone,B<:SA.ExplicitBasis} <: + SimpleIdealCertificate{C,B} cone::C basis::B end diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 51c0ff790..f9a419423 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -541,7 +541,10 @@ function half_newton_polytope( bases = [multiplier_basis(g, bounds).monomials for g in gs] push!( bases, - maxdegree_gram_basis(MB.FullBasis{MB.Monomial,MP.monomial_type(p)}(), _half(bounds)).monomials, + maxdegree_gram_basis( + MB.FullBasis{MB.Monomial,MP.monomial_type(p)}(), + _half(bounds), + ).monomials, ) gs = copy(gs) push!(gs, one(eltype(gs))) diff --git a/src/attributes.jl b/src/attributes.jl index fbea73789..74675aff9 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -139,10 +139,7 @@ MOI.Bridges.Constraint.invariant_under_function_conversion(::_Attributes) = true # be unbridged. function MOI.Bridges.unbridged_function( ::MOI.Bridges.AbstractBridgeOptimizer, - value::Union{ - GramMatrix{T}, - MultivariateMoments.MomentVector{T}, - }, + value::Union{GramMatrix{T},MultivariateMoments.MomentVector{T}}, ) where {T<:Number} return value end diff --git a/src/constraints.jl b/src/constraints.jl index 76f15bb6d..90679d6f5 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -253,7 +253,11 @@ end _concrete_basis(basis::SA.AbstractBasis, _, _) = basis -function _concrete_basis(::Type{B}, ::AbstractVector{M}, ::D) where {B<:MB.AbstractMonomialIndexed,M,D} +function _concrete_basis( + ::Type{B}, + ::AbstractVector{M}, + ::D, +) where {B<:MB.AbstractMonomialIndexed,M,D} return MB.FullBasis{B,_promote_monomial_type(M, D)}() end @@ -287,7 +291,11 @@ function JuMP.moi_set( newton_polytope, ), ) - return SOSPolynomialSet(domain, MB.SubBasis{MB.Monomial}(monos), certificate) + return SOSPolynomialSet( + domain, + MB.SubBasis{MB.Monomial}(monos), + certificate, + ) end function PolyJuMP.bridges( @@ -318,21 +326,15 @@ function PolyJuMP.bridges( return [(Bridges.Constraint.ScaledDiagonallyDominantBridge, Float64)] end -function _bridge_coefficient_type( - ::Type{<:WeightedSOSCone{M}}, -) where {M} +function _bridge_coefficient_type(::Type{<:WeightedSOSCone{M}}) where {M} return _complex(Float64, M) end -function _bridge_coefficient_type( - ::Type{SOSPolynomialSet{D,B,C}}, -) where {D,B,C} +function _bridge_coefficient_type(::Type{SOSPolynomialSet{D,B,C}}) where {D,B,C} return _complex(Float64, matrix_cone_type(C)) end -function PolyJuMP.bridges( - S::Type{<:WeightedSOSCone}, -) +function PolyJuMP.bridges(S::Type{<:WeightedSOSCone}) return Tuple{Type,Type}[( Bridges.Variable.KernelBridge, _bridge_coefficient_type(S), diff --git a/src/variables.jl b/src/variables.jl index c9a62b7de..76a593978 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -19,7 +19,8 @@ for poly in (:DSOSPoly, :SDSOSPoly, :SOSPoly) struct $poly{B<:SA.ExplicitBasis} <: PolyJuMP.AbstractPoly basis::B end - $poly(monos::AbstractVector{<:_APL}) = $poly(MB.SubBasis{MB.Monomial}(monos)) + $poly(monos::AbstractVector{<:_APL}) = + $poly(MB.SubBasis{MB.Monomial}(monos)) end end diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index eac9427bd..35460ab39 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -129,13 +129,7 @@ end sd_tests["sos_scaled_univariate_quadratic"] = sos_scaled_univariate_quadratic_test function sos_scaled_bivariate_quadratic_test(optimizer, config) - return quadratic_test( - optimizer, - config, - SOSCone(), - MB.ScaledMonomial, - true, - ) + return quadratic_test(optimizer, config, SOSCone(), MB.ScaledMonomial, true) end sd_tests["sos_scaled_bivariate_quadratic"] = sos_scaled_bivariate_quadratic_test function sos_cheby_univariate_quadratic_test(optimizer, config) diff --git a/test/certificate.jl b/test/certificate.jl index 2b439e747..c9aacff78 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -181,8 +181,7 @@ function _basis_check_each(basis::SA.ExplicitBasis, basis_type) end end function _basis_check(basis, basis_type) - @test basis isa SA.ExplicitBasis || - basis isa Vector{<:SA.ExplicitBasis} + @test basis isa SA.ExplicitBasis || basis isa Vector{<:SA.ExplicitBasis} if basis isa Vector # FIXME `basis_type` is `Vector{MB.MonomialBasis}` instead of `Vector{MB.MonomialBasis{...}}` # Once this is fixed, we should check @@ -220,7 +219,10 @@ function certificate_api(certificate::Certificate.AbstractPreorderCertificate) for idx in Certificate.preorder_indices(certificate, processed) _basis_check( Certificate.multiplier_basis(certificate, idx, processed), - Certificate.multiplier_basis_type(typeof(certificate), MP.monomial_type(x)), + Certificate.multiplier_basis_type( + typeof(certificate), + MP.monomial_type(x), + ), ) @test Certificate.generator(certificate, idx, processed) isa MP.AbstractPolynomial @@ -296,7 +298,11 @@ function test_putinar_ijk(i, j, k, default::Bool, post_filter::Bool = default) if post_filter newton = Certificate.NewtonFilter(newton) end - cert = Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,MP.monomial_type(x * y)}(), newton) + cert = Certificate.Newton( + SOSCone(), + MB.FullBasis{MB.Monomial,MP.monomial_type(x * y)}(), + newton, + ) certificate = Certificate.Putinar(cert, cert, max(2i, 2j + 1, 2k + 1)) end processed = Certificate.preprocessed_domain(certificate, domain, poly) diff --git a/test/sparsity.jl b/test/sparsity.jl index eb29f73cf..21da8a1ab 100644 --- a/test/sparsity.jl +++ b/test/sparsity.jl @@ -41,7 +41,11 @@ Examples of [MWL19]. """ function wml19() @polyvar x[1:3] - certificate = Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x))}(), tuple()) + certificate = Certificate.Newton( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(prod(x))}(), + tuple(), + ) @testset "Example 4.2" begin f = 1 + x[1]^4 + x[2]^4 + x[3]^4 + prod(x) + x[2] expected_1_false = Set( @@ -126,11 +130,23 @@ function wml19() end @testset "Example 5.4 $(typeof(ideal_certificate))" for ideal_certificate in [ - Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), 4), - Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), tuple()), + Certificate.MaxDegree( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), + 4, + ), + Certificate.Newton( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), + tuple(), + ), ] preorder_certificate = Certificate.Putinar( - Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), 4), + Certificate.MaxDegree( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), + 4, + ), ideal_certificate, 4, ) @@ -250,7 +266,11 @@ Examples of [MWL19]. """ function l09() @polyvar x[1:3] - certificate = Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), tuple()) + certificate = Certificate.Newton( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), + tuple(), + ) @testset "Example 1 and 2" begin f = 1 + x[1]^4 * x[2]^2 + x[1]^2 * x[2]^4 newt = Certificate.NewtonDegreeBounds(tuple()) @@ -369,8 +389,13 @@ function l09() end function square_domain(ideal_certificate, d) @polyvar x y - mult_cert = Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(x * y)}(), d) - preorder_certificate = Certificate.Putinar(mult_cert, ideal_certificate(typeof(x * y)), d) + mult_cert = Certificate.MaxDegree( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(x * y)}(), + d, + ) + preorder_certificate = + Certificate.Putinar(mult_cert, ideal_certificate(typeof(x * y)), d) f = x^2 * y^4 + x^4 * y^2 - 3 * x^2 * y * 2 + 1 K = @set(1 - x^2 >= 0 && 1 - y^2 >= 0) @testset "Square domain $k $use_all_monomials" for k in 0:4, @@ -478,7 +503,11 @@ end function sum_square(n) @testset "Sum square" begin @polyvar x[1:(2n)] - certificate = Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x))}(), tuple()) + certificate = Certificate.Newton( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(prod(x))}(), + tuple(), + ) f = sum((x[1:2:(2n-1)] .- x[2:2:(2n)]) .^ 2) expected = Set( monomial_vector.([ @@ -489,7 +518,11 @@ function sum_square(n) Certificate.Sparsity.sparsity( f, Sparsity.Variable(), - Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x))}(), 2), + Certificate.MaxDegree( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(prod(x))}(), + 2, + ), ), ) == expected expected = Set(monomial_vector.([[x[(2i-1)], x[2i]] for i in 1:n])) @@ -502,7 +535,11 @@ function drop_monomials() @testset "Drop monomials" begin @polyvar x f = polynomial(x^2) - certificate = Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), 2) + certificate = Certificate.MaxDegree( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(x^2)}(), + 2, + ) @testset "$k $use_all_monomials" for k in 0:2, use_all_monomials in [false, true] # The monomial `1˘ is dropped as it is useless. @@ -525,11 +562,23 @@ function drop_monomials() ) == expected end @testset "$(typeof(ideal_certificate))" for ideal_certificate in [ - Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), 4), - Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), tuple()), + Certificate.MaxDegree( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(x^2)}(), + 4, + ), + Certificate.Newton( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(x^2)}(), + tuple(), + ), ] preorder_certificate = Certificate.Putinar( - Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), 3), + Certificate.MaxDegree( + SOSCone(), + MB.FullBasis{MB.Monomial,typeof(x^2)}(), + 3, + ), ideal_certificate, 3, ) @@ -586,8 +635,18 @@ end xor_complement_test() wml19() l09() - square_domain(M -> Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,M}(), 6), 6) - square_domain(M -> Certificate.Newton(SOSCone(), MB.FullBasis{MB.Monomial,M}(), tuple()), 6) + square_domain( + M -> Certificate.MaxDegree(SOSCone(), MB.FullBasis{MB.Monomial,M}(), 6), + 6, + ) + square_domain( + M -> Certificate.Newton( + SOSCone(), + MB.FullBasis{MB.Monomial,M}(), + tuple(), + ), + 6, + ) sum_square(8) @test Certificate.Sparsity.appropriate_type(32) == Int64 sum_square(32) From ef2fb1c5ed9619a40702dba5a7d10cac6a6246c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 23 May 2024 12:26:52 +0200 Subject: [PATCH 09/84] Fixes --- src/Bridges/Constraint/Constraint.jl | 1 + src/Bridges/Constraint/sos_polynomial.jl | 14 +++++----- .../sos_polynomial_in_semialgebraic_set.jl | 2 +- src/constraints.jl | 28 ++++++++++++------- src/sets.jl | 12 ++++---- test/Bridges/Constraint/image.jl | 1 + test/Bridges/Variable/kernel.jl | 1 + test/Tests/term.jl | 10 +++---- test/Tests/term_fixed.jl | 10 +++---- test/certificate.jl | 2 +- 10 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 511a8a835..3bb7c412a 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -3,6 +3,7 @@ module Constraint using LinearAlgebra import MutableArithmetics as MA +import StarAlgebras as SA import MathOptInterface as MOI diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index e90deea34..03468bb1f 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -3,7 +3,7 @@ struct SOSPolynomialBridge{ F<:MOI.AbstractVectorFunction, DT<:SemialgebraicSets.AbstractSemialgebraicSet, M, - G<:MB.AbstractPolynomialBasis, + G<:SA.ExplicitBasis, CT<:SOS.Certificate.AbstractIdealCertificate, MT<:MP.AbstractMonomial, MVT<:AbstractVector{MT}, @@ -28,11 +28,11 @@ function MOI.Bridges.Constraint.bridge_constraint( func::MOI.AbstractVectorFunction, set::SOS.SOSPolynomialSet{<:SemialgebraicSets.AbstractAlgebraicSet}, ) where {T,F,DT,M,G,CT,MT,MVT,W} - @assert MOI.output_dimension(func) == length(set.monomials) + @assert MOI.output_dimension(func) == length(set.basis) # MOI does not modify the coefficients of the functions so we can modify `p`. # without altering `f`. - # The monomials may be copied by MA however so we need to copy it. - p = MP.polynomial(MOI.Utilities.scalarize(func), copy(set.monomials)) + # The basis may be copied by MA however so we need to copy it. + p = MP.polynomial(MOI.Utilities.scalarize(func), copy(set.basis)) # As `*(::MOI.ScalarAffineFunction{T}, ::S)` is only defined if `S == T`, we # need to call `similar`. This is critical since `T` is # `Float64` when used with JuMP and the coefficient type is often `Int` if @@ -51,7 +51,7 @@ function MOI.Bridges.Constraint.bridge_constraint( model, func, SOS.WeightedSOSCone{M}( - MB.SubBasis{MB.Monomial}(set.monomials), + set.basis, [gram_basis], [MP.term(one(T), MP.constant_monomial(p))], ), @@ -97,13 +97,13 @@ function MOI.get( attr::PolyJuMP.MomentsAttribute, bridge::SOSPolynomialBridge, ) - return MultivariateMoments.Measure( + return MultivariateMoments.moment_vector( MOI.get( model, MOI.ConstraintDual(attr.result_index), bridge.constraint, ), - bridge.set.monomials, + bridge.set.basis, ) end diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index e3045f86d..a3c7a7a02 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -15,7 +15,7 @@ struct SOSPolynomialInSemialgebraicSetBridge{ F<:MOI.AbstractVectorFunction, DT<:SemialgebraicSets.AbstractSemialgebraicSet, CT<:Certificate.AbstractIdealCertificate, - B<:Union{Vector{<:MB.AbstractPolynomialBasis},MB.AbstractPolynomialBasis}, + B<:Union{Vector{<:MB.SA.ExplicitBasis},MB.SA.ExplicitBasis}, UMCT<:Union{ Vector{<:MOI.ConstraintIndex{MOI.VectorOfVariables}}, MOI.ConstraintIndex{MOI.VectorOfVariables}, diff --git a/src/constraints.jl b/src/constraints.jl index 90679d6f5..11b930f5d 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -251,23 +251,31 @@ function _promote_monomial_type(::Type{M1}, ::Type{D}) where {M1,D} end end -_concrete_basis(basis::SA.AbstractBasis, _, _) = basis +_default_basis(basis::SA.AbstractBasis, _, _) = basis -function _concrete_basis( +function _default_basis( ::Type{B}, - ::AbstractVector{M}, + ::SubBasis{_B,M}, ::D, -) where {B<:MB.AbstractMonomialIndexed,M,D} +) where {B<:MB.AbstractMonomialIndexed,_B,M,D} return MB.FullBasis{B,_promote_monomial_type(M, D)}() end +function _default_basis( + ::Nothing, + basis::SubBasis, + domain, +) + return _default_basis(MB.Monomial, basis, domain) +end + function JuMP.moi_set( cone::SOSLikeCone, - monos::AbstractVector{<:MP.AbstractMonomial}; + b::SA.ExplicitBasis; domain::AbstractSemialgebraicSet = FullSpace(), - basis = MB.Monomial, + basis = nothing, newton_polytope::Union{Nothing,Tuple} = tuple(), - maxdegree::Union{Nothing,Int} = default_maxdegree(monos, domain), + maxdegree::Union{Nothing,Int} = default_maxdegree(b, domain), sparsity::Certificate.Sparsity.Pattern = Certificate.Sparsity.NoPattern(), symmetry::Union{Nothing,Certificate.Symmetry.Pattern} = nothing, newton_of_remainder::Bool = false, @@ -276,7 +284,7 @@ function JuMP.moi_set( newton_of_remainder, symmetry, sparsity, - _concrete_basis(basis, monos, domain), + _default_basis(basis, b, domain), cone, maxdegree, newton_polytope, @@ -286,14 +294,14 @@ function JuMP.moi_set( sparsity, ideal_certificate, cone, - _concrete_basis(basis, monos, domain), + _default_basis(basis, b, domain), maxdegree, newton_polytope, ), ) return SOSPolynomialSet( domain, - MB.SubBasis{MB.Monomial}(monos), + b, certificate, ) end diff --git a/src/sets.jl b/src/sets.jl index eb1f8153d..8bf710af4 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -111,8 +111,8 @@ end """ struct WeightedSOSCone{ M, - B<:AbstractPolynomialBasis, - G<:AbstractPolynomialBasis, + B<:SA.ExplicitBasis, + G<:SA.ExplicitBasis, W<:MP.AbstractPolynomialLike, } <: MOI.AbstractVectorSet basis::B @@ -130,8 +130,8 @@ See [Papp2017; Section 1.1](@cite) and [Kapelevich2023; Section 1](@cite). """ struct WeightedSOSCone{ M, - B<:AbstractPolynomialBasis, - G<:AbstractPolynomialBasis, + B<:SA.ExplicitBasis, + G<:SA.ExplicitBasis, W<:MP.AbstractPolynomialLike, } <: MOI.AbstractVectorSet basis::B @@ -139,10 +139,10 @@ struct WeightedSOSCone{ weights::Vector{W} end function WeightedSOSCone{M}( - basis::AbstractPolynomialBasis, + basis::SA.ExplicitBasis, gram_bases::Vector{G}, weights::Vector{W}, -) where {M,G<:AbstractPolynomialBasis,W<:MP.AbstractPolynomialLike} +) where {M,G<:SA.ExplicitBasis,W<:MP.AbstractPolynomialLike} return WeightedSOSCone{M,typeof(basis),G,W}(basis, gram_bases, weights) end MOI.dimension(set::WeightedSOSCone) = length(set.basis) diff --git a/test/Bridges/Constraint/image.jl b/test/Bridges/Constraint/image.jl index c963376c1..ea83e619c 100644 --- a/test/Bridges/Constraint/image.jl +++ b/test/Bridges/Constraint/image.jl @@ -1,6 +1,7 @@ module TestConstraintImage using Test +import MultivariateBases as MB using DynamicPolynomials using SumOfSquares diff --git a/test/Bridges/Variable/kernel.jl b/test/Bridges/Variable/kernel.jl index 0f458875e..cf8f45936 100644 --- a/test/Bridges/Variable/kernel.jl +++ b/test/Bridges/Variable/kernel.jl @@ -1,6 +1,7 @@ module TestVariableKernel using Test +import MultivariateBases as MB using DynamicPolynomials using SumOfSquares diff --git a/test/Tests/term.jl b/test/Tests/term.jl index 2fd4f54f0..75f3587d4 100644 --- a/test/Tests/term.jl +++ b/test/Tests/term.jl @@ -38,7 +38,7 @@ function term_test( @test μ isa AbstractMeasure{Float64} @test length(moments(μ)) == 1 @test moment_value(moments(μ)[1]) ≈ 1.0 atol = atol rtol = rtol - @test monomial(moments(μ)[1]) == x^2 + @test moments(μ)[1].polynomial.monomial == x^2 end ν = moment_matrix(cref) @@ -50,8 +50,8 @@ function term_test( } S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, - MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, - SumOfSquares.Certificate.Newton{typeof(cone),MonomialBasis,N}, + SubBasis{Monomial,monomial_type(x),monomial_vector_type(x)}, + SumOfSquares.Certificate.Newton{typeof(cone),FullBasis{Monomial,monomial_type(x)},N}, } @test list_of_constraint_types(model) == [(Vector{VariableRef}, S)] return test_delete_bridge( @@ -64,9 +64,7 @@ function term_test( MOI.VectorAffineFunction{Float64}, SumOfSquares.PolyJuMP.ZeroPolynomialSet{ SumOfSquares.FullSpace, - MonomialBasis, - monomial_type(x), - monomial_vector_type(x), + SubBasis{Monomial,monomial_type(x),monomial_vector_type(x)}, }, 0, ), diff --git a/test/Tests/term_fixed.jl b/test/Tests/term_fixed.jl index e54bc4c75..da73e9467 100644 --- a/test/Tests/term_fixed.jl +++ b/test/Tests/term_fixed.jl @@ -39,7 +39,7 @@ function term_fixed_test( @test μ isa AbstractMeasure{Float64} @test length(moments(μ)) == 1 @test moment_value(moments(μ)[1]) ≈ 1.0 atol = atol rtol = rtol - @test monomial(moments(μ)[1]) == m + @test moments(μ)[1].polynomial.monomial == m end ν = moment_matrix(cref) @@ -51,9 +51,9 @@ function term_fixed_test( } S = SumOfSquares.SOSPolynomialSet{ typeof(set), - MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, + SubBasis{Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Remainder{ - SumOfSquares.Certificate.Newton{typeof(cone),MonomialBasis,N}, + SumOfSquares.Certificate.Newton{typeof(cone),FullBasis{Monomial,monomial_type(x)},N}, }, } @test list_of_constraint_types(model) == [(Vector{JuMP.AffExpr}, S)] @@ -67,9 +67,7 @@ function term_fixed_test( MOI.VectorAffineFunction{Float64}, SumOfSquares.PolyJuMP.ZeroPolynomialSet{ typeof(set), - MonomialBasis, - monomial_type(x), - monomial_vector_type(x), + SubBasis{Monomial,monomial_type(x),monomial_vector_type(x)}, }, 0, ), diff --git a/test/certificate.jl b/test/certificate.jl index c9aacff78..d930e0d5f 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -292,7 +292,7 @@ function test_putinar_ijk(i, j, k, default::Bool, post_filter::Bool = default) domain = @set y^(2k + 1) >= 0 if default certificate = - JuMP.moi_set(SOSCone(), monomials(poly); domain).certificate + JuMP.moi_set(SOSCone(), MB.SubBasis{MB.Monomial}(monomials(poly)); domain).certificate else newton = Certificate.NewtonDegreeBounds(tuple()) if post_filter From 72126eddc845db7d7f237be32d30c9ccd7647431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 23 May 2024 12:26:56 +0200 Subject: [PATCH 10/84] Fix format --- src/constraints.jl | 12 ++---------- test/Tests/term.jl | 6 +++++- test/Tests/term_fixed.jl | 6 +++++- test/certificate.jl | 6 +++++- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/constraints.jl b/src/constraints.jl index 11b930f5d..d8b464f4b 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -261,11 +261,7 @@ function _default_basis( return MB.FullBasis{B,_promote_monomial_type(M, D)}() end -function _default_basis( - ::Nothing, - basis::SubBasis, - domain, -) +function _default_basis(::Nothing, basis::SubBasis, domain) return _default_basis(MB.Monomial, basis, domain) end @@ -299,11 +295,7 @@ function JuMP.moi_set( newton_polytope, ), ) - return SOSPolynomialSet( - domain, - b, - certificate, - ) + return SOSPolynomialSet(domain, b, certificate) end function PolyJuMP.bridges( diff --git a/test/Tests/term.jl b/test/Tests/term.jl index 75f3587d4..1fffb80af 100644 --- a/test/Tests/term.jl +++ b/test/Tests/term.jl @@ -51,7 +51,11 @@ function term_test( S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, SubBasis{Monomial,monomial_type(x),monomial_vector_type(x)}, - SumOfSquares.Certificate.Newton{typeof(cone),FullBasis{Monomial,monomial_type(x)},N}, + SumOfSquares.Certificate.Newton{ + typeof(cone), + FullBasis{Monomial,monomial_type(x)}, + N, + }, } @test list_of_constraint_types(model) == [(Vector{VariableRef}, S)] return test_delete_bridge( diff --git a/test/Tests/term_fixed.jl b/test/Tests/term_fixed.jl index da73e9467..c284beabf 100644 --- a/test/Tests/term_fixed.jl +++ b/test/Tests/term_fixed.jl @@ -53,7 +53,11 @@ function term_fixed_test( typeof(set), SubBasis{Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Remainder{ - SumOfSquares.Certificate.Newton{typeof(cone),FullBasis{Monomial,monomial_type(x)},N}, + SumOfSquares.Certificate.Newton{ + typeof(cone), + FullBasis{Monomial,monomial_type(x)}, + N, + }, }, } @test list_of_constraint_types(model) == [(Vector{JuMP.AffExpr}, S)] diff --git a/test/certificate.jl b/test/certificate.jl index d930e0d5f..c2d7499fe 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -292,7 +292,11 @@ function test_putinar_ijk(i, j, k, default::Bool, post_filter::Bool = default) domain = @set y^(2k + 1) >= 0 if default certificate = - JuMP.moi_set(SOSCone(), MB.SubBasis{MB.Monomial}(monomials(poly)); domain).certificate + JuMP.moi_set( + SOSCone(), + MB.SubBasis{MB.Monomial}(monomials(poly)); + domain, + ).certificate else newton = Certificate.NewtonDegreeBounds(tuple()) if post_filter From 364f00bc61ad97a445e335f460a9b86bb7d1716b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 24 May 2024 19:19:40 +0200 Subject: [PATCH 11/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 21 ++++++++++++--------- src/Certificate/Sparsity/Sparsity.jl | 1 + src/Certificate/Sparsity/ideal.jl | 6 ++++++ src/Certificate/Symmetry/wedderburn.jl | 6 ++++++ src/Certificate/ideal.jl | 12 ++++++++++++ 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 03468bb1f..4db768042 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -2,7 +2,8 @@ struct SOSPolynomialBridge{ T, F<:MOI.AbstractVectorFunction, DT<:SemialgebraicSets.AbstractSemialgebraicSet, - M, + M, # matrix cone type + B, G<:SA.ExplicitBasis, CT<:SOS.Certificate.AbstractIdealCertificate, MT<:MP.AbstractMonomial, @@ -10,24 +11,24 @@ struct SOSPolynomialBridge{ W<:MP.AbstractTerm{T}, } <: MOI.Bridges.Constraint.SetMapBridge{ T, - SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W}, + SOS.WeightedSOSCone{M,B,G,W}, SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}, F, F, } constraint::MOI.ConstraintIndex{ F, - SOS.WeightedSOSCone{M,MB.SubBasis{MB.Monomial,MT,MVT},G,W}, + SOS.WeightedSOSCone{M,B,G,W}, } set::SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT} end function MOI.Bridges.Constraint.bridge_constraint( - ::Type{SOSPolynomialBridge{T,F,DT,M,G,CT,MT,MVT,W}}, + ::Type{SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}}, model::MOI.ModelLike, func::MOI.AbstractVectorFunction, set::SOS.SOSPolynomialSet{<:SemialgebraicSets.AbstractAlgebraicSet}, -) where {T,F,DT,M,G,CT,MT,MVT,W} +) where {T,F,DT,M,B,G,CT,MT,MVT,W} @assert MOI.output_dimension(func) == length(set.basis) # MOI does not modify the coefficients of the functions so we can modify `p`. # without altering `f`. @@ -38,10 +39,11 @@ function MOI.Bridges.Constraint.bridge_constraint( # `Float64` when used with JuMP and the coefficient type is often `Int` if # `set.domain.V` is `FullSpace` or `FixedPolynomialSet`. # FIXME convert needed because the coefficient type of `r` is `Any` otherwise if `domain` is `AlgebraicSet` + domain = MP.similar(set.domain, T) r = SOS.Certificate.reduced_polynomial( set.certificate, p, - MP.similar(set.domain, T), + domain, ) gram_basis = SOS.Certificate.gram_basis( set.certificate, @@ -51,12 +53,12 @@ function MOI.Bridges.Constraint.bridge_constraint( model, func, SOS.WeightedSOSCone{M}( - set.basis, + SOS.Certificate.reduced_basis(set.certificate, set.basis, domain), [gram_basis], [MP.term(one(T), MP.constant_monomial(p))], ), ) - return SOSPolynomialBridge{T,F,DT,M,G,CT,MT,MVT,W}(constraint, set) + return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}(constraint, set) end function MOI.supports_constraint( @@ -75,9 +77,10 @@ function MOI.Bridges.Constraint.concrete_bridge_type( # promotes VectorOfVariables into VectorAffineFunction, it should be enough # for most use cases M = SOS.matrix_cone_type(CT) + B = MA.promote_operation(SOS.Certificate.reduced_basis, CT, MB.SubBasis{MB.Monomial,MT,MVT}, SemialgebraicSets.similar_type(DT, T)) G = SOS.Certificate.gram_basis_type(CT, MT) W = MP.term_type(MT, T) - return SOSPolynomialBridge{T,F,DT,M,G,CT,MT,MVT,W} + return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W} end MOI.Bridges.inverse_map_function(::Type{<:SOSPolynomialBridge}, f) = f diff --git a/src/Certificate/Sparsity/Sparsity.jl b/src/Certificate/Sparsity/Sparsity.jl index 170d5b3c9..a0a49bbf8 100644 --- a/src/Certificate/Sparsity/Sparsity.jl +++ b/src/Certificate/Sparsity/Sparsity.jl @@ -1,5 +1,6 @@ module Sparsity +import MutableArithmetics as MA import MultivariatePolynomials as MP const _APL = MP.AbstractPolynomialLike import MultivariateBases as MB diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index 9d6225c4c..ddce7ebc7 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -90,6 +90,12 @@ function SumOfSquares.Certificate.reduced_polynomial( domain, ) end +function SumOfSquares.Certificate.reduced_basis(certificate::Ideal, basis, domain) + return SumOfSquares.Certificate.reduced_basis(certificate.certificate, basis, domain) +end +function MA.promote_operation(::typeof(SumOfSquares.Certificate.reduced_basis), ::Type{Ideal{S,C}}, ::Type{B}, ::Type{D}) where {S,C,B,D} + return MA.promote_operation(SumOfSquares.Certificate.reduced_basis, C, B, D) +end function SumOfSquares.Certificate.cone(certificate::Ideal) return SumOfSquares.Certificate.cone(certificate.certificate) end diff --git a/src/Certificate/Symmetry/wedderburn.jl b/src/Certificate/Symmetry/wedderburn.jl index db353a523..9ea533ae1 100644 --- a/src/Certificate/Symmetry/wedderburn.jl +++ b/src/Certificate/Symmetry/wedderburn.jl @@ -94,6 +94,12 @@ function SumOfSquares.Certificate.reduced_polynomial( domain, ) end +function SumOfSquares.Certificate.reduced_basis(certificate::Ideal, basis, domain) + return SumOfSquares.Certificate.reduced_basis(certificate.certificate, basis, domain) +end +function MA.promote_operation(::typeof(SumOfSquares.Certificate.reduced_basis), ::Type{Ideal{S,C}}, ::Type{B}, ::Type{D}) where {S,C,B,D} + return MA.promote_operation(SumOfSquares.Certificate.reduced_basis, C, B, D) +end function matrix_reps(pattern, R, basis, ::Type{T}, form) where {T} polys = R * basis.monomials diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 268065d10..cac23b10d 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -6,6 +6,10 @@ abstract type AbstractIdealCertificate <: AbstractCertificate end abstract type SimpleIdealCertificate{C,B} <: AbstractIdealCertificate end reduced_polynomial(::SimpleIdealCertificate, poly, domain) = poly +reduced_basis(::SimpleIdealCertificate, basis, domain) = basis +function MA.promote_operation(::typeof(reduced_basis), ::Type{<:SimpleIdealCertificate}, ::Type{B}, ::Type) where {B} + return B +end cone(certificate::SimpleIdealCertificate) = certificate.cone function SumOfSquares.matrix_cone_type( @@ -146,6 +150,14 @@ function reduced_polynomial(::Remainder, poly, domain) return convert(typeof(poly), rem(poly, ideal(domain))) end +function reduced_basis(::Remainder, basis, domain) + return MB.QuotientBasis(basis, ideal(domain)) +end + +function MA.promote_operation(::typeof(reduced_basis), ::Type{<:Remainder}, ::Type{B}, ::Type{D}) where {T,I,B<:SA.AbstractBasis{T,I},D} + return MB.QuotientBasis{T,I,B,MA.promote_operation(SemialgebraicSets.ideal, D)} +end + function gram_basis(certificate::Remainder, poly) return gram_basis(certificate.gram_certificate, poly) end From d6f93e99b2039b8a9486365086d08764b9d32d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 25 May 2024 10:46:30 +0200 Subject: [PATCH 12/84] Fix format --- src/Bridges/Constraint/sos_polynomial.jl | 18 ++++++++---------- src/Certificate/Sparsity/ideal.jl | 19 ++++++++++++++++--- src/Certificate/Symmetry/wedderburn.jl | 19 ++++++++++++++++--- src/Certificate/ideal.jl | 21 ++++++++++++++++++--- 4 files changed, 58 insertions(+), 19 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 4db768042..1bc023494 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -16,10 +16,7 @@ struct SOSPolynomialBridge{ F, F, } - constraint::MOI.ConstraintIndex{ - F, - SOS.WeightedSOSCone{M,B,G,W}, - } + constraint::MOI.ConstraintIndex{F,SOS.WeightedSOSCone{M,B,G,W}} set::SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT} end @@ -40,11 +37,7 @@ function MOI.Bridges.Constraint.bridge_constraint( # `set.domain.V` is `FullSpace` or `FixedPolynomialSet`. # FIXME convert needed because the coefficient type of `r` is `Any` otherwise if `domain` is `AlgebraicSet` domain = MP.similar(set.domain, T) - r = SOS.Certificate.reduced_polynomial( - set.certificate, - p, - domain, - ) + r = SOS.Certificate.reduced_polynomial(set.certificate, p, domain) gram_basis = SOS.Certificate.gram_basis( set.certificate, SOS.Certificate.with_variables(r, set.domain), @@ -77,7 +70,12 @@ function MOI.Bridges.Constraint.concrete_bridge_type( # promotes VectorOfVariables into VectorAffineFunction, it should be enough # for most use cases M = SOS.matrix_cone_type(CT) - B = MA.promote_operation(SOS.Certificate.reduced_basis, CT, MB.SubBasis{MB.Monomial,MT,MVT}, SemialgebraicSets.similar_type(DT, T)) + B = MA.promote_operation( + SOS.Certificate.reduced_basis, + CT, + MB.SubBasis{MB.Monomial,MT,MVT}, + SemialgebraicSets.similar_type(DT, T), + ) G = SOS.Certificate.gram_basis_type(CT, MT) W = MP.term_type(MT, T) return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W} diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index ddce7ebc7..410dfd650 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -90,10 +90,23 @@ function SumOfSquares.Certificate.reduced_polynomial( domain, ) end -function SumOfSquares.Certificate.reduced_basis(certificate::Ideal, basis, domain) - return SumOfSquares.Certificate.reduced_basis(certificate.certificate, basis, domain) +function SumOfSquares.Certificate.reduced_basis( + certificate::Ideal, + basis, + domain, +) + return SumOfSquares.Certificate.reduced_basis( + certificate.certificate, + basis, + domain, + ) end -function MA.promote_operation(::typeof(SumOfSquares.Certificate.reduced_basis), ::Type{Ideal{S,C}}, ::Type{B}, ::Type{D}) where {S,C,B,D} +function MA.promote_operation( + ::typeof(SumOfSquares.Certificate.reduced_basis), + ::Type{Ideal{S,C}}, + ::Type{B}, + ::Type{D}, +) where {S,C,B,D} return MA.promote_operation(SumOfSquares.Certificate.reduced_basis, C, B, D) end function SumOfSquares.Certificate.cone(certificate::Ideal) diff --git a/src/Certificate/Symmetry/wedderburn.jl b/src/Certificate/Symmetry/wedderburn.jl index 9ea533ae1..0809d2d26 100644 --- a/src/Certificate/Symmetry/wedderburn.jl +++ b/src/Certificate/Symmetry/wedderburn.jl @@ -94,10 +94,23 @@ function SumOfSquares.Certificate.reduced_polynomial( domain, ) end -function SumOfSquares.Certificate.reduced_basis(certificate::Ideal, basis, domain) - return SumOfSquares.Certificate.reduced_basis(certificate.certificate, basis, domain) +function SumOfSquares.Certificate.reduced_basis( + certificate::Ideal, + basis, + domain, +) + return SumOfSquares.Certificate.reduced_basis( + certificate.certificate, + basis, + domain, + ) end -function MA.promote_operation(::typeof(SumOfSquares.Certificate.reduced_basis), ::Type{Ideal{S,C}}, ::Type{B}, ::Type{D}) where {S,C,B,D} +function MA.promote_operation( + ::typeof(SumOfSquares.Certificate.reduced_basis), + ::Type{Ideal{S,C}}, + ::Type{B}, + ::Type{D}, +) where {S,C,B,D} return MA.promote_operation(SumOfSquares.Certificate.reduced_basis, C, B, D) end diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index cac23b10d..5f4181274 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -7,7 +7,12 @@ abstract type AbstractIdealCertificate <: AbstractCertificate end abstract type SimpleIdealCertificate{C,B} <: AbstractIdealCertificate end reduced_polynomial(::SimpleIdealCertificate, poly, domain) = poly reduced_basis(::SimpleIdealCertificate, basis, domain) = basis -function MA.promote_operation(::typeof(reduced_basis), ::Type{<:SimpleIdealCertificate}, ::Type{B}, ::Type) where {B} +function MA.promote_operation( + ::typeof(reduced_basis), + ::Type{<:SimpleIdealCertificate}, + ::Type{B}, + ::Type, +) where {B} return B end @@ -154,8 +159,18 @@ function reduced_basis(::Remainder, basis, domain) return MB.QuotientBasis(basis, ideal(domain)) end -function MA.promote_operation(::typeof(reduced_basis), ::Type{<:Remainder}, ::Type{B}, ::Type{D}) where {T,I,B<:SA.AbstractBasis{T,I},D} - return MB.QuotientBasis{T,I,B,MA.promote_operation(SemialgebraicSets.ideal, D)} +function MA.promote_operation( + ::typeof(reduced_basis), + ::Type{<:Remainder}, + ::Type{B}, + ::Type{D}, +) where {T,I,B<:SA.AbstractBasis{T,I},D} + return MB.QuotientBasis{ + T, + I, + B, + MA.promote_operation(SemialgebraicSets.ideal, D), + } end function gram_basis(certificate::Remainder, poly) From 623e0e2d82667f6b22a3ac501c32b3f9a60cac06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 27 May 2024 10:35:51 +0200 Subject: [PATCH 13/84] up --- src/Bridges/Constraint/sos_polynomial.jl | 28 ++++++++------- src/Certificate/Sparsity/ideal.jl | 16 ++++++--- src/Certificate/Symmetry/wedderburn.jl | 16 ++++++--- src/Certificate/ideal.jl | 46 ++++++++++++++++++++---- src/Certificate/preorder.jl | 4 +-- src/attributes.jl | 2 +- test/Mock/mock_tests.jl | 18 +++++----- test/Project.toml | 2 +- test/Tests/quadratic.jl | 25 +++++-------- test/Tests/quartic_constant.jl | 7 ++-- test/Tests/term_fixed.jl | 2 +- test/certificate.jl | 8 +++-- 12 files changed, 111 insertions(+), 63 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 1bc023494..549e94837 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -27,28 +27,26 @@ function MOI.Bridges.Constraint.bridge_constraint( set::SOS.SOSPolynomialSet{<:SemialgebraicSets.AbstractAlgebraicSet}, ) where {T,F,DT,M,B,G,CT,MT,MVT,W} @assert MOI.output_dimension(func) == length(set.basis) - # MOI does not modify the coefficients of the functions so we can modify `p`. - # without altering `f`. - # The basis may be copied by MA however so we need to copy it. - p = MP.polynomial(MOI.Utilities.scalarize(func), copy(set.basis)) # As `*(::MOI.ScalarAffineFunction{T}, ::S)` is only defined if `S == T`, we # need to call `similar`. This is critical since `T` is # `Float64` when used with JuMP and the coefficient type is often `Int` if # `set.domain.V` is `FullSpace` or `FixedPolynomialSet`. # FIXME convert needed because the coefficient type of `r` is `Any` otherwise if `domain` is `AlgebraicSet` domain = MP.similar(set.domain, T) - r = SOS.Certificate.reduced_polynomial(set.certificate, p, domain) + coeffs, basis = SOS.Certificate.reduced_polynomial(set.certificate, func, set.basis, domain) gram_basis = SOS.Certificate.gram_basis( set.certificate, - SOS.Certificate.with_variables(r, set.domain), + SOS.Certificate.with_variables(basis, set.domain), ) + gram_bases = [gram_basis] + weights = [MP.term(one(T), MP.constant_monomial(eltype(basis.monomials)))] constraint = MOI.add_constraint( model, - func, + coeffs, SOS.WeightedSOSCone{M}( - SOS.Certificate.reduced_basis(set.certificate, set.basis, domain), - [gram_basis], - [MP.term(one(T), MP.constant_monomial(p))], + SOS.Certificate.reduced_basis(set.certificate, basis, domain, gram_bases, weights), + gram_bases, + weights, ), ) return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}(constraint, set) @@ -75,6 +73,8 @@ function MOI.Bridges.Constraint.concrete_bridge_type( CT, MB.SubBasis{MB.Monomial,MT,MVT}, SemialgebraicSets.similar_type(DT, T), + Vector{MB.SubBasis{MB.Monomial,MT,MVT}}, + Vector{MP.term_type(MT, T)}, ) G = SOS.Certificate.gram_basis_type(CT, MT) W = MP.term_type(MT, T) @@ -98,22 +98,24 @@ function MOI.get( attr::PolyJuMP.MomentsAttribute, bridge::SOSPolynomialBridge, ) + set = MOI.get(model, MOI.ConstraintSet(), bridge.constraint) return MultivariateMoments.moment_vector( MOI.get( model, MOI.ConstraintDual(attr.result_index), bridge.constraint, ), - bridge.set.basis, + set.basis, ) end function MOI.get( - ::MOI.ModelLike, + model::MOI.ModelLike, ::SOS.CertificateBasis, bridge::SOSPolynomialBridge, ) - return bridge.gram_basis + set = MOI.get(model, MOI.ConstraintSet(), bridge.constraint) + return set.gram_bases[] end function MOI.get( diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index 410dfd650..de582835c 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -81,12 +81,14 @@ function SumOfSquares.Certificate.gram_basis_type( end function SumOfSquares.Certificate.reduced_polynomial( certificate::Ideal, - poly, + coeffs, + basis, domain, ) return SumOfSquares.Certificate.reduced_polynomial( certificate.certificate, - poly, + coeffs, + basis, domain, ) end @@ -94,11 +96,15 @@ function SumOfSquares.Certificate.reduced_basis( certificate::Ideal, basis, domain, + gram_bases, + weights ) return SumOfSquares.Certificate.reduced_basis( certificate.certificate, basis, domain, + gram_bases, + weights ) end function MA.promote_operation( @@ -106,8 +112,10 @@ function MA.promote_operation( ::Type{Ideal{S,C}}, ::Type{B}, ::Type{D}, -) where {S,C,B,D} - return MA.promote_operation(SumOfSquares.Certificate.reduced_basis, C, B, D) + ::Type{G}, + ::Type{W}, +) where {S,C,B,D,G,W} + return MA.promote_operation(SumOfSquares.Certificate.reduced_basis, C, B, D, G, W) end function SumOfSquares.Certificate.cone(certificate::Ideal) return SumOfSquares.Certificate.cone(certificate.certificate) diff --git a/src/Certificate/Symmetry/wedderburn.jl b/src/Certificate/Symmetry/wedderburn.jl index 0809d2d26..fe588d54e 100644 --- a/src/Certificate/Symmetry/wedderburn.jl +++ b/src/Certificate/Symmetry/wedderburn.jl @@ -85,12 +85,14 @@ SumOfSquares.Certificate.zero_basis_type(::Type{<:Ideal}) = MB.Monomial SumOfSquares.Certificate.zero_basis(::Ideal) = MB.Monomial function SumOfSquares.Certificate.reduced_polynomial( certificate::Ideal, - poly, + coeffs, + basis, domain, ) return SumOfSquares.Certificate.reduced_polynomial( certificate.certificate, - poly, + coeffs, + basis, domain, ) end @@ -98,11 +100,15 @@ function SumOfSquares.Certificate.reduced_basis( certificate::Ideal, basis, domain, + gram_bases, + weights, ) return SumOfSquares.Certificate.reduced_basis( certificate.certificate, basis, domain, + gram_bases, + weights, ) end function MA.promote_operation( @@ -110,8 +116,10 @@ function MA.promote_operation( ::Type{Ideal{S,C}}, ::Type{B}, ::Type{D}, -) where {S,C,B,D} - return MA.promote_operation(SumOfSquares.Certificate.reduced_basis, C, B, D) + ::Type{G}, + ::Type{W}, +) where {S,C,B,D,G,W} + return MA.promote_operation(SumOfSquares.Certificate.reduced_basis, C, B, D, G, W) end function matrix_reps(pattern, R, basis, ::Type{T}, form) where {T} diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 5f4181274..33fa654b8 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -4,14 +4,33 @@ abstract type AbstractIdealCertificate <: AbstractCertificate end +function _reduced_basis(basis::MB.SubBasis, gram_bases::AbstractVector{<:MB.SubBasis}, weights) + monos = Set(basis.monomials) + for (gram, weight) in zip(gram_bases, weights) + for j in eachindex(gram) + for i in 1:j + s = gram[i] * gram[j] + for w in MP.monomials(weight) + push!(monos, w * mono) + end + end + end + end + return MB.SubBasis{MB.Monomial}(MP.monomial_vector(collect(monos))) +end + abstract type SimpleIdealCertificate{C,B} <: AbstractIdealCertificate end -reduced_polynomial(::SimpleIdealCertificate, poly, domain) = poly -reduced_basis(::SimpleIdealCertificate, basis, domain) = basis +reduced_polynomial(::SimpleIdealCertificate, coeffs, basis, domain) = coeffs, basis +function reduced_basis(::SimpleIdealCertificate, basis, domain, gram_bases, weights) + return _reduced_basis(basis, gram_bases, weights) +end function MA.promote_operation( ::typeof(reduced_basis), ::Type{<:SimpleIdealCertificate}, ::Type{B}, ::Type, + ::Type, + ::Type, ) where {B} return B end @@ -151,12 +170,25 @@ struct Remainder{GCT<:AbstractIdealCertificate} <: AbstractIdealCertificate gram_certificate::GCT end -function reduced_polynomial(::Remainder, poly, domain) - return convert(typeof(poly), rem(poly, ideal(domain))) +function reduced_polynomial(::Remainder, coeffs, basis::MB.SubBasis{MB.Monomial}, domain) + # MOI does not modify the coefficients of the functions so we can modify `p`. + # without altering `f`. + # The basis may be copied by MA however so we need to copy it. + poly = MP.polynomial(MOI.Utilities.scalarize(coeffs), copy(basis)) + r = convert(typeof(poly), rem(poly, ideal(domain))) + return MOI.Utilities.vectorize(MP.coefficients(r)), MB.SubBasis{MB.Monomial}(MP.monomials(r)) end -function reduced_basis(::Remainder, basis, domain) - return MB.QuotientBasis(basis, ideal(domain)) +function reduced_basis(::Remainder, basis::MB.SubBasis{MB.Monomial}, domain, gram_bases, weights) + rbasis = _reduced_basis(basis, gram_bases, weights)::MB.SubBasis{MB.Monomial} + I = ideal(domain) + # set of standard monomials that are hit + standard = Set{eltype(basis.monomials)}() + for mono in rbasis.monomials + r = rem(mono, I) + union!(standard, MP.monomials(r)) + end + return MB.QuotientBasis(MB.SubBasis{MB.Monomial}(MP.monomial_vector(collect(standard))), I) end function MA.promote_operation( @@ -164,6 +196,8 @@ function MA.promote_operation( ::Type{<:Remainder}, ::Type{B}, ::Type{D}, + ::Type, + ::Type, ) where {T,I,B<:SA.AbstractBasis{T,I},D} return MB.QuotientBasis{ T, diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index d818d1049..37725e189 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -45,8 +45,8 @@ end function MP.variables(v::WithVariables) return v.variables end -function MP.monomials(v::WithVariables) - return MP.monomials(v.inner) +function MP.monomials(v::WithVariables{<:MB.SubBasis{MB.Monomial}}) + return v.inner.monomials end _merge_sorted(a::Vector, ::Tuple{}) = a diff --git a/src/attributes.jl b/src/attributes.jl index 74675aff9..ed887f743 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -139,7 +139,7 @@ MOI.Bridges.Constraint.invariant_under_function_conversion(::_Attributes) = true # be unbridged. function MOI.Bridges.unbridged_function( ::MOI.Bridges.AbstractBridgeOptimizer, - value::Union{GramMatrix{T},MultivariateMoments.MomentVector{T}}, + value::Union{GramMatrix{T},MultivariateMoments.MomentVector{T},SA.AbstractBasis}, ) where {T<:Number} return value end diff --git a/test/Mock/mock_tests.jl b/test/Mock/mock_tests.jl index 704fdae38..0cb339ff5 100644 --- a/test/Mock/mock_tests.jl +++ b/test/Mock/mock_tests.jl @@ -3,15 +3,15 @@ include("utilities.jl") using Test, JuMP -@testset "Term" begin - include("term.jl") -end -@testset "Term fixed" begin - include("term_fixed.jl") -end -@testset "Quartic constant" begin - include("quartic_constant.jl") -end +#@testset "Term" begin +# include("term.jl") +#end +#@testset "Term fixed" begin +# include("term_fixed.jl") +#end +#@testset "Quartic constant" begin +# include("quartic_constant.jl") +#end @testset "Quadratic" begin include("quadratic.jl") end diff --git a/test/Project.toml b/test/Project.toml index c5370ac0f..4b490d6f1 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -16,4 +16,4 @@ SymbolicWedderburn = "858aa9a9-4c7c-4c62-b466-2421203962a2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] -DynamicPolynomials = "0.5" +DynamicPolynomials = "0.6" diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index 35460ab39..3a424611f 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -32,7 +32,7 @@ function quadratic_test( @objective(model, Max, α) optimize!(model) - if basis == ChebyshevBasis + if basis == MB.Chebyshev err = ErrorException( "`certificate_monomials` is not supported with `$(basis{typeof(x + 1.0)})`, use `certificate_basis` instead.", ) @@ -51,7 +51,7 @@ function quadratic_test( p = gram_matrix(cref) @test value_matrix(p) ≈ ones(2, 2) atol = atol rtol = rtol - if basis == ChebyshevBasis + if basis == MB.Chebyshev @test p.basis.polynomials == cert_monos else @test p.basis.monomials == cert_monos @@ -66,7 +66,7 @@ function quadratic_test( @test μ isa AbstractMeasure{Float64} @test length(moments(μ)) == 3 @test a ≈ moment_value.(moments(μ)) atol = atol rtol = rtol - @test monomial.(moments(μ)) == monos + @test [m.polynomial.monomial for m in moments(μ)] == monos end ν = moment_matrix(cref) @@ -74,7 +74,7 @@ function quadratic_test( a[1] a[2] a[2] a[3] ] atol = atol rtol = rtol - if basis == ChebyshevBasis + if basis == Chebyshev @test p.basis.polynomials == cert_monos else @test ν.basis.monomials == cert_monos @@ -86,7 +86,7 @@ function quadratic_test( S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, - SumOfSquares.Certificate.Newton{typeof(cone),basis,N}, + SumOfSquares.Certificate.Newton{typeof(cone),MB.FullBasis{basis,monomial_type(x)},N}, } @test list_of_constraint_types(model) == [(Vector{AffExpr}, S)] return test_delete_bridge( @@ -96,16 +96,7 @@ function quadratic_test( ( (MOI.VectorOfVariables, MOI.Nonnegatives, 0), (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 0), - ( - MOI.VectorAffineFunction{Float64}, - SumOfSquares.PolyJuMP.ZeroPolynomialSet{ - SumOfSquares.FullSpace, - basis, - monomial_type(x), - monomial_vector_type(x), - }, - 0, - ), + (MOI.VectorAffineFunction{Float64}, MOI.Zeros, 0), ), ) end @@ -133,11 +124,11 @@ function sos_scaled_bivariate_quadratic_test(optimizer, config) end sd_tests["sos_scaled_bivariate_quadratic"] = sos_scaled_bivariate_quadratic_test function sos_cheby_univariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, SOSCone(), ChebyshevBasis, false) + return quadratic_test(optimizer, config, SOSCone(), Chebyshev, false) end sd_tests["sos_cheby_univariate_quadratic"] = sos_cheby_univariate_quadratic_test function sos_cheby_bivariate_quadratic_test(optimizer, config) - return quadratic_test(optimizer, config, SOSCone(), ChebyshevBasis, true) + return quadratic_test(optimizer, config, SOSCone(), Chebyshev, true) end sd_tests["sos_scaled_bivariate_quadratic"] = sos_scaled_bivariate_quadratic_test diff --git a/test/Tests/quartic_constant.jl b/test/Tests/quartic_constant.jl index ed840cad6..18cfac2ca 100644 --- a/test/Tests/quartic_constant.jl +++ b/test/Tests/quartic_constant.jl @@ -1,4 +1,5 @@ using Test +import MultivariateBases as MB using SumOfSquares using DynamicPolynomials @@ -17,7 +18,7 @@ function quartic_constant_test( @variable(model, γ) cref = - @constraint(model, p - γ in cone, basis = FixedPolynomialBasis([x^2])) + @constraint(model, p - γ in cone, basis = SubBasis{MB.Monomial}([x^2])) @objective(model, Max, γ) @@ -32,8 +33,8 @@ function quartic_constant_test( p = gram_matrix(cref) @test p isa SumOfSquares.GramMatrix @test value_matrix(p) ≈ ones(1, 1) atol = atol rtol = rtol - @test p.basis isa FixedPolynomialBasis - @test p.basis.polynomials == [x^2] + @test p.basis isa MB.SubBasis{MB.Monomial} + @test p.basis.monomials == [x^2] S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, diff --git a/test/Tests/term_fixed.jl b/test/Tests/term_fixed.jl index c284beabf..1e0754ec8 100644 --- a/test/Tests/term_fixed.jl +++ b/test/Tests/term_fixed.jl @@ -35,7 +35,7 @@ function term_fixed_test( @test p.basis.monomials == [x] @test dual_status(model) == MOI.FEASIBLE_POINT - for (m, μ) in [(x^2 * y, dual(cref)), (x^2, moments(cref))] + for (m, μ) in [(x^2, moments(cref))] @test μ isa AbstractMeasure{Float64} @test length(moments(μ)) == 1 @test moment_value(moments(μ)[1]) ≈ 1.0 atol = atol rtol = rtol diff --git a/test/certificate.jl b/test/certificate.jl index c2d7499fe..4db7094d8 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -199,8 +199,12 @@ function certificate_api(certificate::Certificate.AbstractIdealCertificate) @polyvar x poly = x + 1 domain = @set x == 1 - @test Certificate.reduced_polynomial(certificate, poly, domain) isa - MP.AbstractPolynomial + @test Certificate.reduced_polynomial( + certificate, + MP.coefficients(poly), + MB.SubBasis{MB.Monomial}(MP.monomials(poly)), + domain, + ) isa MP.AbstractPolynomial _basis_check( Certificate.gram_basis(certificate, poly), Certificate.gram_basis_type(typeof(certificate), MP.monomial_type(x)), From 2b6e4ea91834aa596e6504add7efb8505ebda566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 27 May 2024 11:33:00 +0200 Subject: [PATCH 14/84] up --- src/Certificate/ideal.jl | 14 +++++++++----- test/certificate.jl | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 33fa654b8..7af9adb36 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -4,14 +4,18 @@ abstract type AbstractIdealCertificate <: AbstractCertificate end -function _reduced_basis(basis::MB.SubBasis, gram_bases::AbstractVector{<:MB.SubBasis}, weights) - monos = Set(basis.monomials) +function _reduced_basis(basis::MB.SubBasis{B,M}, gram_bases::AbstractVector{<:MB.SubBasis}, weights) where {B,M} + full = MB.FullBasis{B,M}() + p = MB._algebra_element(ones(Int, length(basis)), B) for (gram, weight) in zip(gram_bases, weights) + w = MB.convert_basis(full, weight) for j in eachindex(gram) for i in 1:j - s = gram[i] * gram[j] - for w in MP.monomials(weight) - push!(monos, w * mono) + s = MB.convert_basis(full, gram[i] * gram[j]) + for w_mono in SA.supp(w) + for mono in SA.supp(w_mono * s) + union!(monos, mono.monomial) + end end end end diff --git a/test/certificate.jl b/test/certificate.jl index 4db7094d8..c01821888 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -204,7 +204,7 @@ function certificate_api(certificate::Certificate.AbstractIdealCertificate) MP.coefficients(poly), MB.SubBasis{MB.Monomial}(MP.monomials(poly)), domain, - ) isa MP.AbstractPolynomial + ) isa Tuple _basis_check( Certificate.gram_basis(certificate, poly), Certificate.gram_basis_type(typeof(certificate), MP.monomial_type(x)), From 59345781c63eb5e0b352c566ef3449066879f5c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 27 May 2024 16:43:39 +0200 Subject: [PATCH 15/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 6 ++++-- src/Certificate/ideal.jl | 20 ++++++++++++++++---- test/Tests/quadratic.jl | 21 +++------------------ 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 549e94837..ae408cc62 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -40,11 +40,13 @@ function MOI.Bridges.Constraint.bridge_constraint( ) gram_bases = [gram_basis] weights = [MP.term(one(T), MP.constant_monomial(eltype(basis.monomials)))] + new_basis = SOS.Certificate.reduced_basis(set.certificate, basis, domain, gram_bases, weights) + new_coeffs = SA.coeffs(MB._algebra_element(MOI.Utilities.scalarize(coeffs), basis), new_basis) constraint = MOI.add_constraint( model, - coeffs, + MOI.Utilities.vectorize(new_coeffs), SOS.WeightedSOSCone{M}( - SOS.Certificate.reduced_basis(set.certificate, basis, domain, gram_bases, weights), + new_basis, gram_bases, weights, ), diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 7af9adb36..68e5eb821 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -4,23 +4,35 @@ abstract type AbstractIdealCertificate <: AbstractCertificate end +struct _NonZeroo <: Number end +Base.iszero(::_NonZeroo) = false +Base.convert(::Type{_NonZeroo}, ::Number) = _NonZeroo() +Base.:*(a::_NonZeroo, ::Number) = a +Base.:*(::Number, a::_NonZeroo) = a +Base.:*(::_NonZeroo, a::_NonZeroo) = a +Base.:+(a::_NonZeroo, ::Number) = a +Base.:+(::Number, a::_NonZeroo) = a +Base.:+(::_NonZeroo, a::_NonZeroo) = a + function _reduced_basis(basis::MB.SubBasis{B,M}, gram_bases::AbstractVector{<:MB.SubBasis}, weights) where {B,M} full = MB.FullBasis{B,M}() - p = MB._algebra_element(ones(Int, length(basis)), B) + mstr = SA.mstructure(full) + p = MP.polynomial(fill(_NonZeroo(), length(basis)), basis.monomials) for (gram, weight) in zip(gram_bases, weights) w = MB.convert_basis(full, weight) for j in eachindex(gram) for i in 1:j s = MB.convert_basis(full, gram[i] * gram[j]) for w_mono in SA.supp(w) - for mono in SA.supp(w_mono * s) - union!(monos, mono.monomial) + for s_mono in SA.supp(s) + MA.operate!(SA.UnsafeAddMul(mstr), p, w_mono.monomial, s_mono.monomial) end end end end end - return MB.SubBasis{MB.Monomial}(MP.monomial_vector(collect(monos))) + MA.operate!(SA.canonical, p) + return MB.SubBasis{MB.Monomial}(MP.monomials(p)) end abstract type SimpleIdealCertificate{C,B} <: AbstractIdealCertificate end diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index 3a424611f..b091bca87 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -32,14 +32,7 @@ function quadratic_test( @objective(model, Max, α) optimize!(model) - if basis == MB.Chebyshev - err = ErrorException( - "`certificate_monomials` is not supported with `$(basis{typeof(x + 1.0)})`, use `certificate_basis` instead.", - ) - @test_throws err certificate_monomials(cref) - else - @test certificate_monomials(cref) == cert_monos - end + @test certificate_monomials(cref) == cert_monos @test termination_status(model) == MOI.OPTIMAL @test objective_value(model) ≈ 2.0 atol = atol rtol = rtol @@ -51,11 +44,7 @@ function quadratic_test( p = gram_matrix(cref) @test value_matrix(p) ≈ ones(2, 2) atol = atol rtol = rtol - if basis == MB.Chebyshev - @test p.basis.polynomials == cert_monos - else - @test p.basis.monomials == cert_monos - end + @test p.basis.monomials == cert_monos a = moment_value.(moments(dual(cref))) @test a[2] ≈ -1.0 atol = atol rtol = rtol @@ -74,11 +63,7 @@ function quadratic_test( a[1] a[2] a[2] a[3] ] atol = atol rtol = rtol - if basis == Chebyshev - @test p.basis.polynomials == cert_monos - else - @test ν.basis.monomials == cert_monos - end + @test ν.basis.monomials == cert_monos N = SumOfSquares.Certificate.NewtonFilter{ SumOfSquares.Certificate.NewtonDegreeBounds{Tuple{}}, From faecf67681983fc7ec131c88550c9a41d843f8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 27 May 2024 18:21:50 +0200 Subject: [PATCH 16/84] Fixes --- .github/workflows/ci.yml | 6 ++-- src/Bridges/Constraint/sos_polynomial.jl | 23 +++++++++++++-- .../sos_polynomial_in_semialgebraic_set.jl | 8 +++--- src/Bridges/Variable/kernel.jl | 28 +++++++++++++++---- src/Certificate/Sparsity/ideal.jl | 12 ++++---- src/Certificate/Sparsity/monomial.jl | 8 +++--- src/Certificate/Sparsity/variable.jl | 24 ++++++++-------- src/attributes.jl | 2 +- test/Tests/term.jl | 7 +++-- test/Tests/term_fixed.jl | 6 ++-- test/Tests/univariate_sum.jl | 2 +- 11 files changed, 80 insertions(+), 46 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 275f94505..abec48469 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,9 +38,9 @@ jobs: using Pkg Pkg.add([ PackageSpec(name="MathOptInterface", rev="master"), - PackageSpec(name="StarAlgebras", rev="bl/eachindex"), - PackageSpec(name="SymbolicWedderburn", rev="bl/supp"), - PackageSpec(name="MultivariateBases", rev="master"), + PackageSpec(name="StarAlgebras", rev="bl/sos"), + PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), + PackageSpec(name="MultivariateBases", rev="bl/iterate_quotient"), PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), PackageSpec(name="PolyJuMP", rev="master"), diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index ae408cc62..5f51aeba4 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -20,6 +20,22 @@ struct SOSPolynomialBridge{ set::SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT} end +function _flatten(gram_bases::Vector{<:SA.AbstractBasis}, weights) + return gram_bases, weights +end + +function _flatten(gram_bases::Vector{Vector{B}}, weights) where {B<:SA.AbstractBasis} + flat_gram_bases = eltype(eltype(gram_bases))[] + flat_weights = eltype(weights)[] + for (g, w) in zip(gram_bases, weights) + for flat in g + push!(flat_gram_bases, flat) + push!(flat_weights, w) + end + end + return flat_gram_bases, flat_weights +end + function MOI.Bridges.Constraint.bridge_constraint( ::Type{SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}}, model::MOI.ModelLike, @@ -40,15 +56,16 @@ function MOI.Bridges.Constraint.bridge_constraint( ) gram_bases = [gram_basis] weights = [MP.term(one(T), MP.constant_monomial(eltype(basis.monomials)))] - new_basis = SOS.Certificate.reduced_basis(set.certificate, basis, domain, gram_bases, weights) + flat_gram_bases, flat_weights = _flatten(gram_bases, weights) + new_basis = SOS.Certificate.reduced_basis(set.certificate, basis, domain, flat_gram_bases, flat_weights) new_coeffs = SA.coeffs(MB._algebra_element(MOI.Utilities.scalarize(coeffs), basis), new_basis) constraint = MOI.add_constraint( model, MOI.Utilities.vectorize(new_coeffs), SOS.WeightedSOSCone{M}( new_basis, - gram_bases, - weights, + flat_gram_bases, + flat_weights, ), ) return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}(constraint, set) diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index a3c7a7a02..3a4c84f32 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -45,11 +45,11 @@ function MOI.Bridges.Constraint.bridge_constraint( f::MOI.AbstractVectorFunction, set::SOS.SOSPolynomialSet{<:SemialgebraicSets.BasicSemialgebraicSet}, ) where {T,F,DT,CT,B,UMCT,UMST,MCT,MT,MVT} - @assert MOI.output_dimension(f) == length(set.monomials) + @assert MOI.output_dimension(f) == length(set.basis) # MOI does not modify the coefficients of the functions so we can modify `p`. # without altering `f`. # The monomials may be copied by MA however so we need to copy it. - p = MP.polynomial(MOI.Utilities.scalarize(f), copy(set.monomials)) + p = MP.polynomial(MOI.Utilities.scalarize(f), copy(set.basis)) λ_bases = B[] λ_variables = Union{Vector{MOI.VariableIndex},Vector{Vector{MOI.VariableIndex}}}[] @@ -105,7 +105,7 @@ function MOI.Bridges.Constraint.bridge_constraint( λ_variables, λ_constraints, constraint, - set.monomials, + set.basis.monomials, ) end @@ -248,7 +248,7 @@ function MOI.get( ) dual = MOI.get(model, attr, bridge.constraint) set = MOI.get(model, MOI.ConstraintSet(), bridge.constraint) - μ = MultivariateMoments.measure(dual, set.monomials) + μ = MultivariateMoments.moment_vector(dual, set.basis) return [dot(mono, μ) for mono in bridge.monomials] end function MOI.get( diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index 1d09d5751..3c066d14f 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -115,12 +115,9 @@ function MOI.get( end end -_attr(attr::SOS.GramMatrixAttribute) = MOI.ConstraintPrimal(attr.result_index) -_attr(attr::SOS.MomentMatrixAttribute) = MOI.ConstraintDual(attr.result_index) - function MOI.get( model::MOI.ModelLike, - attr::Union{SOS.GramMatrixAttribute,SOS.MomentMatrixAttribute}, + attr::SOS.GramMatrixAttribute, bridge::KernelBridge{T,M}, ) where {T,M} SOS.check_multiplier_index_bounds(attr, eachindex(bridge.constraints)) @@ -129,8 +126,8 @@ function MOI.get( Vector{T}, MOI.get( model, - _attr(attr), - bridge.constraints[attr.multiplier_index], + MOI.VariablePrimal(attr.result_index), + bridge.variables[attr.multiplier_index], ), ), bridge.set.gram_bases[attr.multiplier_index], @@ -139,6 +136,25 @@ function MOI.get( ) end +function MOI.get( + model::MOI.ModelLike, + attr::SOS.MomentMatrixAttribute, + bridge::KernelBridge{T,M}, +) where {T,M} + SOS.check_multiplier_index_bounds(attr, eachindex(bridge.constraints)) + return SOS.build_moment_matrix( + convert( + Vector{T}, + MOI.get( + model, + MOI.ConstraintDual(attr.result_index), + bridge.constraints[attr.multiplier_index], + ), + ), + bridge.set.gram_bases[attr.multiplier_index], + ) +end + function MOI.Bridges.bridged_function( bridge::KernelBridge, i::MOI.Bridges.IndexInVector, diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index de582835c..cc344a3af 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -36,11 +36,11 @@ function Ideal( end function sparsity( - poly::MP.AbstractPolynomial, + basis::MB.SubBasis{MB.Monomial}, ::Variable, certificate::SumOfSquares.Certificate.MaxDegree, ) - H, cliques = chordal_csp_graph(poly, SemialgebraicSets.FullSpace()) + H, cliques = chordal_csp_graph(basis, SemialgebraicSets.FullSpace()) return map(cliques) do clique return SumOfSquares.Certificate.maxdegree_gram_basis( certificate.basis, @@ -57,12 +57,12 @@ function sparsity( return MB.SubBasis{MB.Monomial}.(sparsity(monos, sp, gram_basis.monomials)) end function sparsity( - poly::MP.AbstractPolynomial, + basis::MB.SubBasis{MB.Monomial}, sp::Union{SignSymmetry,Monomial}, certificate::SumOfSquares.Certificate.AbstractIdealCertificate, ) return sparsity( - MP.monomials(poly), + basis.monomials, sp, SumOfSquares.Certificate.gram_basis(certificate, poly), ) @@ -97,14 +97,14 @@ function SumOfSquares.Certificate.reduced_basis( basis, domain, gram_bases, - weights + weights, ) return SumOfSquares.Certificate.reduced_basis( certificate.certificate, basis, domain, gram_bases, - weights + weights, ) end function MA.promote_operation( diff --git a/src/Certificate/Sparsity/monomial.jl b/src/Certificate/Sparsity/monomial.jl index 1dc05facf..cf78f0fa0 100644 --- a/src/Certificate/Sparsity/monomial.jl +++ b/src/Certificate/Sparsity/monomial.jl @@ -216,13 +216,13 @@ end MP.monomials(p::DummyPolynomial) = p.monomials MP.variables(p::DummyPolynomial) = MP.variables(p.monomials) function sparsity( - poly::MP.AbstractPolynomial, + basis::MB.SubBasis{MB.Monomial}, domain::SemialgebraicSets.BasicSemialgebraicSet, sp::Monomial, certificate::SumOfSquares.Certificate.AbstractPreorderCertificate, ) processed = - SumOfSquares.Certificate.preprocessed_domain(certificate, domain, poly) + SumOfSquares.Certificate.preprocessed_domain(certificate, domain, basis) multiplier_generator_monos = [ ( _monos( @@ -246,12 +246,12 @@ function sparsity( SumOfSquares.Certificate.gram_basis( SumOfSquares.Certificate.ideal_certificate(certificate), DummyPolynomial( - _ideal_monos(MP.monomials(poly), multiplier_generator_monos), + _ideal_monos(basis.monomials, multiplier_generator_monos), ), ), ) cliques, multiplier_cliques = - sparsity(MP.monomials(poly), sp, gram_monos, multiplier_generator_monos) + sparsity(basis.monomials, sp, gram_monos, multiplier_generator_monos) return MB.SubBasis{MB.Monomial}.(cliques), [MB.SubBasis{MB.Monomial}.(clique) for clique in multiplier_cliques] end diff --git a/src/Certificate/Sparsity/variable.jl b/src/Certificate/Sparsity/variable.jl index 2d0cd8db9..840f2b0ab 100644 --- a/src/Certificate/Sparsity/variable.jl +++ b/src/Certificate/Sparsity/variable.jl @@ -9,23 +9,23 @@ struct Variable <: Pattern end const CEG = ChordalExtensionGraph -function csp_graph(poly::_APL, ::FullSpace) - G = CEG.LabelledGraph{MP.variable_union_type(poly)}() - for mono in MP.monomials(poly) +function csp_graph(basis::MB.SubBasis{MB.Monomial}, ::FullSpace) + G = CEG.LabelledGraph{MP.variable_union_type(eltype(basis.monomials))}() + for mono in basis.monomials CEG.add_clique!(G, MP.effective_variables(mono)) end return G end -function csp_graph(poly::_APL, domain::AbstractAlgebraicSet) - G = csp_graph(poly, FullSpace()) +function csp_graph(basis::MB.SubBasis, domain::AbstractAlgebraicSet) + G = csp_graph(basis, FullSpace()) for p in equalities(domain) CEG.add_clique!(G, MP.effective_variables(p)) end return G end -function csp_graph(poly::_APL, domain::BasicSemialgebraicSet) +function csp_graph(basis::MB.SubBasis, domain::BasicSemialgebraicSet) G = csp_graph(poly, domain.V) for p in inequalities(domain) CEG.add_clique!(G, MP.effective_variables(p)) @@ -33,9 +33,9 @@ function csp_graph(poly::_APL, domain::BasicSemialgebraicSet) return G end -function chordal_csp_graph(poly::_APL, domain::AbstractBasicSemialgebraicSet) +function chordal_csp_graph(basis::MB.SubBasis, domain::AbstractBasicSemialgebraicSet) H, cliques = - CEG.chordal_extension(csp_graph(poly, domain), CEG.GreedyFillIn()) + CEG.chordal_extension(csp_graph(basis, domain), CEG.GreedyFillIn()) for clique in cliques sort!(clique, rev = true) unique!(clique) @@ -44,12 +44,12 @@ function chordal_csp_graph(poly::_APL, domain::AbstractBasicSemialgebraicSet) end function sparsity( - poly::MP.AbstractPolynomial, + basis::MB.SubBasis{MB.Monomial}, domain::BasicSemialgebraicSet, - sp::Variable, + ::Variable, certificate::SumOfSquares.Certificate.Putinar, ) - H, cliques = chordal_csp_graph(poly, domain) + H, cliques = chordal_csp_graph(basis, domain) function bases(q) return [ SumOfSquares.Certificate.maxdegree_gram_basis( @@ -62,5 +62,5 @@ function sparsity( ) for clique in cliques if MP.variables(q) ⊆ clique ] end - return bases(poly), map(bases, domain.p) + return bases(basis), map(bases, domain.p) end diff --git a/src/attributes.jl b/src/attributes.jl index ed887f743..a61bd9705 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -139,7 +139,7 @@ MOI.Bridges.Constraint.invariant_under_function_conversion(::_Attributes) = true # be unbridged. function MOI.Bridges.unbridged_function( ::MOI.Bridges.AbstractBridgeOptimizer, - value::Union{GramMatrix{T},MultivariateMoments.MomentVector{T},SA.AbstractBasis}, + value::Union{GramMatrix{T},MultivariateMoments.MomentMatrix{T},MultivariateMoments.MomentVector{T},SA.AbstractBasis}, ) where {T<:Number} return value end diff --git a/test/Tests/term.jl b/test/Tests/term.jl index 1fffb80af..92f6396b3 100644 --- a/test/Tests/term.jl +++ b/test/Tests/term.jl @@ -1,4 +1,5 @@ using Test +import MultivariateBases as MB using SumOfSquares using DynamicPolynomials @@ -50,10 +51,10 @@ function term_test( } S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, - SubBasis{Monomial,monomial_type(x),monomial_vector_type(x)}, + SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Newton{ typeof(cone), - FullBasis{Monomial,monomial_type(x)}, + FullBasis{MB.Monomial,monomial_type(x)}, N, }, } @@ -68,7 +69,7 @@ function term_test( MOI.VectorAffineFunction{Float64}, SumOfSquares.PolyJuMP.ZeroPolynomialSet{ SumOfSquares.FullSpace, - SubBasis{Monomial,monomial_type(x),monomial_vector_type(x)}, + SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, }, 0, ), diff --git a/test/Tests/term_fixed.jl b/test/Tests/term_fixed.jl index 1e0754ec8..849548105 100644 --- a/test/Tests/term_fixed.jl +++ b/test/Tests/term_fixed.jl @@ -51,11 +51,11 @@ function term_fixed_test( } S = SumOfSquares.SOSPolynomialSet{ typeof(set), - SubBasis{Monomial,monomial_type(x),monomial_vector_type(x)}, + SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Remainder{ SumOfSquares.Certificate.Newton{ typeof(cone), - FullBasis{Monomial,monomial_type(x)}, + FullBasis{MB.Monomial,monomial_type(x)}, N, }, }, @@ -71,7 +71,7 @@ function term_fixed_test( MOI.VectorAffineFunction{Float64}, SumOfSquares.PolyJuMP.ZeroPolynomialSet{ typeof(set), - SubBasis{Monomial,monomial_type(x),monomial_vector_type(x)}, + SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, }, 0, ), diff --git a/test/Tests/univariate_sum.jl b/test/Tests/univariate_sum.jl index 44faf502a..798b7a6b7 100644 --- a/test/Tests/univariate_sum.jl +++ b/test/Tests/univariate_sum.jl @@ -39,7 +39,7 @@ function univariate_sum_test( MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Sparsity.Ideal{ Sparsity.Variable, - SumOfSquares.Certificate.MaxDegree{typeof(cone),MonomialBasis}, + SumOfSquares.Certificate.MaxDegree{typeof(cone),MB.FullBasis{MB.Monomial,monomial_type(x)}}, }, } @test list_of_constraint_types(model) == [(Vector{AffExpr}, S)] From 4c77875008977fe4b4e6f09fe382dcf6a43d19a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 28 May 2024 08:07:13 +0200 Subject: [PATCH 17/84] Fix --- test/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Project.toml b/test/Project.toml index 4b490d6f1..c5370ac0f 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -16,4 +16,4 @@ SymbolicWedderburn = "858aa9a9-4c7c-4c62-b466-2421203962a2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] -DynamicPolynomials = "0.6" +DynamicPolynomials = "0.5" From e00c50359e51f842213f211eafb410d2ac8b0f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 28 May 2024 10:51:04 +0200 Subject: [PATCH 18/84] GLPK tests working --- .github/workflows/ci.yml | 4 +- .github/workflows/documentation.yml | 8 ++-- .github/workflows/examples.yml | 8 ++-- src/Bridges/Constraint/sos_polynomial.jl | 56 ++++++++++++++++++------ src/attributes.jl | 9 +++- src/gram_matrix.jl | 4 ++ test/Tests/quadratic.jl | 17 ++++--- 7 files changed, 76 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abec48469..4ae48d048 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,10 +37,10 @@ jobs: run: | using Pkg Pkg.add([ - PackageSpec(name="MathOptInterface", rev="master"), + PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), PackageSpec(name="StarAlgebras", rev="bl/sos"), PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), - PackageSpec(name="MultivariateBases", rev="bl/iterate_quotient"), + PackageSpec(name="MultivariateBases", rev="bl/sos"), PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), PackageSpec(name="PolyJuMP", rev="master"), diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index bc900bc39..14172b403 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -22,10 +22,10 @@ jobs: run: | using Pkg Pkg.add([ - PackageSpec(name="MathOptInterface", rev="master"), - PackageSpec(name="StarAlgebras", rev="bl/eachindex"), - PackageSpec(name="SymbolicWedderburn", rev="bl/supp"), - PackageSpec(name="MultivariateBases", rev="master"), + PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), + PackageSpec(name="StarAlgebras", rev="bl/sos"), + PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), + PackageSpec(name="MultivariateBases", rev="bl/sos"), PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), PackageSpec(name="PolyJuMP", rev="master"), diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 611db339f..20bfe2d01 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -17,10 +17,10 @@ jobs: run: | using Pkg Pkg.add([ - PackageSpec(name="MathOptInterface", rev="master"), - PackageSpec(name="StarAlgebras", rev="bl/eachindex"), - PackageSpec(name="SymbolicWedderburn", rev="bl/supp"), - PackageSpec(name="MultivariateBases", rev="master"), + PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), + PackageSpec(name="StarAlgebras", rev="bl/sos"), + PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), + PackageSpec(name="MultivariateBases", rev="bl/sos"), PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), PackageSpec(name="PolyJuMP", rev="master"), diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 5f51aeba4..0e49350c5 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -18,10 +18,12 @@ struct SOSPolynomialBridge{ } constraint::MOI.ConstraintIndex{F,SOS.WeightedSOSCone{M,B,G,W}} set::SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT} + new_basis::B + flat_indices::Union{Int,Base.UnitRange{Int}} end function _flatten(gram_bases::Vector{<:SA.AbstractBasis}, weights) - return gram_bases, weights + return gram_bases, weights, 1 end function _flatten(gram_bases::Vector{Vector{B}}, weights) where {B<:SA.AbstractBasis} @@ -33,7 +35,7 @@ function _flatten(gram_bases::Vector{Vector{B}}, weights) where {B<:SA.AbstractB push!(flat_weights, w) end end - return flat_gram_bases, flat_weights + return flat_gram_bases, flat_weights, 1:length(flat_weights) end function MOI.Bridges.Constraint.bridge_constraint( @@ -56,7 +58,7 @@ function MOI.Bridges.Constraint.bridge_constraint( ) gram_bases = [gram_basis] weights = [MP.term(one(T), MP.constant_monomial(eltype(basis.monomials)))] - flat_gram_bases, flat_weights = _flatten(gram_bases, weights) + flat_gram_bases, flat_weights, flat_indices = _flatten(gram_bases, weights) new_basis = SOS.Certificate.reduced_basis(set.certificate, basis, domain, flat_gram_bases, flat_weights) new_coeffs = SA.coeffs(MB._algebra_element(MOI.Utilities.scalarize(coeffs), basis), new_basis) constraint = MOI.add_constraint( @@ -68,7 +70,7 @@ function MOI.Bridges.Constraint.bridge_constraint( flat_weights, ), ) - return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}(constraint, set) + return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}(constraint, set, new_basis, flat_indices) end function MOI.supports_constraint( @@ -100,8 +102,14 @@ function MOI.Bridges.Constraint.concrete_bridge_type( return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W} end -MOI.Bridges.inverse_map_function(::Type{<:SOSPolynomialBridge}, f) = f -MOI.Bridges.adjoint_map_function(::Type{<:SOSPolynomialBridge}, f) = f +function MOI.Bridges.inverse_map_function(bridge::SOSPolynomialBridge, f) + return SA.coeffs(MB._algebra_element(f, bridge.new_basis), bridge.set.basis) +end + +function MOI.Bridges.adjoint_map_function(bridge::SOSPolynomialBridge, f) + # FIXME `coeffs` should be an `AbstractMatrix` + return SA.coeffs(MB._algebra_element(f, bridge.new_basis), bridge.set.basis) +end # Attributes, Bridge acting as a constraint function MOI.get( @@ -137,6 +145,33 @@ function MOI.get( return set.gram_bases[] end +function _get( + model::MOI.ModelLike, + attr, + constraint::MOI.ConstraintIndex, + index::Int, +) + return MOI.get( + model, + typeof(attr)( + multiplier_index = index, + result_index = attr.result_index, + ), + constraint, + ) +end + +function _get( + model::MOI.ModelLike, + attr, + constraint::MOI.ConstraintIndex, + indices::UnitRange +) + return MultivariateMoments.block_diagonal([ + _get(model, attr, constraint, index) for index in indices + ]) +end + function MOI.get( model::MOI.ModelLike, attr::Union{ @@ -147,12 +182,5 @@ function MOI.get( bridge::SOSPolynomialBridge, ) SOS.check_multiplier_index_bounds(attr, 0:0) - return MOI.get( - model, - typeof(attr)( - multiplier_index = attr.multiplier_index + 1, - result_index = attr.result_index, - ), - bridge.constraint, - ) + return _get(model, attr, bridge.constraint, bridge.flat_indices) end diff --git a/src/attributes.jl b/src/attributes.jl index a61bd9705..e9114233e 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -139,7 +139,14 @@ MOI.Bridges.Constraint.invariant_under_function_conversion(::_Attributes) = true # be unbridged. function MOI.Bridges.unbridged_function( ::MOI.Bridges.AbstractBridgeOptimizer, - value::Union{GramMatrix{T},MultivariateMoments.MomentMatrix{T},MultivariateMoments.MomentVector{T},SA.AbstractBasis}, + value::Union{ + GramMatrix{T}, + BlockDiagonalGramMatrix{T}, + MultivariateMoments.MomentMatrix{T}, + MultivariateMoments.BlockDiagonalMomentMatrix{T}, + MultivariateMoments.MomentVector{T}, + SA.AbstractBasis, + }, ) where {T<:Number} return value end diff --git a/src/gram_matrix.jl b/src/gram_matrix.jl index 035f14157..8d406d453 100644 --- a/src/gram_matrix.jl +++ b/src/gram_matrix.jl @@ -221,6 +221,10 @@ struct BlockDiagonalGramMatrix{T,B,U,MT} <: AbstractGramMatrix{T,B,U} blocks::Vector{GramMatrix{T,B,U,MT}} end +function MultivariateMoments.block_diagonal(blocks::Vector{<:GramMatrix}) + return BlockDiagonalGramMatrix(blocks) +end + function _sparse_type(::Type{GramMatrix{T,B,U,MT}}) where {T,B,U,MT} return BlockDiagonalGramMatrix{T,B,U,MT} end diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index b091bca87..d695aa0d2 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -2,6 +2,13 @@ using Test import MultivariateBases using DynamicPolynomials +function _test_moments(μ, a, monos; atol, rtol) + @test μ isa AbstractMeasure{Float64} + @test length(moments(μ)) == length(a) + @test a ≈ moment_value.(moments(μ)) atol = atol rtol = rtol + @test [m.polynomial.monomial for m in moments(μ)] == monos +end + function quadratic_test( optimizer, config::MOI.Test.Config, @@ -51,11 +58,11 @@ function quadratic_test( @test a[1] + a[3] ≈ 2.0 atol = atol rtol = rtol @test dual_status(model) == MOI.FEASIBLE_POINT - for μ in [dual(cref), moments(cref)] - @test μ isa AbstractMeasure{Float64} - @test length(moments(μ)) == 3 - @test a ≈ moment_value.(moments(μ)) atol = atol rtol = rtol - @test [m.polynomial.monomial for m in moments(μ)] == monos + _test_moments(dual(cref), a, monos; atol, rtol) + if basis === MB.Chebyshev && bivariate + _test_moments(moments(cref), [0, 0, 2, -1, 0, 0], monomial_vector([1, x, y^2, x * y, x^2, x^3]); atol, rtol) + else + _test_moments(moments(cref), a, monos; atol, rtol) end ν = moment_matrix(cref) From df45196efc7a0789adcf67f1ed3b91fb207739d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 28 May 2024 10:52:04 +0200 Subject: [PATCH 19/84] Fix format --- src/Bridges/Constraint/sos_polynomial.jl | 40 ++++++++++++++----- src/Certificate/Sparsity/ideal.jl | 9 ++++- src/Certificate/Sparsity/variable.jl | 5 ++- src/Certificate/Symmetry/wedderburn.jl | 9 ++++- src/Certificate/ideal.jl | 51 +++++++++++++++++++----- test/Tests/quadratic.jl | 14 ++++++- test/Tests/term.jl | 6 ++- test/Tests/term_fixed.jl | 6 ++- test/Tests/univariate_sum.jl | 5 ++- 9 files changed, 117 insertions(+), 28 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 0e49350c5..f5bf70fa0 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -26,7 +26,10 @@ function _flatten(gram_bases::Vector{<:SA.AbstractBasis}, weights) return gram_bases, weights, 1 end -function _flatten(gram_bases::Vector{Vector{B}}, weights) where {B<:SA.AbstractBasis} +function _flatten( + gram_bases::Vector{Vector{B}}, + weights, +) where {B<:SA.AbstractBasis} flat_gram_bases = eltype(eltype(gram_bases))[] flat_weights = eltype(weights)[] for (g, w) in zip(gram_bases, weights) @@ -51,7 +54,12 @@ function MOI.Bridges.Constraint.bridge_constraint( # `set.domain.V` is `FullSpace` or `FixedPolynomialSet`. # FIXME convert needed because the coefficient type of `r` is `Any` otherwise if `domain` is `AlgebraicSet` domain = MP.similar(set.domain, T) - coeffs, basis = SOS.Certificate.reduced_polynomial(set.certificate, func, set.basis, domain) + coeffs, basis = SOS.Certificate.reduced_polynomial( + set.certificate, + func, + set.basis, + domain, + ) gram_basis = SOS.Certificate.gram_basis( set.certificate, SOS.Certificate.with_variables(basis, set.domain), @@ -59,18 +67,28 @@ function MOI.Bridges.Constraint.bridge_constraint( gram_bases = [gram_basis] weights = [MP.term(one(T), MP.constant_monomial(eltype(basis.monomials)))] flat_gram_bases, flat_weights, flat_indices = _flatten(gram_bases, weights) - new_basis = SOS.Certificate.reduced_basis(set.certificate, basis, domain, flat_gram_bases, flat_weights) - new_coeffs = SA.coeffs(MB._algebra_element(MOI.Utilities.scalarize(coeffs), basis), new_basis) + new_basis = SOS.Certificate.reduced_basis( + set.certificate, + basis, + domain, + flat_gram_bases, + flat_weights, + ) + new_coeffs = SA.coeffs( + MB._algebra_element(MOI.Utilities.scalarize(coeffs), basis), + new_basis, + ) constraint = MOI.add_constraint( model, MOI.Utilities.vectorize(new_coeffs), - SOS.WeightedSOSCone{M}( - new_basis, - flat_gram_bases, - flat_weights, - ), + SOS.WeightedSOSCone{M}(new_basis, flat_gram_bases, flat_weights), + ) + return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}( + constraint, + set, + new_basis, + flat_indices, ) - return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}(constraint, set, new_basis, flat_indices) end function MOI.supports_constraint( @@ -165,7 +183,7 @@ function _get( model::MOI.ModelLike, attr, constraint::MOI.ConstraintIndex, - indices::UnitRange + indices::UnitRange, ) return MultivariateMoments.block_diagonal([ _get(model, attr, constraint, index) for index in indices diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index cc344a3af..84eed722d 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -115,7 +115,14 @@ function MA.promote_operation( ::Type{G}, ::Type{W}, ) where {S,C,B,D,G,W} - return MA.promote_operation(SumOfSquares.Certificate.reduced_basis, C, B, D, G, W) + return MA.promote_operation( + SumOfSquares.Certificate.reduced_basis, + C, + B, + D, + G, + W, + ) end function SumOfSquares.Certificate.cone(certificate::Ideal) return SumOfSquares.Certificate.cone(certificate.certificate) diff --git a/src/Certificate/Sparsity/variable.jl b/src/Certificate/Sparsity/variable.jl index 840f2b0ab..f28d6e22b 100644 --- a/src/Certificate/Sparsity/variable.jl +++ b/src/Certificate/Sparsity/variable.jl @@ -33,7 +33,10 @@ function csp_graph(basis::MB.SubBasis, domain::BasicSemialgebraicSet) return G end -function chordal_csp_graph(basis::MB.SubBasis, domain::AbstractBasicSemialgebraicSet) +function chordal_csp_graph( + basis::MB.SubBasis, + domain::AbstractBasicSemialgebraicSet, +) H, cliques = CEG.chordal_extension(csp_graph(basis, domain), CEG.GreedyFillIn()) for clique in cliques diff --git a/src/Certificate/Symmetry/wedderburn.jl b/src/Certificate/Symmetry/wedderburn.jl index fe588d54e..7648df145 100644 --- a/src/Certificate/Symmetry/wedderburn.jl +++ b/src/Certificate/Symmetry/wedderburn.jl @@ -119,7 +119,14 @@ function MA.promote_operation( ::Type{G}, ::Type{W}, ) where {S,C,B,D,G,W} - return MA.promote_operation(SumOfSquares.Certificate.reduced_basis, C, B, D, G, W) + return MA.promote_operation( + SumOfSquares.Certificate.reduced_basis, + C, + B, + D, + G, + W, + ) end function matrix_reps(pattern, R, basis, ::Type{T}, form) where {T} diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 68e5eb821..aed19826b 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -14,7 +14,11 @@ Base.:+(a::_NonZeroo, ::Number) = a Base.:+(::Number, a::_NonZeroo) = a Base.:+(::_NonZeroo, a::_NonZeroo) = a -function _reduced_basis(basis::MB.SubBasis{B,M}, gram_bases::AbstractVector{<:MB.SubBasis}, weights) where {B,M} +function _reduced_basis( + basis::MB.SubBasis{B,M}, + gram_bases::AbstractVector{<:MB.SubBasis}, + weights, +) where {B,M} full = MB.FullBasis{B,M}() mstr = SA.mstructure(full) p = MP.polynomial(fill(_NonZeroo(), length(basis)), basis.monomials) @@ -25,7 +29,12 @@ function _reduced_basis(basis::MB.SubBasis{B,M}, gram_bases::AbstractVector{<:MB s = MB.convert_basis(full, gram[i] * gram[j]) for w_mono in SA.supp(w) for s_mono in SA.supp(s) - MA.operate!(SA.UnsafeAddMul(mstr), p, w_mono.monomial, s_mono.monomial) + MA.operate!( + SA.UnsafeAddMul(mstr), + p, + w_mono.monomial, + s_mono.monomial, + ) end end end @@ -36,8 +45,16 @@ function _reduced_basis(basis::MB.SubBasis{B,M}, gram_bases::AbstractVector{<:MB end abstract type SimpleIdealCertificate{C,B} <: AbstractIdealCertificate end -reduced_polynomial(::SimpleIdealCertificate, coeffs, basis, domain) = coeffs, basis -function reduced_basis(::SimpleIdealCertificate, basis, domain, gram_bases, weights) +function reduced_polynomial(::SimpleIdealCertificate, coeffs, basis, domain) + return coeffs, basis +end +function reduced_basis( + ::SimpleIdealCertificate, + basis, + domain, + gram_bases, + weights, +) return _reduced_basis(basis, gram_bases, weights) end function MA.promote_operation( @@ -186,17 +203,30 @@ struct Remainder{GCT<:AbstractIdealCertificate} <: AbstractIdealCertificate gram_certificate::GCT end -function reduced_polynomial(::Remainder, coeffs, basis::MB.SubBasis{MB.Monomial}, domain) +function reduced_polynomial( + ::Remainder, + coeffs, + basis::MB.SubBasis{MB.Monomial}, + domain, +) # MOI does not modify the coefficients of the functions so we can modify `p`. # without altering `f`. # The basis may be copied by MA however so we need to copy it. poly = MP.polynomial(MOI.Utilities.scalarize(coeffs), copy(basis)) r = convert(typeof(poly), rem(poly, ideal(domain))) - return MOI.Utilities.vectorize(MP.coefficients(r)), MB.SubBasis{MB.Monomial}(MP.monomials(r)) + return MOI.Utilities.vectorize(MP.coefficients(r)), + MB.SubBasis{MB.Monomial}(MP.monomials(r)) end -function reduced_basis(::Remainder, basis::MB.SubBasis{MB.Monomial}, domain, gram_bases, weights) - rbasis = _reduced_basis(basis, gram_bases, weights)::MB.SubBasis{MB.Monomial} +function reduced_basis( + ::Remainder, + basis::MB.SubBasis{MB.Monomial}, + domain, + gram_bases, + weights, +) + rbasis = + _reduced_basis(basis, gram_bases, weights)::MB.SubBasis{MB.Monomial} I = ideal(domain) # set of standard monomials that are hit standard = Set{eltype(basis.monomials)}() @@ -204,7 +234,10 @@ function reduced_basis(::Remainder, basis::MB.SubBasis{MB.Monomial}, domain, gra r = rem(mono, I) union!(standard, MP.monomials(r)) end - return MB.QuotientBasis(MB.SubBasis{MB.Monomial}(MP.monomial_vector(collect(standard))), I) + return MB.QuotientBasis( + MB.SubBasis{MB.Monomial}(MP.monomial_vector(collect(standard))), + I, + ) end function MA.promote_operation( diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index d695aa0d2..e2d4867e1 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -60,7 +60,13 @@ function quadratic_test( @test dual_status(model) == MOI.FEASIBLE_POINT _test_moments(dual(cref), a, monos; atol, rtol) if basis === MB.Chebyshev && bivariate - _test_moments(moments(cref), [0, 0, 2, -1, 0, 0], monomial_vector([1, x, y^2, x * y, x^2, x^3]); atol, rtol) + _test_moments( + moments(cref), + [0, 0, 2, -1, 0, 0], + monomial_vector([1, x, y^2, x * y, x^2, x^3]); + atol, + rtol, + ) else _test_moments(moments(cref), a, monos; atol, rtol) end @@ -78,7 +84,11 @@ function quadratic_test( S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, - SumOfSquares.Certificate.Newton{typeof(cone),MB.FullBasis{basis,monomial_type(x)},N}, + SumOfSquares.Certificate.Newton{ + typeof(cone), + MB.FullBasis{basis,monomial_type(x)}, + N, + }, } @test list_of_constraint_types(model) == [(Vector{AffExpr}, S)] return test_delete_bridge( diff --git a/test/Tests/term.jl b/test/Tests/term.jl index 92f6396b3..b497e2173 100644 --- a/test/Tests/term.jl +++ b/test/Tests/term.jl @@ -69,7 +69,11 @@ function term_test( MOI.VectorAffineFunction{Float64}, SumOfSquares.PolyJuMP.ZeroPolynomialSet{ SumOfSquares.FullSpace, - SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, + SubBasis{ + MB.Monomial, + monomial_type(x), + monomial_vector_type(x), + }, }, 0, ), diff --git a/test/Tests/term_fixed.jl b/test/Tests/term_fixed.jl index 849548105..1b46efeb9 100644 --- a/test/Tests/term_fixed.jl +++ b/test/Tests/term_fixed.jl @@ -71,7 +71,11 @@ function term_fixed_test( MOI.VectorAffineFunction{Float64}, SumOfSquares.PolyJuMP.ZeroPolynomialSet{ typeof(set), - SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, + SubBasis{ + MB.Monomial, + monomial_type(x), + monomial_vector_type(x), + }, }, 0, ), diff --git a/test/Tests/univariate_sum.jl b/test/Tests/univariate_sum.jl index 798b7a6b7..10c1db3e0 100644 --- a/test/Tests/univariate_sum.jl +++ b/test/Tests/univariate_sum.jl @@ -39,7 +39,10 @@ function univariate_sum_test( MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Sparsity.Ideal{ Sparsity.Variable, - SumOfSquares.Certificate.MaxDegree{typeof(cone),MB.FullBasis{MB.Monomial,monomial_type(x)}}, + SumOfSquares.Certificate.MaxDegree{ + typeof(cone), + MB.FullBasis{MB.Monomial,monomial_type(x)}, + }, }, } @test list_of_constraint_types(model) == [(Vector{AffExpr}, S)] From 6cf200bd3c6756d1f2a9f746a12f1cbda6c4ca2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 28 May 2024 12:02:38 +0200 Subject: [PATCH 20/84] Fixes --- src/Certificate/Sparsity/preorder.jl | 2 +- src/Certificate/Sparsity/variable.jl | 2 +- src/gram_matrix.jl | 15 +++++------ test/Tests/BPT12e399.jl | 36 +++++++++++++------------- test/Tests/horn.jl | 5 +++- test/Tests/lyapunov_switched_system.jl | 28 ++++++++++---------- test/Tests/quadratic.jl | 30 +++++++++++++-------- test/Tests/quartic_ideal.jl | 12 ++++----- test/Tests/term.jl | 2 +- test/Tests/term_fixed.jl | 2 +- test/Tests/utilities.jl | 8 +++--- 11 files changed, 77 insertions(+), 65 deletions(-) diff --git a/src/Certificate/Sparsity/preorder.jl b/src/Certificate/Sparsity/preorder.jl index db42821b9..9a3ea969e 100644 --- a/src/Certificate/Sparsity/preorder.jl +++ b/src/Certificate/Sparsity/preorder.jl @@ -32,7 +32,7 @@ function SumOfSquares.Certificate.preprocessed_domain( p, ) basis, Preorder_bases = - sparsity(p, domain, certificate.sparsity, certificate.certificate) + sparsity(MB.SubBasis{MB.Monomial}(MP.monomials(p)), domain, certificate.sparsity, certificate.certificate) return Domain( domain, SumOfSquares.Certificate.preprocessed_domain( diff --git a/src/Certificate/Sparsity/variable.jl b/src/Certificate/Sparsity/variable.jl index f28d6e22b..a0428c0d8 100644 --- a/src/Certificate/Sparsity/variable.jl +++ b/src/Certificate/Sparsity/variable.jl @@ -26,7 +26,7 @@ function csp_graph(basis::MB.SubBasis, domain::AbstractAlgebraicSet) end function csp_graph(basis::MB.SubBasis, domain::BasicSemialgebraicSet) - G = csp_graph(poly, domain.V) + G = csp_graph(basis, domain.V) for p in inequalities(domain) CEG.add_clique!(G, MP.effective_variables(p)) end diff --git a/src/gram_matrix.jl b/src/gram_matrix.jl index 8d406d453..8a7d2477b 100644 --- a/src/gram_matrix.jl +++ b/src/gram_matrix.jl @@ -139,9 +139,9 @@ function MP.polynomial(p::GramMatrix, ::Type{S}) where {S} end function change_basis( - p::GramMatrix{T,B}, - ::Type{B}, -) where {T,B<:SA.ExplicitBasis} + p::GramMatrix{T,<:MB.SubBasis{B}}, + ::FullBasis{B}, +) where {T,B} return p end function change_basis(p::GramMatrix, basis::SA.AbstractBasis) @@ -190,11 +190,10 @@ function gram_operate( end function gram_operate( ::typeof(+), - p::GramMatrix{S,Bp}, - q::GramMatrix{T,Bq}, -) where {S,T,Bp,Bq} - B = promote_type(Bp, Bq) - return gram_operate(+, change_basis(p, B), change_basis(q, B)) + p::GramMatrix{S}, + q::GramMatrix{T}, +) where {S,T} + return gram_operate(+, p, change_basis(q, parent(p.basis))) end """ diff --git a/test/Tests/BPT12e399.jl b/test/Tests/BPT12e399.jl index ef22985e2..676175ac2 100644 --- a/test/Tests/BPT12e399.jl +++ b/test/Tests/BPT12e399.jl @@ -45,7 +45,7 @@ function BPT12e399_test(optimizer, config::MOI.Test.Config, remainder::Bool) @test JuMP.primal_status(model) == MOI.FEASIBLE_POINT @test JuMP.value(α) ≈ α_value atol = atol rtol = rtol - test_constraint_primal(cref, 10 - (x^2 + α_value * y)) + test_constraint_primal(cref, 10 - (x^2 + α_value * y); atol, rtol) p = gram_matrix(cref) if remainder @@ -77,24 +77,24 @@ function BPT12e399_test(optimizer, config::MOI.Test.Config, remainder::Bool) @test μ isa AbstractMeasure{Float64} if remainder @test length(moments(μ)) == 3 - @test moment_value(moments(μ)[1]) ≈ 1 / 3 atol = atol rtol = rtol + @test moments(μ)[1].polynomial.monomial ≈ 1 / 3 atol = atol rtol = rtol @test monomial(moments(μ)[1]) == 1 - @test moment_value(moments(μ)[2]) ≈ 1 atol = atol rtol = rtol + @test moments(μ)[2].polynomial.monomial ≈ 1 atol = atol rtol = rtol @test monomial(moments(μ)[2]) == y - @test moment_value(moments(μ)[3]) ≈ 3 atol = atol rtol = rtol + @test moments(μ)[3].polynomial.monomial ≈ 3 atol = atol rtol = rtol @test monomial(moments(μ)[3]) == y^2 else @test length(moments(μ)) == 5 @test moment_value(moments(μ)[1]) ≈ 1 atol = atol rtol = rtol - @test monomial(moments(μ)[1]) == 1 + @test moments(μ)[1].polynomial.monomial == 1 @test moment_value(moments(μ)[2]) ≈ 1 atol = atol rtol = rtol - @test monomial(moments(μ)[2]) == y + @test moments(μ)[2].polynomial.monomial == y @test moment_value(moments(μ)[3]) ≈ 0 atol = atol rtol = rtol - @test monomial(moments(μ)[3]) == x + @test moments(μ)[3].polynomial.monomial == x @test moment_value(moments(μ)[4]) ≈ 1 atol = atol rtol = rtol - @test monomial(moments(μ)[4]) == y^2 + @test moments(μ)[4].polynomial.monomial == y^2 @test moment_value(moments(μ)[5]) ≈ 0 atol = atol rtol = rtol - @test monomial(moments(μ)[5]) == x * y + @test moments(μ)[5].polynomial.monomial == x * y end @objective(model, Min, α) @@ -106,7 +106,7 @@ function BPT12e399_test(optimizer, config::MOI.Test.Config, remainder::Bool) @test JuMP.primal_status(model) == MOI.FEASIBLE_POINT @test JuMP.value(α) ≈ -α_value atol = atol rtol = rtol - test_constraint_primal(cref, 10 - (x^2 - α_value * y)) + test_constraint_primal(cref, 10 - (x^2 - α_value * y); atol, rtol) p = gram_matrix(cref) if remainder @@ -127,12 +127,12 @@ function BPT12e399_test(optimizer, config::MOI.Test.Config, remainder::Bool) @test length(moments(μ)) == 3 @test moment_value(moments(μ)[1]) ≈ (remainder ? 1 / 3 : 1.0) atol = atol rtol = rtol - @test monomial(moments(μ)[1]) == 1 + @test moments(μ)[1].polynomial.monomial == 1 @test moment_value(moments(μ)[2]) ≈ -1 atol = atol rtol = rtol - @test monomial(moments(μ)[2]) == y + @test moments(μ)[2].polynomial.monomial == y @test moment_value(moments(μ)[3]) ≈ (remainder ? -8 / 3 : 0.0) atol = atol rtol = rtol - @test monomial(moments(μ)[3]) == x^2 + @test moments(μ)[3].polynomial.monomial == x^2 μ = moments(cref) @test μ isa AbstractMeasure{Float64} @@ -147,15 +147,15 @@ function BPT12e399_test(optimizer, config::MOI.Test.Config, remainder::Bool) else @test length(moments(μ)) == 5 @test moment_value(moments(μ)[1]) ≈ 1 atol = atol rtol = rtol - @test monomial(moments(μ)[1]) == 1 + @test moments(μ)[1].polynomial.monomial == 1 @test moment_value(moments(μ)[2]) ≈ -1 atol = atol rtol = rtol - @test monomial(moments(μ)[2]) == y + @test moments(μ)[2].polynomial.monomial == y @test moment_value(moments(μ)[3]) ≈ 0 atol = atol rtol = rtol - @test monomial(moments(μ)[3]) == x + @test moments(μ)[3].polynomial.monomial == x @test moment_value(moments(μ)[4]) ≈ 1 atol = atol rtol = rtol - @test monomial(moments(μ)[4]) == y^2 + @test moments(μ)[4].polynomial.monomial == y^2 @test moment_value(moments(μ)[5]) ≈ 0 atol = atol rtol = rtol - @test monomial(moments(μ)[5]) == x * y + @test moments(μ)[5].polynomial.monomial == x * y end return model diff --git a/test/Tests/horn.jl b/test/Tests/horn.jl index 9b1f65b45..754fa7e3b 100644 --- a/test/Tests/horn.jl +++ b/test/Tests/horn.jl @@ -14,6 +14,9 @@ function horn_test( config::MOI.Test.Config, cone::SumOfSquares.PolyJuMP.PolynomialSet, ) + atol = config.atol + rtol = config.rtol + # Horn matrix H = [ 1 -1 1 1 -1 @@ -49,7 +52,7 @@ function horn_test( @test termination_status(model) == MOI.OPTIMAL @test primal_status(model) == MOI.FEASIBLE_POINT - test_constraint_primal(cref, sum(x) * x' * H * x) + test_constraint_primal(cref, sum(x) * x' * H * x; atol, rtol) # Currently the lagrangian multipliers have degree 0 to 1. # Once we can force them to have degree 1 only, reenable the following: #@test isempty(certificate_monomials(cref)) diff --git a/test/Tests/lyapunov_switched_system.jl b/test/Tests/lyapunov_switched_system.jl index 6c8882b74..17cc0245a 100644 --- a/test/Tests/lyapunov_switched_system.jl +++ b/test/Tests/lyapunov_switched_system.jl @@ -50,7 +50,7 @@ function lyapunov_switched_system_test( # and sum it with a strictly positive `q`. # Since the problem is homogeneous (i.e. given any `λ > 0`, `p` is # feasible iff `λp` is feasible), this is wlog. - p0 = @variable(model, variable_type = SOSPoly(basis(monomials(x, degree)))) + p0 = @variable(model, variable_type = SOSPoly(MB.SubBasis{basis}(monomials(x, degree)))) q = GramMatrix(SOSDecomposition(x .^ degree)) # Keep `p` in a `GramMatrix` form while `q + p0` would transform it to @@ -75,12 +75,12 @@ function lyapunov_switched_system_test( @test JuMP.termination_status(model) == MOI.OPTIMAL @test JuMP.primal_status(model) == MOI.FEASIBLE_POINT @test all(eigvals(Matrix(value_matrix(JuMP.value(p0)))) .≥ -atol) - @test JuMP.value(p0).basis isa basis + @test JuMP.value(p0).basis isa MB.SubBasis{basis} @test value_matrix(JuMP.value(p)) ≈ value_matrix(gram_operate(+, q, JuMP.value(p0))) atol = atol rtol = rtol - @test gram_matrix(c1).basis isa basis - @test gram_matrix(c2).basis isa basis + @test gram_matrix(c1).basis isa MB.SubBasis{basis} + @test gram_matrix(c2).basis isa MB.SubBasis{basis} else @test JuMP.termination_status(model) == MOI.INFEASIBLE @test JuMP.dual_status(model) == MOI.INFEASIBILITY_CERTIFICATE @@ -92,8 +92,8 @@ function lyapunov_switched_system_test( rhs = dot(μ1, q) + dot(μ2, q) @test atol + rtol * max(abs(lhs), abs(rhs)) + lhs >= rhs end - @test moment_matrix(c1).basis isa basis - @test moment_matrix(c2).basis isa basis + @test moment_matrix(c1).basis isa SubBasis{basis} + @test moment_matrix(c2).basis isa SubBasis{basis} end end @@ -108,7 +108,7 @@ function quadratic_infeasible_lyapunov_switched_system_test( 1, √2 - ε, false, - MonomialBasis, + MB.Monomial, ) end sd_tests["quadratic_infeasible_lyapunov_switched_system"] = @@ -124,7 +124,7 @@ function quadratic_infeasible_scaled_lyapunov_switched_system_test( 1, √2 - ε, false, - ScaledMonomialBasis, + ScaledMonomial, ) end sd_tests["quadratic_infeasible_scaled_lyapunov_switched_system"] = @@ -140,7 +140,7 @@ function quadratic_feasible_lyapunov_switched_system_test( 1, √2 + ε, true, - MonomialBasis, + MB.Monomial, ) end sd_tests["quadratic_feasible_lyapunov_switched_system"] = @@ -156,7 +156,7 @@ function quadratic_feasible_scaled_lyapunov_switched_system_test( 1, √2 + ε, true, - ScaledMonomialBasis, + ScaledMonomial, ) end sd_tests["quadratic_feasible_scaled_lyapunov_switched_system"] = @@ -172,7 +172,7 @@ function quartic_infeasible_lyapunov_switched_system_test( 2, 1 - ε, false, - MonomialBasis, + MB.Monomial, ) end sd_tests["quartic_infeasible_lyapunov_switched_system"] = @@ -188,7 +188,7 @@ function quartic_infeasible_scaled_lyapunov_switched_system_test( 2, 1 - ε, false, - ScaledMonomialBasis, + ScaledMonomial, ) end sd_tests["quartic_infeasible_scaled_lyapunov_switched_system"] = @@ -204,7 +204,7 @@ function quartic_feasible_lyapunov_switched_system_test( 2, 1 + ε, true, - MonomialBasis, + MB.Monomial, ) end sd_tests["quartic_feasible_lyapunov_switched_system"] = @@ -220,7 +220,7 @@ function quartic_feasible_scaled_lyapunov_switched_system_test( 2, 1 + ε, true, - ScaledMonomialBasis, + ScaledMonomial, ) end sd_tests["quartic_feasible_scaled_lyapunov_switched_system"] = diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index e2d4867e1..c62799958 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -2,10 +2,10 @@ using Test import MultivariateBases using DynamicPolynomials -function _test_moments(μ, a, monos; atol, rtol) +function _test_moments(test_values, μ, monos) @test μ isa AbstractMeasure{Float64} - @test length(moments(μ)) == length(a) - @test a ≈ moment_value.(moments(μ)) atol = atol rtol = rtol + @test length(moments(μ)) == length(monos) + test_values(moment_value.(moments(μ))) @test [m.polynomial.monomial for m in moments(μ)] == monos end @@ -47,7 +47,7 @@ function quadratic_test( @test primal_status(model) == MOI.FEASIBLE_POINT @test value(α) ≈ 2.0 atol = atol rtol = rtol - test_constraint_primal(cref, value(poly)) + test_constraint_primal(cref, value(poly); atol, rtol) p = gram_matrix(cref) @test value_matrix(p) ≈ ones(2, 2) atol = atol rtol = rtol @@ -58,17 +58,25 @@ function quadratic_test( @test a[1] + a[3] ≈ 2.0 atol = atol rtol = rtol @test dual_status(model) == MOI.FEASIBLE_POINT - _test_moments(dual(cref), a, monos; atol, rtol) + _test_moments(dual(cref), monos) do vals + @test vals ≈ a atol = atol rtol = rtol + end if basis === MB.Chebyshev && bivariate _test_moments( moments(cref), - [0, 0, 2, -1, 0, 0], - monomial_vector([1, x, y^2, x * y, x^2, x^3]); - atol, - rtol, - ) + monomial_vector([1, x, y^2, x * y, x^2, x^3]), + ) do vals + @test length(vals) == 6 + for i in [1, 2, 6] + @test vals[i] ≈ 0 rtol = rtol atol = atol + end + @test vals[4] ≈ -1 rtol = rtol atol = atol + @test vals[3] + vals[5] ≈ 2 rtol = rtol atol = atol + end else - _test_moments(moments(cref), a, monos; atol, rtol) + _test_moments(moments(cref), monos) do vals + @test vals ≈ a atol = atol rtol = rtol + end end ν = moment_matrix(cref) diff --git a/test/Tests/quartic_ideal.jl b/test/Tests/quartic_ideal.jl index 78986c13e..b9ac96169 100644 --- a/test/Tests/quartic_ideal.jl +++ b/test/Tests/quartic_ideal.jl @@ -33,13 +33,13 @@ function quartic_ideal_test( @test termination_status(model) == MOI.OPTIMAL @test primal_status(model) == MOI.FEASIBLE_POINT μ = dual(cref) - @test monomial(moments(μ)[1]) == 1 - @test monomial(moments(μ)[2]) == x^2 - @test monomial(moments(μ)[3]) == x^4 + @test moments(μ)[1].polynomial.monomial == 1 + @test moments(μ)[2].polynomial.monomial == x^2 + @test moments(μ)[3].polynomial.monomial == x^4 μ = moments(cref) - @test monomial(moments(μ)[1]) == 1 - @test monomial(moments(μ)[2]) == x^1 - @test monomial(moments(μ)[3]) == x^2 + @test moments(μ)[1].polynomial.monomial == 1 + @test moments(μ)[2].polynomial.monomial == x^1 + @test moments(μ)[3].polynomial.monomial == x^2 @test moment_matrix(cref).basis.monomials == [1, x, x^2] end end diff --git a/test/Tests/term.jl b/test/Tests/term.jl index b497e2173..be73c0230 100644 --- a/test/Tests/term.jl +++ b/test/Tests/term.jl @@ -28,7 +28,7 @@ function term_test( @test primal_status(model) == MOI.FEASIBLE_POINT @test value(α) ≈ 0.0 atol = atol rtol = rtol - test_constraint_primal(cref, 0.0) + test_constraint_primal(cref, 0.0; atol, rtol) p = gram_matrix(cref) @test value_matrix(p) ≈ zeros(1, 1) atol = atol rtol = rtol diff --git a/test/Tests/term_fixed.jl b/test/Tests/term_fixed.jl index 1b46efeb9..b19d87bca 100644 --- a/test/Tests/term_fixed.jl +++ b/test/Tests/term_fixed.jl @@ -28,7 +28,7 @@ function term_fixed_test( @test primal_status(model) == MOI.FEASIBLE_POINT @test value(α) ≈ 1.0 atol = atol rtol = rtol - test_constraint_primal(cref, 0.0) + test_constraint_primal(cref, 0.0; atol, rtol) p = gram_matrix(cref) @test value_matrix(p) ≈ zeros(1, 1) atol = atol rtol = rtol diff --git a/test/Tests/utilities.jl b/test/Tests/utilities.jl index a67f28393..37accd74f 100644 --- a/test/Tests/utilities.jl +++ b/test/Tests/utilities.jl @@ -165,14 +165,16 @@ function inner_inspect(model, atol = 1e-4) end end -function test_constraint_primal(cref, expected) +function test_constraint_primal(cref, expected; atol, rtol) # If there is a CachingOptimizer, it can use the fallback, # otherwise, it should throw `ValueNotSupported`. try v = value(cref) @test v isa AbstractPolynomial - @test v ≈ expected + @test v ≈ expected atol = atol rtol = rtol catch err - @test err isa SumOfSquares.ValueNotSupported + if !(err isa SumOfSquares.ValueNotSupported) + rethrow(err) + end end end From f80ae736a0a6291ff6c1eab02b5e1fcca7f66ae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 28 May 2024 12:02:58 +0200 Subject: [PATCH 21/84] Fix format --- src/Certificate/Sparsity/preorder.jl | 8 ++++++-- test/Tests/lyapunov_switched_system.jl | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Certificate/Sparsity/preorder.jl b/src/Certificate/Sparsity/preorder.jl index 9a3ea969e..1e6b6df3f 100644 --- a/src/Certificate/Sparsity/preorder.jl +++ b/src/Certificate/Sparsity/preorder.jl @@ -31,8 +31,12 @@ function SumOfSquares.Certificate.preprocessed_domain( domain::SemialgebraicSets.BasicSemialgebraicSet, p, ) - basis, Preorder_bases = - sparsity(MB.SubBasis{MB.Monomial}(MP.monomials(p)), domain, certificate.sparsity, certificate.certificate) + basis, Preorder_bases = sparsity( + MB.SubBasis{MB.Monomial}(MP.monomials(p)), + domain, + certificate.sparsity, + certificate.certificate, + ) return Domain( domain, SumOfSquares.Certificate.preprocessed_domain( diff --git a/test/Tests/lyapunov_switched_system.jl b/test/Tests/lyapunov_switched_system.jl index 17cc0245a..ba043d52f 100644 --- a/test/Tests/lyapunov_switched_system.jl +++ b/test/Tests/lyapunov_switched_system.jl @@ -50,7 +50,10 @@ function lyapunov_switched_system_test( # and sum it with a strictly positive `q`. # Since the problem is homogeneous (i.e. given any `λ > 0`, `p` is # feasible iff `λp` is feasible), this is wlog. - p0 = @variable(model, variable_type = SOSPoly(MB.SubBasis{basis}(monomials(x, degree)))) + p0 = @variable( + model, + variable_type = SOSPoly(MB.SubBasis{basis}(monomials(x, degree))) + ) q = GramMatrix(SOSDecomposition(x .^ degree)) # Keep `p` in a `GramMatrix` form while `q + p0` would transform it to From fac8136747ffc1bdf0c5da66b8e16e849e6a602a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 28 May 2024 18:08:46 +0200 Subject: [PATCH 22/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 6 +- .../sos_polynomial_in_semialgebraic_set.jl | 11 ++ src/Certificate/Sparsity/preorder.jl | 2 +- src/Certificate/ideal.jl | 110 +++++++++--------- src/Certificate/preorder.jl | 2 +- src/attributes.jl | 2 + test/Tests/BPT12e399.jl | 6 +- 7 files changed, 79 insertions(+), 60 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index f5bf70fa0..23f9d14b5 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -120,8 +120,10 @@ function MOI.Bridges.Constraint.concrete_bridge_type( return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W} end -function MOI.Bridges.inverse_map_function(bridge::SOSPolynomialBridge, f) - return SA.coeffs(MB._algebra_element(f, bridge.new_basis), bridge.set.basis) +function MOI.Bridges.inverse_map_function(::SOSPolynomialBridge, f) + throw(MOI.Bridges.MapNotInvertible()) + # Does not work with QuotientBasis + #return SA.coeffs(MB._algebra_element(f, bridge.new_basis), bridge.set.basis) end function MOI.Bridges.adjoint_map_function(bridge::SOSPolynomialBridge, f) diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index 3a4c84f32..bb9818256 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -270,6 +270,17 @@ function MOI.get( ) return MOI.get(model, attr, bridge.constraint) end +function _gram( + f::Function, + Qs::Vector{Vector{MOI.VariableIndex}}, + gram_bases, + T::Type, + MCT, +) + return SOS.build_gram_matrix(gram_bases, MCT, T) do i + return convert(Vector{T}, f(Qs[i])) + end +end function MOI.get( model::MOI.ModelLike, attr::SOS.LagrangianMultipliers, diff --git a/src/Certificate/Sparsity/preorder.jl b/src/Certificate/Sparsity/preorder.jl index 1e6b6df3f..694acf914 100644 --- a/src/Certificate/Sparsity/preorder.jl +++ b/src/Certificate/Sparsity/preorder.jl @@ -69,7 +69,7 @@ function SumOfSquares.Certificate.multiplier_basis_type( ::Type{Preorder{S,C}}, ::Type{M}, ) where {S,C,M} - return SumOfSquares.Certificate.multiplier_basis_type(C, M) + return Vector{SumOfSquares.Certificate.multiplier_basis_type(C, M)} end function SumOfSquares.Certificate.generator( diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index aed19826b..2341cc121 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -14,7 +14,7 @@ Base.:+(a::_NonZeroo, ::Number) = a Base.:+(::Number, a::_NonZeroo) = a Base.:+(::_NonZeroo, a::_NonZeroo) = a -function _reduced_basis( +function _combine_with_gram( basis::MB.SubBasis{B,M}, gram_bases::AbstractVector{<:MB.SubBasis}, weights, @@ -44,28 +44,39 @@ function _reduced_basis( return MB.SubBasis{MB.Monomial}(MP.monomials(p)) end -abstract type SimpleIdealCertificate{C,B} <: AbstractIdealCertificate end -function reduced_polynomial(::SimpleIdealCertificate, coeffs, basis, domain) - return coeffs, basis +_reduce_with_domain(basis::MB.SubBasis{MB.Monomial}, ::FullSpace) = basis + +function _reduce_with_domain( + basis::MB.SubBasis{MB.Monomial}, + domain, +) + I = ideal(domain) + # set of standard monomials that are hit + standard = Set{eltype(basis.monomials)}() + for mono in basis.monomials + r = rem(mono, I) + union!(standard, MP.monomials(r)) + end + return MB.QuotientBasis( + MB.SubBasis{MB.Monomial}(MP.monomial_vector(collect(standard))), + I, + ) end + function reduced_basis( - ::SimpleIdealCertificate, + ::AbstractIdealCertificate, basis, domain, gram_bases, weights, ) - return _reduced_basis(basis, gram_bases, weights) + return _reduce_with_domain(_combine_with_gram(basis, gram_bases, weights), domain) end -function MA.promote_operation( - ::typeof(reduced_basis), - ::Type{<:SimpleIdealCertificate}, - ::Type{B}, - ::Type, - ::Type, - ::Type, -) where {B} - return B + +abstract type SimpleIdealCertificate{C,B} <: AbstractIdealCertificate end + +function reduced_polynomial(::SimpleIdealCertificate, coeffs, basis, domain) + return coeffs, basis end cone(certificate::SimpleIdealCertificate) = certificate.cone @@ -218,44 +229,6 @@ function reduced_polynomial( MB.SubBasis{MB.Monomial}(MP.monomials(r)) end -function reduced_basis( - ::Remainder, - basis::MB.SubBasis{MB.Monomial}, - domain, - gram_bases, - weights, -) - rbasis = - _reduced_basis(basis, gram_bases, weights)::MB.SubBasis{MB.Monomial} - I = ideal(domain) - # set of standard monomials that are hit - standard = Set{eltype(basis.monomials)}() - for mono in rbasis.monomials - r = rem(mono, I) - union!(standard, MP.monomials(r)) - end - return MB.QuotientBasis( - MB.SubBasis{MB.Monomial}(MP.monomial_vector(collect(standard))), - I, - ) -end - -function MA.promote_operation( - ::typeof(reduced_basis), - ::Type{<:Remainder}, - ::Type{B}, - ::Type{D}, - ::Type, - ::Type, -) where {T,I,B<:SA.AbstractBasis{T,I},D} - return MB.QuotientBasis{ - T, - I, - B, - MA.promote_operation(SemialgebraicSets.ideal, D), - } -end - function gram_basis(certificate::Remainder, poly) return gram_basis(certificate.gram_certificate, poly) end @@ -270,3 +243,34 @@ function SumOfSquares.matrix_cone_type(::Type{Remainder{GCT}}) where {GCT} end zero_basis(certificate::Remainder) = zero_basis(certificate.gram_certificate) zero_basis_type(::Type{Remainder{GCT}}) where {GCT} = zero_basis_type(GCT) + +function _quotient_basis_type(::Type{B}, ::Type{D}) where {T,I,B<:SA.AbstractBasis{T,I},D} + return MB.QuotientBasis{ + T, + I, + B, + MA.promote_operation(SemialgebraicSets.ideal, D), + } +end + +function MA.promote_operation( + ::typeof(reduced_basis), + ::Type{<:Union{SimpleIdealCertificate,Remainder}}, + ::Type{B}, + ::Type{SemialgebraicSets.FullSpace}, + ::Type, + ::Type, +) where {B} + return B +end + +function MA.promote_operation( + ::typeof(reduced_basis), + ::Type{<:Union{SimpleIdealCertificate,Remainder}}, + ::Type{B}, + ::Type{D}, + ::Type, + ::Type, +) where {B,D} + return _quotient_basis_type(B, D) +end diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index 37725e189..b8003d914 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -133,7 +133,7 @@ function multiplier_basis( ) end function multiplier_basis( - certificate::Putinar{<:Newton}, + ::Putinar{<:Newton}, index::PreorderIndex, domain::WithFixedBases, ) diff --git a/src/attributes.jl b/src/attributes.jl index e9114233e..51945065d 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -141,7 +141,9 @@ function MOI.Bridges.unbridged_function( ::MOI.Bridges.AbstractBridgeOptimizer, value::Union{ GramMatrix{T}, + Vector{<:GramMatrix{T}}, BlockDiagonalGramMatrix{T}, + Vector{<:BlockDiagonalGramMatrix{T}}, MultivariateMoments.MomentMatrix{T}, MultivariateMoments.BlockDiagonalMomentMatrix{T}, MultivariateMoments.MomentVector{T}, diff --git a/test/Tests/BPT12e399.jl b/test/Tests/BPT12e399.jl index 676175ac2..79a38f02d 100644 --- a/test/Tests/BPT12e399.jl +++ b/test/Tests/BPT12e399.jl @@ -66,12 +66,12 @@ function BPT12e399_test(optimizer, config::MOI.Test.Config, remainder::Bool) @test length(moments(μ)) == 3 @test moment_value(moments(μ)[1]) ≈ (remainder ? 1 / 3 : 1.0) atol = atol rtol = rtol - @test monomial(moments(μ)[1]) == 1 + @test moments(μ)[1].polynomial.monomial == 1 @test moment_value(moments(μ)[2]) ≈ 1 atol = atol rtol = rtol - @test monomial(moments(μ)[2]) == y + @test moments(μ)[2].polynomial.monomial == y @test moment_value(moments(μ)[3]) ≈ (remainder ? -8 / 3 : 0.0) atol = atol rtol = rtol - @test monomial(moments(μ)[3]) == x^2 + @test moments(μ)[3].polynomial.monomial == x^2 μ = moments(cref) @test μ isa AbstractMeasure{Float64} From 623e3e89681c96b007d55d5c66d30f5d6c88e417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 28 May 2024 18:12:35 +0200 Subject: [PATCH 23/84] Fixes --- test/Tests/BPT12e399.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/Tests/BPT12e399.jl b/test/Tests/BPT12e399.jl index 79a38f02d..9314f788e 100644 --- a/test/Tests/BPT12e399.jl +++ b/test/Tests/BPT12e399.jl @@ -78,11 +78,11 @@ function BPT12e399_test(optimizer, config::MOI.Test.Config, remainder::Bool) if remainder @test length(moments(μ)) == 3 @test moments(μ)[1].polynomial.monomial ≈ 1 / 3 atol = atol rtol = rtol - @test monomial(moments(μ)[1]) == 1 + @test moments(μ)[1].polynomial.monomial == 1 @test moments(μ)[2].polynomial.monomial ≈ 1 atol = atol rtol = rtol - @test monomial(moments(μ)[2]) == y + @test moments(μ)[2].polynomial.monomial == y @test moments(μ)[3].polynomial.monomial ≈ 3 atol = atol rtol = rtol - @test monomial(moments(μ)[3]) == y^2 + @test moments(μ)[3].polynomial.monomial == y^2 else @test length(moments(μ)) == 5 @test moment_value(moments(μ)[1]) ≈ 1 atol = atol rtol = rtol @@ -139,11 +139,11 @@ function BPT12e399_test(optimizer, config::MOI.Test.Config, remainder::Bool) if remainder @test length(moments(μ)) == 3 @test moment_value(moments(μ)[1]) ≈ 1 / 3 atol = atol rtol = rtol - @test monomial(moments(μ)[1]) == 1 + @test moments(μ)[1].polynomial.monomial == 1 @test moment_value(moments(μ)[2]) ≈ -1 atol = atol rtol = rtol - @test monomial(moments(μ)[2]) == y + @test moments(μ)[2].polynomial.monomial == y @test moment_value(moments(μ)[3]) ≈ 3 atol = atol rtol = rtol - @test monomial(moments(μ)[3]) == y^2 + @test moments(μ)[3].polynomial.monomial == y^2 else @test length(moments(μ)) == 5 @test moment_value(moments(μ)[1]) ≈ 1 atol = atol rtol = rtol From 76f634de19dbaea6d11a37b359a17e8aa24d7333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 28 May 2024 18:34:28 +0200 Subject: [PATCH 24/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 2 +- test/Tests/BPT12e399.jl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 23f9d14b5..0c8d0b58b 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -128,7 +128,7 @@ end function MOI.Bridges.adjoint_map_function(bridge::SOSPolynomialBridge, f) # FIXME `coeffs` should be an `AbstractMatrix` - return SA.coeffs(MB._algebra_element(f, bridge.new_basis), bridge.set.basis) + return MB.adjoint_coeffs(f, bridge.new_basis, bridge.set.basis) end # Attributes, Bridge acting as a constraint diff --git a/test/Tests/BPT12e399.jl b/test/Tests/BPT12e399.jl index 9314f788e..aba307047 100644 --- a/test/Tests/BPT12e399.jl +++ b/test/Tests/BPT12e399.jl @@ -77,11 +77,11 @@ function BPT12e399_test(optimizer, config::MOI.Test.Config, remainder::Bool) @test μ isa AbstractMeasure{Float64} if remainder @test length(moments(μ)) == 3 - @test moments(μ)[1].polynomial.monomial ≈ 1 / 3 atol = atol rtol = rtol + @test moment_value(moments(μ)[1]) ≈ 1 / 3 atol = atol rtol = rtol @test moments(μ)[1].polynomial.monomial == 1 - @test moments(μ)[2].polynomial.monomial ≈ 1 atol = atol rtol = rtol + @test moment_value(moments(μ)[2]) ≈ 1 atol = atol rtol = rtol @test moments(μ)[2].polynomial.monomial == y - @test moments(μ)[3].polynomial.monomial ≈ 3 atol = atol rtol = rtol + @test moment_value(moments(μ)[3]) ≈ 3 atol = atol rtol = rtol @test moments(μ)[3].polynomial.monomial == y^2 else @test length(moments(μ)) == 5 From 1a69467eb6a155935d6f061f27c58c28bb693297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 28 May 2024 22:07:03 +0200 Subject: [PATCH 25/84] Fix format --- src/Certificate/ideal.jl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 2341cc121..ccd088d0c 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -46,10 +46,7 @@ end _reduce_with_domain(basis::MB.SubBasis{MB.Monomial}, ::FullSpace) = basis -function _reduce_with_domain( - basis::MB.SubBasis{MB.Monomial}, - domain, -) +function _reduce_with_domain(basis::MB.SubBasis{MB.Monomial}, domain) I = ideal(domain) # set of standard monomials that are hit standard = Set{eltype(basis.monomials)}() @@ -70,7 +67,10 @@ function reduced_basis( gram_bases, weights, ) - return _reduce_with_domain(_combine_with_gram(basis, gram_bases, weights), domain) + return _reduce_with_domain( + _combine_with_gram(basis, gram_bases, weights), + domain, + ) end abstract type SimpleIdealCertificate{C,B} <: AbstractIdealCertificate end @@ -244,7 +244,10 @@ end zero_basis(certificate::Remainder) = zero_basis(certificate.gram_certificate) zero_basis_type(::Type{Remainder{GCT}}) where {GCT} = zero_basis_type(GCT) -function _quotient_basis_type(::Type{B}, ::Type{D}) where {T,I,B<:SA.AbstractBasis{T,I},D} +function _quotient_basis_type( + ::Type{B}, + ::Type{D}, +) where {T,I,B<:SA.AbstractBasis{T,I},D} return MB.QuotientBasis{ T, I, From fe9dd3220973e0f226437f852ccb0a388d1351a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 29 May 2024 00:03:15 +0200 Subject: [PATCH 26/84] Fix --- test/certificate.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/certificate.jl b/test/certificate.jl index c01821888..cc57e70cf 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -199,14 +199,15 @@ function certificate_api(certificate::Certificate.AbstractIdealCertificate) @polyvar x poly = x + 1 domain = @set x == 1 + basis = MB.SubBasis{MB.Monomial}(MP.monomials(poly)) @test Certificate.reduced_polynomial( certificate, MP.coefficients(poly), - MB.SubBasis{MB.Monomial}(MP.monomials(poly)), + basis, domain, ) isa Tuple _basis_check( - Certificate.gram_basis(certificate, poly), + Certificate.gram_basis(certificate, basis), Certificate.gram_basis_type(typeof(certificate), MP.monomial_type(x)), ) zbasis = Certificate.zero_basis(certificate) From ebc8868eb0fef8e21c53880d88b54699d9166e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 29 May 2024 22:04:16 +0200 Subject: [PATCH 27/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 5 ++++- src/Certificate/Sparsity/ideal.jl | 11 +++++++++-- src/Certificate/ideal.jl | 4 ++-- src/Certificate/newton_polytope.jl | 8 ++++++++ src/Certificate/preorder.jl | 5 ++--- test/certificate.jl | 5 +---- test/csp_test.jl | 6 +++--- test/sparsity.jl | 12 ++++++------ 8 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 0c8d0b58b..9550e7eb3 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -99,6 +99,9 @@ function MOI.supports_constraint( return MOI.Utilities.is_coefficient_type(F, T) end +_eltype(::Type{Vector{T}}) where T = T +_eltype(::Type{T}) where T = T + function MOI.Bridges.Constraint.concrete_bridge_type( ::Type{<:SOSPolynomialBridge{T}}, F::Type{<:MOI.AbstractVectorFunction}, @@ -117,7 +120,7 @@ function MOI.Bridges.Constraint.concrete_bridge_type( ) G = SOS.Certificate.gram_basis_type(CT, MT) W = MP.term_type(MT, T) - return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W} + return SOSPolynomialBridge{T,F,DT,M,B,_eltype(G),CT,MT,MVT,W} end function MOI.Bridges.inverse_map_function(::SOSPolynomialBridge, f) diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index 84eed722d..b8168dfff 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -56,6 +56,13 @@ function sparsity( ) return MB.SubBasis{MB.Monomial}.(sparsity(monos, sp, gram_basis.monomials)) end +# Backward compatibility, we may remove this at some time +function sparsity( + p::MP.AbstractPolynomialLike, + args... +) + return sparsity(MB.SubBasis{MB.Monomial}(MP.monomials(p)), args...) +end function sparsity( basis::MB.SubBasis{MB.Monomial}, sp::Union{SignSymmetry,Monomial}, @@ -64,7 +71,7 @@ function sparsity( return sparsity( basis.monomials, sp, - SumOfSquares.Certificate.gram_basis(certificate, poly), + SumOfSquares.Certificate.gram_basis(certificate, basis), ) end function sparsity(v::SumOfSquares.Certificate.WithVariables, sp, certificate) @@ -77,7 +84,7 @@ function SumOfSquares.Certificate.gram_basis_type( ::Type{Ideal{S,C}}, ::Type{M}, ) where {S,C,M} - return SumOfSquares.Certificate.gram_basis_type(C, M) + return Vector{SumOfSquares.Certificate.gram_basis_type(C, M)} end function SumOfSquares.Certificate.reduced_polynomial( certificate::Ideal, diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index ccd088d0c..02088b09c 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -183,10 +183,10 @@ function Newton(cone, basis, variable_groups::Tuple) ) end -function gram_basis(certificate::Newton, poly) +function gram_basis(certificate::Newton, basis) return MB.basis_covering_monomials( certificate.basis, - monomials_half_newton_polytope(MP.monomials(poly), certificate.newton), + monomials_half_newton_polytope(_basis(basis), certificate.newton), ) end diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index f9a419423..e73abe567 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -273,6 +273,14 @@ function post_filter(monos, X) return monos[findall(keep)] end +# FIXME update API with basis, this is a hack +function monomials_half_newton_polytope( + basis::MB.SubBasis{MB.Monomial}, + newton::AbstractNewtonPolytopeApproximation, +) + return half_newton_polytope(basis.monomials, newton) +end + function monomials_half_newton_polytope( monos::AbstractVector, newton::AbstractNewtonPolytopeApproximation, diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index b8003d914..d0082ce1c 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -45,9 +45,8 @@ end function MP.variables(v::WithVariables) return v.variables end -function MP.monomials(v::WithVariables{<:MB.SubBasis{MB.Monomial}}) - return v.inner.monomials -end +_basis(basis::SA.AbstractBasis) = basis +_basis(v::WithVariables) = _basis(v.inner) _merge_sorted(a::Vector, ::Tuple{}) = a function _merge_sorted(a::Vector, b::Vector) diff --git a/test/certificate.jl b/test/certificate.jl index cc57e70cf..04a510f40 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -166,7 +166,6 @@ function _certificate_api(certificate::Certificate.AbstractCertificate) MOI.AbstractVectorSet end function _basis_check_each(basis::SA.ExplicitBasis, basis_type) - @test basis isa basis_type if basis isa MB.SubBasis # This fails if `basis` is `Vector{<:Monomial}` instead of `MonomialVector` # for DynamicPolynomials. This is important as @@ -182,10 +181,8 @@ function _basis_check_each(basis::SA.ExplicitBasis, basis_type) end function _basis_check(basis, basis_type) @test basis isa SA.ExplicitBasis || basis isa Vector{<:SA.ExplicitBasis} + @test basis isa basis_type if basis isa Vector - # FIXME `basis_type` is `Vector{MB.MonomialBasis}` instead of `Vector{MB.MonomialBasis{...}}` - # Once this is fixed, we should check - # @test basis isa basis_type for b in basis _basis_check_each(b, basis_type) end diff --git a/test/csp_test.jl b/test/csp_test.jl index feee1e8f1..405a39c26 100644 --- a/test/csp_test.jl +++ b/test/csp_test.jl @@ -3,21 +3,21 @@ @polyvar x y z p = x * y + y * z S = @set x^2 + y^2 == 1 && y^2 + z^2 == 1 - G = SumOfSquares.Certificate.Sparsity.csp_graph(p, S) + G = SumOfSquares.Certificate.Sparsity.csp_graph(MB.SubBasis{MB.Monomial}(MP.monomials(p)), S) @test sort(G.int2n) == [z, y, x] end @testset "chordal_csp" begin @polyvar x y z p = x * y + y * z S = @set x^2 + y^2 == 1 && y^2 + z^2 == 1 - H, cliques = SumOfSquares.Certificate.Sparsity.chordal_csp_graph(p, S) + H, cliques = SumOfSquares.Certificate.Sparsity.chordal_csp_graph(MB.SubBasis{MB.Monomial}(MP.monomials(p)), S) @test length(cliques) == 2 svar = cliques[1] ∪ cliques[2] @test H.int2n ⊆ svar @test svar ⊆ H.int2n @test sort!(svar) == sort!(H.int2n) I, cliquesI = - SumOfSquares.Certificate.Sparsity.chordal_csp_graph(p, FullSpace()) + SumOfSquares.Certificate.Sparsity.chordal_csp_graph(MB.SubBasis{MB.Monomial}(MP.monomials(p)), FullSpace()) @test sort!(I.int2n) == sort!(H.int2n) @test sort!(collect.(I.graph.neighbors)) == sort!(collect.(H.graph.neighbors)) diff --git a/test/sparsity.jl b/test/sparsity.jl index 21da8a1ab..daf34a525 100644 --- a/test/sparsity.jl +++ b/test/sparsity.jl @@ -213,6 +213,7 @@ function wml19() f = 1 + x[1]^2 * x[2]^4 + x[1]^4 * x[2]^2 + x[1]^4 * x[2]^4 - x[1] * x[2]^2 - 3x[1]^2 * x[2]^2 + basis = MB.SubBasis{MB.Monomial}(MP.monomials(f)) @testset "$completion $k $use_all_monomials" for completion in [ ClusterCompletion(), ChordalCompletion(), @@ -240,14 +241,14 @@ function wml19() end @test set_monos( Certificate.Sparsity.sparsity( - f, + basis, Sparsity.Monomial(completion, k, use_all_monomials), certificate, ), ) == expected end @test set_monos( - Certificate.Sparsity.sparsity(f, SignSymmetry(), certificate), + Certificate.Sparsity.sparsity(basis, SignSymmetry(), certificate), ) == Set( monomial_vector.([ [x[1]^2 * x[2]^2, x[1] * x[2]^2, 1], @@ -534,7 +535,7 @@ end function drop_monomials() @testset "Drop monomials" begin @polyvar x - f = polynomial(x^2) + basis = MB.SubBasis{MB.Monomial}([x^2]) certificate = Certificate.MaxDegree( SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), @@ -551,7 +552,7 @@ function drop_monomials() end @test set_monos( Certificate.Sparsity.sparsity( - f, + basis, Sparsity.Monomial( ChordalCompletion(), k, @@ -582,13 +583,12 @@ function drop_monomials() ideal_certificate, 3, ) - f = polynomial(x^3) K = @set x >= 0 @testset "$k $use_all_monomials" for k in 0:3, use_all_monomials in [false, true] basis, preorder_bases = Certificate.Sparsity.sparsity( - f, + MB.SubBasis{MB.Monomial}([x^3]), K, Sparsity.Monomial( ChordalCompletion(), From a614315bf4d083a113cffc2fc456ff3005c66e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 29 May 2024 23:35:19 +0200 Subject: [PATCH 28/84] WIP --- src/Bridges/Constraint/sos_polynomial.jl | 21 ++++++++------- src/Certificate/Sparsity/ideal.jl | 6 ++--- src/Certificate/Symmetry/wedderburn.jl | 6 ++--- src/Certificate/ideal.jl | 33 ++++++++++-------------- src/Certificate/preorder.jl | 3 +-- test/certificate.jl | 3 +-- 6 files changed, 30 insertions(+), 42 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 9550e7eb3..87172eb60 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -54,30 +54,31 @@ function MOI.Bridges.Constraint.bridge_constraint( # `set.domain.V` is `FullSpace` or `FixedPolynomialSet`. # FIXME convert needed because the coefficient type of `r` is `Any` otherwise if `domain` is `AlgebraicSet` domain = MP.similar(set.domain, T) - coeffs, basis = SOS.Certificate.reduced_polynomial( + @show set.certificate + @show typeof(MP.polynomial(MOI.Utilities.scalarize(func), copy(set.basis))) + poly = SOS.Certificate.reduced_polynomial( set.certificate, - func, - set.basis, + # MOI does not modify the coefficients of the functions so we can modify `p`. + # without altering `f`. + # The basis may be copied by MA however so we need to copy it. + MP.polynomial(MOI.Utilities.scalarize(func), copy(set.basis)), domain, ) gram_basis = SOS.Certificate.gram_basis( set.certificate, - SOS.Certificate.with_variables(basis, set.domain), + SOS.Certificate.with_variables(poly, set.domain), ) gram_bases = [gram_basis] - weights = [MP.term(one(T), MP.constant_monomial(eltype(basis.monomials)))] + weights = [MP.term(one(T), MP.constant_monomial(poly))] flat_gram_bases, flat_weights, flat_indices = _flatten(gram_bases, weights) new_basis = SOS.Certificate.reduced_basis( set.certificate, - basis, + SA.basis(poly), domain, flat_gram_bases, flat_weights, ) - new_coeffs = SA.coeffs( - MB._algebra_element(MOI.Utilities.scalarize(coeffs), basis), - new_basis, - ) + new_coeffs = SA.coeffs(poly, new_basis) constraint = MOI.add_constraint( model, MOI.Utilities.vectorize(new_coeffs), diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index b8168dfff..9e2c341dd 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -88,14 +88,12 @@ function SumOfSquares.Certificate.gram_basis_type( end function SumOfSquares.Certificate.reduced_polynomial( certificate::Ideal, - coeffs, - basis, + poly, domain, ) return SumOfSquares.Certificate.reduced_polynomial( certificate.certificate, - coeffs, - basis, + poly, domain, ) end diff --git a/src/Certificate/Symmetry/wedderburn.jl b/src/Certificate/Symmetry/wedderburn.jl index 7648df145..cb7b88612 100644 --- a/src/Certificate/Symmetry/wedderburn.jl +++ b/src/Certificate/Symmetry/wedderburn.jl @@ -85,14 +85,12 @@ SumOfSquares.Certificate.zero_basis_type(::Type{<:Ideal}) = MB.Monomial SumOfSquares.Certificate.zero_basis(::Ideal) = MB.Monomial function SumOfSquares.Certificate.reduced_polynomial( certificate::Ideal, - coeffs, - basis, + poly, domain, ) return SumOfSquares.Certificate.reduced_polynomial( certificate.certificate, - coeffs, - basis, + poly, domain, ) end diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 02088b09c..8cce877c3 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -4,15 +4,15 @@ abstract type AbstractIdealCertificate <: AbstractCertificate end -struct _NonZeroo <: Number end -Base.iszero(::_NonZeroo) = false -Base.convert(::Type{_NonZeroo}, ::Number) = _NonZeroo() -Base.:*(a::_NonZeroo, ::Number) = a -Base.:*(::Number, a::_NonZeroo) = a -Base.:*(::_NonZeroo, a::_NonZeroo) = a -Base.:+(a::_NonZeroo, ::Number) = a -Base.:+(::Number, a::_NonZeroo) = a -Base.:+(::_NonZeroo, a::_NonZeroo) = a +struct _NonZero <: Number end +Base.iszero(::_NonZero) = false +Base.convert(::Type{_NonZero}, ::Number) = _NonZero() +Base.:*(a::_NonZero, ::Number) = a +Base.:*(::Number, a::_NonZero) = a +Base.:*(::_NonZero, a::_NonZero) = a +Base.:+(a::_NonZero, ::Number) = a +Base.:+(::Number, a::_NonZero) = a +Base.:+(::_NonZero, a::_NonZero) = a function _combine_with_gram( basis::MB.SubBasis{B,M}, @@ -21,7 +21,7 @@ function _combine_with_gram( ) where {B,M} full = MB.FullBasis{B,M}() mstr = SA.mstructure(full) - p = MP.polynomial(fill(_NonZeroo(), length(basis)), basis.monomials) + p = MP.polynomial(fill(_NonZero(), length(basis)), basis.monomials) for (gram, weight) in zip(gram_bases, weights) w = MB.convert_basis(full, weight) for j in eachindex(gram) @@ -75,9 +75,7 @@ end abstract type SimpleIdealCertificate{C,B} <: AbstractIdealCertificate end -function reduced_polynomial(::SimpleIdealCertificate, coeffs, basis, domain) - return coeffs, basis -end +reduced_polynomial(::SimpleIdealCertificate, poly, domain) = poly cone(certificate::SimpleIdealCertificate) = certificate.cone function SumOfSquares.matrix_cone_type( @@ -186,7 +184,7 @@ end function gram_basis(certificate::Newton, basis) return MB.basis_covering_monomials( certificate.basis, - monomials_half_newton_polytope(_basis(basis), certificate.newton), + monomials_half_newton_polytope(SA.basis(basis), certificate.newton), ) end @@ -216,14 +214,9 @@ end function reduced_polynomial( ::Remainder, - coeffs, - basis::MB.SubBasis{MB.Monomial}, + poly, domain, ) - # MOI does not modify the coefficients of the functions so we can modify `p`. - # without altering `f`. - # The basis may be copied by MA however so we need to copy it. - poly = MP.polynomial(MOI.Utilities.scalarize(coeffs), copy(basis)) r = convert(typeof(poly), rem(poly, ideal(domain))) return MOI.Utilities.vectorize(MP.coefficients(r)), MB.SubBasis{MB.Monomial}(MP.monomials(r)) diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index d0082ce1c..19ea2abfb 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -45,8 +45,7 @@ end function MP.variables(v::WithVariables) return v.variables end -_basis(basis::SA.AbstractBasis) = basis -_basis(v::WithVariables) = _basis(v.inner) +SA.basis(v::WithVariables) = SA.basis(v.inner) _merge_sorted(a::Vector, ::Tuple{}) = a function _merge_sorted(a::Vector, b::Vector) diff --git a/test/certificate.jl b/test/certificate.jl index 04a510f40..d72aac04f 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -199,8 +199,7 @@ function certificate_api(certificate::Certificate.AbstractIdealCertificate) basis = MB.SubBasis{MB.Monomial}(MP.monomials(poly)) @test Certificate.reduced_polynomial( certificate, - MP.coefficients(poly), - basis, + poly, domain, ) isa Tuple _basis_check( From 5aa328e1a12ac0995f3212d07dfcc10c32d52801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 30 May 2024 10:05:10 +0200 Subject: [PATCH 29/84] WIP --- src/Bridges/Constraint/sos_polynomial.jl | 17 ++++++------ src/gram_matrix.jl | 33 +++++++++++++++++++++--- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 87172eb60..c99dc7067 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -8,7 +8,7 @@ struct SOSPolynomialBridge{ CT<:SOS.Certificate.AbstractIdealCertificate, MT<:MP.AbstractMonomial, MVT<:AbstractVector{MT}, - W<:MP.AbstractTerm{T}, + W<:SA.AlgebraElement, } <: MOI.Bridges.Constraint.SetMapBridge{ T, SOS.WeightedSOSCone{M,B,G,W}, @@ -69,7 +69,7 @@ function MOI.Bridges.Constraint.bridge_constraint( SOS.Certificate.with_variables(poly, set.domain), ) gram_bases = [gram_basis] - weights = [MP.term(one(T), MP.constant_monomial(poly))] + weights = [MB.constant_polynomial(typeof(SA.basis(poly)), T)] flat_gram_bases, flat_weights, flat_indices = _flatten(gram_bases, weights) new_basis = SOS.Certificate.reduced_basis( set.certificate, @@ -111,23 +111,24 @@ function MOI.Bridges.Constraint.concrete_bridge_type( # promotes VectorOfVariables into VectorAffineFunction, it should be enough # for most use cases M = SOS.matrix_cone_type(CT) - B = MA.promote_operation( + B = MB.SubBasis{MB.Monomial,MT,MVT} + W = MP.polynomial_type(B, T) + R = MA.promote_operation( SOS.Certificate.reduced_basis, CT, - MB.SubBasis{MB.Monomial,MT,MVT}, + B, SemialgebraicSets.similar_type(DT, T), Vector{MB.SubBasis{MB.Monomial,MT,MVT}}, - Vector{MP.term_type(MT, T)}, + Vector{W}, ) G = SOS.Certificate.gram_basis_type(CT, MT) - W = MP.term_type(MT, T) - return SOSPolynomialBridge{T,F,DT,M,B,_eltype(G),CT,MT,MVT,W} + return SOSPolynomialBridge{T,F,DT,M,R,_eltype(G),CT,MT,MVT,W} end function MOI.Bridges.inverse_map_function(::SOSPolynomialBridge, f) throw(MOI.Bridges.MapNotInvertible()) # Does not work with QuotientBasis - #return SA.coeffs(MB._algebra_element(f, bridge.new_basis), bridge.set.basis) + #return SA.coeffs(MP.polynomial(f, bridge.new_basis), bridge.set.basis) end function MOI.Bridges.adjoint_map_function(bridge::SOSPolynomialBridge, f) diff --git a/src/gram_matrix.jl b/src/gram_matrix.jl index 8a7d2477b..d8c939fd6 100644 --- a/src/gram_matrix.jl +++ b/src/gram_matrix.jl @@ -28,10 +28,18 @@ function MP.term_type( ) where {T,B,U} return MP.term_type(MP.polynomial_type(p)) end + function MP.polynomial_type( ::Union{AbstractGramMatrix{T,B,U},Type{<:AbstractGramMatrix{T,B,U}}}, + ::Type{S}, +) where {T,B,U,S} + return MP.polynomial_type(B, S) +end + +function MP.polynomial_type( + G::Union{AbstractGramMatrix{T,B,U},Type{<:AbstractGramMatrix{T,B,U}}}, ) where {T,B,U} - return MP.polynomial_type(B, U) + return MP.polynomial_type(G, U) end Base.:(==)(p::_APL, q::AbstractGramMatrix) = p == MP.polynomial(q) @@ -134,8 +142,27 @@ end function MP.polynomial(p::GramMatrix{T,B,U}) where {T,B,U} return MP.polynomial(p, U) end -function MP.polynomial(p::GramMatrix, ::Type{S}) where {S} - return MP.polynomial(value_matrix(p), p.basis, S) + +function MA.operate!(op::SA.UnsafeAddMul{typeof(*)}, p::SA.AlgebraElement, g::GramMatrix) + for col in eachindex(g.basis) + for row in eachindex(g.basis) + MA.operate_to!( + op, + p, + g.Q[row, col], + SA.star(g.basis[row]) * g.basis[col], + ) + end + end +end + +function MP.polynomial(g::GramMatrix, ::Type{T}) where {T} + n = length(g.basis) + @assert size(g.Q) == (n, n) + p = zero(MP.polynomial_type(typeof(g), T)) + MA.operate(SA.UnsafeAddMul(*), p, g) + MA.operate(SA.canonical, p) + return p end function change_basis( From edd442a7c8d6f0a82c8a3419486aab4f6135550c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 30 May 2024 11:31:41 +0200 Subject: [PATCH 30/84] WIP --- src/Bridges/Constraint/sos_polynomial.jl | 8 ++---- src/Bridges/Variable/Variable.jl | 2 ++ src/Bridges/Variable/kernel.jl | 7 ++--- src/Certificate/Sparsity/Sparsity.jl | 1 + src/Certificate/Sparsity/ideal.jl | 3 ++ src/Certificate/ideal.jl | 8 ++++-- src/gram_matrix.jl | 35 ++++++++++++++---------- src/sets.jl | 4 +-- test/Solvers/scs_tests.jl | 14 +++++----- test/Tests/utilities.jl | 8 ++++-- 10 files changed, 52 insertions(+), 38 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index c99dc7067..1d3faa759 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -54,14 +54,12 @@ function MOI.Bridges.Constraint.bridge_constraint( # `set.domain.V` is `FullSpace` or `FixedPolynomialSet`. # FIXME convert needed because the coefficient type of `r` is `Any` otherwise if `domain` is `AlgebraicSet` domain = MP.similar(set.domain, T) - @show set.certificate - @show typeof(MP.polynomial(MOI.Utilities.scalarize(func), copy(set.basis))) poly = SOS.Certificate.reduced_polynomial( set.certificate, # MOI does not modify the coefficients of the functions so we can modify `p`. # without altering `f`. # The basis may be copied by MA however so we need to copy it. - MP.polynomial(MOI.Utilities.scalarize(func), copy(set.basis)), + MB.algebra_element(MOI.Utilities.scalarize(func), copy(set.basis)), domain, ) gram_basis = SOS.Certificate.gram_basis( @@ -69,7 +67,7 @@ function MOI.Bridges.Constraint.bridge_constraint( SOS.Certificate.with_variables(poly, set.domain), ) gram_bases = [gram_basis] - weights = [MB.constant_polynomial(typeof(SA.basis(poly)), T)] + weights = [MB.constant_algebra_element(typeof(SA.basis(poly)), T)] flat_gram_bases, flat_weights, flat_indices = _flatten(gram_bases, weights) new_basis = SOS.Certificate.reduced_basis( set.certificate, @@ -112,7 +110,7 @@ function MOI.Bridges.Constraint.concrete_bridge_type( # for most use cases M = SOS.matrix_cone_type(CT) B = MB.SubBasis{MB.Monomial,MT,MVT} - W = MP.polynomial_type(B, T) + W = SA.AlgebraElement{MB.algebra_type(B),T,Vector{T}} R = MA.promote_operation( SOS.Certificate.reduced_basis, CT, diff --git a/src/Bridges/Variable/Variable.jl b/src/Bridges/Variable/Variable.jl index eee99b38d..f18d09eab 100644 --- a/src/Bridges/Variable/Variable.jl +++ b/src/Bridges/Variable/Variable.jl @@ -1,6 +1,8 @@ module Variable +import SparseArrays import MutableArithmetics as MA +import StarAlgebras as SA import MathOptInterface as MOI import MultivariatePolynomials as MP import SumOfSquares as SOS diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index 3c066d14f..b132e1a25 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -12,15 +12,14 @@ function MOI.Bridges.Variable.bridge_constrained_variable( ) where {T,M} variables = Vector{MOI.VariableIndex}[] constraints = MOI.ConstraintIndex{MOI.VectorOfVariables}[] - acc = MA.Zero() + acc = SA.AlgebraElement(SparseArrays.sparsevec(SA.key_type(typeof(set.basis))[], MOI.ScalarAffineFunction{T}[], length(set.basis)), SA.algebra(set.basis)) for (gram_basis, weight) in zip(set.gram_bases, set.weights) gram, vars, con = SOS.add_gram_matrix(model, M, gram_basis, T) push!(variables, vars) push!(constraints, con) - acc = MA.add_mul!!(acc, weight, gram) + MA.operate!(SA.UnsafeAddMul(*), acc, weight, gram) end - affine = MP.coefficients(acc, set.basis) - return KernelBridge{T,M}(affine, variables, constraints, set) + return KernelBridge{T,M}(SA.coeffs(acc), variables, constraints, set) end function MOI.Bridges.Variable.supports_constrained_variable( diff --git a/src/Certificate/Sparsity/Sparsity.jl b/src/Certificate/Sparsity/Sparsity.jl index a0a49bbf8..8ec60c372 100644 --- a/src/Certificate/Sparsity/Sparsity.jl +++ b/src/Certificate/Sparsity/Sparsity.jl @@ -1,6 +1,7 @@ module Sparsity import MutableArithmetics as MA +import StarAlgebras as SA import MultivariatePolynomials as MP const _APL = MP.AbstractPolynomialLike import MultivariateBases as MB diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index 9e2c341dd..a5ec5ef54 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -74,6 +74,9 @@ function sparsity( SumOfSquares.Certificate.gram_basis(certificate, basis), ) end +function sparsity(a::SA.AlgebraElement, sp, certificate) + return sparsity(SA.basis(a), sp, certificate) +end function sparsity(v::SumOfSquares.Certificate.WithVariables, sp, certificate) return sparsity(v.inner, sp, certificate) end diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 8cce877c3..4170be048 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -212,12 +212,16 @@ struct Remainder{GCT<:AbstractIdealCertificate} <: AbstractIdealCertificate gram_certificate::GCT end +function _rem(coeffs, basis::MB.SubBasis{MB.Monomial}, I) + return rem(MP.polynomial(coeffs, basis.monomials), I) +end + function reduced_polynomial( ::Remainder, - poly, + a::SA.AlgebraElement, domain, ) - r = convert(typeof(poly), rem(poly, ideal(domain))) + r = convert(typeof(poly), _rem(SA.coeffs(a), SA.basis(a), ideal(domain))) return MOI.Utilities.vectorize(MP.coefficients(r)), MB.SubBasis{MB.Monomial}(MP.monomials(r)) end diff --git a/src/gram_matrix.jl b/src/gram_matrix.jl index d8c939fd6..26b9a6ccd 100644 --- a/src/gram_matrix.jl +++ b/src/gram_matrix.jl @@ -143,28 +143,33 @@ function MP.polynomial(p::GramMatrix{T,B,U}) where {T,B,U} return MP.polynomial(p, U) end -function MA.operate!(op::SA.UnsafeAddMul{typeof(*)}, p::SA.AlgebraElement, g::GramMatrix) +function MA.operate!(op::SA.UnsafeAddMul{typeof(*)}, p::SA.AlgebraElement, w::SA.AlgebraElement, g::GramMatrix) for col in eachindex(g.basis) for row in eachindex(g.basis) - MA.operate_to!( - op, - p, - g.Q[row, col], - SA.star(g.basis[row]) * g.basis[col], - ) + for (k, v) in SA.nonzero_pairs(SA.coeffs(w)) + MA.operate!( + op, + p, + v * g.Q[row, col], + SA.basis(w)[k], + SA.star(g.basis[row]), + g.basis[col], + ) + end end end -end - -function MP.polynomial(g::GramMatrix, ::Type{T}) where {T} - n = length(g.basis) - @assert size(g.Q) == (n, n) - p = zero(MP.polynomial_type(typeof(g), T)) - MA.operate(SA.UnsafeAddMul(*), p, g) - MA.operate(SA.canonical, p) return p end +#function MP.polynomial(g::GramMatrix, ::Type{T}) where {T} +# n = length(g.basis) +# @assert size(g.Q) == (n, n) +# p = zero(MP.polynomial_type(typeof(g), T)) +# MA.operate(SA.UnsafeAddMul(*), p, g) +# MA.operate(SA.canonical, p) +# return p +#end + function change_basis( p::GramMatrix{T,<:MB.SubBasis{B}}, ::FullBasis{B}, diff --git a/src/sets.jl b/src/sets.jl index 8bf710af4..7cbcef073 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -132,7 +132,7 @@ struct WeightedSOSCone{ M, B<:SA.ExplicitBasis, G<:SA.ExplicitBasis, - W<:MP.AbstractPolynomialLike, + W<:SA.AlgebraElement, } <: MOI.AbstractVectorSet basis::B gram_bases::Vector{G} @@ -142,7 +142,7 @@ function WeightedSOSCone{M}( basis::SA.ExplicitBasis, gram_bases::Vector{G}, weights::Vector{W}, -) where {M,G<:SA.ExplicitBasis,W<:MP.AbstractPolynomialLike} +) where {M,G<:SA.ExplicitBasis,W<:SA.AlgebraElement} return WeightedSOSCone{M,typeof(basis),G,W}(basis, gram_bases, weights) end MOI.dimension(set::WeightedSOSCone) = length(set.basis) diff --git a/test/Solvers/scs_tests.jl b/test/Solvers/scs_tests.jl index bf6ced18a..7dc52efa8 100644 --- a/test/Solvers/scs_tests.jl +++ b/test/Solvers/scs_tests.jl @@ -3,11 +3,11 @@ import SCS factory = optimizer_with_attributes(SCS.Optimizer, MOI.Silent() => true) config = MOI.Test.Config(atol = 1e-3, rtol = 1e-3) @testset "Linear" begin - Tests.linear_test(factory, config) -end -@testset "SOC" begin - Tests.soc_test(factory, config) -end -@testset "SDP" begin - Tests.sd_test(factory, config) + Tests.linear_test(factory, config, include=["dsos_term"]) end +#@testset "SOC" begin +# Tests.soc_test(factory, config) +#end +#@testset "SDP" begin +# Tests.sd_test(factory, config) +#end diff --git a/test/Tests/utilities.jl b/test/Tests/utilities.jl index 37accd74f..9c85df970 100644 --- a/test/Tests/utilities.jl +++ b/test/Tests/utilities.jl @@ -30,7 +30,7 @@ macro test_suite(setname, subsets = false) testname = Symbol(string(setname) * "_test") testdict = Symbol(string(testname) * "s") if subsets - runtest = :(f(model, config, exclude)) + runtest = :(f(model, config; exclude)) else runtest = :(f(model, config)) end @@ -38,10 +38,12 @@ macro test_suite(setname, subsets = false) :( function $testname( model, # could be ModelLike or an optimizer constructor - config::$MOI.Test.Config, + config::$MOI.Test.Config; + include::Vector{String} = collect(keys($testdict)), exclude::Vector{String} = String[], ) - for (name, f) in $testdict + for name in include + f = $(testdict)[name] if name in exclude continue end From 22129293fde6464ffdef4e2518cdd858bfba36a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 30 May 2024 14:04:55 +0200 Subject: [PATCH 31/84] up --- .../sos_polynomial_in_semialgebraic_set.jl | 2 +- src/Certificate/newton_polytope.jl | 5 +++++ test/Solvers/scs_tests.jl | 14 +++++++------- test/Tests/utilities.jl | 3 ++- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index bb9818256..ab0465c7a 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -49,7 +49,7 @@ function MOI.Bridges.Constraint.bridge_constraint( # MOI does not modify the coefficients of the functions so we can modify `p`. # without altering `f`. # The monomials may be copied by MA however so we need to copy it. - p = MP.polynomial(MOI.Utilities.scalarize(f), copy(set.basis)) + p = MB.algebra_element(MOI.Utilities.scalarize(f), copy(set.basis)) λ_bases = B[] λ_variables = Union{Vector{MOI.VariableIndex},Vector{Vector{MOI.VariableIndex}}}[] diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index e73abe567..71b177ab3 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -674,3 +674,8 @@ function post_filter(poly, generators, multipliers_gram_monos) (keep, gram_monos) in zip(keep, multipliers_gram_monos) ] end + +function half_newton_polytope(a::SA.AlgebraElement, args...) + @assert SA.basis(a) isa MB.SubBasis{MB.Monomial} + half_newton_polytope(SA.coeffs(a, MB.FullBasis{MB.Monomial,MP.monomial_type(typeof(a))}()), args...) +end diff --git a/test/Solvers/scs_tests.jl b/test/Solvers/scs_tests.jl index 7dc52efa8..bf6ced18a 100644 --- a/test/Solvers/scs_tests.jl +++ b/test/Solvers/scs_tests.jl @@ -3,11 +3,11 @@ import SCS factory = optimizer_with_attributes(SCS.Optimizer, MOI.Silent() => true) config = MOI.Test.Config(atol = 1e-3, rtol = 1e-3) @testset "Linear" begin - Tests.linear_test(factory, config, include=["dsos_term"]) + Tests.linear_test(factory, config) +end +@testset "SOC" begin + Tests.soc_test(factory, config) +end +@testset "SDP" begin + Tests.sd_test(factory, config) end -#@testset "SOC" begin -# Tests.soc_test(factory, config) -#end -#@testset "SDP" begin -# Tests.sd_test(factory, config) -#end diff --git a/test/Tests/utilities.jl b/test/Tests/utilities.jl index 9c85df970..46f572143 100644 --- a/test/Tests/utilities.jl +++ b/test/Tests/utilities.jl @@ -1,3 +1,4 @@ +import StarAlgebras as SA using Test, JuMP using SumOfSquares @@ -172,7 +173,7 @@ function test_constraint_primal(cref, expected; atol, rtol) # otherwise, it should throw `ValueNotSupported`. try v = value(cref) - @test v isa AbstractPolynomial + @test v isa SA.AlgebraElement @test v ≈ expected atol = atol rtol = rtol catch err if !(err isa SumOfSquares.ValueNotSupported) From bdde3a2745ffd1559403781ea54f5f38a666e911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 30 May 2024 14:10:45 +0200 Subject: [PATCH 32/84] Fixes --- src/Certificate/ideal.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 4170be048..5e223382e 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -221,9 +221,11 @@ function reduced_polynomial( a::SA.AlgebraElement, domain, ) - r = convert(typeof(poly), _rem(SA.coeffs(a), SA.basis(a), ideal(domain))) - return MOI.Utilities.vectorize(MP.coefficients(r)), - MB.SubBasis{MB.Monomial}(MP.monomials(r)) + r = _rem(SA.coeffs(a), SA.basis(a), ideal(domain)) + return MB.algebra_element( + MP.coefficients(r), + MB.SubBasis{MB.Monomial}(MP.monomials(r)), + ) end function gram_basis(certificate::Remainder, poly) From 3fe5bcf1b9f340f933fc69e480a2f2d29acecf9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 31 May 2024 08:29:30 +0200 Subject: [PATCH 33/84] Missing canonical --- src/Bridges/Variable/kernel.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index b132e1a25..56bfb29b5 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -19,6 +19,7 @@ function MOI.Bridges.Variable.bridge_constrained_variable( push!(constraints, con) MA.operate!(SA.UnsafeAddMul(*), acc, weight, gram) end + MA.operate!(SA.canonical, acc) return KernelBridge{T,M}(SA.coeffs(acc), variables, constraints, set) end From 70b7f54b08a0353f17901a1a5a192baddf09e536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 3 Jun 2024 15:36:46 +0200 Subject: [PATCH 34/84] Fixes --- src/Certificate/ideal.jl | 3 ++- src/Certificate/newton_polytope.jl | 20 +++++++++++++------- test/Solvers/scs_tests.jl | 15 ++++++++------- test/Tests/utilities.jl | 1 + 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 5e223382e..6113b13e7 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -213,7 +213,8 @@ struct Remainder{GCT<:AbstractIdealCertificate} <: AbstractIdealCertificate end function _rem(coeffs, basis::MB.SubBasis{MB.Monomial}, I) - return rem(MP.polynomial(coeffs, basis.monomials), I) + poly = MP.polynomial(coeffs, basis.monomials) + return convert(typeof(poly), rem(poly, I)) end function reduced_polynomial( diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 71b177ab3..7a72fb340 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -376,9 +376,9 @@ _sign(a) = missing function deg_sign(deg, p, d) sgn = nothing - for t in MP.terms(p) - if deg(t) == d - s = _sign(MP.coefficient(t)) + for (k, v) in SA.nonzero_pairs(SA.coeffs(p)) + if deg(SA.basis(p)[k]) == d + s = _sign(v) if isnothing(sgn) sgn = s else @@ -456,21 +456,26 @@ function deg_range(deg, p, gs, gram_deg, range::UnitRange) return end +_mindegree(p::MP.AbstractPolynomialLike) = MP.mindegree(p) +_mindegree(a::SA.AlgebraElement) = _mindegree(SA.basis(a)) +_mindegree(basis::MB.SubBasis{MB.Monomial}) = MP.mindegree(basis.monomials) +_mindegree(p::MB.Polynomial{MB.Monomial}) = MP.mindegree(p.monomial) + function putinar_degree_bounds( - p::MP.AbstractPolynomialLike, + p::SA.AlgebraElement, gs::AbstractVector{<:MP.AbstractPolynomialLike}, vars, maxdegree, ) mindegree = 0 # TODO homogeneous case - mindeg(g) = _min_half(min_shift(mindegree, MP.mindegree(g))) + mindeg(g) = _min_half(min_shift(mindegree, _mindegree(g))) maxdeg(g) = _max_half(maxdegree - MP.maxdegree(g)) degrange(g) = mindeg(g):maxdeg(g) minus_degrange(g) = -maxdeg(g):-mindeg(g) # The multiplier will have degree `0:2fld(maxdegree - MP.maxdegree(g), 2)` mindegree = - -deg_range(p -> -MP.mindegree(p), p, gs, minus_degrange, -maxdegree:0) + -deg_range(p -> -_mindegree(p), p, gs, minus_degrange, -maxdegree:0) if isnothing(mindegree) return end @@ -539,7 +544,7 @@ function half_newton_polytope( end function half_newton_polytope( - p::MP.AbstractPolynomialLike, + p::SA.AlgebraElement, gs::AbstractVector{<:MP.AbstractPolynomialLike}, vars, maxdegree, @@ -560,6 +565,7 @@ function half_newton_polytope( # The last one will be recomputed by the ideal certificate return MB.SubBasis{MB.Monomial}.(filtered_bases[1:(end-1)]) end + struct SignCount unknown::Int positive::Int diff --git a/test/Solvers/scs_tests.jl b/test/Solvers/scs_tests.jl index bf6ced18a..1050719c9 100644 --- a/test/Solvers/scs_tests.jl +++ b/test/Solvers/scs_tests.jl @@ -3,11 +3,12 @@ import SCS factory = optimizer_with_attributes(SCS.Optimizer, MOI.Silent() => true) config = MOI.Test.Config(atol = 1e-3, rtol = 1e-3) @testset "Linear" begin - Tests.linear_test(factory, config) -end -@testset "SOC" begin - Tests.soc_test(factory, config) -end -@testset "SDP" begin - Tests.sd_test(factory, config) + Tests.linear_test(factory, config, include=["dsos_horn",#"dsos_concave_then_convex_cubic" + ]) end +#@testset "SOC" begin +# Tests.soc_test(factory, config) +#end +#@testset "SDP" begin +# Tests.sd_test(factory, config) +#end diff --git a/test/Tests/utilities.jl b/test/Tests/utilities.jl index 46f572143..5aec8012d 100644 --- a/test/Tests/utilities.jl +++ b/test/Tests/utilities.jl @@ -1,5 +1,6 @@ import StarAlgebras as SA using Test, JuMP +import StarAlgebras as SA using SumOfSquares function _model(optimizer::MOI.AbstractOptimizer) From 3173afccdf7019a56c1c267dcbcee5aa77000a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 3 Jun 2024 16:17:19 +0200 Subject: [PATCH 35/84] up --- test/Solvers/Manifest.toml | 599 +++++++++++++++++++++---------------- test/Solvers/Project.toml | 6 +- 2 files changed, 336 insertions(+), 269 deletions(-) diff --git a/test/Solvers/Manifest.toml b/test/Solvers/Manifest.toml index 990aba4b1..f7cfa1982 100644 --- a/test/Solvers/Manifest.toml +++ b/test/Solvers/Manifest.toml @@ -1,20 +1,20 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.8.5" +julia_version = "1.10.3" manifest_format = "2.0" -project_hash = "2e15969113c025ff915ac251c4281a4079e26b1b" +project_hash = "a750a66f9ca9e0a5efeca4cf930bf0068e5f21c7" [[deps.AMD]] -deps = ["Libdl", "LinearAlgebra", "SparseArrays", "Test"] -git-tree-sha1 = "00163dc02b882ca5ec032400b919e5f5011dbd31" +deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse_jll"] +git-tree-sha1 = "45a1272e3f809d36431e57ab22703c6896b8908f" uuid = "14f7f29c-3bd6-536c-9a0b-7339e30b5a3e" -version = "0.5.0" +version = "0.5.3" -[[deps.AbstractAlgebra]] -deps = ["GroupsCore", "InteractiveUtils", "LinearAlgebra", "MacroTools", "Markdown", "Random", "RandomExtensions", "SparseArrays", "Test"] -git-tree-sha1 = "29e65c331f97db9189ef00a4c7aed8127c2fd2d4" -uuid = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" -version = "0.27.10" +[[deps.AbstractPermutations]] +deps = ["GroupsCore"] +git-tree-sha1 = "fbede79e328d45b2f9258abbb83b1af4a4900a5e" +uuid = "36d08e8a-54dd-435f-8c9e-38a475050b11" +version = "0.3.0" [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" @@ -40,32 +40,26 @@ uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[deps.BenchmarkTools]] deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] -git-tree-sha1 = "d9a9701b899b30332bbcb3e1679c41cce81fb0e8" +git-tree-sha1 = "f1dff6729bc61f4d49e140da1af55dcd1ac97b2f" uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -version = "1.3.2" +version = "1.5.0" [[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" +git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+0" - -[[deps.CDCS]] -deps = ["LinearAlgebra", "MATLAB", "MathOptInterface", "SparseArrays"] -git-tree-sha1 = "97972ef9e8db9543f11660291421094020063e30" -uuid = "4fe2ecd4-b952-581a-b4b6-a532675a646e" -version = "0.2.0" +version = "1.0.8+1" [[deps.CEnum]] -git-tree-sha1 = "eb4cb44a499229b3b8426dcfb5dd85333951ff90" +git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc" uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" -version = "0.4.2" +version = "0.5.0" [[deps.COSMO]] deps = ["AMD", "COSMOAccelerators", "DataStructures", "IterTools", "LinearAlgebra", "MathOptInterface", "Pkg", "Printf", "QDLDL", "Random", "Reexport", "Requires", "SparseArrays", "Statistics", "SuiteSparse", "Test", "UnsafeArrays"] -git-tree-sha1 = "820b146e7d267be941cd07f03ae45fd0bde3800a" +git-tree-sha1 = "9ee9809bde10b6875ca58aadcb22af8e36bd83f9" uuid = "1e616198-aa4e-51ec-90a2-23f7fbd31d8d" -version = "0.8.7" +version = "0.8.9" [[deps.COSMOAccelerators]] deps = ["LinearAlgebra", "Random", "SparseArrays", "Test"] @@ -73,11 +67,17 @@ git-tree-sha1 = "b1153b40dd95f856e379f25ae335755ecc24298e" uuid = "bbd8fffe-5ad0-4d78-a55e-85575421b4ac" version = "0.1.0" +[[deps.CRlibm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" +uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" +version = "1.0.1+0" + [[deps.CSDP]] deps = ["CSDP_jll", "LinearAlgebra", "MathOptInterface", "SparseArrays"] -git-tree-sha1 = "2c98acdf7cbf137db2f2b43172cfd973e190aefa" +git-tree-sha1 = "51b3e3f9da11db1bae9921253edc97ef73b61a43" uuid = "0a46da34-8e4b-519e-b418-48813639ff34" -version = "1.1.0" +version = "1.1.1" [[deps.CSDP_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "Pkg"] @@ -86,33 +86,43 @@ uuid = "9ce75daa-2788-5e2c-ba1d-cf8c48367b27" version = "600.200.1+0" [[deps.ChainRulesCore]] -deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "c6d890a52d2c4d55d326439580c3b8d0875a77d9" +deps = ["Compat", "LinearAlgebra"] +git-tree-sha1 = "575cd02e080939a33b6df6c5853d14924c08e35b" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.15.7" +version = "1.23.0" +weakdeps = ["SparseArrays"] -[[deps.ChangesOfVariables]] -deps = ["ChainRulesCore", "LinearAlgebra", "Test"] -git-tree-sha1 = "485193efd2176b88e6622a39a246f8c5b600e74e" -uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" -version = "0.1.6" + [deps.ChainRulesCore.extensions] + ChainRulesCoreSparseArraysExt = "SparseArrays" [[deps.CodecBzip2]] deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"] -git-tree-sha1 = "2e62a725210ce3c3c2e1a3080190e7ca491f18d7" +git-tree-sha1 = "9b1ca1aa6ce3f71b3d1840c538a8210a043625eb" uuid = "523fee87-0ab8-5b00-afb7-3ecf72e48cfd" -version = "0.7.2" +version = "0.8.2" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "9c209fb7536406834aa938fb149964b985de6c83" +git-tree-sha1 = "59939d8a997469ee05c4b4944560a820f9ba0d73" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.1" +version = "0.7.4" -[[deps.Combinatorics]] -git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" -uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "1.0.2" +[[deps.ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.11.5" + +[[deps.Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] +git-tree-sha1 = "362a287c3aa50601b0bc359053d5c2468f0e7ce0" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.12.11" + +[[deps.CommonSolve]] +git-tree-sha1 = "0eee5eb66b1cf62cd6ad1b460238e60e4b09400c" +uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" +version = "0.2.4" [[deps.CommonSubexpressions]] deps = ["MacroTools", "Test"] @@ -121,27 +131,30 @@ uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" version = "0.3.0" [[deps.Compat]] -deps = ["Dates", "LinearAlgebra", "UUIDs"] -git-tree-sha1 = "7a60c856b9fa189eb34f5f8a6f6b5529b7942957" +deps = ["TOML", "UUIDs"] +git-tree-sha1 = "b1c55339b7c6c350ee89f2c1604299660525b248" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.6.1" +version = "4.15.0" +weakdeps = ["Dates", "LinearAlgebra"] + + [deps.Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.0.1+0" +version = "1.1.1+0" -[[deps.ComplexOptInterface]] -deps = ["JuMP", "LinearAlgebra", "MathOptInterface", "MutableArithmetics"] -git-tree-sha1 = "5e32aa323db217ee2ea99470130db4c09816c09f" -uuid = "25c3070e-1275-41b5-afef-2f982c87090a" -version = "0.1.2" +[[deps.Crayons]] +git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" +uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" +version = "4.1.1" [[deps.CxxWrap]] deps = ["Libdl", "MacroTools", "libcxxwrap_julia_jll"] -git-tree-sha1 = "d837e21aab6eb37023470e347faf71fc84386def" +git-tree-sha1 = "3345cb637ca1efb2ebf7f5145558522b92660d1f" uuid = "1f15a43c-97ca-5a2a-ae31-89f07a497df4" -version = "0.12.1" +version = "0.14.2" [[deps.Cyclotomics]] deps = ["LRUCache", "Memoize", "Primes", "SparseArrays", "Test"] @@ -149,11 +162,21 @@ git-tree-sha1 = "dc2e5fd64c188399434e83fa5c10c6fa4eff962a" uuid = "da8f5974-afbb-4dc8-91d8-516d5257c83b" version = "0.3.2" +[[deps.DataAPI]] +git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.16.0" + [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" +git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.13" +version = "0.18.20" + +[[deps.DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" [[deps.Dates]] deps = ["Printf"] @@ -167,9 +190,9 @@ version = "1.1.0" [[deps.DiffRules]] deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] -git-tree-sha1 = "a4ad7ef19d2cdc2eff57abbbe68032b1cd0bd8f8" +git-tree-sha1 = "23163d55f885173722d1e4cf0f6110cdbaf7e272" uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" -version = "1.13.0" +version = "1.15.1" [[deps.DocStringExtensions]] deps = ["LibGit2"] @@ -183,16 +206,16 @@ uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" [[deps.DynamicPolynomials]] -deps = ["DataStructures", "Future", "LinearAlgebra", "MultivariatePolynomials", "MutableArithmetics", "Pkg", "Reexport", "Test"] -git-tree-sha1 = "8b84876e31fa39479050e2d3395c4b3b210db8b0" +deps = ["Future", "LinearAlgebra", "MultivariatePolynomials", "MutableArithmetics", "Pkg", "Reexport", "Test"] +git-tree-sha1 = "30a1848c4f4fc35d1d4bbbd125650f6a11b5bc6c" uuid = "7c1d4256-1411-5781-91ec-d7bc3513ac07" -version = "0.4.6" +version = "0.5.7" [[deps.ECOS]] deps = ["CEnum", "ECOS_jll", "MathOptInterface"] -git-tree-sha1 = "a10ccdc509a938d02b32904edb037b7045c49665" +git-tree-sha1 = "ea9f95d78d94af14e0f50973267c9c2209338079" uuid = "e2685f51-7e38-5353-a97d-a921fd2c8199" -version = "1.1.0" +version = "1.1.2" [[deps.ECOS_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -201,18 +224,30 @@ uuid = "c2c64177-6a8e-5dca-99a7-64895ad7445f" version = "200.0.800+0" [[deps.ExprTools]] -git-tree-sha1 = "c1d06d129da9f55715c6c212866f5b1bddc5fa00" +git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" -version = "0.1.9" +version = "0.1.10" [[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" +[[deps.FixedPointNumbers]] +deps = ["Statistics"] +git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.5" + [[deps.ForwardDiff]] -deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "00e252f4d706b3d55a8863432e742bf5717b498d" +deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] +git-tree-sha1 = "cf0fe81336da9fb90944683b8c41984b08793dad" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "0.10.35" +version = "0.10.36" + + [deps.ForwardDiff.extensions] + ForwardDiffStaticArraysExt = "StaticArrays" + + [deps.ForwardDiff.weakdeps] + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [[deps.Future]] deps = ["Random"] @@ -220,9 +255,9 @@ uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" [[deps.GLPK]] deps = ["GLPK_jll", "MathOptInterface"] -git-tree-sha1 = "c1ec0b87c6891a45892809e0ed0faa8d39198bab" +git-tree-sha1 = "1d706bd23e5d2d407bfd369499ee6f96afb0c3ad" uuid = "60bf3e95-4087-53dc-ae20-288a0d20c6a6" -version = "1.1.1" +version = "1.2.1" [[deps.GLPK_jll]] deps = ["Artifacts", "GMP_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -233,34 +268,34 @@ version = "5.0.1+0" [[deps.GMP_jll]] deps = ["Artifacts", "Libdl"] uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" -version = "6.2.1+2" +version = "6.2.1+6" [[deps.GroupsCore]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "9e1a5e9f3b81ad6a5c613d181664a0efc6fe6dd7" +deps = ["Random"] +git-tree-sha1 = "36b28fedf46e05edbde98ad3ec73d6a5d8897ed1" uuid = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" -version = "0.4.0" +version = "0.5.0" [[deps.IntegerMathUtils]] -git-tree-sha1 = "f366daebdfb079fd1fe4e3d560f99a0c892e15bc" +git-tree-sha1 = "b8ffb903da9f7b8cf695a8bead8e01814aa24b30" uuid = "18e54dd8-cb9d-406c-a71d-865a43cbb235" -version = "0.1.0" - -[[deps.IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "d979e54b71da82f3a65b62553da4fc3d18c9004c" -uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2018.0.3+2" +version = "0.1.2" [[deps.InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -[[deps.InverseFunctions]] -deps = ["Test"] -git-tree-sha1 = "49510dfcb407e572524ba94aeae2fced1f3feb0f" -uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.8" +[[deps.IntervalArithmetic]] +deps = ["CRlibm_jll", "MacroTools", "RoundingEmulator"] +git-tree-sha1 = "e75c4e33afbc631aa62671ebba12863321c1d46e" +uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" +version = "0.22.12" +weakdeps = ["DiffRules", "ForwardDiff", "RecipesBase"] + + [deps.IntervalArithmetic.extensions] + IntervalArithmeticDiffRulesExt = "DiffRules" + IntervalArithmeticForwardDiffExt = "ForwardDiff" + IntervalArithmeticRecipesBaseExt = "RecipesBase" [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" @@ -268,27 +303,38 @@ uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" version = "0.2.2" [[deps.IterTools]] -git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5" +git-tree-sha1 = "42d5f897009e7ff2cf88db414a389e5ed1bdd023" uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -version = "1.4.0" +version = "1.10.0" + +[[deps.IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" [[deps.JLLWrappers]] -deps = ["Preferences"] -git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.4.1" +version = "1.5.0" [[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.3" +version = "0.21.4" [[deps.JuMP]] -deps = ["LinearAlgebra", "MathOptInterface", "MutableArithmetics", "OrderedCollections", "Printf", "SnoopPrecompile", "SparseArrays"] -git-tree-sha1 = "611b9f12f02c587d860c813743e6cec6264e94d8" +deps = ["LinearAlgebra", "MacroTools", "MathOptInterface", "MutableArithmetics", "OrderedCollections", "PrecompileTools", "Printf", "SparseArrays"] +git-tree-sha1 = "28f9313ba6603e0d2850fc3eae617e769c99bf83" uuid = "4076af6c-e467-56ae-b986-b466b2749572" -version = "1.9.0" +version = "1.22.1" + + [deps.JuMP.extensions] + JuMPDimensionalDataExt = "DimensionalData" + + [deps.JuMP.weakdeps] + DimensionalData = "0703355e-b756-11e9-17c0-8b28908087d0" [[deps.KrylovKit]] deps = ["LinearAlgebra", "Printf"] @@ -296,82 +342,96 @@ git-tree-sha1 = "49b0c1dd5c292870577b8f58c51072bd558febb9" uuid = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" version = "0.5.4" +[[deps.LLVMOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "d986ce2d884d49126836ea94ed5bfb0f12679713" +uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" +version = "15.0.7+0" + [[deps.LRUCache]] -git-tree-sha1 = "d862633ef6097461037a00a13f709a62ae4bdfdd" +git-tree-sha1 = "b3cc6698599b10e652832c2f23db3cab99d51b59" uuid = "8ac3fa9e-de4c-5943-b1dc-09c6b5f20637" -version = "1.4.0" +version = "1.6.1" +weakdeps = ["Serialization"] -[[deps.LazyArtifacts]] -deps = ["Artifacts", "Pkg"] -uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" + [deps.LRUCache.extensions] + SerializationExt = ["Serialization"] + +[[deps.LaTeXStrings]] +git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.3.1" [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.3" +version = "0.6.4" [[deps.LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "7.84.0+0" +version = "8.4.0+0" [[deps.LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.6.4+0" + [[deps.LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.10.2+0" +version = "1.11.0+1" [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" [[deps.LinearAlgebra]] -deps = ["Libdl", "libblastrampoline_jll"] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [[deps.LogExpFunctions]] -deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "0a1b7c2863e44523180fdb3146534e265a91870b" +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "18144f3e9cbe9b15b070288eef858f71b291ce37" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.23" +version = "0.3.27" -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + [deps.LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" -[[deps.MATLAB]] -deps = ["Libdl", "SparseArrays"] -git-tree-sha1 = "e263657fe013cb02450c5d4210d2c50a354a5e08" -uuid = "10e44e05-a98a-55b3-a45b-ba969058deb6" -version = "0.8.3" + [deps.LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" -[[deps.MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"] -git-tree-sha1 = "2ce8695e1e699b68702c03402672a69f54b8aca9" -uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2022.2.0+0" +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" [[deps.MacroTools]] deps = ["Markdown", "Random"] -git-tree-sha1 = "42324d08725e200c23d4dfb549e0d5d89dede2d2" +git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.10" +version = "0.5.13" [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[deps.MathOptInterface]] -deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MutableArithmetics", "NaNMath", "OrderedCollections", "Printf", "SnoopPrecompile", "SparseArrays", "SpecialFunctions", "Test", "Unicode"] -git-tree-sha1 = "3ba708c18f4a5ee83f3a6fb67a2775147a1f59f5" +deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MutableArithmetics", "NaNMath", "OrderedCollections", "PrecompileTools", "Printf", "SparseArrays", "SpecialFunctions", "Test", "Unicode"] +path = "../../../MathOptInterface" uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" -version = "1.13.2" +version = "1.30.0" [[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.0+0" +version = "2.28.2+1" [[deps.Memoize]] deps = ["MacroTools"] @@ -383,44 +443,44 @@ version = "0.4.4" uuid = "a63ad114-7e13-5084-954f-fe012c677804" [[deps.Mosek]] -deps = ["Libdl", "Pkg", "Printf", "SparseArrays", "WinReg"] -git-tree-sha1 = "4d639c16d45bd7962613ff984046c120915d82b2" +deps = ["Libdl", "Pkg", "Printf", "SparseArrays"] +git-tree-sha1 = "d745a2a4a0f71c4bcc8a3f5be3aff9feade3f644" uuid = "6405355b-0ac2-5fba-af84-adbd65488c0e" -version = "10.0.2" +version = "10.1.4" [[deps.MosekTools]] deps = ["MathOptInterface", "Mosek", "Printf"] -git-tree-sha1 = "dd0c0836bdd9d4062811a84e139977b62a21eb0d" +git-tree-sha1 = "db3472bbf2d7565c895470e97ccd3834ae663282" uuid = "1ec41992-ff65-5c91-ac43-2df89e9693a4" -version = "0.13.3" +version = "0.15.1" [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2022.2.1" +version = "2023.1.10" [[deps.MultivariateBases]] -deps = ["MultivariatePolynomials", "MutableArithmetics"] -git-tree-sha1 = "61f1618015032b7199833e1ec94c4b2e0578e1a2" +deps = ["LinearAlgebra", "MultivariatePolynomials", "MutableArithmetics", "StarAlgebras"] +path = "../../../MultivariateBases" uuid = "be282fd4-ad43-11e9-1d11-8bd9d7e43378" -version = "0.1.5" +version = "0.2.2" [[deps.MultivariateMoments]] -deps = ["LinearAlgebra", "MultivariateBases", "MultivariatePolynomials", "MutableArithmetics", "RowEchelon", "SemialgebraicSets"] -git-tree-sha1 = "1d23024f02735a88a50385117010c5e71a0d2a24" +deps = ["Colors", "CommonSolve", "LinearAlgebra", "MultivariateBases", "MultivariatePolynomials", "MutableArithmetics", "REPL", "RecipesBase", "RowEchelon", "SemialgebraicSets", "SparseArrays", "StarAlgebras"] +path = "../../../MultivariateMoments" uuid = "f4abf1af-0426-5881-a0da-e2f168889b5e" -version = "0.3.10" +version = "0.4.6" [[deps.MultivariatePolynomials]] deps = ["ChainRulesCore", "DataStructures", "LinearAlgebra", "MutableArithmetics"] -git-tree-sha1 = "eaa98afe2033ffc0629f9d0d83961d66a021dfcc" +git-tree-sha1 = "dad7be0c92b688bf8f24af170825ccedc104b116" uuid = "102ac46a-7ee4-5c85-9060-abc95bfdeaa3" -version = "0.4.7" +version = "0.5.5" [[deps.MutableArithmetics]] deps = ["LinearAlgebra", "SparseArrays", "Test"] -git-tree-sha1 = "3295d296288ab1a0a2528feb424b854418acff57" +git-tree-sha1 = "a3589efe0005fc4718775d8641b2de9060d23f73" uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" -version = "1.2.3" +version = "1.4.4" [[deps.NaNMath]] deps = ["OpenLibm_jll"] @@ -433,20 +493,20 @@ uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" [[deps.OpenBLAS32_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9c6c2ed4b7acd2137b878eb96c68e63b76199d0f" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6065c4cff8fee6c6770b277af45d5082baacdba1" uuid = "656ef2d0-ae68-5445-9ca0-591084a874a2" -version = "0.3.17+0" +version = "0.3.24+0" [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.20+0" +version = "0.3.23+4" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+0" +version = "0.8.1+2" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -455,44 +515,56 @@ uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" version = "0.5.5+0" [[deps.OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" +version = "1.6.3" [[deps.Parsers]] -deps = ["Dates", "SnoopPrecompile"] -git-tree-sha1 = "478ac6c952fddd4399e71d4779797c538d0ff2bf" +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.5.8" +version = "2.8.1" [[deps.PermutationGroups]] -deps = ["AbstractAlgebra", "GroupsCore", "Markdown", "Random"] -git-tree-sha1 = "1bfba1a836e2c085270d3f5657803e0902e20564" +deps = ["AbstractPermutations", "GroupsCore", "PrecompileTools", "Random"] +git-tree-sha1 = "0a06e2ad1dc725df809d6c6dbc6b2453690448d3" uuid = "8bc5a954-2dfc-11e9-10e6-cd969bffa420" -version = "0.3.3" +version = "0.6.3" [[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.8.0" +version = "1.10.0" [[deps.PolyJuMP]] -deps = ["JuMP", "LinearAlgebra", "MathOptInterface", "MultivariateBases", "MultivariateMoments", "MultivariatePolynomials", "MutableArithmetics", "SemialgebraicSets"] -git-tree-sha1 = "4b6ea54520081bb18c5b2a86a35e73d617f436c4" +deps = ["DataStructures", "DynamicPolynomials", "IntervalArithmetic", "JuMP", "LinearAlgebra", "MathOptInterface", "MultivariateBases", "MultivariateMoments", "MultivariatePolynomials", "MutableArithmetics", "SemialgebraicSets", "StarAlgebras"] +path = "../../../PolyJuMP" uuid = "ddf597a6-d67e-5340-b84c-e37d84115374" -version = "0.6.2" +version = "0.7.4" + +[[deps.PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.2.1" [[deps.Preferences]] deps = ["TOML"] -git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" +git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.3.0" +version = "1.4.3" + +[[deps.PrettyTables]] +deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "Reexport", "StringManipulation", "Tables"] +git-tree-sha1 = "66b20dd35966a748321d3b2537c4584cf40387c7" +uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" +version = "2.3.2" [[deps.Primes]] deps = ["IntegerMathUtils"] -git-tree-sha1 = "311a2aa90a64076ea0fac2ad7492e914e6feeb81" +git-tree-sha1 = "cb420f77dc474d23ee47ca8d14c90810cafe69e7" uuid = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" -version = "0.5.3" +version = "0.5.6" [[deps.Printf]] deps = ["Unicode"] @@ -503,10 +575,10 @@ deps = ["Printf"] uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" [[deps.ProxSDP]] -deps = ["Arpack", "KrylovKit", "LinearAlgebra", "Logging", "MathOptInterface", "Printf", "Random", "SparseArrays", "TimerOutputs"] -git-tree-sha1 = "1bb9806265b7dfac72044e3115d205c9d2311138" +deps = ["Arpack", "JuMP", "KrylovKit", "LinearAlgebra", "Logging", "MathOptInterface", "PrecompileTools", "Printf", "Random", "SparseArrays", "TimerOutputs"] +git-tree-sha1 = "0ca8d20076eac7fef4ea8394082bd3ffdd3c4733" uuid = "65e78d25-6039-50a4-9445-38022e3d2eb3" -version = "1.8.2" +version = "1.8.3" [[deps.QDLDL]] deps = ["AMD", "LinearAlgebra", "SparseArrays"] @@ -519,14 +591,14 @@ deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.Random]] -deps = ["SHA", "Serialization"] +deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -[[deps.RandomExtensions]] -deps = ["Random", "SparseArrays"] -git-tree-sha1 = "062986376ce6d394b23d5d90f01d81426113a3c9" -uuid = "fb686558-2515-59ef-acaa-46db3789a887" -version = "0.4.3" +[[deps.RecipesBase]] +deps = ["PrecompileTools"] +git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" +uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" +version = "1.3.4" [[deps.Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" @@ -539,6 +611,11 @@ git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" +[[deps.RoundingEmulator]] +git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" +uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" +version = "0.2.1" + [[deps.RowEchelon]] deps = ["LinearAlgebra"] git-tree-sha1 = "f479526c4f6efcbf01e7a8f4223d62cfe801c974" @@ -546,137 +623,132 @@ uuid = "af85af4c-bcd5-5d23-b03a-a909639aa875" version = "0.2.1" [[deps.SCS]] -deps = ["MathOptInterface", "Requires", "SCS_GPU_jll", "SCS_MKL_jll", "SCS_jll", "SparseArrays"] -git-tree-sha1 = "2e3ca40559ecaed6ffe9410b06aabcc1e087215d" +deps = ["MathOptInterface", "Requires", "SCS_jll", "SparseArrays"] +git-tree-sha1 = "940b069bb150151cba9b12a1ea8678f60f324e7a" uuid = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13" -version = "1.1.3" +version = "2.0.0" -[[deps.SCS_GPU_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "Pkg"] -git-tree-sha1 = "2b3799ff650d0530a19c2a3bd4b158a4f3e4581a" -uuid = "af6e375f-46ec-5fa0-b791-491b0dfa44a4" -version = "3.2.1+0" + [deps.SCS.extensions] + SCSSCS_GPU_jllExt = ["SCS_GPU_jll"] + SCSSCS_MKL_jllExt = ["SCS_MKL_jll"] -[[deps.SCS_MKL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "MKL_jll", "Pkg"] -git-tree-sha1 = "a4923177e60fdb7f802e1a42a73d0af400eea163" -uuid = "3f2553a9-4106-52be-b7dd-865123654657" -version = "3.2.2+0" + [deps.SCS.weakdeps] + SCS_GPU_jll = "af6e375f-46ec-5fa0-b791-491b0dfa44a4" + SCS_MKL_jll = "3f2553a9-4106-52be-b7dd-865123654657" [[deps.SCS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "Pkg"] -git-tree-sha1 = "5544538910047c7522908cf87bb0c884a7afff92" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl", "OpenBLAS32_jll"] +git-tree-sha1 = "f7765a35d074b3b357aa6d84e732bbcda150f909" uuid = "f4f2fc5b-1d94-523c-97ea-2ab488bedf4b" -version = "3.2.1+0" +version = "3.2.4+1" [[deps.SDPA]] -deps = ["CxxWrap", "Libdl", "LinearAlgebra", "MathOptInterface", "SDPA_jll", "Test"] -git-tree-sha1 = "d77d8a4df56c21077fb012b6ac22e8d5106dc5d3" +deps = ["CxxWrap", "LinearAlgebra", "MathOptInterface", "SDPA_jll"] +git-tree-sha1 = "02cd33e060ba4d639806b613f2e51a3ca0811d97" uuid = "b9a10b5b-afa4-512f-a053-bb3d8080febc" -version = "0.4.0" +version = "0.5.1" [[deps.SDPA_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "Pkg", "libcxxwrap_julia_jll"] -git-tree-sha1 = "433f67305c2616abb8ddb354dc28eb4b6373c332" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "libcxxwrap_julia_jll"] +git-tree-sha1 = "e95fc4710ee0cbc4be6ca76dfce652677b8ab941" uuid = "7fc90fd6-dbef-5a6a-93f8-169f2a2e705b" -version = "700.300.801+1" - -[[deps.SDPNAL]] -deps = ["LinearAlgebra", "MATLAB", "MathOptInterface", "SparseArrays"] -git-tree-sha1 = "1ca30fdcb80f11ab6d2baabd6b99b4a31086fdd5" -uuid = "f7a9608b-7678-5329-8a05-97541ebc462c" -version = "0.1.0" - -[[deps.SDPT3]] -deps = ["LinearAlgebra", "MATLAB", "MathOptInterface", "SparseArrays"] -git-tree-sha1 = "d76205ada2d0b0056ed45c7996c572c0ce1024ff" -uuid = "e33b2407-87ff-50a0-8b27-f0fe7855237d" -version = "0.1.0" +version = "700.300.1701+1" [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" version = "0.7.0" -[[deps.SeDuMi]] -deps = ["LinearAlgebra", "MATLAB", "MathOptInterface", "SparseArrays"] -git-tree-sha1 = "a3d5e618812270facbc47410dbe424ccfdc8d0d2" -uuid = "a895aaad-f784-5544-9392-bb281339c1b2" -version = "0.4.1" - [[deps.SemialgebraicSets]] -deps = ["LinearAlgebra", "MultivariatePolynomials", "MutableArithmetics", "Random"] -git-tree-sha1 = "c186186e979b3891636e368af39ed8f66eeed147" +deps = ["CommonSolve", "LinearAlgebra", "MultivariatePolynomials", "MutableArithmetics", "Random"] +path = "../../../SemialgebraicSets" uuid = "8e049039-38e8-557d-ae3a-bc521ccf6204" -version = "0.2.5" +version = "0.3.2" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -[[deps.SnoopPrecompile]] -deps = ["Preferences"] -git-tree-sha1 = "e760a70afdcd461cf01a575947738d359234665c" -uuid = "66db9d55-30c0-4569-8b51-7e840670fc0c" -version = "1.0.3" - [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" [[deps.SparseArrays]] -deps = ["LinearAlgebra", "Random"] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.10.0" [[deps.SpecialFunctions]] -deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "ef28127915f4229c971eb43f3fc075dd3fe91880" +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.2.0" +version = "2.4.0" +weakdeps = ["ChainRulesCore"] + + [deps.SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" [[deps.StarAlgebras]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "265b89a5dfb38fe94ad48b997a253b2393fce6f1" +deps = ["LinearAlgebra", "MutableArithmetics", "SparseArrays"] +path = "../../../StarAlgebras" uuid = "0c0c59c1-dc5f-42e9-9a8b-b5dc384a6cd1" -version = "0.2.0" - -[[deps.StaticArrays]] -deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] -git-tree-sha1 = "b8d897fe7fa688e93aef573711cb207c08c9e11e" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.5.19" +version = "0.3.0" [[deps.StaticArraysCore]] -git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a" +git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.0" +version = "1.4.2" [[deps.Statistics]] deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.10.0" + +[[deps.StringManipulation]] +deps = ["PrecompileTools"] +git-tree-sha1 = "a04cabe79c5f01f4d723cc6704070ada0b9d46d5" +uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" +version = "0.3.4" [[deps.SuiteSparse]] deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "7.2.1+1" + [[deps.SumOfSquares]] -deps = ["Combinatorics", "ComplexOptInterface", "DataStructures", "JuMP", "LinearAlgebra", "MathOptInterface", "MultivariateBases", "MultivariateMoments", "MultivariatePolynomials", "MutableArithmetics", "PolyJuMP", "Reexport", "SemialgebraicSets", "SparseArrays", "SymbolicWedderburn"] +deps = ["DataStructures", "JuMP", "LinearAlgebra", "MathOptInterface", "MultivariateBases", "MultivariateMoments", "MultivariatePolynomials", "MutableArithmetics", "PolyJuMP", "Reexport", "SemialgebraicSets", "SparseArrays", "StarAlgebras", "SymbolicWedderburn"] path = "../.." uuid = "4b9e565b-77fc-50a5-a571-1244f986bda1" -version = "0.6.4" +version = "0.7.3" [[deps.SymbolicWedderburn]] -deps = ["Cyclotomics", "GroupsCore", "LinearAlgebra", "PermutationGroups", "Primes", "SparseArrays", "StarAlgebras"] -git-tree-sha1 = "63e13ce79ee97e3a08ebbd763a569d47e7d613eb" +deps = ["AbstractPermutations", "Cyclotomics", "GroupsCore", "LinearAlgebra", "PermutationGroups", "PrecompileTools", "PrettyTables", "Primes", "SparseArrays", "StarAlgebras"] +path = "../../../SymbolicWedderburn" uuid = "858aa9a9-4c7c-4c62-b466-2421203962a2" -version = "0.3.3" +version = "0.4.0" [[deps.TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.0" +version = "1.0.3" + +[[deps.TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "cb76cf677714c095e535e3501ac7954732aeea2d" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.11.1" [[deps.Tar]] deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.1" +version = "1.10.0" [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] @@ -684,15 +756,18 @@ uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] -git-tree-sha1 = "f2fd3f288dfc6f507b0c3a2eb3bac009251e548b" +git-tree-sha1 = "5a13ae8a41237cff5ecf34f73eb1b8f42fff6531" uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" -version = "0.5.22" +version = "0.5.24" [[deps.TranscodingStreams]] -deps = ["Random", "Test"] -git-tree-sha1 = "94f38103c984f89cf77c402f2a68dbd870f8165f" +git-tree-sha1 = "5d54d076465da49d6746c647022f3b3674e64156" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.9.11" +version = "0.10.8" +weakdeps = ["Random", "Test"] + + [deps.TranscodingStreams.extensions] + TestExt = ["Test", "Random"] [[deps.UUIDs]] deps = ["Random", "SHA"] @@ -702,38 +777,32 @@ uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [[deps.UnsafeArrays]] -git-tree-sha1 = "3350f94f6caa02f324a23645bf524fc9334c7488" +git-tree-sha1 = "e7f1c67ba99ac6df440de191fa4d5cbfcbdddcd1" uuid = "c4a57d5a-5b31-53a6-b365-19f8c011fbd6" -version = "1.0.4" - -[[deps.WinReg]] -deps = ["Test"] -git-tree-sha1 = "808380e0a0483e134081cc54150be4177959b5f4" -uuid = "1b915085-20d7-51cf-bf83-8f477d6f5128" -version = "0.3.1" +version = "1.0.5" [[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.12+3" +version = "1.2.13+1" [[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] +deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.1.1+0" +version = "5.8.0+1" [[deps.libcxxwrap_julia_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3261d533540cf55fa5ad9805bf59d0a7a530dc31" +git-tree-sha1 = "02d0a0a623248c709727088aaf722ab14f1463a5" uuid = "3eaa8342-bff7-56a5-9981-c04077f7cee7" -version = "0.9.4+0" +version = "0.11.2+1" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.48.0+0" +version = "1.52.0+1" [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+0" +version = "17.4.0+2" diff --git a/test/Solvers/Project.toml b/test/Solvers/Project.toml index 23c935301..844f595b5 100644 --- a/test/Solvers/Project.toml +++ b/test/Solvers/Project.toml @@ -1,5 +1,4 @@ [deps] -CDCS = "4fe2ecd4-b952-581a-b4b6-a532675a646e" COSMO = "1e616198-aa4e-51ec-90a2-23f7fbd31d8d" CSDP = "0a46da34-8e4b-519e-b418-48813639ff34" DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07" @@ -15,8 +14,7 @@ PolyJuMP = "ddf597a6-d67e-5340-b84c-e37d84115374" ProxSDP = "65e78d25-6039-50a4-9445-38022e3d2eb3" SCS = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13" SDPA = "b9a10b5b-afa4-512f-a053-bb3d8080febc" -SDPNAL = "f7a9608b-7678-5329-8a05-97541ebc462c" -SDPT3 = "e33b2407-87ff-50a0-8b27-f0fe7855237d" -SeDuMi = "a895aaad-f784-5544-9392-bb281339c1b2" SemialgebraicSets = "8e049039-38e8-557d-ae3a-bc521ccf6204" +StarAlgebras = "0c0c59c1-dc5f-42e9-9a8b-b5dc384a6cd1" SumOfSquares = "4b9e565b-77fc-50a5-a571-1244f986bda1" +SymbolicWedderburn = "858aa9a9-4c7c-4c62-b466-2421203962a2" From ff616d939e05d5017772e76b1e5aeaf765113cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 3 Jun 2024 17:03:07 +0200 Subject: [PATCH 36/84] up --- src/Bridges/Variable/kernel.jl | 2 +- src/Certificate/newton_polytope.jl | 73 ++++++++++++++++++++---------- src/Certificate/preorder.jl | 2 +- src/constraints.jl | 8 ++-- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index 56bfb29b5..68563eeb0 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -19,7 +19,7 @@ function MOI.Bridges.Variable.bridge_constrained_variable( push!(constraints, con) MA.operate!(SA.UnsafeAddMul(*), acc, weight, gram) end - MA.operate!(SA.canonical, acc) + MA.operate!(SA.canonical, SA.coeffs(acc)) return KernelBridge{T,M}(SA.coeffs(acc), variables, constraints, set) end diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 7a72fb340..ed936732c 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -316,9 +316,11 @@ function _half(d::DegreeBounds) ) end -function min_degree(p, v) - return mapreduce(Base.Fix2(MP.degree, v), min, MP.monomials(p)) +function min_degree(basis::MB.SubBasis{MB.Monomial}, v) + return mapreduce(Base.Fix2(MP.degree, v), min, basis.monomials) end +min_degree(p::SA.AlgebraElement, v) = min_degree(SA.basis(p), v) +min_degree(p::MB.Polynomial{MB.Monomial}, v) = MP.degree(p.monomial, v) minus_min_degree(p, v) = -min_degree(p, v) function _monomial(vars, exps) @@ -328,9 +330,12 @@ function _monomial(vars, exps) return prod(vars .^ exps) end -function max_degree(p, v) - return mapreduce(Base.Fix2(MP.degree, v), max, MP.monomials(p)) +function max_degree(basis::MB.SubBasis{MB.Monomial}, v) + return mapreduce(Base.Fix2(MP.degree, v), max, basis.monomials) end +max_degree(p::SA.AlgebraElement, v) = max_degree(SA.basis(p), v) +max_degree(p::MB.Polynomial{MB.Monomial}, v) = MP.degree(p.monomial, v) + function min_shift(d, shift) return max(0, d - shift) @@ -339,7 +344,7 @@ end function minus_shift( deg, mono::MP.AbstractMonomial, - p::MP.AbstractPolynomialLike, + p::SA.AlgebraElement, shift, ) return _map_powers(mono) do (v, d) @@ -347,7 +352,7 @@ function minus_shift( end end -function minus_shift(d::DegreeBounds, p::MP.AbstractPolynomialLike) +function minus_shift(d::DegreeBounds, p::SA.AlgebraElement) var_mindegree = minus_shift(min_degree, d.variablewise_mindegree, p, min_shift) var_maxdegree = minus_shift(max_degree, d.variablewise_maxdegree, p, -) @@ -355,8 +360,8 @@ function minus_shift(d::DegreeBounds, p::MP.AbstractPolynomialLike) return end return DegreeBounds( - min_shift(d.mindegree, MP.mindegree(p)), - d.maxdegree - MP.maxdegree(p), + min_shift(d.mindegree, _mindegree(p)), + d.maxdegree - _maxdegree(p), var_mindegree, var_maxdegree, ) @@ -456,30 +461,33 @@ function deg_range(deg, p, gs, gram_deg, range::UnitRange) return end -_mindegree(p::MP.AbstractPolynomialLike) = MP.mindegree(p) +#_mindegree(p::MP.AbstractPolynomialLike) = MP.mindegree(p) _mindegree(a::SA.AlgebraElement) = _mindegree(SA.basis(a)) +_maxdegree(a::SA.AlgebraElement) = _maxdegree(SA.basis(a)) _mindegree(basis::MB.SubBasis{MB.Monomial}) = MP.mindegree(basis.monomials) +_maxdegree(basis::MB.SubBasis{MB.Monomial}) = MP.maxdegree(basis.monomials) _mindegree(p::MB.Polynomial{MB.Monomial}) = MP.mindegree(p.monomial) +_maxdegree(p::MB.Polynomial{MB.Monomial}) = MP.maxdegree(p.monomial) function putinar_degree_bounds( p::SA.AlgebraElement, - gs::AbstractVector{<:MP.AbstractPolynomialLike}, + gs::AbstractVector{<:SA.AlgebraElement}, vars, maxdegree, ) mindegree = 0 # TODO homogeneous case mindeg(g) = _min_half(min_shift(mindegree, _mindegree(g))) - maxdeg(g) = _max_half(maxdegree - MP.maxdegree(g)) + maxdeg(g) = _max_half(maxdegree - _maxdegree(g)) degrange(g) = mindeg(g):maxdeg(g) minus_degrange(g) = -maxdeg(g):-mindeg(g) - # The multiplier will have degree `0:2fld(maxdegree - MP.maxdegree(g), 2)` + # The multiplier will have degree `0:2fld(maxdegree - _maxdegree(g), 2)` mindegree = -deg_range(p -> -_mindegree(p), p, gs, minus_degrange, -maxdegree:0) if isnothing(mindegree) return end - maxdegree = deg_range(MP.maxdegree, p, gs, degrange, 0:maxdegree) + maxdegree = deg_range(_maxdegree, p, gs, degrange, 0:maxdegree) if isnothing(maxdegree) return end @@ -511,14 +519,14 @@ function putinar_degree_bounds( ) end -function multiplier_basis(g::MP.AbstractPolynomialLike, bounds::DegreeBounds) +function multiplier_basis(g::SA.AlgebraElement, bounds::DegreeBounds) shifted = minus_shift(bounds, g) if isnothing(shifted) halved = nothing else halved = _half(shifted) end - basis = MB.FullBasis{MB.Monomial,MP.monomial_type(g)}() + basis = MB.FullBasis{MB.Monomial,MP.monomial_type(typeof(g))}() if isnothing(halved) # TODO add `MB.empty_basis` to API return MB.maxdegree_basis( @@ -548,6 +556,22 @@ function half_newton_polytope( gs::AbstractVector{<:MP.AbstractPolynomialLike}, vars, maxdegree, + filter, +) + return half_newton_polytope( + p, + [MB.algebra_element(MP.coefficients(g), MB.SubBasis{MB.Monomial}(MP.monomials(g))) for g in gs], + vars, + maxdegree, + filter, + ) +end + +function half_newton_polytope( + p::SA.AlgebraElement, + gs::AbstractVector{<:SA.AlgebraElement}, + vars, + maxdegree, ::NewtonFilter{<:NewtonDegreeBounds}, ) bounds = putinar_degree_bounds(p, gs, vars, maxdegree) @@ -555,12 +579,12 @@ function half_newton_polytope( push!( bases, maxdegree_gram_basis( - MB.FullBasis{MB.Monomial,MP.monomial_type(p)}(), + MB.FullBasis{MB.Monomial,MP.monomial_type(typeof(p))}(), _half(bounds), ).monomials, ) gs = copy(gs) - push!(gs, one(eltype(gs))) + push!(gs, MB.constant_algebra_element(MA.promote_operation(SA.basis, eltype(gs)), eltype(eltype(gs)))) filtered_bases = post_filter(p, gs, bases) # The last one will be recomputed by the ideal certificate return MB.SubBasis{MB.Monomial}.(filtered_bases[1:(end-1)]) @@ -603,7 +627,7 @@ function add(c::SignCount, a::Number, Δ) error("Cannot determine sign of `$a`.") end end -function add(counter, sign, mono, Δ) +function add(counter, sign, mono::MB.Polynomial, Δ) count = get(counter, mono, SignCount()) return counter[mono] = add(count, sign, Δ) end @@ -617,16 +641,15 @@ function increase(counter, generator_sign, monos, mult) end end -function post_filter(poly, generators, multipliers_gram_monos) - counter = Dict{MP.monomial_type(poly),SignCount}() - for t in MP.terms(poly) +function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos) + counter = Dict{eltype(MA.promote_operation(SA.basis, typeof(poly))),SignCount}() + for (mono, v) in SA.nonzero_pairs(poly) coef = SignCount() - counter[MP.monomial(t)] = add(coef, _sign(MP.coefficient(t)), 1) + counter[mono] = add(coef, _sign(v), 1) end for (mult, gram_monos) in zip(generators, multipliers_gram_monos) - for t in MP.terms(mult) - sign = -_sign(MP.coefficient(t)) - mono = MP.monomial(t) + for (mono, v) in SA.nonzero_pairs(mult) + sign = -_sign(v) increase(counter, sign, gram_monos, mono) end end diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index 19ea2abfb..c288c06b2 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -84,7 +84,7 @@ function with_fixed_basis( v = with_variables(domain, p) return WithFixedBases( v.inner, - half_newton_polytope(p, domain.p, v.variables, maxdegree, newton), + half_newton_polytope(p, SemialgebraicSets.inequalities(domain), v.variables, maxdegree, newton), ) end diff --git a/src/constraints.jl b/src/constraints.jl index d8b464f4b..0c1672542 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -151,7 +151,7 @@ function default_ideal_certificate( end function default_ideal_certificate(domain::BasicSemialgebraicSet, args...) - return default_ideal_certificate(domain.V, args...) + return default_ideal_certificate(SemialgebraicSets.algebraic_set(domain), args...) end function default_certificate( @@ -198,7 +198,7 @@ function default_certificate( # that wouldn't work if `ideal_certificate` is `Remainder`, # `Sparseity.Ideal` or `Symmetry.Ideal` multipliers_certificate = default_ideal_certificate( - domain.V, + SemialgebraicSets.algebraic_set(domain), basis, cone, maxdegree, @@ -222,11 +222,11 @@ _max_maxdegree(p) = mapreduce(MP.maxdegree, max, p, init = 0) _maxdegree(domain) = 0 function _maxdegree(domain::AlgebraicSet) - return _max_maxdegree(domain.I.p) + return _max_maxdegree(SemialgebraicSets.equalities(domain)) end function _maxdegree(domain::BasicSemialgebraicSet) - return max(_max_maxdegree(domain.p), _maxdegree(domain.V)) + return max(_max_maxdegree(SemialgebraicSets.inequalities(domain)), _maxdegree(SemialgebraicSets.algebraic_set(domain))) end """ From d283ba289e09f3a1770fc23d40f78ab87d6877ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 4 Jun 2024 15:54:29 +0200 Subject: [PATCH 37/84] Fixes --- .../sos_polynomial_in_semialgebraic_set.jl | 17 +- src/Bridges/Variable/Variable.jl | 1 + src/Bridges/Variable/kernel.jl | 7 +- src/Certificate/newton_polytope.jl | 171 +++++++++++------- src/Certificate/preorder.jl | 7 + src/constraints.jl | 2 +- src/gram_matrix.jl | 6 +- test/Solvers/scs_tests.jl | 3 +- 8 files changed, 141 insertions(+), 73 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index ab0465c7a..59548d6a3 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -49,7 +49,10 @@ function MOI.Bridges.Constraint.bridge_constraint( # MOI does not modify the coefficients of the functions so we can modify `p`. # without altering `f`. # The monomials may be copied by MA however so we need to copy it. - p = MB.algebra_element(MOI.Utilities.scalarize(f), copy(set.basis)) + p = MB.algebra_element( + MP.polynomial(MOI.Utilities.scalarize(f), copy(set.basis.monomials)), + MB.implicit_basis(set.basis), + ) λ_bases = B[] λ_variables = Union{Vector{MOI.VariableIndex},Vector{Vector{MOI.VariableIndex}}}[] @@ -73,19 +76,25 @@ function MOI.Bridges.Constraint.bridge_constraint( # `set.domain.V` is `FullSpace` or `FixedPolynomialSet`. g = Certificate.generator(set.certificate, index, preprocessed) # TODO replace with `MA.sub_mul` when it works. - p = MA.operate!!(MA.add_mul, p, -one(T), λ, similar(g, T)) + p = MA.operate!( + SA.UnsafeAddMul(*), + p, + MB.algebra_element(-one(T) * similar(g, T), MB.FullBasis{MB.Monomial,MP.monomial_type(g)}()), + λ, + ) end + MA.operate!(SA.canonical, SA.coeffs(p)) new_set = SOS.SOSPolynomialSet( set.domain.V, # For terms, `monomials` is `OneOrZeroElementVector` # so we convert it with `monomial_vector` # Later, we'll use `MP.MonomialBasis` which is going to do that anyway - MB.SubBasis{MB.Monomial}(MP.monomial_vector(MP.monomials(p))), + MB.SubBasis{MB.Monomial}(MP.monomial_vector(MP.monomials(SA.coeffs(p)))), Certificate.ideal_certificate(set.certificate), ) constraint = MOI.add_constraint( model, - MOI.Utilities.vectorize(MP.coefficients(p)), + MOI.Utilities.vectorize(MP.coefficients(SA.coeffs(p))), new_set, ) diff --git a/src/Bridges/Variable/Variable.jl b/src/Bridges/Variable/Variable.jl index f18d09eab..4a154fd2d 100644 --- a/src/Bridges/Variable/Variable.jl +++ b/src/Bridges/Variable/Variable.jl @@ -3,6 +3,7 @@ module Variable import SparseArrays import MutableArithmetics as MA import StarAlgebras as SA +import MultivariateBases as MB import MathOptInterface as MOI import MultivariatePolynomials as MP import SumOfSquares as SOS diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index 68563eeb0..818d75176 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -12,7 +12,10 @@ function MOI.Bridges.Variable.bridge_constrained_variable( ) where {T,M} variables = Vector{MOI.VariableIndex}[] constraints = MOI.ConstraintIndex{MOI.VectorOfVariables}[] - acc = SA.AlgebraElement(SparseArrays.sparsevec(SA.key_type(typeof(set.basis))[], MOI.ScalarAffineFunction{T}[], length(set.basis)), SA.algebra(set.basis)) + acc = MB.algebra_element( + zero(MP.polynomial_type(MP.monomial_type(typeof(set.basis)), MOI.ScalarAffineFunction{T})), + MB.implicit_basis(set.basis), + ) for (gram_basis, weight) in zip(set.gram_bases, set.weights) gram, vars, con = SOS.add_gram_matrix(model, M, gram_basis, T) push!(variables, vars) @@ -20,7 +23,7 @@ function MOI.Bridges.Variable.bridge_constrained_variable( MA.operate!(SA.UnsafeAddMul(*), acc, weight, gram) end MA.operate!(SA.canonical, SA.coeffs(acc)) - return KernelBridge{T,M}(SA.coeffs(acc), variables, constraints, set) + return KernelBridge{T,M}(SA.coeffs(acc, set.basis), variables, constraints, set) end function MOI.Bridges.Variable.supports_constrained_variable( diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index ed936732c..aae7b40d7 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -316,10 +316,9 @@ function _half(d::DegreeBounds) ) end -function min_degree(basis::MB.SubBasis{MB.Monomial}, v) - return mapreduce(Base.Fix2(MP.degree, v), min, basis.monomials) +function min_degree(p::SA.AlgebraElement, v) + return mapreduce(Base.Fix2(min_degree, v), min, SA.supp(p)) end -min_degree(p::SA.AlgebraElement, v) = min_degree(SA.basis(p), v) min_degree(p::MB.Polynomial{MB.Monomial}, v) = MP.degree(p.monomial, v) minus_min_degree(p, v) = -min_degree(p, v) @@ -330,13 +329,11 @@ function _monomial(vars, exps) return prod(vars .^ exps) end -function max_degree(basis::MB.SubBasis{MB.Monomial}, v) - return mapreduce(Base.Fix2(MP.degree, v), max, basis.monomials) +function max_degree(p::SA.AlgebraElement, v) + return mapreduce(Base.Fix2(max_degree, v), max, SA.supp(p)) end -max_degree(p::SA.AlgebraElement, v) = max_degree(SA.basis(p), v) max_degree(p::MB.Polynomial{MB.Monomial}, v) = MP.degree(p.monomial, v) - function min_shift(d, shift) return max(0, d - shift) end @@ -462,10 +459,10 @@ function deg_range(deg, p, gs, gram_deg, range::UnitRange) end #_mindegree(p::MP.AbstractPolynomialLike) = MP.mindegree(p) -_mindegree(a::SA.AlgebraElement) = _mindegree(SA.basis(a)) -_maxdegree(a::SA.AlgebraElement) = _maxdegree(SA.basis(a)) -_mindegree(basis::MB.SubBasis{MB.Monomial}) = MP.mindegree(basis.monomials) -_maxdegree(basis::MB.SubBasis{MB.Monomial}) = MP.maxdegree(basis.monomials) +_mindegree(a::SA.AlgebraElement) = minimum(_mindegree, SA.supp(a)) +_maxdegree(a::SA.AlgebraElement) = maximum(_maxdegree, SA.supp(a)) +#_mindegree(basis::MB.SubBasis{MB.Monomial}) = MP.mindegree(basis.monomials) +#_maxdegree(basis::MB.SubBasis{MB.Monomial}) = MP.maxdegree(basis.monomials) _mindegree(p::MB.Polynomial{MB.Monomial}) = MP.mindegree(p.monomial) _maxdegree(p::MB.Polynomial{MB.Monomial}) = MP.maxdegree(p.monomial) @@ -575,20 +572,29 @@ function half_newton_polytope( ::NewtonFilter{<:NewtonDegreeBounds}, ) bounds = putinar_degree_bounds(p, gs, vars, maxdegree) - bases = [multiplier_basis(g, bounds).monomials for g in gs] + bases = [multiplier_basis(g, bounds) for g in gs] push!( bases, maxdegree_gram_basis( MB.FullBasis{MB.Monomial,MP.monomial_type(typeof(p))}(), _half(bounds), - ).monomials, + ), ) gs = copy(gs) push!(gs, MB.constant_algebra_element(MA.promote_operation(SA.basis, eltype(gs)), eltype(eltype(gs)))) filtered_bases = post_filter(p, gs, bases) # The last one will be recomputed by the ideal certificate - return MB.SubBasis{MB.Monomial}.(filtered_bases[1:(end-1)]) + return filtered_bases[1:(end-1)] +end + +struct SignChange{T} + sign::T + Δ::Int end +Base.copy(s::SignChange) = s +Base.iszero(::SignChange) = false +MA.scaling_convert(::Type, s::SignChange) = s +Base.:*(s::SignChange, α::Real) = SignChange(s.sign * α, s.Δ) struct SignCount unknown::Int @@ -596,6 +602,7 @@ struct SignCount negative::Int end SignCount() = SignCount(0, 0, 0) +Base.iszero(::SignCount) = false function _sign(c::SignCount) if !iszero(c.unknown) return missing @@ -608,61 +615,91 @@ function _sign(c::SignCount) end end -function add(c::SignCount, ::Missing, Δ) - @assert c.unknown >= -Δ - return SignCount(c.unknown + Δ, c.positive, c.negative) +function Base.:+(c::SignCount, a::SignChange{Missing}) + @assert c.unknown >= -a.Δ + return SignCount(c.unknown + a.Δ, c.positive, c.negative) end -function add(c::SignCount, a::Number, Δ) - if a > 0 - @assert c.positive >= -Δ - return SignCount(c.unknown, c.positive + Δ, c.negative) - elseif a < 0 - @assert c.negative >= -Δ - return SignCount(c.unknown, c.positive, c.negative + Δ) - elseif iszero(a) +function Base.:+(c::SignCount, a::SignChange{<:Number}) + if a.sign > 0 + @assert c.positive >= -a.Δ + return SignCount(c.unknown, c.positive + a.Δ, c.negative) + elseif a.sign < 0 + @assert c.negative >= -a.Δ + return SignCount(c.unknown, c.positive, c.negative + a.Δ) + elseif iszero(a.sign) error( - "A polynomial should never contain a term with zero coefficient but found `$a`.", + "A polynomial should never contain a term with zero coefficient but found `$(a.sign)`.", ) else - error("Cannot determine sign of `$a`.") + error("Cannot determine sign of `$(a.sign)`.") end end -function add(counter, sign, mono::MB.Polynomial, Δ) - count = get(counter, mono, SignCount()) - return counter[mono] = add(count, sign, Δ) -end -function increase(counter, generator_sign, monos, mult) +Base.convert(::Type{SignCount}, Δ::SignChange) = SignCount() + Δ + +function increase(cache, counter, generator_sign, monos, mult) for a in monos for b in monos - sign = (a != b) ? missing : generator_sign - add(counter, sign, a * b * mult, 1) + MA.operate_to!( + cache, + *, + MB.algebra_element(mult), + MB.algebra_element(a), + MB.algebra_element(b), + ) + MA.operate!( + SA.UnsafeAddMul(*), + counter, + SignChange((a != b) ? missing : generator_sign, 1,), + cache, + ) end end end function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos) - counter = Dict{eltype(MA.promote_operation(SA.basis, typeof(poly))),SignCount}() + counter = MB.algebra_element( + zero(MP.polynomial_type(MP.monomial_type(typeof(SA.basis(poly))), SignCount)), + MB.implicit_basis(SA.basis(poly)), + ) + cache = MB.algebra_element( + zero(MP.polynomial_type(MP.monomial_type(typeof(SA.basis(poly))), Float64)), + MB.implicit_basis(SA.basis(poly)), + ) for (mono, v) in SA.nonzero_pairs(poly) - coef = SignCount() - counter[mono] = add(coef, _sign(v), 1) + MA.operate!( + SA.UnsafeAddMul(*), + counter, + SignChange(_sign(v), 1), + mono, + ) end for (mult, gram_monos) in zip(generators, multipliers_gram_monos) for (mono, v) in SA.nonzero_pairs(mult) - sign = -_sign(v) - increase(counter, sign, gram_monos, mono) + increase(cache, counter, -_sign(v), gram_monos, mono) end end - function decrease(sign, mono) - count = add(counter, sign, mono, -1) - count_sign = _sign(count) - # This means the `counter` has a sign and it didn't have a sign before - # so we need to delete back edges - if !ismissing(count_sign) && (ismissing(count) || count != count_sign) - # TODO could see later if deleting the counter improves perf - if haskey(back, mono) - for (i, j) in back[mono] - delete(i, j) + function decrease(sign, a, b, c) + MA.operate_to!( + cache, + *, + MB.algebra_element(a), + MB.algebra_element(b), + MB.algebra_element(c), + ) + MA.operate!(SA.UnsafeAddMul(*), counter, SignChange(sign, -1), cache) + MA.operate!(SA.canonical, counter) + for mono in SA.supp(cache) + count = counter[mono] + count_sign = _sign(count) + # This means the `counter` has a sign and it didn't have a sign before + # so we need to delete back edges + if !ismissing(count_sign) && (ismissing(count) || count != count_sign) + # TODO could see later if deleting the counter improves perf + if haskey(back, mono) + for (i, j) in back[mono] + delete(i, j) + end end end end @@ -675,35 +712,47 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos end keep[i][j] = false a = multipliers_gram_monos[i][j] - for t in MP.terms(generators[i]) - sign = -_sign(MP.coefficient(t)) - decrease(sign, MP.monomial(t) * a^2) + for (k, v) in SA.nonzero_pairs(generators[i]) + sign = -_sign(v) + decrease(sign, k, a, a) for (j, b) in enumerate(multipliers_gram_monos[i]) if keep[i][j] - decrease(missing, MP.monomial(t) * a * b) - decrease(missing, MP.monomial(t) * b * a) + decrease(missing, k, a, b) + decrease(missing, k, b, a) end end end end for i in eachindex(generators) - for t in MP.terms(generators[i]) + for k in SA.supp(generators[i]) for (j, mono) in enumerate(multipliers_gram_monos[i]) - w = MP.monomial(t) * mono^2 - if ismissing(_sign(counter[w])) - push!(get(back, w, Tuple{Int,Int}[]), (i, j)) - else - delete(i, j) + MA.operate_to!( + cache, + *, + MB.algebra_element(k), + MB.algebra_element(mono), + MB.algebra_element(mono), + ) + for w in SA.supp(cache) + if ismissing(_sign(SA.coeffs(counter)[SA.basis(counter)[w]])) + push!(get(back, w, Tuple{Int,Int}[]), (i, j)) + else + delete(i, j) + end end end end end return [ - gram_monos[findall(keep)] for + _sub(gram_monos, keep) for (keep, gram_monos) in zip(keep, multipliers_gram_monos) ] end +function _sub(basis::SubBasis{B}, I) where {B} + return SubBasis{B}(basis.monomials[I]) +end + function half_newton_polytope(a::SA.AlgebraElement, args...) @assert SA.basis(a) isa MB.SubBasis{MB.Monomial} half_newton_polytope(SA.coeffs(a, MB.FullBasis{MB.Monomial,MP.monomial_type(typeof(a))}()), args...) diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index c288c06b2..edf848644 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -69,6 +69,13 @@ function _merge_sorted(a::Tuple, b::Tuple) end _vars(::SemialgebraicSets.FullSpace) = tuple() +function _vars(x::SA.AlgebraElement) + if SA.basis(x) isa SA.ImplicitBasis + return MP.variables(SA.coeffs(x)) + else + return MP.variables(SA.basis(x)) + end +end _vars(x) = MP.variables(x) function with_variables(inner, outer) diff --git a/src/constraints.jl b/src/constraints.jl index 0c1672542..7b7b4bc54 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -347,7 +347,7 @@ function PolyJuMP.bridges( ) # Needed so that `KernelBridge` is added as well return Tuple{Type,Type}[( MOI.Bridges.Constraint.VectorSlackBridge, - PolyJuMP._coef_type(F), + PolyJuMP.coefficient_type_or_float(F), )] end diff --git a/src/gram_matrix.jl b/src/gram_matrix.jl index 26b9a6ccd..1882fa1df 100644 --- a/src/gram_matrix.jl +++ b/src/gram_matrix.jl @@ -151,9 +151,9 @@ function MA.operate!(op::SA.UnsafeAddMul{typeof(*)}, p::SA.AlgebraElement, w::SA op, p, v * g.Q[row, col], - SA.basis(w)[k], - SA.star(g.basis[row]), - g.basis[col], + MB.algebra_element(SA.basis(w)[k]), + MB.algebra_element(SA.star(g.basis[row])), + MB.algebra_element(g.basis[col]), ) end end diff --git a/test/Solvers/scs_tests.jl b/test/Solvers/scs_tests.jl index 1050719c9..470d4f337 100644 --- a/test/Solvers/scs_tests.jl +++ b/test/Solvers/scs_tests.jl @@ -3,8 +3,7 @@ import SCS factory = optimizer_with_attributes(SCS.Optimizer, MOI.Silent() => true) config = MOI.Test.Config(atol = 1e-3, rtol = 1e-3) @testset "Linear" begin - Tests.linear_test(factory, config, include=["dsos_horn",#"dsos_concave_then_convex_cubic" - ]) + Tests.linear_test(factory, config) end #@testset "SOC" begin # Tests.soc_test(factory, config) From b2e1653a64542d36a1aefa6f5b2ec843a6ad36b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 4 Jun 2024 16:23:06 +0200 Subject: [PATCH 38/84] Fixes --- src/constraints.jl | 66 ++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/src/constraints.jl b/src/constraints.jl index 7b7b4bc54..8257be5e6 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -242,36 +242,12 @@ function default_maxdegree(monos, domain) return max(MP.maxdegree(monos), _maxdegree(domain)) end -function _promote_monomial_type(::Type{M1}, ::Type{D}) where {M1,D} - M2 = MP.monomial_type(D) - if isnothing(M2) - return M1 - else - return promote_type(M1, M2) - end -end - -_default_basis(basis::SA.AbstractBasis, _, _) = basis - -function _default_basis( - ::Type{B}, - ::SubBasis{_B,M}, - ::D, -) where {B<:MB.AbstractMonomialIndexed,_B,M,D} - return MB.FullBasis{B,_promote_monomial_type(M, D)}() -end - -function _default_basis(::Nothing, basis::SubBasis, domain) - return _default_basis(MB.Monomial, basis, domain) -end - function JuMP.moi_set( cone::SOSLikeCone, - b::SA.ExplicitBasis; + basis::SA.ExplicitBasis; domain::AbstractSemialgebraicSet = FullSpace(), - basis = nothing, newton_polytope::Union{Nothing,Tuple} = tuple(), - maxdegree::Union{Nothing,Int} = default_maxdegree(b, domain), + maxdegree::Union{Nothing,Int} = default_maxdegree(basis, domain), sparsity::Certificate.Sparsity.Pattern = Certificate.Sparsity.NoPattern(), symmetry::Union{Nothing,Certificate.Symmetry.Pattern} = nothing, newton_of_remainder::Bool = false, @@ -280,7 +256,7 @@ function JuMP.moi_set( newton_of_remainder, symmetry, sparsity, - _default_basis(basis, b, domain), + MB.implicit_basis(basis), cone, maxdegree, newton_polytope, @@ -290,12 +266,12 @@ function JuMP.moi_set( sparsity, ideal_certificate, cone, - _default_basis(basis, b, domain), + MB.implicit_basis(basis), maxdegree, newton_polytope, ), ) - return SOSPolynomialSet(domain, b, certificate) + return SOSPolynomialSet(domain, basis, certificate) end function PolyJuMP.bridges( @@ -385,10 +361,36 @@ end _promote_coef_type(::Type{V}, ::Type) where {V<:JuMP.AbstractVariableRef} = V _promote_coef_type(::Type{F}, ::Type{T}) where {F,T} = promote_type(F, T) -function JuMP.build_constraint(_error::Function, p, cone::SOSLikeCone; kws...) - basis = MB.SubBasis{MB.Monomial}(MP.monomials(p)) +function _default_basis(coeffs, basis::MB.SubBasis{B}, ::Union{Nothing,Type{B}}) where {B} + return coeffs, basis +end + +function _default_basis(p::MP.AbstractPolynomialLike, ::MB.FullBasis{B}, basis) where {B} + return _default_basis(MP.coefficients(p), MB.SubBasis{B}(MP.monomials(p)), basis) +end + +function _default_basis(a::SA.AlgebraElement, basis) + return _default_basis(SA.coeffs(a), SA.basis(a), basis) +end + +function _default_basis(a::SA.AlgebraElement, basis::SA.ExplicitBasis) + return SA.coeffs(a, basis), basis +end + +function _default_basis(p, basis) + return _default_basis(MB.algebra_element(p, MB.FullBasis{MB.Monomial,MP.monomial_type(p)}()), basis) +end + +function JuMP.build_constraint( + _error::Function, + p, + cone::SOSLikeCone; + basis = nothing, + kws..., +) + __coefs, basis = _default_basis(p, basis) set = JuMP.moi_set(cone, basis; kws...) - _coefs = PolyJuMP.non_constant_coefficients(p) + _coefs = PolyJuMP.non_constant(__coefs) # If a polynomial with real coefficients is used with the Hermitian SOS # cone, we want to promote the coefficients to complex T = _bridge_coefficient_type(typeof(set)) From 5e6988737fa5421986ff98277668ee7add5bb972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 4 Jun 2024 22:01:11 +0200 Subject: [PATCH 39/84] up --- src/constraints.jl | 17 +++++++++++++---- test/Solvers/scs_tests.jl | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/constraints.jl b/src/constraints.jl index 8257be5e6..6e6257690 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -361,12 +361,21 @@ end _promote_coef_type(::Type{V}, ::Type) where {V<:JuMP.AbstractVariableRef} = V _promote_coef_type(::Type{F}, ::Type{T}) where {F,T} = promote_type(F, T) -function _default_basis(coeffs, basis::MB.SubBasis{B}, ::Union{Nothing,Type{B}}) where {B} - return coeffs, basis +function _default_basis(coeffs, basis::MB.SubBasis{B}, b) where {B} + if isnothing(b) || B == b + return coeffs, basis + else + return _default_basis(MP.polynomial(coeffs, basis.monomials), MB.implicit_basis(basis), b) + end end -function _default_basis(p::MP.AbstractPolynomialLike, ::MB.FullBasis{B}, basis) where {B} - return _default_basis(MP.coefficients(p), MB.SubBasis{B}(MP.monomials(p)), basis) +function _default_basis(p::MP.AbstractPolynomialLike, basis::MB.FullBasis{B}, b) where {B} + if isnothing(b) || B == b + return _default_basis(MP.coefficients(p), MB.SubBasis{B}(MP.monomials(p)), b) + else + new_basis = MB.FullBasis{b,MP.monomial_type(typeof(basis))}() + return _default_basis(SA.coeffs(p, basis, new_basis), new_basis, b) + end end function _default_basis(a::SA.AlgebraElement, basis) diff --git a/test/Solvers/scs_tests.jl b/test/Solvers/scs_tests.jl index 470d4f337..9fd27579f 100644 --- a/test/Solvers/scs_tests.jl +++ b/test/Solvers/scs_tests.jl @@ -3,7 +3,7 @@ import SCS factory = optimizer_with_attributes(SCS.Optimizer, MOI.Silent() => true) config = MOI.Test.Config(atol = 1e-3, rtol = 1e-3) @testset "Linear" begin - Tests.linear_test(factory, config) + Tests.linear_test(factory, config, include = ["dsos_scaled_univariate_quadratic"]) end #@testset "SOC" begin # Tests.soc_test(factory, config) From 02bfd28831d89727def4ccfb460b12ce450cce1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 4 Jun 2024 23:36:07 +0200 Subject: [PATCH 40/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 30 +++++++++++------------- src/Certificate/Sparsity/ideal.jl | 5 ++-- src/Certificate/Symmetry/wedderburn.jl | 3 +-- src/Certificate/ideal.jl | 12 +++++----- src/Certificate/newton_polytope.jl | 17 +------------- src/Certificate/preorder.jl | 4 ++-- test/certificate.jl | 2 +- 7 files changed, 27 insertions(+), 46 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 1d3faa759..83e7a36e3 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -3,21 +3,20 @@ struct SOSPolynomialBridge{ F<:MOI.AbstractVectorFunction, DT<:SemialgebraicSets.AbstractSemialgebraicSet, M, # matrix cone type + BT, B, G<:SA.ExplicitBasis, CT<:SOS.Certificate.AbstractIdealCertificate, - MT<:MP.AbstractMonomial, - MVT<:AbstractVector{MT}, W<:SA.AlgebraElement, } <: MOI.Bridges.Constraint.SetMapBridge{ T, SOS.WeightedSOSCone{M,B,G,W}, - SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}, + SOS.SOSPolynomialSet{DT,BT,CT}, F, F, } constraint::MOI.ConstraintIndex{F,SOS.WeightedSOSCone{M,B,G,W}} - set::SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT} + set::SOS.SOSPolynomialSet{DT,BT,CT} new_basis::B flat_indices::Union{Int,Base.UnitRange{Int}} end @@ -42,11 +41,11 @@ function _flatten( end function MOI.Bridges.Constraint.bridge_constraint( - ::Type{SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}}, + ::Type{SOSPolynomialBridge{T,F,DT,M,BT,B,G,CT,W}}, model::MOI.ModelLike, func::MOI.AbstractVectorFunction, set::SOS.SOSPolynomialSet{<:SemialgebraicSets.AbstractAlgebraicSet}, -) where {T,F,DT,M,B,G,CT,MT,MVT,W} +) where {T,F,DT,M,BT,B,G,CT,W} @assert MOI.output_dimension(func) == length(set.basis) # As `*(::MOI.ScalarAffineFunction{T}, ::S)` is only defined if `S == T`, we # need to call `similar`. This is critical since `T` is @@ -82,7 +81,7 @@ function MOI.Bridges.Constraint.bridge_constraint( MOI.Utilities.vectorize(new_coeffs), SOS.WeightedSOSCone{M}(new_basis, flat_gram_bases, flat_weights), ) - return SOSPolynomialBridge{T,F,DT,M,B,G,CT,MT,MVT,W}( + return SOSPolynomialBridge{T,F,DT,M,BT,B,G,CT,W}( constraint, set, new_basis, @@ -104,23 +103,22 @@ _eltype(::Type{T}) where T = T function MOI.Bridges.Constraint.concrete_bridge_type( ::Type{<:SOSPolynomialBridge{T}}, F::Type{<:MOI.AbstractVectorFunction}, - ::Type{SOS.SOSPolynomialSet{DT,MB.SubBasis{MB.Monomial,MT,MVT},CT}}, -) where {T,DT<:SemialgebraicSets.AbstractAlgebraicSet,MT,MVT,CT} + ::Type{SOS.SOSPolynomialSet{DT,BT,CT}}, +) where {T,DT<:SemialgebraicSets.AbstractAlgebraicSet,BT,CT} # promotes VectorOfVariables into VectorAffineFunction, it should be enough # for most use cases M = SOS.matrix_cone_type(CT) - B = MB.SubBasis{MB.Monomial,MT,MVT} - W = SA.AlgebraElement{MB.algebra_type(B),T,Vector{T}} - R = MA.promote_operation( + W = SA.AlgebraElement{MB.algebra_type(BT),T,Vector{T}} + B = MA.promote_operation( SOS.Certificate.reduced_basis, CT, - B, + BT, SemialgebraicSets.similar_type(DT, T), - Vector{MB.SubBasis{MB.Monomial,MT,MVT}}, + Vector{BT}, Vector{W}, ) - G = SOS.Certificate.gram_basis_type(CT, MT) - return SOSPolynomialBridge{T,F,DT,M,R,_eltype(G),CT,MT,MVT,W} + G = SOS.Certificate.gram_basis_type(CT) + return SOSPolynomialBridge{T,F,DT,M,BT,B,_eltype(G),CT,W} end function MOI.Bridges.inverse_map_function(::SOSPolynomialBridge, f) diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index a5ec5ef54..5d89e9648 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -85,9 +85,8 @@ function SumOfSquares.Certificate.gram_basis(certificate::Ideal, poly) end function SumOfSquares.Certificate.gram_basis_type( ::Type{Ideal{S,C}}, - ::Type{M}, -) where {S,C,M} - return Vector{SumOfSquares.Certificate.gram_basis_type(C, M)} +) where {S,C} + return Vector{SumOfSquares.Certificate.gram_basis_type(C)} end function SumOfSquares.Certificate.reduced_polynomial( certificate::Ideal, diff --git a/src/Certificate/Symmetry/wedderburn.jl b/src/Certificate/Symmetry/wedderburn.jl index cb7b88612..a8b278a62 100644 --- a/src/Certificate/Symmetry/wedderburn.jl +++ b/src/Certificate/Symmetry/wedderburn.jl @@ -77,8 +77,7 @@ function SumOfSquares.matrix_cone_type(::Type{<:Ideal{C}}) where {C} end function SumOfSquares.Certificate.gram_basis_type( ::Type{<:Ideal}, - ::Type{M}, -) where {M} +) return Vector{Vector{MB.FixedPolynomialBasis}} end SumOfSquares.Certificate.zero_basis_type(::Type{<:Ideal}) = MB.Monomial diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 6113b13e7..3f8747542 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -117,7 +117,7 @@ function gram_basis(certificate::MaxDegree, poly) certificate.maxdegree, ) end -function gram_basis_type(::Type{MaxDegree{C,B}}, ::Type{M}) where {C,B,M} # TODO remove `M` it's not needed anymore +function gram_basis_type(::Type{MaxDegree{C,B}}) where {C,B} return MB.explicit_basis_type(B) end @@ -141,7 +141,7 @@ end function gram_basis(certificate::FixedBasis, _) return certificate.basis end -gram_basis_type(::Type{FixedBasis{C,B}}, ::Type) where {C,B} = B +gram_basis_type(::Type{FixedBasis{C,B}}) where {C,B} = B """ struct Newton{ @@ -184,11 +184,11 @@ end function gram_basis(certificate::Newton, basis) return MB.basis_covering_monomials( certificate.basis, - monomials_half_newton_polytope(SA.basis(basis), certificate.newton), + half_newton_polytope(SA.basis(basis), certificate.newton), ) end -function gram_basis_type(::Type{<:Newton{C,B}}, ::Type{M}) where {C,B,M} +function gram_basis_type(::Type{<:Newton{C,B}}) where {C,B} return MB.explicit_basis_type(B) end @@ -233,8 +233,8 @@ function gram_basis(certificate::Remainder, poly) return gram_basis(certificate.gram_certificate, poly) end -function gram_basis_type(::Type{Remainder{GCT}}, ::Type{M}) where {GCT,M} - return gram_basis_type(GCT, M) +function gram_basis_type(::Type{Remainder{GCT}}) where {GCT} + return gram_basis_type(GCT) end cone(certificate::Remainder) = cone(certificate.gram_certificate) diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index aae7b40d7..62625138a 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -204,7 +204,7 @@ function half_newton_polytope(X::AbstractVector, ::NewtonDegreeBounds{Tuple{}}) end end -function half_newton_polytope(monos::AbstractVector, newton::NewtonFilter) +function half_newton_polytope(basis::SA.ExplicitBasis, newton::NewtonFilter) gram_monos = half_newton_polytope(monos, newton.outer_approximation) return post_filter(gram_monos, monos) end @@ -273,21 +273,6 @@ function post_filter(monos, X) return monos[findall(keep)] end -# FIXME update API with basis, this is a hack -function monomials_half_newton_polytope( - basis::MB.SubBasis{MB.Monomial}, - newton::AbstractNewtonPolytopeApproximation, -) - return half_newton_polytope(basis.monomials, newton) -end - -function monomials_half_newton_polytope( - monos::AbstractVector, - newton::AbstractNewtonPolytopeApproximation, -) - return half_newton_polytope(MP.monomial_vector(monos), newton) -end - struct DegreeBounds{M} mindegree::Int maxdegree::Int diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index edf848644..be5ceb0ab 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -144,8 +144,8 @@ function multiplier_basis( ) return domain.bases[index.value] end -function multiplier_basis_type(::Type{<:Putinar{MC}}, ::Type{M}) where {MC,M} - return gram_basis_type(MC, M) +function multiplier_basis_type(::Type{<:Putinar{MC}}) where {MC} + return gram_basis_type(MC) end function generator( diff --git a/test/certificate.jl b/test/certificate.jl index d72aac04f..514fc7708 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -204,7 +204,7 @@ function certificate_api(certificate::Certificate.AbstractIdealCertificate) ) isa Tuple _basis_check( Certificate.gram_basis(certificate, basis), - Certificate.gram_basis_type(typeof(certificate), MP.monomial_type(x)), + Certificate.gram_basis_type(typeof(certificate)), ) zbasis = Certificate.zero_basis(certificate) @test zbasis isa SA.AbstractBasis From 65999b4a83a002a07a2353dac671db744f6f3890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 5 Jun 2024 10:36:39 +0200 Subject: [PATCH 41/84] Fixes --- src/Certificate/newton_polytope.jl | 185 ++++++++++++++--------------- 1 file changed, 89 insertions(+), 96 deletions(-) diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 62625138a..dd6b1b2d4 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -209,70 +209,6 @@ function half_newton_polytope(basis::SA.ExplicitBasis, newton::NewtonFilter) return post_filter(gram_monos, monos) end -# If `mono` is such that there is no other way to have `mono^2` by multiplying -# two different monomials of `monos` and `mono` is not in `X` then, the corresponding -# diagonal entry of the Gram matrix will be zero hence the whole column and row -# will be zero hence we can remove this monomial. -# See [Proposition 3.7, CLR95], [Theorem 2, L09] or [Section 2.4, BKP16]. - -# [CLR95] Choi, M. D. and Lam, T. Y. and Reznick, B. -# *Sum of Squares of Real Polynomials*. -# Proceedings of Symposia in Pure mathematics (1995) -# -# [L09] Lofberg, Johan. -# *Pre-and post-processing sum-of-squares programs in practice*. -# IEEE transactions on automatic control 54.5 (2009): 1007-1011. -# -# [BKP16] Sabine Burgdorf, Igor Klep, and Janez Povh. -# *Optimization of polynomials in non-commuting variables*. -# Berlin: Springer, 2016. -function post_filter(monos, X) - num = Dict(mono => 1 for mono in X) - function _increase(mono) - return num[mono] = get(num, mono, 0) + 1 - end - function _decrease(mono) - value = num[mono] - 1 - if iszero(value) - delete!(num, mono) - else - num[mono] = value - end - return iszero(value) - end - for a in monos - for b in monos - if a != b - _increase(a * b) - end - end - end - back = Dict{eltype(monos),Int}() - keep = ones(Bool, length(monos)) - function _delete(i) - keep[i] = false - a = monos[i] - for (j, b) in enumerate(monos) - if keep[j] && a != b - for w in [a * b, b * a] - if _decrease(w) && haskey(back, w) - _delete(back[w]) - end - end - end - end - end - for (i, mono) in enumerate(monos) - w = mono^2 - if haskey(num, w) - back[w] = i - else - _delete(i) - end - end - return monos[findall(keep)] -end - struct DegreeBounds{M} mindegree::Int maxdegree::Int @@ -305,7 +241,6 @@ function min_degree(p::SA.AlgebraElement, v) return mapreduce(Base.Fix2(min_degree, v), min, SA.supp(p)) end min_degree(p::MB.Polynomial{MB.Monomial}, v) = MP.degree(p.monomial, v) -minus_min_degree(p, v) = -min_degree(p, v) function _monomial(vars, exps) if any(Base.Fix2(isless, 0), exps) @@ -389,13 +324,21 @@ function _interval(a, b) end end +function _gram_shift_min(d_g, d_s, g::SA.AlgebraElement) + if _is_monomial_basis(typeof(g)) + return d_g + 2minimum(d_s) + else + return 0 + end +end + function deg_range(deg, p, gs, gram_deg, truncation) d_max = min(deg(p), truncation) sign = deg_sign(deg, p, d_max) for g in gs d_g, sign_g = deg_sign(deg, g) d_s = gram_deg(g) - if isempty(d_s) || d_g + 2minimum(d_s) > truncation + if isempty(d_s) || _gram_shift_min(d_g, d_s, g) > truncation continue end d = d_g + 2maximum(d_s) @@ -429,7 +372,7 @@ end # deg_range(deg, p, gs, gram_deg, range) # -# Maximum value of `deg(s_0 = p - sum s_i g_i for g in gs) in range` where +# Maximum value of `deg(s_0 = p - sum s_i g_i for i in eachindex(gs)) in range` where # `s_0, s_i` are SOS and `deg(s_i) <= gram_deg(g)`. # Note that `range` should be in increasing order. function deg_range(deg, p, gs, gram_deg, range::UnitRange) @@ -451,6 +394,40 @@ _maxdegree(a::SA.AlgebraElement) = maximum(_maxdegree, SA.supp(a)) _mindegree(p::MB.Polynomial{MB.Monomial}) = MP.mindegree(p.monomial) _maxdegree(p::MB.Polynomial{MB.Monomial}) = MP.maxdegree(p.monomial) +_is_monomial_basis(::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}) = true +_is_monomial_basis(::Type{<:SA.AlgebraElement}) = false + +# Minimum degree of a gram basis for a gram matrix `s` +# such that the minimum degree of `s * g` is at least `mindegree`. +function _multiplier_mindegree( + mindegree, + g::SA.AlgebraElement, +) + if _is_monomial_basis(typeof(g)) + return _min_half(min_shift(mindegree, _mindegree(g))) + else + # For instance, with `Chebyshev` a square already produces + # a monomial of degree 0 so let's just give up here + # The Chebyshev Newton polytope of the product of polynomials + # with Chebyshev Newton polytope `N1` and `N2` is a subset + # of `(N1 + N2 - R_+^n) ∩ R_+^n` so we compute + # `σ(y, cheby(N1) + cheby(N2))` as `σ(max.(y, 0), N1 + N2)` + return 0 + end +end + +# Maximum degree of a gram basis for a gram matrix `s` +# such that the maximum degree of `s * g` is at most `maxdegree`. +# Here, we assume that the degree of `*(a::MB.Polynomial, b::MB.Polynomial)` +# is bounded by the sum of the degree of `a` and `b` for any basis. +function _multiplier_maxdegree(maxdegree, g::SA.AlgebraElement) + return _max_half(maxdegree - _maxdegree(g)) +end + +function _multiplier_deg_range(range, g::SA.AlgebraElement) + return _multiplier_mindegree(minimum(range), g):_multiplier_maxdegree(maximum(range), g) +end + function putinar_degree_bounds( p::SA.AlgebraElement, gs::AbstractVector{<:SA.AlgebraElement}, @@ -459,13 +436,11 @@ function putinar_degree_bounds( ) mindegree = 0 # TODO homogeneous case - mindeg(g) = _min_half(min_shift(mindegree, _mindegree(g))) - maxdeg(g) = _max_half(maxdegree - _maxdegree(g)) - degrange(g) = mindeg(g):maxdeg(g) - minus_degrange(g) = -maxdeg(g):-mindeg(g) + degrange(g) = _multiplier_deg_range(mindegree:maxdegree, g) + minus_degrange(g) = (-).(degrange(g)) # The multiplier will have degree `0:2fld(maxdegree - _maxdegree(g), 2)` mindegree = - -deg_range(p -> -_mindegree(p), p, gs, minus_degrange, -maxdegree:0) + -deg_range((-) ∘ _mindegree, p, gs, minus_degrange, -maxdegree:0) if isnothing(mindegree) return end @@ -474,13 +449,17 @@ function putinar_degree_bounds( return end vars_mindeg = map(vars) do v - return -deg_range( - Base.Fix2(minus_min_degree, v), - p, - gs, - minus_degrange, - -maxdegree:0, - ) + return if _is_monomial_basis(eltype(gs)) + -deg_range( + (-) ∘ Base.Fix2(min_degree, v), + p, + gs, + minus_degrange, + -maxdegree:0, + ) + else + 0 + end end if any(isnothing, vars_mindeg) return @@ -501,29 +480,24 @@ function putinar_degree_bounds( ) end -function multiplier_basis(g::SA.AlgebraElement, bounds::DegreeBounds) +function multiplier_basis(g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, bounds::DegreeBounds) where {BT,B} shifted = minus_shift(bounds, g) if isnothing(shifted) halved = nothing else halved = _half(shifted) end - basis = MB.FullBasis{MB.Monomial,MP.monomial_type(typeof(g))}() + basis = MB.FullBasis{B,MP.monomial_type(typeof(g))}() if isnothing(halved) - # TODO add `MB.empty_basis` to API - return MB.maxdegree_basis( - basis, - MP.variables(bounds.variablewise_mindegree), - -1, - ) + return MB.empty_basis(MB.explicit_basis_type(typeof(basis))) else return maxdegree_gram_basis(basis, halved) end end function half_newton_polytope( - p::MP.AbstractPolynomialLike, - gs::AbstractVector{<:MP.AbstractPolynomialLike}, + p::SA.AlgebraElement, + gs::AbstractVector{<:SA.AlgebraElement}, vars, maxdegree, ::NewtonDegreeBounds, @@ -554,10 +528,10 @@ function half_newton_polytope( gs::AbstractVector{<:SA.AlgebraElement}, vars, maxdegree, - ::NewtonFilter{<:NewtonDegreeBounds}, + filter::NewtonFilter{<:NewtonDegreeBounds}, ) bounds = putinar_degree_bounds(p, gs, vars, maxdegree) - bases = [multiplier_basis(g, bounds) for g in gs] + bases = half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) push!( bases, maxdegree_gram_basis( @@ -604,6 +578,7 @@ function Base.:+(c::SignCount, a::SignChange{Missing}) @assert c.unknown >= -a.Δ return SignCount(c.unknown + a.Δ, c.positive, c.negative) end + function Base.:+(c::SignCount, a::SignChange{<:Number}) if a.sign > 0 @assert c.positive >= -a.Δ @@ -642,6 +617,29 @@ function increase(cache, counter, generator_sign, monos, mult) end end +# If `mono` is such that there is no other way to have `mono^2` by multiplying +# two different monomials of `monos` and `mono` is not in `X` then, the corresponding +# diagonal entry of the Gram matrix will be zero hence the whole column and row +# will be zero hence we can remove this monomial. +# See [Proposition 3.7, CLR95], [Theorem 2, L09] or [Section 2.4, BKP16]. + +# This is generalized here to the case with constraints as detailed in [L23]. + +# [CLR95] Choi, M. D. and Lam, T. Y. and Reznick, B. +# *Sum of Squares of Real Polynomials*. +# Proceedings of Symposia in Pure mathematics (1995) +# +# [L09] Löfberg, Johan. +# *Pre-and post-processing sum-of-squares programs in practice*. +# IEEE transactions on automatic control 54.5 (2009): 1007-1011. +# +# [BKP16] Sabine Burgdorf, Igor Klep, and Janez Povh. +# *Optimization of polynomials in non-commuting variables*. +# Berlin: Springer, 2016. +# +# [L23] Legat, Benoît +# *Exploiting the Structure of a Polynomial Optimization Problem* +# SIAM Conference on Applications of Dynamical Systems, 2023 function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos) counter = MB.algebra_element( zero(MP.polynomial_type(MP.monomial_type(typeof(SA.basis(poly))), SignCount)), @@ -737,8 +735,3 @@ end function _sub(basis::SubBasis{B}, I) where {B} return SubBasis{B}(basis.monomials[I]) end - -function half_newton_polytope(a::SA.AlgebraElement, args...) - @assert SA.basis(a) isa MB.SubBasis{MB.Monomial} - half_newton_polytope(SA.coeffs(a, MB.FullBasis{MB.Monomial,MP.monomial_type(typeof(a))}()), args...) -end From 30d7fb90ee14509bc42b9e380b5be13155411ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 5 Jun 2024 18:44:54 +0200 Subject: [PATCH 42/84] Fixes --- src/Certificate/ideal.jl | 5 +- src/Certificate/newton_polytope.jl | 259 ++++++----------------------- src/Certificate/preorder.jl | 12 +- 3 files changed, 57 insertions(+), 219 deletions(-) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 3f8747542..76d2f4054 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -181,10 +181,11 @@ function Newton(cone, basis, variable_groups::Tuple) ) end -function gram_basis(certificate::Newton, basis) +function gram_basis(certificate::Newton, poly) + a = _algebra_element(poly) return MB.basis_covering_monomials( certificate.basis, - half_newton_polytope(SA.basis(basis), certificate.newton), + half_newton_polytope(a, typeof(a)[], MP.variables(poly), _maxdegree(a), certificate.newton), ) end diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index dd6b1b2d4..27206e8d7 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -1,74 +1,3 @@ -cfld(x::NTuple{2,Int}, n) = (cld(x[1], n), fld(x[2], n)) - -function sub_extdegree(X::AbstractVector{<:MP.AbstractMonomial}, vars) - if isempty(X) - return (0, 0) - else - return extrema(map(mono -> sum(var -> MP.degree(mono, var), vars), X)) - end -end - -# Cheap approximation of the convex hull as the approximation of: -# -# z such that mindegree < sum(z) < maxdegree -# |\ -# |#\ <-------- sum(z) = maxdegree -# |##\ -# |\<-\-------- sum(z) = mindegree -# | \##\ -# +--------- -# -# and: -# -# z such that minmultideg < z < maxmultideg -# | +----+ <--- maxmultidegree -# | |####| -# | |####| -# | +----+ -# | ^---------- minmultidegree -# +--------- - -function _filter(X::AbstractVector{<:MP.AbstractMonomial}, extdeg, exp, n) - mindeg, maxdeg = cfld(extdeg, 2) - minmultideg, maxmultideg = Vector{Int}(undef, n), Vector{Int}(undef, n) - for i in 1:n - exponent_i(mono) = exp(mono, i) - minmultideg[i] = cld(mapreduce(exponent_i, min, X), 2) - maxmultideg[i] = fld(mapreduce(exponent_i, max, X), 2) - end - return mindeg, - maxdeg, - mono -> begin - all(i -> minmultideg[i] <= exp(mono, i) <= maxmultideg[i], 1:n) - end -end -function _full_filter(X::AbstractVector{<:MP.AbstractMonomial}, extdeg, exp, n) - mindeg, maxdeg, filter = _filter(X, extdeg, exp, n) - return mono -> mindeg <= MP.degree(mono) <= maxdeg && filter(mono) -end - -function _sub_half_newton_polytope( - X::AbstractVector{<:MP.AbstractMonomial}, - extdeg, - exp, - vars, -) - mindeg, maxdeg, filter = _filter(X, extdeg, exp, length(vars)) - return MP.monomials(vars, mindeg:maxdeg, filter) -end - -function sub_half_newton_polytope( - X::AbstractVector{<:MP.AbstractMonomial}, - vars, -) - return _sub_half_newton_polytope( - X, - sub_extdegree(X, vars), - (mono, i) -> MP.degree(mono, vars[i]), - vars, - ) -end - function is_commutative(vars) return length(vars) < 2 || prod(vars[1:2]) == prod(reverse(vars[1:2])) end @@ -81,49 +10,6 @@ struct NewtonDegreeBounds{NPT} <: AbstractNewtonPolytopeApproximation variable_groups::NPT end -# Multipartite -# TODO we might do this recursively : do 2 parts, merge them, merge with next -# one and so on so that the filter at the end prunes more. -function half_newton_polytope(X::AbstractVector, newton::NewtonDegreeBounds) - if !is_commutative(MP.variables(X)) - throw( - ArgumentError( - "Multipartite Newton polytope not supported with noncommutative variables.", - ), - ) - end - parts = newton.variable_groups - if !all( - i -> all(j -> i == j || isempty(parts[i] ∩ parts[j]), 1:length(parts)), - 1:length(parts), - ) - throw( - ArgumentError( - "Parts are not disjoint in multipartite Newton polytope estimation: $parts.", - ), - ) - end - # Some variables might be in no part... - missing = setdiff(MP.variables(X), reduce(union, parts)) - if isempty(missing) - all_parts = parts - else - # in that case, create a part with the missing ones - all_parts = (parts..., missing) - end - if length(all_parts) == 1 - # all variables on same part, fallback to shortcut - return half_newton_polytope(X, NewtonDegreeBounds(tuple())) - end - monomial_vectors = map(vars -> sub_half_newton_polytope(X, vars), all_parts) - # Cartesian product of the newton polytopes of the different parts - product = [prod(monos) for monos in Iterators.product(monomial_vectors...)] - mindeg, maxdeg = cfld(MP.extdegree(X), 2) - # We know that the degree inequalities are satisfied variable-wise and - # part-wise but for all variables together so we filter with that - gram_monos = filter(mono -> mindeg <= MP.degree(mono) <= maxdeg, product) - return MP.monomial_vector(gram_monos) -end # Filters out points ouside the Newton polytope from the # outer approximation given by `outer_approximation`. @@ -132,83 +18,6 @@ struct NewtonFilter{N<:AbstractNewtonPolytopeApproximation} <: outer_approximation::N end -function __chip(cur, i, vars, exps, n, op) - if n > 0 - exp = min(exps[i], n) - next = iszero(exp) ? cur : op(cur, vars[i]^exp) - return __chip(next, i + 1, vars, exps, n - exp, op) - else - return cur - end -end -function _chip(mono, n) - vars = MP.variables(mono) - exps = MP.exponents(mono) - if n < 0 - vars = reverse(vars) - exps = reverse(exps) - op(a, b) = b * a - else - op = * - end - return __chip(MP.constant_monomial(mono), 1, vars, exps, abs(n), op) -end - -function _is_hermitian_square(mono) - d = MP.degree(mono) - if isodd(d) - return false - end - n = div(d, 2) - return _chip(mono, n) == _chip(mono, -n) -end - -# Shortcut for more efficient `extdeg` and `exp` function in case all the -# variables are in the same part -function half_newton_polytope(X::AbstractVector, ::NewtonDegreeBounds{Tuple{}}) - vars = MP.variables(X) - if is_commutative(vars) - # Commutative variables - exp(mono, i) = MP.exponents(mono)[i] - return _sub_half_newton_polytope(X, MP.extdegree(X), exp, vars) - else - # Non-commutative variables - # We use Newton chip method of [Section 2.3, BKP16]. - # - # [BKP16] Sabine Burgdorf, Igor Klep, and Janez Povh. - # *Optimization of polynomials in non-commuting variables*. - # Berlin: Springer, 2016. - vars = unique!(sort(vars)) - function ncexp(mono, i) - mvars = MP.variables(mono) - return mapreduce( - j -> mvars[j] == vars[i] ? MP.exponents(mono)[j] : 0, - +, - eachindex(mvars), - init = 0, - ) - end - filter = _full_filter(X, MP.extdegree(X), ncexp, length(vars)) - _monos = eltype(X)[] - for mono in X - if _is_hermitian_square(mono) - for i in 1:div(MP.degree(mono), 2) - w = _chip(mono, -i) - if filter(w) - push!(_monos, w) - end - end - end - end - return MP.monomial_vector(_monos) - end -end - -function half_newton_polytope(basis::SA.ExplicitBasis, newton::NewtonFilter) - gram_monos = half_newton_polytope(monos, newton.outer_approximation) - return post_filter(gram_monos, monos) -end - struct DegreeBounds{M} mindegree::Int maxdegree::Int @@ -240,7 +49,13 @@ end function min_degree(p::SA.AlgebraElement, v) return mapreduce(Base.Fix2(min_degree, v), min, SA.supp(p)) end -min_degree(p::MB.Polynomial{MB.Monomial}, v) = MP.degree(p.monomial, v) +function min_degree(p::MB.Polynomial{B}, v) where {B} + if _is_monomial_basis(B) + return MP.degree(p.monomial, v) + else + error("TODO $B") + end +end function _monomial(vars, exps) if any(Base.Fix2(isless, 0), exps) @@ -252,7 +67,7 @@ end function max_degree(p::SA.AlgebraElement, v) return mapreduce(Base.Fix2(max_degree, v), max, SA.supp(p)) end -max_degree(p::MB.Polynomial{MB.Monomial}, v) = MP.degree(p.monomial, v) +max_degree(p::MB.Polynomial, v) = MP.degree(p.monomial, v) function min_shift(d, shift) return max(0, d - shift) @@ -391,11 +206,18 @@ _mindegree(a::SA.AlgebraElement) = minimum(_mindegree, SA.supp(a)) _maxdegree(a::SA.AlgebraElement) = maximum(_maxdegree, SA.supp(a)) #_mindegree(basis::MB.SubBasis{MB.Monomial}) = MP.mindegree(basis.monomials) #_maxdegree(basis::MB.SubBasis{MB.Monomial}) = MP.maxdegree(basis.monomials) -_mindegree(p::MB.Polynomial{MB.Monomial}) = MP.mindegree(p.monomial) -_maxdegree(p::MB.Polynomial{MB.Monomial}) = MP.maxdegree(p.monomial) +function _mindegree(p::MB.Polynomial{B}) where {B} + if _is_monomial_basis(B) + MP.mindegree(p.monomial) + else + error("TODO $B") + end +end +_maxdegree(p::MB.Polynomial) = MP.maxdegree(p.monomial) -_is_monomial_basis(::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}) = true -_is_monomial_basis(::Type{<:SA.AlgebraElement}) = false +_is_monomial_basis(::Type{<:MB.AbstractMonomialIndexed}) = false +_is_monomial_basis(::Type{<:Union{MB.Monomial,MB.ScaledMonomial}}) = true +_is_monomial_basis(::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}) where {BT,B} = _is_monomial_basis(B) # Minimum degree of a gram basis for a gram matrix `s` # such that the minimum degree of `s * g` is at least `mindegree`. @@ -428,6 +250,25 @@ function _multiplier_deg_range(range, g::SA.AlgebraElement) return _multiplier_mindegree(minimum(range), g):_multiplier_maxdegree(maximum(range), g) end +# Cheap approximation of the convex hull as the approximation of: +# +# z such that mindegree < sum(z) < maxdegree +# |\ +# |#\ <-------- sum(z) = maxdegree +# |##\ +# |\<-\-------- sum(z) = mindegree +# | \##\ +# +--------- +# +# and: +# +# z such that minmultideg < z < maxmultideg +# | +----+ <--- maxmultidegree +# | |####| +# | |####| +# | +----+ +# | ^---------- minmultidegree +# +--------- function putinar_degree_bounds( p::SA.AlgebraElement, gs::AbstractVector{<:SA.AlgebraElement}, @@ -508,15 +349,15 @@ function half_newton_polytope( end function half_newton_polytope( - p::SA.AlgebraElement, + p::SA.AlgebraElement{<:MB.Algebra{BT,B}}, gs::AbstractVector{<:MP.AbstractPolynomialLike}, vars, maxdegree, filter, -) +) where {BT,B} return half_newton_polytope( p, - [MB.algebra_element(MP.coefficients(g), MB.SubBasis{MB.Monomial}(MP.monomials(g))) for g in gs], + [MB.algebra_element(MP.coefficients(g), MB.SubBasis{B}(MP.monomials(g))) for g in gs], vars, maxdegree, filter, @@ -524,18 +365,18 @@ function half_newton_polytope( end function half_newton_polytope( - p::SA.AlgebraElement, + p::SA.AlgebraElement{<:MB.Algebra{BT,B,M}}, gs::AbstractVector{<:SA.AlgebraElement}, vars, maxdegree, filter::NewtonFilter{<:NewtonDegreeBounds}, -) +) where {BT,B,M} bounds = putinar_degree_bounds(p, gs, vars, maxdegree) bases = half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) push!( bases, maxdegree_gram_basis( - MB.FullBasis{MB.Monomial,MP.monomial_type(typeof(p))}(), + MB.FullBasis{B,M}(), _half(bounds), ), ) @@ -641,20 +482,14 @@ end # *Exploiting the Structure of a Polynomial Optimization Problem* # SIAM Conference on Applications of Dynamical Systems, 2023 function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos) - counter = MB.algebra_element( - zero(MP.polynomial_type(MP.monomial_type(typeof(SA.basis(poly))), SignCount)), - MB.implicit_basis(SA.basis(poly)), - ) - cache = MB.algebra_element( - zero(MP.polynomial_type(MP.monomial_type(typeof(SA.basis(poly))), Float64)), - MB.implicit_basis(SA.basis(poly)), - ) + counter = zero(SignCount, SA.algebra(MB.implicit_basis(SA.basis(poly)))) + cache = similar(counter, Float64) for (mono, v) in SA.nonzero_pairs(poly) MA.operate!( SA.UnsafeAddMul(*), counter, SignChange(_sign(v), 1), - mono, + MB.algebra_element(mono), ) end for (mult, gram_monos) in zip(generators, multipliers_gram_monos) diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index be5ceb0ab..db76cc7c8 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -37,15 +37,17 @@ struct WithVariables{S,V} variables::V end -struct WithFixedBases{S,B} - inner::S - bases::Vector{B} -end - function MP.variables(v::WithVariables) return v.variables end SA.basis(v::WithVariables) = SA.basis(v.inner) +_algebra_element(v::WithVariables) = v.inner +_algebra_element(a::SA.AlgebraElement) = a + +struct WithFixedBases{S,B} + inner::S + bases::Vector{B} +end _merge_sorted(a::Vector, ::Tuple{}) = a function _merge_sorted(a::Vector, b::Vector) From 13bddca66b5dc0fa5a6e0f43aad2ea6fd9cb7524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 5 Jun 2024 23:53:09 +0200 Subject: [PATCH 43/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 5 +++ src/Bridges/Variable/kernel.jl | 6 +-- src/Certificate/Certificate.jl | 6 +-- src/Certificate/ideal.jl | 42 ++++++++++----------- src/Certificate/newton_polytope.jl | 47 +++++++++++++++--------- src/Certificate/preorder.jl | 2 +- src/constraints.jl | 2 + test/Tests/quadratic.jl | 2 +- 8 files changed, 62 insertions(+), 50 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 83e7a36e3..581aa96d0 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -46,6 +46,8 @@ function MOI.Bridges.Constraint.bridge_constraint( func::MOI.AbstractVectorFunction, set::SOS.SOSPolynomialSet{<:SemialgebraicSets.AbstractAlgebraicSet}, ) where {T,F,DT,M,BT,B,G,CT,W} + @show func + @show set.basis @assert MOI.output_dimension(func) == length(set.basis) # As `*(::MOI.ScalarAffineFunction{T}, ::S)` is only defined if `S == T`, we # need to call `similar`. This is critical since `T` is @@ -67,7 +69,9 @@ function MOI.Bridges.Constraint.bridge_constraint( ) gram_bases = [gram_basis] weights = [MB.constant_algebra_element(typeof(SA.basis(poly)), T)] + @show typeof(gram_bases) flat_gram_bases, flat_weights, flat_indices = _flatten(gram_bases, weights) + @show flat_indices new_basis = SOS.Certificate.reduced_basis( set.certificate, SA.basis(poly), @@ -76,6 +80,7 @@ function MOI.Bridges.Constraint.bridge_constraint( flat_weights, ) new_coeffs = SA.coeffs(poly, new_basis) + @show new_coeffs constraint = MOI.add_constraint( model, MOI.Utilities.vectorize(new_coeffs), diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index 818d75176..45ee710c2 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -12,10 +12,7 @@ function MOI.Bridges.Variable.bridge_constrained_variable( ) where {T,M} variables = Vector{MOI.VariableIndex}[] constraints = MOI.ConstraintIndex{MOI.VectorOfVariables}[] - acc = MB.algebra_element( - zero(MP.polynomial_type(MP.monomial_type(typeof(set.basis)), MOI.ScalarAffineFunction{T})), - MB.implicit_basis(set.basis), - ) + acc = zero(MOI.ScalarAffineFunction{T}, SA.algebra(MB.implicit_basis(set.basis))) for (gram_basis, weight) in zip(set.gram_bases, set.weights) gram, vars, con = SOS.add_gram_matrix(model, M, gram_basis, T) push!(variables, vars) @@ -23,6 +20,7 @@ function MOI.Bridges.Variable.bridge_constrained_variable( MA.operate!(SA.UnsafeAddMul(*), acc, weight, gram) end MA.operate!(SA.canonical, SA.coeffs(acc)) + @show SA.coeffs(acc, set.basis) return KernelBridge{T,M}(SA.coeffs(acc, set.basis), variables, constraints, set) end diff --git a/src/Certificate/Certificate.jl b/src/Certificate/Certificate.jl index a56d579de..90aacdfc5 100644 --- a/src/Certificate/Certificate.jl +++ b/src/Certificate/Certificate.jl @@ -45,20 +45,20 @@ function multiplier_basis_type end abstract type AbstractCertificate end -function maxdegree_gram_basis(::MB.FullBasis{MB.Monomial}, bounds::DegreeBounds) +function maxdegree_gram_basis(::MB.FullBasis{B}, bounds::DegreeBounds) where {B<:MB.AbstractMonomial} variables = MP.variables(bounds.variablewise_maxdegree) function filter(mono) return MP.divides(bounds.variablewise_mindegree, mono) && MP.divides(mono, bounds.variablewise_maxdegree) end - return MB.SubBasis{MB.Monomial}( + return MB.SubBasis{B}( MP.monomials(variables, bounds.mindegree:bounds.maxdegree, filter), ) end function maxdegree_gram_basis(basis::SA.AbstractBasis, bounds::DegreeBounds) # TODO use bounds here too variables = MP.variables(bounds.variablewise_maxdegree) - return maxdegree_gram_basis(basis, variables, bounds.maxdegree) + return MB.maxdegree_basis(basis, variables, bounds.maxdegree) end function maxdegree_gram_basis( basis::SA.AbstractBasis, diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 76d2f4054..dfa43bdf1 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -19,32 +19,31 @@ function _combine_with_gram( gram_bases::AbstractVector{<:MB.SubBasis}, weights, ) where {B,M} - full = MB.FullBasis{B,M}() - mstr = SA.mstructure(full) - p = MP.polynomial(fill(_NonZero(), length(basis)), basis.monomials) + p = zero(_NonZero, SA.algebra(MB.FullBasis{B,M}())) + for mono in basis + MA.operate!(SA.UnsafeAddMul(*), p, _NonZero(), MB.algebra_element(mono)) + end for (gram, weight) in zip(gram_bases, weights) - w = MB.convert_basis(full, weight) - for j in eachindex(gram) - for i in 1:j - s = MB.convert_basis(full, gram[i] * gram[j]) - for w_mono in SA.supp(w) - for s_mono in SA.supp(s) - MA.operate!( - SA.UnsafeAddMul(mstr), - p, - w_mono.monomial, - s_mono.monomial, - ) - end + for col in gram + for row in gram + for mono in SA.supp(weight) + MA.operate!( + SA.UnsafeAddMul(*), + p, + _NonZero(), + MB.algebra_element(mono), + MB.algebra_element(SA.star(row)), + MB.algebra_element(col), + ) end end end end - MA.operate!(SA.canonical, p) - return MB.SubBasis{MB.Monomial}(MP.monomials(p)) + MA.operate!(SA.canonical, SA.coeffs(p)) + return MB.SubBasis{B}(keys(SA.coeffs(p))) end -_reduce_with_domain(basis::MB.SubBasis{MB.Monomial}, ::FullSpace) = basis +_reduce_with_domain(basis::MB.SubBasis, ::FullSpace) = basis function _reduce_with_domain(basis::MB.SubBasis{MB.Monomial}, domain) I = ideal(domain) @@ -183,10 +182,7 @@ end function gram_basis(certificate::Newton, poly) a = _algebra_element(poly) - return MB.basis_covering_monomials( - certificate.basis, - half_newton_polytope(a, typeof(a)[], MP.variables(poly), _maxdegree(a), certificate.newton), - ) + return half_newton_polytope(a, typeof(a)[], MP.variables(poly), _maxdegree(a), certificate.newton)[1] end function gram_basis_type(::Type{<:Newton{C,B}}) where {C,B} diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 27206e8d7..62ff0c56f 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -337,27 +337,31 @@ function multiplier_basis(g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, bounds::Degr end function half_newton_polytope( - p::SA.AlgebraElement, + p::SA.AlgebraElement{<:MB.Algebra{BT,B,M}}, gs::AbstractVector{<:SA.AlgebraElement}, vars, maxdegree, ::NewtonDegreeBounds, -) +) where {BT,B,M} # TODO take `variable_groups` into account bounds = putinar_degree_bounds(p, gs, vars, maxdegree) - return [multiplier_basis(g, bounds) for g in gs] + @show bounds + return maxdegree_gram_basis( + MB.FullBasis{B,M}(), + _half(bounds), + ), [multiplier_basis(g, bounds) for g in gs] end function half_newton_polytope( - p::SA.AlgebraElement{<:MB.Algebra{BT,B}}, + p::SA.AlgebraElement, gs::AbstractVector{<:MP.AbstractPolynomialLike}, vars, maxdegree, filter, -) where {BT,B} +) return half_newton_polytope( p, - [MB.algebra_element(MP.coefficients(g), MB.SubBasis{B}(MP.monomials(g))) for g in gs], + [MB.algebra_element(MP.coefficients(g), MB.SubBasis{MB.Monomial}(MP.monomials(g))) for g in gs], vars, maxdegree, filter, @@ -365,26 +369,21 @@ function half_newton_polytope( end function half_newton_polytope( - p::SA.AlgebraElement{<:MB.Algebra{BT,B,M}}, + p::SA.AlgebraElement, gs::AbstractVector{<:SA.AlgebraElement}, vars, maxdegree, filter::NewtonFilter{<:NewtonDegreeBounds}, -) where {BT,B,M} - bounds = putinar_degree_bounds(p, gs, vars, maxdegree) - bases = half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) - push!( - bases, - maxdegree_gram_basis( - MB.FullBasis{B,M}(), - _half(bounds), - ), - ) +) + basis, multipliers_bases = half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) + @show basis + bases = copy(multipliers_bases) + push!(bases, basis) gs = copy(gs) push!(gs, MB.constant_algebra_element(MA.promote_operation(SA.basis, eltype(gs)), eltype(eltype(gs)))) filtered_bases = post_filter(p, gs, bases) # The last one will be recomputed by the ideal certificate - return filtered_bases[1:(end-1)] + return filtered_bases[end], filtered_bases[1:(end-1)] end struct SignChange{T} @@ -415,6 +414,11 @@ function _sign(c::SignCount) end end +function Base.:+(a::SignCount, b::SignCount) + return SignCount(a.unknown + b.unknown, a.positive + b.positive, a.negative + b.negative) +end + + function Base.:+(c::SignCount, a::SignChange{Missing}) @assert c.unknown >= -a.Δ return SignCount(c.unknown + a.Δ, c.positive, c.negative) @@ -439,6 +443,8 @@ end Base.convert(::Type{SignCount}, Δ::SignChange) = SignCount() + Δ function increase(cache, counter, generator_sign, monos, mult) + @show monos + @show mult for a in monos for b in monos MA.operate_to!( @@ -456,6 +462,7 @@ function increase(cache, counter, generator_sign, monos, mult) ) end end + @show counter end # If `mono` is such that there is no other way to have `mono^2` by multiplying @@ -551,7 +558,11 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos MB.algebra_element(mono), MB.algebra_element(mono), ) + MA.operate!(SA.canonical, SA.coeffs(counter)) + @show counter + @show cache for w in SA.supp(cache) + @show w if ismissing(_sign(SA.coeffs(counter)[SA.basis(counter)[w]])) push!(get(back, w, Tuple{Int,Int}[]), (i, j)) else diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index db76cc7c8..f722a7543 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -93,7 +93,7 @@ function with_fixed_basis( v = with_variables(domain, p) return WithFixedBases( v.inner, - half_newton_polytope(p, SemialgebraicSets.inequalities(domain), v.variables, maxdegree, newton), + half_newton_polytope(p, SemialgebraicSets.inequalities(domain), v.variables, maxdegree, newton)[2], ) end diff --git a/src/constraints.jl b/src/constraints.jl index 6e6257690..b463f6949 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -374,6 +374,8 @@ function _default_basis(p::MP.AbstractPolynomialLike, basis::MB.FullBasis{B}, b) return _default_basis(MP.coefficients(p), MB.SubBasis{B}(MP.monomials(p)), b) else new_basis = MB.FullBasis{b,MP.monomial_type(typeof(basis))}() + @show p + @show SA.coeffs(p, basis, new_basis) return _default_basis(SA.coeffs(p, basis, new_basis), new_basis, b) end end diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index c62799958..0c3a6a297 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -91,7 +91,7 @@ function quadratic_test( } S = SumOfSquares.SOSPolynomialSet{ SumOfSquares.FullSpace, - MB.SubBasis{MB.Monomial,monomial_type(x),monomial_vector_type(x)}, + MB.SubBasis{basis,monomial_type(x),monomial_vector_type(x)}, SumOfSquares.Certificate.Newton{ typeof(cone), MB.FullBasis{basis,monomial_type(x)}, From ba0907d734101509311b7bf0e425b36fbd6eb683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 6 Jun 2024 08:09:56 +0200 Subject: [PATCH 44/84] Remove debug --- src/Bridges/Constraint/sos_polynomial.jl | 5 ----- src/Bridges/Variable/kernel.jl | 1 - src/Certificate/newton_polytope.jl | 8 -------- 3 files changed, 14 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 581aa96d0..83e7a36e3 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -46,8 +46,6 @@ function MOI.Bridges.Constraint.bridge_constraint( func::MOI.AbstractVectorFunction, set::SOS.SOSPolynomialSet{<:SemialgebraicSets.AbstractAlgebraicSet}, ) where {T,F,DT,M,BT,B,G,CT,W} - @show func - @show set.basis @assert MOI.output_dimension(func) == length(set.basis) # As `*(::MOI.ScalarAffineFunction{T}, ::S)` is only defined if `S == T`, we # need to call `similar`. This is critical since `T` is @@ -69,9 +67,7 @@ function MOI.Bridges.Constraint.bridge_constraint( ) gram_bases = [gram_basis] weights = [MB.constant_algebra_element(typeof(SA.basis(poly)), T)] - @show typeof(gram_bases) flat_gram_bases, flat_weights, flat_indices = _flatten(gram_bases, weights) - @show flat_indices new_basis = SOS.Certificate.reduced_basis( set.certificate, SA.basis(poly), @@ -80,7 +76,6 @@ function MOI.Bridges.Constraint.bridge_constraint( flat_weights, ) new_coeffs = SA.coeffs(poly, new_basis) - @show new_coeffs constraint = MOI.add_constraint( model, MOI.Utilities.vectorize(new_coeffs), diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index 45ee710c2..134b013ab 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -20,7 +20,6 @@ function MOI.Bridges.Variable.bridge_constrained_variable( MA.operate!(SA.UnsafeAddMul(*), acc, weight, gram) end MA.operate!(SA.canonical, SA.coeffs(acc)) - @show SA.coeffs(acc, set.basis) return KernelBridge{T,M}(SA.coeffs(acc, set.basis), variables, constraints, set) end diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 62ff0c56f..09479c2a8 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -345,7 +345,6 @@ function half_newton_polytope( ) where {BT,B,M} # TODO take `variable_groups` into account bounds = putinar_degree_bounds(p, gs, vars, maxdegree) - @show bounds return maxdegree_gram_basis( MB.FullBasis{B,M}(), _half(bounds), @@ -376,7 +375,6 @@ function half_newton_polytope( filter::NewtonFilter{<:NewtonDegreeBounds}, ) basis, multipliers_bases = half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) - @show basis bases = copy(multipliers_bases) push!(bases, basis) gs = copy(gs) @@ -443,8 +441,6 @@ end Base.convert(::Type{SignCount}, Δ::SignChange) = SignCount() + Δ function increase(cache, counter, generator_sign, monos, mult) - @show monos - @show mult for a in monos for b in monos MA.operate_to!( @@ -462,7 +458,6 @@ function increase(cache, counter, generator_sign, monos, mult) ) end end - @show counter end # If `mono` is such that there is no other way to have `mono^2` by multiplying @@ -559,10 +554,7 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos MB.algebra_element(mono), ) MA.operate!(SA.canonical, SA.coeffs(counter)) - @show counter - @show cache for w in SA.supp(cache) - @show w if ismissing(_sign(SA.coeffs(counter)[SA.basis(counter)[w]])) push!(get(back, w, Tuple{Int,Int}[]), (i, j)) else From 57a10a4112a23c8db34be8513abe8f309620bae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 6 Jun 2024 08:10:15 +0200 Subject: [PATCH 45/84] Remove debug --- src/constraints.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/constraints.jl b/src/constraints.jl index b463f6949..6e6257690 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -374,8 +374,6 @@ function _default_basis(p::MP.AbstractPolynomialLike, basis::MB.FullBasis{B}, b) return _default_basis(MP.coefficients(p), MB.SubBasis{B}(MP.monomials(p)), b) else new_basis = MB.FullBasis{b,MP.monomial_type(typeof(basis))}() - @show p - @show SA.coeffs(p, basis, new_basis) return _default_basis(SA.coeffs(p, basis, new_basis), new_basis, b) end end From 15fcd6632498626f9457c0fd9e3899bab62b4b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 6 Jun 2024 13:29:28 +0200 Subject: [PATCH 46/84] Fixes --- .../sos_polynomial_in_semialgebraic_set.jl | 7 +-- src/Certificate/ideal.jl | 5 +- src/Certificate/newton_polytope.jl | 14 +++--- src/Certificate/preorder.jl | 2 +- src/constraints.jl | 49 ++++++++++++------- test/Solvers/scs_tests.jl | 3 +- test/Tests/quadratic.jl | 11 +++-- 7 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index 59548d6a3..aa58223d9 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -49,8 +49,9 @@ function MOI.Bridges.Constraint.bridge_constraint( # MOI does not modify the coefficients of the functions so we can modify `p`. # without altering `f`. # The monomials may be copied by MA however so we need to copy it. + # TODO remove `collect` when `DynamicPolynomials.MonomialVector` can be used as keys p = MB.algebra_element( - MP.polynomial(MOI.Utilities.scalarize(f), copy(set.basis.monomials)), + SA.SparseCoefficients(copy(collect(set.basis.monomials)), MOI.Utilities.scalarize(f)), MB.implicit_basis(set.basis), ) λ_bases = B[] @@ -89,12 +90,12 @@ function MOI.Bridges.Constraint.bridge_constraint( # For terms, `monomials` is `OneOrZeroElementVector` # so we convert it with `monomial_vector` # Later, we'll use `MP.MonomialBasis` which is going to do that anyway - MB.SubBasis{MB.Monomial}(MP.monomial_vector(MP.monomials(SA.coeffs(p)))), + MB.SubBasis{MB.Monomial}(MP.monomial_vector(SA.keys(SA.coeffs(p)))), Certificate.ideal_certificate(set.certificate), ) constraint = MOI.add_constraint( model, - MOI.Utilities.vectorize(MP.coefficients(SA.coeffs(p))), + MOI.Utilities.vectorize(SA.values(SA.coeffs(p))), new_set, ) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index dfa43bdf1..d10bab863 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -45,7 +45,10 @@ end _reduce_with_domain(basis::MB.SubBasis, ::FullSpace) = basis -function _reduce_with_domain(basis::MB.SubBasis{MB.Monomial}, domain) +function _reduce_with_domain(basis::MB.SubBasis{B}, domain) where {B} + if B !== MB.Monomial + error("Only Monomial basis support with an equalities in domain") + end I = ideal(domain) # set of standard monomials that are hit standard = Set{eltype(basis.monomials)}() diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 09479c2a8..2ae6f0c75 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -203,7 +203,7 @@ end #_mindegree(p::MP.AbstractPolynomialLike) = MP.mindegree(p) _mindegree(a::SA.AlgebraElement) = minimum(_mindegree, SA.supp(a)) -_maxdegree(a::SA.AlgebraElement) = maximum(_maxdegree, SA.supp(a)) +_maxdegree(a::SA.AlgebraElement) = maximum(_maxdegree, SA.supp(a), init = 0) #_mindegree(basis::MB.SubBasis{MB.Monomial}) = MP.mindegree(basis.monomials) #_maxdegree(basis::MB.SubBasis{MB.Monomial}) = MP.maxdegree(basis.monomials) function _mindegree(p::MB.Polynomial{B}) where {B} @@ -280,8 +280,11 @@ function putinar_degree_bounds( degrange(g) = _multiplier_deg_range(mindegree:maxdegree, g) minus_degrange(g) = (-).(degrange(g)) # The multiplier will have degree `0:2fld(maxdegree - _maxdegree(g), 2)` - mindegree = + mindegree = if _is_monomial_basis(typeof(p)) -deg_range((-) ∘ _mindegree, p, gs, minus_degrange, -maxdegree:0) + else + 0 + end if isnothing(mindegree) return end @@ -345,10 +348,9 @@ function half_newton_polytope( ) where {BT,B,M} # TODO take `variable_groups` into account bounds = putinar_degree_bounds(p, gs, vars, maxdegree) - return maxdegree_gram_basis( - MB.FullBasis{B,M}(), - _half(bounds), - ), [multiplier_basis(g, bounds) for g in gs] + full = MB.FullBasis{B,M}() + return maxdegree_gram_basis(full, _half(bounds)), + MB.explicit_basis_type(typeof(full))[multiplier_basis(g, bounds) for g in gs] end function half_newton_polytope( diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index f722a7543..c1c707325 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -146,7 +146,7 @@ function multiplier_basis( ) return domain.bases[index.value] end -function multiplier_basis_type(::Type{<:Putinar{MC}}) where {MC} +function multiplier_basis_type(::Type{<:Putinar{MC}}, ::Type) where {MC} return gram_basis_type(MC) end diff --git a/src/constraints.jl b/src/constraints.jl index 6e6257690..910828ca8 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -244,7 +244,8 @@ end function JuMP.moi_set( cone::SOSLikeCone, - basis::SA.ExplicitBasis; + basis::SA.ExplicitBasis, + gram_basis::SA.AbstractBasis; domain::AbstractSemialgebraicSet = FullSpace(), newton_polytope::Union{Nothing,Tuple} = tuple(), maxdegree::Union{Nothing,Int} = default_maxdegree(basis, domain), @@ -256,7 +257,7 @@ function JuMP.moi_set( newton_of_remainder, symmetry, sparsity, - MB.implicit_basis(basis), + gram_basis, cone, maxdegree, newton_polytope, @@ -266,7 +267,7 @@ function JuMP.moi_set( sparsity, ideal_certificate, cone, - MB.implicit_basis(basis), + gram_basis, maxdegree, newton_polytope, ), @@ -361,33 +362,43 @@ end _promote_coef_type(::Type{V}, ::Type) where {V<:JuMP.AbstractVariableRef} = V _promote_coef_type(::Type{F}, ::Type{T}) where {F,T} = promote_type(F, T) -function _default_basis(coeffs, basis::MB.SubBasis{B}, b) where {B} - if isnothing(b) || B == b +function _default_basis(coeffs, basis::MB.SubBasis{B}, gram_basis::MB.MonomialIndexedBasis{G}) where {B,G} + if B === G return coeffs, basis else - return _default_basis(MP.polynomial(coeffs, basis.monomials), MB.implicit_basis(basis), b) + return _default_basis(SA.SparseCoefficients(basis.monomials, coeffs), MB.implicit_basis(basis), gram_basis) end end -function _default_basis(p::MP.AbstractPolynomialLike, basis::MB.FullBasis{B}, b) where {B} - if isnothing(b) || B == b - return _default_basis(MP.coefficients(p), MB.SubBasis{B}(MP.monomials(p)), b) +function _default_basis(p::SA.AbstractCoefficients, basis::MB.FullBasis{B}, gram_basis::MB.MonomialIndexedBasis{G}) where {B,G} + if B === G + return _default_basis(collect(SA.values(p)), MB.SubBasis{B}(collect(SA.keys(p))), gram_basis) else - new_basis = MB.FullBasis{b,MP.monomial_type(typeof(basis))}() - return _default_basis(SA.coeffs(p, basis, new_basis), new_basis, b) + new_basis = MB.FullBasis{G,MP.monomial_type(typeof(basis))}() + return _default_basis(SA.coeffs(p, basis, new_basis), new_basis, gram_basis) end end -function _default_basis(a::SA.AlgebraElement, basis) - return _default_basis(SA.coeffs(a), SA.basis(a), basis) +function _default_gram_basis(::MB.MonomialIndexedBasis{B,M}, ::Nothing) where {B,M} + return MB.FullBasis{B,M}() +end + +function _default_gram_basis(::MB.MonomialIndexedBasis{_B,M}, ::Type{B}) where {_B,B,M} + return MB.FullBasis{B,M}() end -function _default_basis(a::SA.AlgebraElement, basis::SA.ExplicitBasis) - return SA.coeffs(a, basis), basis +function _default_gram_basis(_, basis::MB.MonomialIndexedBasis) + return basis +end + +function _default_basis(a::SA.AlgebraElement, basis) + b = SA.basis(a) + gram = _default_gram_basis(b, basis) + return _default_basis(SA.coeffs(a), b, gram)..., gram end -function _default_basis(p, basis) - return _default_basis(MB.algebra_element(p, MB.FullBasis{MB.Monomial,MP.monomial_type(p)}()), basis) +function _default_basis(p::MP.AbstractPolynomialLike, basis) + return _default_basis(MB.algebra_element(MB.sparse_coefficients(p), MB.FullBasis{MB.Monomial,MP.monomial_type(p)}()), basis) end function JuMP.build_constraint( @@ -397,8 +408,8 @@ function JuMP.build_constraint( basis = nothing, kws..., ) - __coefs, basis = _default_basis(p, basis) - set = JuMP.moi_set(cone, basis; kws...) + __coefs, basis, gram_basis = _default_basis(p, basis) + set = JuMP.moi_set(cone, basis, gram_basis; kws...) _coefs = PolyJuMP.non_constant(__coefs) # If a polynomial with real coefficients is used with the Hermitian SOS # cone, we want to promote the coefficients to complex diff --git a/test/Solvers/scs_tests.jl b/test/Solvers/scs_tests.jl index 9fd27579f..b51e304d1 100644 --- a/test/Solvers/scs_tests.jl +++ b/test/Solvers/scs_tests.jl @@ -3,7 +3,8 @@ import SCS factory = optimizer_with_attributes(SCS.Optimizer, MOI.Silent() => true) config = MOI.Test.Config(atol = 1e-3, rtol = 1e-3) @testset "Linear" begin - Tests.linear_test(factory, config, include = ["dsos_scaled_univariate_quadratic"]) + Tests.linear_test(factory, config, include = ["dsos_cheby_univariate_quadratic"]) + #Tests.linear_test(factory, config) end #@testset "SOC" begin # Tests.soc_test(factory, config) diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index 0c3a6a297..273a8df17 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -53,8 +53,10 @@ function quadratic_test( @test value_matrix(p) ≈ ones(2, 2) atol = atol rtol = rtol @test p.basis.monomials == cert_monos - a = moment_value.(moments(dual(cref))) - @test a[2] ≈ -1.0 atol = atol rtol = rtol + μ = moments(dual(cref)) + a = moment_value.(μ) + @test μ[2].polynomial == MB.Polynomial{basis}(bivariate ? x * y : x^1) + @test a[2] ≈ (bivariate && basis === MB.ScaledMonomial ? -√2 : -1.0) atol = atol rtol = rtol @test a[1] + a[3] ≈ 2.0 atol = atol rtol = rtol @test dual_status(model) == MOI.FEASIBLE_POINT @@ -80,9 +82,10 @@ function quadratic_test( end ν = moment_matrix(cref) + off = (bivariate && basis === ScaledMonomial) ? a[2] / √2 : a[2] @test value_matrix(ν) ≈ [ - a[1] a[2] - a[2] a[3] + a[1] off + off a[3] ] atol = atol rtol = rtol @test ν.basis.monomials == cert_monos From f67d91a078bfce239ee806544893e37488a28f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 8 Jun 2024 09:23:10 +0200 Subject: [PATCH 47/84] up --- src/Bridges/Constraint/sos_polynomial.jl | 2 +- test/Solvers/scs_tests.jl | 2 +- test/Tests/quadratic.jl | 44 +++++++++++++++++++----- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 83e7a36e3..9c0697cb5 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -129,7 +129,7 @@ end function MOI.Bridges.adjoint_map_function(bridge::SOSPolynomialBridge, f) # FIXME `coeffs` should be an `AbstractMatrix` - return MB.adjoint_coeffs(f, bridge.new_basis, bridge.set.basis) + return SA.adjoint_coeffs(f, bridge.set.basis, bridge.new_basis) end # Attributes, Bridge acting as a constraint diff --git a/test/Solvers/scs_tests.jl b/test/Solvers/scs_tests.jl index b51e304d1..04f7378d9 100644 --- a/test/Solvers/scs_tests.jl +++ b/test/Solvers/scs_tests.jl @@ -3,7 +3,7 @@ import SCS factory = optimizer_with_attributes(SCS.Optimizer, MOI.Silent() => true) config = MOI.Test.Config(atol = 1e-3, rtol = 1e-3) @testset "Linear" begin - Tests.linear_test(factory, config, include = ["dsos_cheby_univariate_quadratic"]) + Tests.linear_test(factory, config, include = ["dsos_cheby_bivariate_quadratic"]) #Tests.linear_test(factory, config) end #@testset "SOC" begin diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index 273a8df17..e300027bc 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -27,8 +27,17 @@ function quadratic_test( if bivariate @polyvar y poly = x^2 + α * x * y + y^2 - cert_monos = [y, x] - monos = [y^2, x * y, x^2] + if basis === Chebyshev + # See https://github.com/jump-dev/SumOfSquares.jl/issues/357 + cert_monos = [1, y, x] + else + cert_monos = [y, x] + end + if basis === Chebyshev + monos = [1, y^2, x * y, x^2] + else + monos = [y^2, x * y, x^2] + end else poly = x^2 + α * x + 1 cert_monos = [1, x] @@ -50,14 +59,26 @@ function quadratic_test( test_constraint_primal(cref, value(poly); atol, rtol) p = gram_matrix(cref) - @test value_matrix(p) ≈ ones(2, 2) atol = atol rtol = rtol + if basis === Chebyshev && bivariate + # See https://github.com/jump-dev/SumOfSquares.jl/issues/357 + @test value_matrix(p) ≈ [zeros(1, 3); zeros(2) ones(2, 2)] atol = atol rtol = rtol + else + @test value_matrix(p) ≈ ones(2, 2) atol = atol rtol = rtol + end @test p.basis.monomials == cert_monos μ = moments(dual(cref)) a = moment_value.(μ) - @test μ[2].polynomial == MB.Polynomial{basis}(bivariate ? x * y : x^1) - @test a[2] ≈ (bivariate && basis === MB.ScaledMonomial ? -√2 : -1.0) atol = atol rtol = rtol - @test a[1] + a[3] ≈ 2.0 atol = atol rtol = rtol + if bivariate && basis === MB.Chebyshev + b = a[2:end] + [a[1], 0, a[1]] + b[1] /= 2 + b[3] /= 2 + else + b = a + end + @test b[2] ≈ (bivariate && basis === MB.ScaledMonomial ? -√2 : -1.0) atol = atol rtol = rtol + @test b[1] + b[3] ≈ 2.0 atol = atol rtol = rtol + @test μ[2].polynomial == MB.Polynomial{basis}(bivariate ? (basis === MB.Chebyshev ? y^2 : x * y) : x^1) @test dual_status(model) == MOI.FEASIBLE_POINT _test_moments(dual(cref), monos) do vals @@ -83,9 +104,14 @@ function quadratic_test( ν = moment_matrix(cref) off = (bivariate && basis === ScaledMonomial) ? a[2] / √2 : a[2] - @test value_matrix(ν) ≈ [ - a[1] off - off a[3] + M = value_matrix(ν) + if basis === Chebyshev && bivariate + display(M) + M = M[2:end, 2:end] + end + @test M ≈ [ + b[1] off + off b[3] ] atol = atol rtol = rtol @test ν.basis.monomials == cert_monos From 850e32c7119e59348eeb021bb36f1f8e6609af89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 8 Jun 2024 10:11:44 +0200 Subject: [PATCH 48/84] Fxies --- test/Tests/quadratic.jl | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index e300027bc..7dd43dc9a 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -87,14 +87,14 @@ function quadratic_test( if basis === MB.Chebyshev && bivariate _test_moments( moments(cref), - monomial_vector([1, x, y^2, x * y, x^2, x^3]), + monomial_vector([1, y, x, y^2, x * y, x^2]), ) do vals @test length(vals) == 6 - for i in [1, 2, 6] + for i in [2, 3] @test vals[i] ≈ 0 rtol = rtol atol = atol end - @test vals[4] ≈ -1 rtol = rtol atol = atol - @test vals[3] + vals[5] ≈ 2 rtol = rtol atol = atol + @test vals[5] ≈ -1 rtol = rtol atol = atol + @test vals[4] + vals[6] + 2vals[1] ≈ 4 rtol = rtol atol = atol end else _test_moments(moments(cref), monos) do vals @@ -103,10 +103,15 @@ function quadratic_test( end ν = moment_matrix(cref) - off = (bivariate && basis === ScaledMonomial) ? a[2] / √2 : a[2] + off = if bivariate && basis === ScaledMonomial + a[2] / √2 + elseif bivariate && basis == Chebyshev + -(a[1] + a[2]) / 2 + else + a[2] + end M = value_matrix(ν) if basis === Chebyshev && bivariate - display(M) M = M[2:end, 2:end] end @test M ≈ [ From d48a255312affc368e13fdc4ef37b5a6704d9d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 8 Jun 2024 10:23:50 +0200 Subject: [PATCH 49/84] Fixes --- src/gram_matrix.jl | 16 ++++++++-------- test/Solvers/scs_tests.jl | 13 ++++++------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/gram_matrix.jl b/src/gram_matrix.jl index 1882fa1df..ef1c33586 100644 --- a/src/gram_matrix.jl +++ b/src/gram_matrix.jl @@ -161,14 +161,14 @@ function MA.operate!(op::SA.UnsafeAddMul{typeof(*)}, p::SA.AlgebraElement, w::SA return p end -#function MP.polynomial(g::GramMatrix, ::Type{T}) where {T} -# n = length(g.basis) -# @assert size(g.Q) == (n, n) -# p = zero(MP.polynomial_type(typeof(g), T)) -# MA.operate(SA.UnsafeAddMul(*), p, g) -# MA.operate(SA.canonical, p) -# return p -#end +function MP.polynomial(g::GramMatrix, ::Type{T}) where {T} + p = zero(T, SA.algebra(g.basis)) + # TODO Remove the need for this multiplier + multiplier = MB.algebra_element(MB.sparse_coefficients(MP.term(true, MP.constant_monomial(g))), MB.implicit_basis(g.basis)) + MA.operate!(SA.UnsafeAddMul(*), p, multiplier, g) + MA.operate!(SA.canonical, p) + return MP.polynomial(SA.coeffs(p, MB.FullBasis{MB.Monomial,MP.monomial_type(g)}())) +end function change_basis( p::GramMatrix{T,<:MB.SubBasis{B}}, diff --git a/test/Solvers/scs_tests.jl b/test/Solvers/scs_tests.jl index 04f7378d9..ecee40745 100644 --- a/test/Solvers/scs_tests.jl +++ b/test/Solvers/scs_tests.jl @@ -2,13 +2,12 @@ include("solver_preamble.jl") import SCS factory = optimizer_with_attributes(SCS.Optimizer, MOI.Silent() => true) config = MOI.Test.Config(atol = 1e-3, rtol = 1e-3) -@testset "Linear" begin - Tests.linear_test(factory, config, include = ["dsos_cheby_bivariate_quadratic"]) - #Tests.linear_test(factory, config) -end +#@testset "Linear" begin +# Tests.linear_test(factory, config) +#end #@testset "SOC" begin # Tests.soc_test(factory, config) #end -#@testset "SDP" begin -# Tests.sd_test(factory, config) -#end +@testset "SDP" begin + Tests.sd_test(factory, config, include = ["sosdemo5_infeasible"]) +end From f0c93066e8f9a7d4aa50bd055b1f58bc8f94d286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 10 Jun 2024 15:20:03 +0200 Subject: [PATCH 50/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 23 ++++- .../sos_polynomial_in_semialgebraic_set.jl | 5 +- src/Bridges/Variable/kernel.jl | 2 +- src/Certificate/Sparsity/ideal.jl | 2 +- src/Certificate/Sparsity/preorder.jl | 6 +- src/Certificate/ideal.jl | 17 ++-- src/Certificate/newton_polytope.jl | 38 ++++++-- src/gram_matrix.jl | 89 ++++++++----------- src/sosdec.jl | 55 ++++++------ test/Solvers/scs_tests.jl | 2 +- test/Tests/lyapunov_switched_system.jl | 2 +- 11 files changed, 133 insertions(+), 108 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 9c0697cb5..450f07a52 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -40,6 +40,13 @@ function _flatten( return flat_gram_bases, flat_weights, 1:length(flat_weights) end +function _poly(coeffs, basis::MB.MonomialIndexedBasis{B,M}) where {B,M} + return MB.algebra_element( + MB.sparse_coefficients(MP.polynomial(coeffs, basis.monomials)), + MB.FullBasis{B,M}(), + ) +end + function MOI.Bridges.Constraint.bridge_constraint( ::Type{SOSPolynomialBridge{T,F,DT,M,BT,B,G,CT,W}}, model::MOI.ModelLike, @@ -58,7 +65,7 @@ function MOI.Bridges.Constraint.bridge_constraint( # MOI does not modify the coefficients of the functions so we can modify `p`. # without altering `f`. # The basis may be copied by MA however so we need to copy it. - MB.algebra_element(MOI.Utilities.scalarize(func), copy(set.basis)), + _poly(MOI.Utilities.scalarize(func), copy(set.basis)), domain, ) gram_basis = SOS.Certificate.gram_basis( @@ -70,7 +77,7 @@ function MOI.Bridges.Constraint.bridge_constraint( flat_gram_bases, flat_weights, flat_indices = _flatten(gram_bases, weights) new_basis = SOS.Certificate.reduced_basis( set.certificate, - SA.basis(poly), + MB.explicit_basis(poly), domain, flat_gram_bases, flat_weights, @@ -108,7 +115,17 @@ function MOI.Bridges.Constraint.concrete_bridge_type( # promotes VectorOfVariables into VectorAffineFunction, it should be enough # for most use cases M = SOS.matrix_cone_type(CT) - W = SA.AlgebraElement{MB.algebra_type(BT),T,Vector{T}} + W = SA.AlgebraElement{ + MA.promote_operation( + SA.algebra, + MA.promote_operation(MB.implicit_basis, BT), + ), + T, + MA.promote_operation( + MB.sparse_coefficients, + MP.polynomial_type(MP.monomial_type(BT), T), + ), + } B = MA.promote_operation( SOS.Certificate.reduced_basis, CT, diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index aa58223d9..d4eec5e87 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -80,8 +80,11 @@ function MOI.Bridges.Constraint.bridge_constraint( p = MA.operate!( SA.UnsafeAddMul(*), p, - MB.algebra_element(-one(T) * similar(g, T), MB.FullBasis{MB.Monomial,MP.monomial_type(g)}()), λ, + MB.algebra_element( + MB.sparse_coefficients(-one(T) * similar(g, T)), + MB.FullBasis{MB.Monomial,MP.monomial_type(g)}(), + ), ) end MA.operate!(SA.canonical, SA.coeffs(p)) diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index 134b013ab..83cd97cfd 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -17,7 +17,7 @@ function MOI.Bridges.Variable.bridge_constrained_variable( gram, vars, con = SOS.add_gram_matrix(model, M, gram_basis, T) push!(variables, vars) push!(constraints, con) - MA.operate!(SA.UnsafeAddMul(*), acc, weight, gram) + MA.operate!(SA.UnsafeAddMul(*), acc, gram, weight) end MA.operate!(SA.canonical, SA.coeffs(acc)) return KernelBridge{T,M}(SA.coeffs(acc, set.basis), variables, constraints, set) diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index 5d89e9648..d44ab1dcd 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -75,7 +75,7 @@ function sparsity( ) end function sparsity(a::SA.AlgebraElement, sp, certificate) - return sparsity(SA.basis(a), sp, certificate) + return sparsity(MB.explicit_basis(a), sp, certificate) end function sparsity(v::SumOfSquares.Certificate.WithVariables, sp, certificate) return sparsity(v.inner, sp, certificate) diff --git a/src/Certificate/Sparsity/preorder.jl b/src/Certificate/Sparsity/preorder.jl index 694acf914..994b96e93 100644 --- a/src/Certificate/Sparsity/preorder.jl +++ b/src/Certificate/Sparsity/preorder.jl @@ -31,8 +31,8 @@ function SumOfSquares.Certificate.preprocessed_domain( domain::SemialgebraicSets.BasicSemialgebraicSet, p, ) - basis, Preorder_bases = sparsity( - MB.SubBasis{MB.Monomial}(MP.monomials(p)), + _, preorder_bases = sparsity( + MB.explicit_basis(p), domain, certificate.sparsity, certificate.certificate, @@ -44,7 +44,7 @@ function SumOfSquares.Certificate.preprocessed_domain( domain, p, ), - Preorder_bases, + preorder_bases, ) end diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index d10bab863..828dcc4e4 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -23,6 +23,8 @@ function _combine_with_gram( for mono in basis MA.operate!(SA.UnsafeAddMul(*), p, _NonZero(), MB.algebra_element(mono)) end + # TODO, could refactor with + # MA.operate!(SA.UnsafeAddMul(*), p, GramMatrix(_ -> _NonZero(), gram), weight) for (gram, weight) in zip(gram_bases, weights) for col in gram for row in gram @@ -31,9 +33,9 @@ function _combine_with_gram( SA.UnsafeAddMul(*), p, _NonZero(), - MB.algebra_element(mono), MB.algebra_element(SA.star(row)), MB.algebra_element(col), + MB.algebra_element(mono), ) end end @@ -212,9 +214,10 @@ struct Remainder{GCT<:AbstractIdealCertificate} <: AbstractIdealCertificate gram_certificate::GCT end -function _rem(coeffs, basis::MB.SubBasis{MB.Monomial}, I) - poly = MP.polynomial(coeffs, basis.monomials) - return convert(typeof(poly), rem(poly, I)) +function _rem(coeffs, basis::MB.FullBasis{MB.Monomial}, I) + poly = MP.polynomial(SA.values(coeffs), SA.keys(coeffs)) + r = convert(typeof(poly), rem(poly, I)) + return MB.algebra_element(MB.sparse_coefficients(r), basis) end function reduced_polynomial( @@ -222,11 +225,7 @@ function reduced_polynomial( a::SA.AlgebraElement, domain, ) - r = _rem(SA.coeffs(a), SA.basis(a), ideal(domain)) - return MB.algebra_element( - MP.coefficients(r), - MB.SubBasis{MB.Monomial}(MP.monomials(r)), - ) + return _rem(SA.coeffs(a), SA.basis(a), ideal(domain)) end function gram_basis(certificate::Remainder, poly) diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 2ae6f0c75..a4cb09936 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -462,6 +462,24 @@ function increase(cache, counter, generator_sign, monos, mult) end end +struct _DictCoefficients{K,V} <: SA.AbstractCoefficients{K,V} + inner::Dict{K,V} +end + +SA.nonzero_pairs(d::_DictCoefficients) = d.inner +Base.keys(d::_DictCoefficients) = keys(d.inner) + +Base.getindex(d::_DictCoefficients{K}, key::K) where {K} = d.inner[key] + +function SA.unsafe_push!(c::_DictCoefficients{K}, key::K, value) where {K} + c.inner[key] = if haskey(c.inner, key) + MA.operate!!(+, c.inner[key], value) + else + value + end + return c +end + # If `mono` is such that there is no other way to have `mono^2` by multiplying # two different monomials of `monos` and `mono` is not in `X` then, the corresponding # diagonal entry of the Gram matrix will be zero hence the whole column and row @@ -486,8 +504,11 @@ end # *Exploiting the Structure of a Polynomial Optimization Problem* # SIAM Conference on Applications of Dynamical Systems, 2023 function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos) - counter = zero(SignCount, SA.algebra(MB.implicit_basis(SA.basis(poly)))) - cache = similar(counter, Float64) + # We use `_DictCoefficients` instead `SA.SparseCoefficients` because + # we need to keep it canonicalized (without duplicate actually) + # and don't care about the list of monomials being ordered + counter = MB.algebra_element(_DictCoefficients(Dict{MP.monomial_type(typeof(poly)),SignCount}()), MB.implicit_basis(SA.basis(poly))) + cache = zero(Float64, SA.algebra(MB.implicit_basis(SA.basis(poly)))) for (mono, v) in SA.nonzero_pairs(poly) MA.operate!( SA.UnsafeAddMul(*), @@ -510,9 +531,8 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos MB.algebra_element(c), ) MA.operate!(SA.UnsafeAddMul(*), counter, SignChange(sign, -1), cache) - MA.operate!(SA.canonical, counter) for mono in SA.supp(cache) - count = counter[mono] + count = SA.coeffs(counter)[SA.basis(counter)[mono]] count_sign = _sign(count) # This means the `counter` has a sign and it didn't have a sign before # so we need to delete back edges @@ -534,13 +554,14 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos end keep[i][j] = false a = multipliers_gram_monos[i][j] - for (k, v) in SA.nonzero_pairs(generators[i]) + for (k, v) in SA.nonzero_pairs(SA.coeffs(generators[i])) + mono = SA.basis(generators[i])[k] sign = -_sign(v) - decrease(sign, k, a, a) + decrease(sign, mono, a, a) for (j, b) in enumerate(multipliers_gram_monos[i]) if keep[i][j] - decrease(missing, k, a, b) - decrease(missing, k, b, a) + decrease(missing, mono, a, b) + decrease(missing, mono, b, a) end end end @@ -555,7 +576,6 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos MB.algebra_element(mono), MB.algebra_element(mono), ) - MA.operate!(SA.canonical, SA.coeffs(counter)) for w in SA.supp(cache) if ismissing(_sign(SA.coeffs(counter)[SA.basis(counter)[w]])) push!(get(back, w, Tuple{Int,Int}[]), (i, j)) diff --git a/src/gram_matrix.jl b/src/gram_matrix.jl index ef1c33586..105a359d6 100644 --- a/src/gram_matrix.jl +++ b/src/gram_matrix.jl @@ -90,6 +90,8 @@ function MP.similar_type( return GramMatrix{S,B,US,MS} end +MB.implicit_basis(g::GramMatrix) = MB.implicit_basis(g.basis) + # When taking the promotion of a GramMatrix of JuMP.Variable with a Polynomial JuMP.Variable, it should be a Polynomial of AffExpr MP.constant_monomial(p::GramMatrix) = MP.constant_monomial(MP.monomial_type(p)) MP.variables(p::GramMatrix) = MP.variables(p.basis) @@ -135,51 +137,28 @@ function GramMatrix(Q::AbstractMatrix, monos::AbstractVector) return GramMatrix(Q, MB.SubBasis{MB.Monomial}(sorted_monos), σ) end -#function Base.convert{T, PT <: AbstractPolynomial{T}}(::Type{PT}, p::GramMatrix) -# # coefficient_type(p) may be different than T and MP.polynomial(p) may be different than PT (different module) -# convert(PT, MP.polynomial(p)) -#end -function MP.polynomial(p::GramMatrix{T,B,U}) where {T,B,U} - return MP.polynomial(p, U) +function _term_element(α, p::MB.Polynomial{B,M}) where {B,M} + return MB.algebra_element( + MB.sparse_coefficients(MP.term(α, p.monomial)), + MB.FullBasis{B,M}(), + ) end -function MA.operate!(op::SA.UnsafeAddMul{typeof(*)}, p::SA.AlgebraElement, w::SA.AlgebraElement, g::GramMatrix) +function MA.operate!(op::SA.UnsafeAddMul{typeof(*)}, p::SA.AlgebraElement, g::GramMatrix, args::Vararg{Any,N}) where {N} for col in eachindex(g.basis) for row in eachindex(g.basis) - for (k, v) in SA.nonzero_pairs(SA.coeffs(w)) - MA.operate!( - op, - p, - v * g.Q[row, col], - MB.algebra_element(SA.basis(w)[k]), - MB.algebra_element(SA.star(g.basis[row])), - MB.algebra_element(g.basis[col]), - ) - end + MA.operate!( + op, + p, + _term_element(true, SA.star(g.basis[row])), + _term_element(g.Q[row, col], g.basis[col]), + args..., + ) end end return p end -function MP.polynomial(g::GramMatrix, ::Type{T}) where {T} - p = zero(T, SA.algebra(g.basis)) - # TODO Remove the need for this multiplier - multiplier = MB.algebra_element(MB.sparse_coefficients(MP.term(true, MP.constant_monomial(g))), MB.implicit_basis(g.basis)) - MA.operate!(SA.UnsafeAddMul(*), p, multiplier, g) - MA.operate!(SA.canonical, p) - return MP.polynomial(SA.coeffs(p, MB.FullBasis{MB.Monomial,MP.monomial_type(g)}())) -end - -function change_basis( - p::GramMatrix{T,<:MB.SubBasis{B}}, - ::FullBasis{B}, -) where {T,B} - return p -end -function change_basis(p::GramMatrix, basis::SA.AbstractBasis) - return GramMatrix(MultivariateBases.change_basis(p.Q, p.basis, basis)...) -end - """ gram_operate(::typeof(+), p::GramMatrix, q::GramMatrix) @@ -220,13 +199,6 @@ function gram_operate( end return GramMatrix(Q, basis) end -function gram_operate( - ::typeof(+), - p::GramMatrix{S}, - q::GramMatrix{T}, -) where {S,T} - return gram_operate(+, p, change_basis(q, parent(p.basis))) -end """ gram_operate(/, p::GramMatrix, α) @@ -252,6 +224,8 @@ struct BlockDiagonalGramMatrix{T,B,U,MT} <: AbstractGramMatrix{T,B,U} blocks::Vector{GramMatrix{T,B,U,MT}} end +MB.implicit_basis(g::BlockDiagonalGramMatrix) = MB.implicit_basis(first(g.blocks)) + function MultivariateMoments.block_diagonal(blocks::Vector{<:GramMatrix}) return BlockDiagonalGramMatrix(blocks) end @@ -270,13 +244,12 @@ end function Base.zero(::Type{BlockDiagonalGramMatrix{T,B,U,MT}}) where {T,B,U,MT} return BlockDiagonalGramMatrix(GramMatrix{T,B,U,MT}[]) end -function MP.polynomial(p::BlockDiagonalGramMatrix) - return mapreduce( - identity, - MA.add!!, - p.blocks, - init = zero(MP.polynomial_type(p)), - ) + +function MA.operate!(op::SA.UnsafeAddMul{typeof(*)}, p::SA.AlgebraElement, g::BlockDiagonalGramMatrix, args::Vararg{Any,N}) where {N} + for block in g.blocks + MA.operate!(op, p, block, args...) + end + return p end function Base.show(io::IO, M::BlockDiagonalGramMatrix) @@ -284,3 +257,19 @@ function Base.show(io::IO, M::BlockDiagonalGramMatrix) MultivariateMoments.show_basis_indexed_blocks(io, M.blocks) return end + +#function Base.convert{T, PT <: AbstractPolynomial{T}}(::Type{PT}, p::GramMatrix) +# # coefficient_type(p) may be different than T and MP.polynomial(p) may be different than PT (different module) +# convert(PT, MP.polynomial(p)) +#end + +function MP.polynomial(p::Union{GramMatrix{T,B,U},BlockDiagonalGramMatrix{T,B,U}}) where {T,B,U} + return MP.polynomial(p, U) +end + +function MP.polynomial(g::Union{GramMatrix,BlockDiagonalGramMatrix}, ::Type{T}) where {T} + p = zero(T, SA.algebra(MB.implicit_basis(g))) + MA.operate!(SA.UnsafeAddMul(*), p, g) + MA.operate!(SA.canonical, SA.coeffs(p)) + return MP.polynomial(SA.coeffs(p, MB.FullBasis{MB.Monomial,MP.monomial_type(g)}())) +end diff --git a/src/sosdec.jl b/src/sosdec.jl index 78eed9cdb..6f09cc187 100644 --- a/src/sosdec.jl +++ b/src/sosdec.jl @@ -5,43 +5,39 @@ export SOSDecomposition, SOSDecompositionWithDomain, sos_decomposition Represents a Sum-of-Squares decomposition without domain. """ -struct SOSDecomposition{T,PT<:_APL{T},U} <: AbstractDecomposition{U} - ps::Vector{PT} - function SOSDecomposition{T,PT,U}(ps::Vector{PT}) where {T,PT,U} +struct SOSDecomposition{A,T,V,U} <: AbstractDecomposition{U} + ps::Vector{SA.AlgebraElement{A,T,V}} # TODO rename `elements` + function SOSDecomposition{A,T,V,U}(ps::Vector{SA.AlgebraElement{A,T,V}}) where {A,T,V,U} return new(ps) end end -function SOSDecomposition(ps::Vector{PT}) where {T,PT<:_APL{T}} - return SOSDecomposition{T,PT,_promote_add_mul(T)}(ps) +function SOSDecomposition(elements::Vector{SA.AlgebraElement{A,T,V}}) where {A,T,V} + return SOSDecomposition{A,T,V,_promote_add_mul(T)}(elements) end function MP.polynomial_type( - ::Union{SOSDecomposition{T,PT,U},Type{SOSDecomposition{T,PT,U}}}, -) where {T,PT,U} - return MP.polynomial_type(PT, U) + ::Union{SOSDecomposition{A,T,V,U},Type{SOSDecomposition{A,T,V,U}}}, +) where {A,T,V,U} + return MP.polynomial_type(MP.polynomial_type(SA.AlgebraElement{A,T,V}), U) end -#function SOSDecomposition(ps::Vector) -# T = reduce(promote_type, Int, map(eltype, ps)) -# SOSDecomposition{T}(ps) -#end - -function GramMatrix(p::SOSDecomposition{T}) where {T} - X = MP.merge_monomial_vectors(map(MP.monomials, p)) - m = length(p) - n = length(X) +function GramMatrix(p::SOSDecomposition{A,T}) where {A,T} + basis = mapreduce(SA.basis, (b1, b2) -> MB.merge_bases(b1, b2)[1], p.ps) + m = length(p.ps) + n = length(basis) Q = zeros(T, m, n) for i in 1:m j = 1 - for t in MP.terms(p[i]) - while X[j] != MP.monomial(t) + for (k, v) in SA.nonzero_pairs(SA.coeffs(p.ps[i])) + poly = SA.basis(p.ps[i])[k] + while j in eachindex(basis) && basis[j] != poly j += 1 end - Q[i, j] = MP.coefficient(t) + Q[i, j] = v j += 1 end end - return GramMatrix(Q' * Q, X) + return GramMatrix(Q' * Q, basis) end _lazy_adjoint(x::AbstractVector{<:Real}) = x @@ -52,21 +48,22 @@ function SOSDecomposition( ranktol = 0.0, dec::MultivariateMoments.LowRankLDLTAlgorithm = SVDLDLT(), ) - n = length(p.basis) # TODO LDL^T factorization for SDP is missing in Julia # it would be nice to have though - ldlt = - MultivariateMoments.low_rank_ldlt(Matrix(value_matrix(p)), dec, ranktol) + ldlt = MultivariateMoments.low_rank_ldlt( + Matrix(value_matrix(p)), + dec, + ranktol, + ) # The Sum-of-Squares decomposition is # ∑ adjoint(u_i) * u_i # and we have `L` of the LDL* so we need to take the adjoint. - ps = [ - MP.polynomial( + return SOSDecomposition(map(axes(ldlt.L, 2)) do i + MB.algebra_element( √ldlt.singular_values[i] * _lazy_adjoint(ldlt.L[:, i]), p.basis, - ) for i in axes(ldlt.L, 2) - ] - return SOSDecomposition(ps) + ) + end) end # Without LDL^T, we need to do float(T) #SOSDecomposition(p::GramMatrix{C, T}) where {C, T} = SOSDecomposition{C, float(T)}(p) diff --git a/test/Solvers/scs_tests.jl b/test/Solvers/scs_tests.jl index ecee40745..39e94b4fd 100644 --- a/test/Solvers/scs_tests.jl +++ b/test/Solvers/scs_tests.jl @@ -9,5 +9,5 @@ config = MOI.Test.Config(atol = 1e-3, rtol = 1e-3) # Tests.soc_test(factory, config) #end @testset "SDP" begin - Tests.sd_test(factory, config, include = ["sosdemo5_infeasible"]) + Tests.sd_test(factory, config, include = ["simple_matrix"]) end diff --git a/test/Tests/lyapunov_switched_system.jl b/test/Tests/lyapunov_switched_system.jl index ba043d52f..32a776bde 100644 --- a/test/Tests/lyapunov_switched_system.jl +++ b/test/Tests/lyapunov_switched_system.jl @@ -54,7 +54,7 @@ function lyapunov_switched_system_test( model, variable_type = SOSPoly(MB.SubBasis{basis}(monomials(x, degree))) ) - q = GramMatrix(SOSDecomposition(x .^ degree)) + q = GramMatrix(SOSDecomposition([MB.algebra_element([1], MB.SubBasis{basis}([var^degree])) for var in x])) # Keep `p` in a `GramMatrix` form while `q + p0` would transform it to # a polynomial. It is not mandatory to keep it in its `GramMatrix` form From f6e30aecd575f4828c2e3785f1e1757c616bc7e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 10 Jun 2024 18:24:48 +0200 Subject: [PATCH 51/84] SCS tests passing --- src/Certificate/Certificate.jl | 17 ++++-- src/Certificate/ideal.jl | 17 +++++- src/Certificate/newton_polytope.jl | 93 ++++++++++++++++++++++++------ test/Solvers/scs_tests.jl | 14 ++--- test/Tests/simple_matrix.jl | 2 +- 5 files changed, 112 insertions(+), 31 deletions(-) diff --git a/src/Certificate/Certificate.jl b/src/Certificate/Certificate.jl index 90aacdfc5..16359d5ae 100644 --- a/src/Certificate/Certificate.jl +++ b/src/Certificate/Certificate.jl @@ -45,14 +45,21 @@ function multiplier_basis_type end abstract type AbstractCertificate end +function within_total_bounds(mono::MP.AbstractMonomial, bounds::DegreeBounds) + return bounds.mindegree <= MP.degree(mono) <= bounds.maxdegree +end + +function within_variablewise_bounds(mono::MP.AbstractMonomial, bounds::DegreeBounds) + return MP.divides(bounds.variablewise_mindegree, mono) && + MP.divides(mono, bounds.variablewise_maxdegree) +end + +within_bounds(mono, bounds) = within_total_bounds(mono, bounds) && within_variablewise_bounds(mono, bounds) + function maxdegree_gram_basis(::MB.FullBasis{B}, bounds::DegreeBounds) where {B<:MB.AbstractMonomial} variables = MP.variables(bounds.variablewise_maxdegree) - function filter(mono) - return MP.divides(bounds.variablewise_mindegree, mono) && - MP.divides(mono, bounds.variablewise_maxdegree) - end return MB.SubBasis{B}( - MP.monomials(variables, bounds.mindegree:bounds.maxdegree, filter), + MP.monomials(variables, bounds.mindegree:bounds.maxdegree, Base.Fix2(within_variablewise_bounds, bounds)), ) end function maxdegree_gram_basis(basis::SA.AbstractBasis, bounds::DegreeBounds) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 828dcc4e4..aea3fd3fb 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -185,9 +185,24 @@ function Newton(cone, basis, variable_groups::Tuple) ) end +function _weight_type(::Type{T}, ::Type{BT}) where {T,BT} + return SA.AlgebraElement{ + MA.promote_operation( + SA.algebra, + MA.promote_operation(MB.implicit_basis, BT), + ), + T, + MA.promote_operation( + MB.sparse_coefficients, + MP.polynomial_type(MP.monomial_type(BT), T), + ), + } +end + function gram_basis(certificate::Newton, poly) a = _algebra_element(poly) - return half_newton_polytope(a, typeof(a)[], MP.variables(poly), _maxdegree(a), certificate.newton)[1] + vars = MP.variables(poly) + return half_newton_polytope(a, _weight_type(Bool, typeof(SA.basis(poly)))[], vars, _maxdegree(a, vars), certificate.newton)[1] end function gram_basis_type(::Type{<:Newton{C,B}}) where {C,B} diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index a4cb09936..a594d7615 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -91,9 +91,10 @@ function minus_shift(d::DegreeBounds, p::SA.AlgebraElement) if isnothing(var_maxdegree) return end + vars = MP.variables(d.variablewise_maxdegree) return DegreeBounds( - min_shift(d.mindegree, _mindegree(p)), - d.maxdegree - _maxdegree(p), + min_shift(d.mindegree, _mindegree(p, vars)), + d.maxdegree - _maxdegree(p, vars), var_mindegree, var_maxdegree, ) @@ -109,7 +110,7 @@ end _sign(a::Number) = sign(a) # Can be for instance a JuMP or MOI function so the sign can be anything -_sign(a) = missing +_sign(_) = missing function deg_sign(deg, p, d) sgn = nothing @@ -202,18 +203,19 @@ function deg_range(deg, p, gs, gram_deg, range::UnitRange) end #_mindegree(p::MP.AbstractPolynomialLike) = MP.mindegree(p) -_mindegree(a::SA.AlgebraElement) = minimum(_mindegree, SA.supp(a)) -_maxdegree(a::SA.AlgebraElement) = maximum(_maxdegree, SA.supp(a), init = 0) +_mindegree(a::SA.AlgebraElement, vars) = minimum(Base.Fix2(_mindegree, vars), SA.supp(a)) +_maxdegree(a::SA.AlgebraElement, vars) = maximum(Base.Fix2(_maxdegree, vars), SA.supp(a), init = 0) #_mindegree(basis::MB.SubBasis{MB.Monomial}) = MP.mindegree(basis.monomials) #_maxdegree(basis::MB.SubBasis{MB.Monomial}) = MP.maxdegree(basis.monomials) -function _mindegree(p::MB.Polynomial{B}) where {B} +function _mindegree(p::MB.Polynomial{B}, vars) where {B} if _is_monomial_basis(B) - MP.mindegree(p.monomial) + _sub_degree(p.monomial, vars) else error("TODO $B") end end -_maxdegree(p::MB.Polynomial) = MP.maxdegree(p.monomial) +_maxdegree(p::MB.Polynomial, vars) = _sub_degree(p.monomial, vars) +_sub_degree(mono, vars) = sum(var -> MP.degree(mono, var), vars) _is_monomial_basis(::Type{<:MB.AbstractMonomialIndexed}) = false _is_monomial_basis(::Type{<:Union{MB.Monomial,MB.ScaledMonomial}}) = true @@ -224,9 +226,10 @@ _is_monomial_basis(::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}) where {BT,B} function _multiplier_mindegree( mindegree, g::SA.AlgebraElement, + vars, ) if _is_monomial_basis(typeof(g)) - return _min_half(min_shift(mindegree, _mindegree(g))) + return _min_half(min_shift(mindegree, _mindegree(g, vars))) else # For instance, with `Chebyshev` a square already produces # a monomial of degree 0 so let's just give up here @@ -242,12 +245,12 @@ end # such that the maximum degree of `s * g` is at most `maxdegree`. # Here, we assume that the degree of `*(a::MB.Polynomial, b::MB.Polynomial)` # is bounded by the sum of the degree of `a` and `b` for any basis. -function _multiplier_maxdegree(maxdegree, g::SA.AlgebraElement) - return _max_half(maxdegree - _maxdegree(g)) +function _multiplier_maxdegree(maxdegree, g::SA.AlgebraElement, vars) + return _max_half(maxdegree - _maxdegree(g, vars)) end -function _multiplier_deg_range(range, g::SA.AlgebraElement) - return _multiplier_mindegree(minimum(range), g):_multiplier_maxdegree(maximum(range), g) +function _multiplier_deg_range(range, g::SA.AlgebraElement, vars) + return _multiplier_mindegree(minimum(range), g, vars):_multiplier_maxdegree(maximum(range), g, vars) end # Cheap approximation of the convex hull as the approximation of: @@ -277,18 +280,18 @@ function putinar_degree_bounds( ) mindegree = 0 # TODO homogeneous case - degrange(g) = _multiplier_deg_range(mindegree:maxdegree, g) + degrange(g) = _multiplier_deg_range(mindegree:maxdegree, g, vars) minus_degrange(g) = (-).(degrange(g)) # The multiplier will have degree `0:2fld(maxdegree - _maxdegree(g), 2)` mindegree = if _is_monomial_basis(typeof(p)) - -deg_range((-) ∘ _mindegree, p, gs, minus_degrange, -maxdegree:0) + -deg_range((-) ∘ Base.Fix2(_mindegree, vars), p, gs, minus_degrange, -maxdegree:0) else 0 end if isnothing(mindegree) return end - maxdegree = deg_range(_maxdegree, p, gs, degrange, 0:maxdegree) + maxdegree = deg_range(Base.Fix2(_maxdegree, vars), p, gs, degrange, 0:maxdegree) if isnothing(maxdegree) return end @@ -339,12 +342,68 @@ function multiplier_basis(g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, bounds::Degr end end +# Cartesian product of the newton polytopes of the different parts +function _cartesian_product(bases::Vector{<:MB.SubBasis{B}}, bounds) where {B} + monos = [b.monomials for b in bases] + basis = MB.SubBasis{B}(vec([prod(monos) for monos in Iterators.product(monos...)])) + # We know that the degree inequalities are satisfied variable-wise and + # part-wise but for all variables together so we filter with that + if isnothing(bounds) + return MB.empty_basis(typeof(basis)) + else + return MB.SubBasis{B}(filter(Base.Fix2(within_bounds, _half(bounds)), basis.monomials)) + end +end + +function half_newton_polytope( + p::SA.AlgebraElement{<:MB.Algebra{BT,B,M}}, + gs::AbstractVector{<:SA.AlgebraElement}, + vars, + maxdegree, + newton::NewtonDegreeBounds, +) where {BT,B,M} + if !is_commutative(vars) + throw( + ArgumentError( + "Multipartite Newton polytope not supported with noncommutative variables.", + ), + ) + end + parts = newton.variable_groups + if !all( + i -> all(j -> i == j || isempty(parts[i] ∩ parts[j]), eachindex(parts)), + eachindex(parts), + ) + throw( + ArgumentError( + "Parts are not disjoint in multipartite Newton polytope estimation: $parts.", + ), + ) + end + # Some variables might be in no part... + missing_vars = setdiff(vars, reduce(union, parts)) + if isempty(missing_vars) + all_parts = parts + else + # in that case, create a part with the missing ones + all_parts = (parts..., missing_vars) + end + if length(all_parts) == 1 + # all variables on same part, fallback to shortcut + return half_newton_polytope(p, gs, vars, maxdegree, NewtonDegreeBounds(tuple())) + end + bases = map(part -> half_newton_polytope(p, gs, part, maxdegree, NewtonDegreeBounds(tuple())), all_parts) + bounds = putinar_degree_bounds(p, gs, vars, maxdegree) + return _cartesian_product([b[1] for b in bases], bounds), + [_cartesian_product([b[2][i] for b in bases], minus_shift(bounds, gs[i])) for i in eachindex(first(bases)[2])] +end + function half_newton_polytope( p::SA.AlgebraElement{<:MB.Algebra{BT,B,M}}, gs::AbstractVector{<:SA.AlgebraElement}, vars, maxdegree, - ::NewtonDegreeBounds, + ::NewtonDegreeBounds{Tuple{}}, ) where {BT,B,M} # TODO take `variable_groups` into account bounds = putinar_degree_bounds(p, gs, vars, maxdegree) diff --git a/test/Solvers/scs_tests.jl b/test/Solvers/scs_tests.jl index 39e94b4fd..bf6ced18a 100644 --- a/test/Solvers/scs_tests.jl +++ b/test/Solvers/scs_tests.jl @@ -2,12 +2,12 @@ include("solver_preamble.jl") import SCS factory = optimizer_with_attributes(SCS.Optimizer, MOI.Silent() => true) config = MOI.Test.Config(atol = 1e-3, rtol = 1e-3) -#@testset "Linear" begin -# Tests.linear_test(factory, config) -#end -#@testset "SOC" begin -# Tests.soc_test(factory, config) -#end +@testset "Linear" begin + Tests.linear_test(factory, config) +end +@testset "SOC" begin + Tests.soc_test(factory, config) +end @testset "SDP" begin - Tests.sd_test(factory, config, include = ["simple_matrix"]) + Tests.sd_test(factory, config) end diff --git a/test/Tests/simple_matrix.jl b/test/Tests/simple_matrix.jl index 92b3fcfc6..b925e41d0 100644 --- a/test/Tests/simple_matrix.jl +++ b/test/Tests/simple_matrix.jl @@ -9,7 +9,7 @@ Example 3.77 and 3.79 of Blekherman, G., Parrilo, P. A., & Thomas, R. R. (Eds.). Semidefinite optimization and convex algebraic geometry SIAM 2013 """ -function simple_matrix_test(optimizer, config::MOI.Test.Config) +function simple_matrix_test(optimizer, ::MOI.Test.Config) @polyvar x P = [x^2-2x+2 x; x x^2] From 72a2798e7f2d04ceb3a61bd35043a89a07a9fe7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 10 Jun 2024 18:34:08 +0200 Subject: [PATCH 52/84] Simplifty --- src/Certificate/ideal.jl | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index aea3fd3fb..67efe27ae 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -23,23 +23,8 @@ function _combine_with_gram( for mono in basis MA.operate!(SA.UnsafeAddMul(*), p, _NonZero(), MB.algebra_element(mono)) end - # TODO, could refactor with - # MA.operate!(SA.UnsafeAddMul(*), p, GramMatrix(_ -> _NonZero(), gram), weight) for (gram, weight) in zip(gram_bases, weights) - for col in gram - for row in gram - for mono in SA.supp(weight) - MA.operate!( - SA.UnsafeAddMul(*), - p, - _NonZero(), - MB.algebra_element(SA.star(row)), - MB.algebra_element(col), - MB.algebra_element(mono), - ) - end - end - end + MA.operate!(SA.UnsafeAddMul(*), p, GramMatrix{_NonZero}((_, _) -> _NonZero(), gram), weight) end MA.operate!(SA.canonical, SA.coeffs(p)) return MB.SubBasis{B}(keys(SA.coeffs(p))) From eafd066631c6eee637867dd0c82b6066ea735aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 10 Jun 2024 19:29:54 +0200 Subject: [PATCH 53/84] Fixes --- src/Bridges/Constraint/sos_polynomial.jl | 12 +----- src/Certificate/Certificate.jl | 5 +++ src/Certificate/newton_polytope.jl | 22 ++++------ test/certificate.jl | 54 ++++++++++++++++-------- 4 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 450f07a52..c84d5dcc7 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -115,17 +115,7 @@ function MOI.Bridges.Constraint.concrete_bridge_type( # promotes VectorOfVariables into VectorAffineFunction, it should be enough # for most use cases M = SOS.matrix_cone_type(CT) - W = SA.AlgebraElement{ - MA.promote_operation( - SA.algebra, - MA.promote_operation(MB.implicit_basis, BT), - ), - T, - MA.promote_operation( - MB.sparse_coefficients, - MP.polynomial_type(MP.monomial_type(BT), T), - ), - } + W = SOS.Certificate._weight_type(T, BT) B = MA.promote_operation( SOS.Certificate.reduced_basis, CT, diff --git a/src/Certificate/Certificate.jl b/src/Certificate/Certificate.jl index 16359d5ae..427e2d183 100644 --- a/src/Certificate/Certificate.jl +++ b/src/Certificate/Certificate.jl @@ -62,6 +62,11 @@ function maxdegree_gram_basis(::MB.FullBasis{B}, bounds::DegreeBounds) where {B< MP.monomials(variables, bounds.mindegree:bounds.maxdegree, Base.Fix2(within_variablewise_bounds, bounds)), ) end + +function maxdegree_gram_basis(basis::SA.AbstractBasis, ::Nothing) + return MB.empty_basis(MB.explicit_basis_type(typeof(basis))) +end + function maxdegree_gram_basis(basis::SA.AbstractBasis, bounds::DegreeBounds) # TODO use bounds here too variables = MP.variables(bounds.variablewise_maxdegree) diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index a594d7615..b07f484b3 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -37,6 +37,7 @@ end _min_half(d::Integer) = cld(d, 2) _max_half(d::Integer) = fld(d, 2) +_half(::Nothing) = nothing function _half(d::DegreeBounds) return DegreeBounds( _min_half(d.mindegree), @@ -284,7 +285,8 @@ function putinar_degree_bounds( minus_degrange(g) = (-).(degrange(g)) # The multiplier will have degree `0:2fld(maxdegree - _maxdegree(g), 2)` mindegree = if _is_monomial_basis(typeof(p)) - -deg_range((-) ∘ Base.Fix2(_mindegree, vars), p, gs, minus_degrange, -maxdegree:0) + d = deg_range((-) ∘ Base.Fix2(_mindegree, vars), p, gs, minus_degrange, -maxdegree:0) + isnothing(d) ? d : -d else 0 end @@ -328,18 +330,10 @@ function putinar_degree_bounds( end function multiplier_basis(g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, bounds::DegreeBounds) where {BT,B} - shifted = minus_shift(bounds, g) - if isnothing(shifted) - halved = nothing - else - halved = _half(shifted) - end - basis = MB.FullBasis{B,MP.monomial_type(typeof(g))}() - if isnothing(halved) - return MB.empty_basis(MB.explicit_basis_type(typeof(basis))) - else - return maxdegree_gram_basis(basis, halved) - end + return maxdegree_gram_basis( + MB.FullBasis{B,MP.monomial_type(typeof(g))}(), + _half(minus_shift(bounds, g)), + ) end # Cartesian product of the newton polytopes of the different parts @@ -637,7 +631,7 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos ) for w in SA.supp(cache) if ismissing(_sign(SA.coeffs(counter)[SA.basis(counter)[w]])) - push!(get(back, w, Tuple{Int,Int}[]), (i, j)) + push!(get!(back, w, Tuple{Int,Int}[]), (i, j)) else delete(i, j) end diff --git a/test/certificate.jl b/test/certificate.jl index 514fc7708..b85a892a7 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -25,6 +25,24 @@ end @test MP.variables(v) == [a, b, a] end +function _monomials_half_newton_polytope(_monos, filter) + monos = MP.monomial_vector(_monos) + basis = MB.FullBasis{MB.Monomial,eltype(monos)}() + return SumOfSquares.Certificate.half_newton_polytope( + MB.algebra_element( + SA.SparseCoefficients( + monos, + ones(length(monos)), + ), + basis, + ), + SumOfSquares.Certificate._weight_type(Bool, typeof(basis))[], + MP.variables(monos), + MP.maxdegree(monos), + filter, + )[1].monomials +end + @testset "Monomial selection for certificate" begin @polyvar x y z @ncpolyvar a b @@ -33,7 +51,7 @@ end err = ArgumentError( "Multipartite Newton polytope not supported with noncommutative variables.", ) - @test_throws err SumOfSquares.Certificate.monomials_half_newton_polytope( + @test_throws err _monomials_half_newton_polytope( [a * b, b^2], Certificate.NewtonDegreeBounds(parts), ) @@ -46,50 +64,50 @@ end err = ArgumentError( "Parts are not disjoint in multipartite Newton polytope estimation: $parts.", ) - @test_throws err SumOfSquares.Certificate.monomials_half_newton_polytope( + @test_throws err _monomials_half_newton_polytope( [x * y, y^2], Certificate.NewtonDegreeBounds(parts), ) end uni = Certificate.NewtonDegreeBounds(tuple()) @testset "Unipartite" begin - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( [x * y, y^2], uni, ) == [y] @test isempty( - SumOfSquares.Certificate.monomials_half_newton_polytope( + _monomials_half_newton_polytope( [x, y], uni, ), ) - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( [x^2, y^2], uni, ) == [x, y] - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( [x^2, y^2], Certificate.NewtonDegreeBounds(([x, y],)), ) == [x, y] - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( [x^2, y^2], Certificate.NewtonDegreeBounds(([y, x],)), ) == [x, y] - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( [x^2, x^3 * y^2, x^4 * y^4], uni, ) == [x^2 * y^2, x, x * y, x^2, x * y^2, x^2 * y] - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( [x^2, x^3 * y^2, x^4 * y^4], Certificate.NewtonFilter(uni), ) == [x^2 * y^2, x] end @testset "Non-commutative" begin - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( [a^4, a^3 * b, a * b * a^2, a * b * a * b], uni, ) == [a^2, a * b] - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( [ a^2, a^10 * b^20 * a^11, @@ -102,26 +120,26 @@ end @testset "Multipartite" begin # In the part [y, z], the degree is between 0 and 2 X = [x^4, x^2 * y^2, x^2 * z^2, x^2 * y * z, y * z] - @test SumOfSquares.Certificate.monomials_half_newton_polytope(X, uni) == + @test _monomials_half_newton_polytope(X, uni) == [x^2, x * y, x * z, y * z, x, y, z] function full_test(X, Y, part1, part2) - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part1,)), ) == Y - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part2,)), ) == Y - a = SumOfSquares.Certificate.monomials_half_newton_polytope( + a = _monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part2,)), ) - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part1, part2)), ) == Y - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part2, part1)), ) == Y @@ -140,7 +158,7 @@ end [x, y], ) # FIXME: With recursive merging, it should give [x^2, x*y, x*z, x] - @test SumOfSquares.Certificate.monomials_half_newton_polytope( + @test _monomials_half_newton_polytope( [x^4, x^2 * y^2, x^2 * z^2, x^2 * y * z, y * z], Certificate.NewtonDegreeBounds(([x], [y], [z])), ) == [x^2, x * y, x * z, y * z, x, y, z] From f297da6f06c97f97fff6d3f4d6cc3174bf05a451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 11 Jun 2024 15:03:51 +0200 Subject: [PATCH 54/84] Fix format --- src/Bridges/Constraint/sos_polynomial.jl | 4 +- .../sos_polynomial_in_semialgebraic_set.jl | 5 +- src/Bridges/Variable/kernel.jl | 12 +- src/Certificate/Certificate.jl | 23 +++- src/Certificate/Sparsity/ideal.jl | 5 +- src/Certificate/Symmetry/wedderburn.jl | 4 +- src/Certificate/ideal.jl | 21 ++- src/Certificate/newton_polytope.jl | 126 ++++++++++++++---- src/Certificate/preorder.jl | 8 +- src/constraints.jl | 58 ++++++-- src/gram_matrix.jl | 31 ++++- src/sosdec.jl | 29 ++-- test/Tests/lyapunov_switched_system.jl | 7 +- test/Tests/quadratic.jl | 10 +- test/certificate.jl | 28 +--- test/csp_test.jl | 16 ++- 16 files changed, 273 insertions(+), 114 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index c84d5dcc7..f622c14ea 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -104,8 +104,8 @@ function MOI.supports_constraint( return MOI.Utilities.is_coefficient_type(F, T) end -_eltype(::Type{Vector{T}}) where T = T -_eltype(::Type{T}) where T = T +_eltype(::Type{Vector{T}}) where {T} = T +_eltype(::Type{T}) where {T} = T function MOI.Bridges.Constraint.concrete_bridge_type( ::Type{<:SOSPolynomialBridge{T}}, diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index d4eec5e87..ae74548fc 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -51,7 +51,10 @@ function MOI.Bridges.Constraint.bridge_constraint( # The monomials may be copied by MA however so we need to copy it. # TODO remove `collect` when `DynamicPolynomials.MonomialVector` can be used as keys p = MB.algebra_element( - SA.SparseCoefficients(copy(collect(set.basis.monomials)), MOI.Utilities.scalarize(f)), + SA.SparseCoefficients( + copy(collect(set.basis.monomials)), + MOI.Utilities.scalarize(f), + ), MB.implicit_basis(set.basis), ) λ_bases = B[] diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index 83cd97cfd..1bd6a2bc2 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -12,7 +12,10 @@ function MOI.Bridges.Variable.bridge_constrained_variable( ) where {T,M} variables = Vector{MOI.VariableIndex}[] constraints = MOI.ConstraintIndex{MOI.VectorOfVariables}[] - acc = zero(MOI.ScalarAffineFunction{T}, SA.algebra(MB.implicit_basis(set.basis))) + acc = zero( + MOI.ScalarAffineFunction{T}, + SA.algebra(MB.implicit_basis(set.basis)), + ) for (gram_basis, weight) in zip(set.gram_bases, set.weights) gram, vars, con = SOS.add_gram_matrix(model, M, gram_basis, T) push!(variables, vars) @@ -20,7 +23,12 @@ function MOI.Bridges.Variable.bridge_constrained_variable( MA.operate!(SA.UnsafeAddMul(*), acc, gram, weight) end MA.operate!(SA.canonical, SA.coeffs(acc)) - return KernelBridge{T,M}(SA.coeffs(acc, set.basis), variables, constraints, set) + return KernelBridge{T,M}( + SA.coeffs(acc, set.basis), + variables, + constraints, + set, + ) end function MOI.Bridges.Variable.supports_constrained_variable( diff --git a/src/Certificate/Certificate.jl b/src/Certificate/Certificate.jl index 427e2d183..c61d04055 100644 --- a/src/Certificate/Certificate.jl +++ b/src/Certificate/Certificate.jl @@ -49,17 +49,30 @@ function within_total_bounds(mono::MP.AbstractMonomial, bounds::DegreeBounds) return bounds.mindegree <= MP.degree(mono) <= bounds.maxdegree end -function within_variablewise_bounds(mono::MP.AbstractMonomial, bounds::DegreeBounds) +function within_variablewise_bounds( + mono::MP.AbstractMonomial, + bounds::DegreeBounds, +) return MP.divides(bounds.variablewise_mindegree, mono) && - MP.divides(mono, bounds.variablewise_maxdegree) + MP.divides(mono, bounds.variablewise_maxdegree) end -within_bounds(mono, bounds) = within_total_bounds(mono, bounds) && within_variablewise_bounds(mono, bounds) +function within_bounds(mono, bounds) + return within_total_bounds(mono, bounds) && + within_variablewise_bounds(mono, bounds) +end -function maxdegree_gram_basis(::MB.FullBasis{B}, bounds::DegreeBounds) where {B<:MB.AbstractMonomial} +function maxdegree_gram_basis( + ::MB.FullBasis{B}, + bounds::DegreeBounds, +) where {B<:MB.AbstractMonomial} variables = MP.variables(bounds.variablewise_maxdegree) return MB.SubBasis{B}( - MP.monomials(variables, bounds.mindegree:bounds.maxdegree, Base.Fix2(within_variablewise_bounds, bounds)), + MP.monomials( + variables, + bounds.mindegree:bounds.maxdegree, + Base.Fix2(within_variablewise_bounds, bounds), + ), ) end diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index d44ab1dcd..36a7f7d4e 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -57,10 +57,7 @@ function sparsity( return MB.SubBasis{MB.Monomial}.(sparsity(monos, sp, gram_basis.monomials)) end # Backward compatibility, we may remove this at some time -function sparsity( - p::MP.AbstractPolynomialLike, - args... -) +function sparsity(p::MP.AbstractPolynomialLike, args...) return sparsity(MB.SubBasis{MB.Monomial}(MP.monomials(p)), args...) end function sparsity( diff --git a/src/Certificate/Symmetry/wedderburn.jl b/src/Certificate/Symmetry/wedderburn.jl index a8b278a62..3cb73a757 100644 --- a/src/Certificate/Symmetry/wedderburn.jl +++ b/src/Certificate/Symmetry/wedderburn.jl @@ -75,9 +75,7 @@ end function SumOfSquares.matrix_cone_type(::Type{<:Ideal{C}}) where {C} return SumOfSquares.matrix_cone_type(C) end -function SumOfSquares.Certificate.gram_basis_type( - ::Type{<:Ideal}, -) +function SumOfSquares.Certificate.gram_basis_type(::Type{<:Ideal}) return Vector{Vector{MB.FixedPolynomialBasis}} end SumOfSquares.Certificate.zero_basis_type(::Type{<:Ideal}) = MB.Monomial diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 67efe27ae..1f3b3bb81 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -24,7 +24,12 @@ function _combine_with_gram( MA.operate!(SA.UnsafeAddMul(*), p, _NonZero(), MB.algebra_element(mono)) end for (gram, weight) in zip(gram_bases, weights) - MA.operate!(SA.UnsafeAddMul(*), p, GramMatrix{_NonZero}((_, _) -> _NonZero(), gram), weight) + MA.operate!( + SA.UnsafeAddMul(*), + p, + GramMatrix{_NonZero}((_, _) -> _NonZero(), gram), + weight, + ) end MA.operate!(SA.canonical, SA.coeffs(p)) return MB.SubBasis{B}(keys(SA.coeffs(p))) @@ -187,7 +192,13 @@ end function gram_basis(certificate::Newton, poly) a = _algebra_element(poly) vars = MP.variables(poly) - return half_newton_polytope(a, _weight_type(Bool, typeof(SA.basis(poly)))[], vars, _maxdegree(a, vars), certificate.newton)[1] + return half_newton_polytope( + a, + _weight_type(Bool, typeof(SA.basis(poly)))[], + vars, + _maxdegree(a, vars), + certificate.newton, + )[1] end function gram_basis_type(::Type{<:Newton{C,B}}) where {C,B} @@ -220,11 +231,7 @@ function _rem(coeffs, basis::MB.FullBasis{MB.Monomial}, I) return MB.algebra_element(MB.sparse_coefficients(r), basis) end -function reduced_polynomial( - ::Remainder, - a::SA.AlgebraElement, - domain, -) +function reduced_polynomial(::Remainder, a::SA.AlgebraElement, domain) return _rem(SA.coeffs(a), SA.basis(a), ideal(domain)) end diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index b07f484b3..2a0443e0f 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -10,7 +10,6 @@ struct NewtonDegreeBounds{NPT} <: AbstractNewtonPolytopeApproximation variable_groups::NPT end - # Filters out points ouside the Newton polytope from the # outer approximation given by `outer_approximation`. struct NewtonFilter{N<:AbstractNewtonPolytopeApproximation} <: @@ -204,8 +203,12 @@ function deg_range(deg, p, gs, gram_deg, range::UnitRange) end #_mindegree(p::MP.AbstractPolynomialLike) = MP.mindegree(p) -_mindegree(a::SA.AlgebraElement, vars) = minimum(Base.Fix2(_mindegree, vars), SA.supp(a)) -_maxdegree(a::SA.AlgebraElement, vars) = maximum(Base.Fix2(_maxdegree, vars), SA.supp(a), init = 0) +function _mindegree(a::SA.AlgebraElement, vars) + return minimum(Base.Fix2(_mindegree, vars), SA.supp(a)) +end +function _maxdegree(a::SA.AlgebraElement, vars) + return maximum(Base.Fix2(_maxdegree, vars), SA.supp(a), init = 0) +end #_mindegree(basis::MB.SubBasis{MB.Monomial}) = MP.mindegree(basis.monomials) #_maxdegree(basis::MB.SubBasis{MB.Monomial}) = MP.maxdegree(basis.monomials) function _mindegree(p::MB.Polynomial{B}, vars) where {B} @@ -220,15 +223,15 @@ _sub_degree(mono, vars) = sum(var -> MP.degree(mono, var), vars) _is_monomial_basis(::Type{<:MB.AbstractMonomialIndexed}) = false _is_monomial_basis(::Type{<:Union{MB.Monomial,MB.ScaledMonomial}}) = true -_is_monomial_basis(::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}) where {BT,B} = _is_monomial_basis(B) +function _is_monomial_basis( + ::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}, +) where {BT,B} + return _is_monomial_basis(B) +end # Minimum degree of a gram basis for a gram matrix `s` # such that the minimum degree of `s * g` is at least `mindegree`. -function _multiplier_mindegree( - mindegree, - g::SA.AlgebraElement, - vars, -) +function _multiplier_mindegree(mindegree, g::SA.AlgebraElement, vars) if _is_monomial_basis(typeof(g)) return _min_half(min_shift(mindegree, _mindegree(g, vars))) else @@ -251,7 +254,11 @@ function _multiplier_maxdegree(maxdegree, g::SA.AlgebraElement, vars) end function _multiplier_deg_range(range, g::SA.AlgebraElement, vars) - return _multiplier_mindegree(minimum(range), g, vars):_multiplier_maxdegree(maximum(range), g, vars) + return _multiplier_mindegree(minimum(range), g, vars):_multiplier_maxdegree( + maximum(range), + g, + vars, + ) end # Cheap approximation of the convex hull as the approximation of: @@ -285,7 +292,13 @@ function putinar_degree_bounds( minus_degrange(g) = (-).(degrange(g)) # The multiplier will have degree `0:2fld(maxdegree - _maxdegree(g), 2)` mindegree = if _is_monomial_basis(typeof(p)) - d = deg_range((-) ∘ Base.Fix2(_mindegree, vars), p, gs, minus_degrange, -maxdegree:0) + d = deg_range( + (-) ∘ Base.Fix2(_mindegree, vars), + p, + gs, + minus_degrange, + -maxdegree:0, + ) isnothing(d) ? d : -d else 0 @@ -293,7 +306,8 @@ function putinar_degree_bounds( if isnothing(mindegree) return end - maxdegree = deg_range(Base.Fix2(_maxdegree, vars), p, gs, degrange, 0:maxdegree) + maxdegree = + deg_range(Base.Fix2(_maxdegree, vars), p, gs, degrange, 0:maxdegree) if isnothing(maxdegree) return end @@ -329,7 +343,10 @@ function putinar_degree_bounds( ) end -function multiplier_basis(g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, bounds::DegreeBounds) where {BT,B} +function multiplier_basis( + g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, + bounds::DegreeBounds, +) where {BT,B} return maxdegree_gram_basis( MB.FullBasis{B,MP.monomial_type(typeof(g))}(), _half(minus_shift(bounds, g)), @@ -339,13 +356,17 @@ end # Cartesian product of the newton polytopes of the different parts function _cartesian_product(bases::Vector{<:MB.SubBasis{B}}, bounds) where {B} monos = [b.monomials for b in bases] - basis = MB.SubBasis{B}(vec([prod(monos) for monos in Iterators.product(monos...)])) + basis = MB.SubBasis{B}( + vec([prod(monos) for monos in Iterators.product(monos...)]), + ) # We know that the degree inequalities are satisfied variable-wise and # part-wise but for all variables together so we filter with that if isnothing(bounds) return MB.empty_basis(typeof(basis)) else - return MB.SubBasis{B}(filter(Base.Fix2(within_bounds, _half(bounds)), basis.monomials)) + return MB.SubBasis{B}( + filter(Base.Fix2(within_bounds, _half(bounds)), basis.monomials), + ) end end @@ -384,12 +405,32 @@ function half_newton_polytope( end if length(all_parts) == 1 # all variables on same part, fallback to shortcut - return half_newton_polytope(p, gs, vars, maxdegree, NewtonDegreeBounds(tuple())) + return half_newton_polytope( + p, + gs, + vars, + maxdegree, + NewtonDegreeBounds(tuple()), + ) end - bases = map(part -> half_newton_polytope(p, gs, part, maxdegree, NewtonDegreeBounds(tuple())), all_parts) + bases = map( + part -> half_newton_polytope( + p, + gs, + part, + maxdegree, + NewtonDegreeBounds(tuple()), + ), + all_parts, + ) bounds = putinar_degree_bounds(p, gs, vars, maxdegree) return _cartesian_product([b[1] for b in bases], bounds), - [_cartesian_product([b[2][i] for b in bases], minus_shift(bounds, gs[i])) for i in eachindex(first(bases)[2])] + [ + _cartesian_product( + [b[2][i] for b in bases], + minus_shift(bounds, gs[i]), + ) for i in eachindex(first(bases)[2]) + ] end function half_newton_polytope( @@ -403,7 +444,9 @@ function half_newton_polytope( bounds = putinar_degree_bounds(p, gs, vars, maxdegree) full = MB.FullBasis{B,M}() return maxdegree_gram_basis(full, _half(bounds)), - MB.explicit_basis_type(typeof(full))[multiplier_basis(g, bounds) for g in gs] + MB.explicit_basis_type(typeof(full))[ + multiplier_basis(g, bounds) for g in gs + ] end function half_newton_polytope( @@ -415,7 +458,12 @@ function half_newton_polytope( ) return half_newton_polytope( p, - [MB.algebra_element(MP.coefficients(g), MB.SubBasis{MB.Monomial}(MP.monomials(g))) for g in gs], + [ + MB.algebra_element( + MP.coefficients(g), + MB.SubBasis{MB.Monomial}(MP.monomials(g)), + ) for g in gs + ], vars, maxdegree, filter, @@ -429,11 +477,18 @@ function half_newton_polytope( maxdegree, filter::NewtonFilter{<:NewtonDegreeBounds}, ) - basis, multipliers_bases = half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) + basis, multipliers_bases = + half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) bases = copy(multipliers_bases) push!(bases, basis) gs = copy(gs) - push!(gs, MB.constant_algebra_element(MA.promote_operation(SA.basis, eltype(gs)), eltype(eltype(gs)))) + push!( + gs, + MB.constant_algebra_element( + MA.promote_operation(SA.basis, eltype(gs)), + eltype(eltype(gs)), + ), + ) filtered_bases = post_filter(p, gs, bases) # The last one will be recomputed by the ideal certificate return filtered_bases[end], filtered_bases[1:(end-1)] @@ -468,10 +523,13 @@ function _sign(c::SignCount) end function Base.:+(a::SignCount, b::SignCount) - return SignCount(a.unknown + b.unknown, a.positive + b.positive, a.negative + b.negative) + return SignCount( + a.unknown + b.unknown, + a.positive + b.positive, + a.negative + b.negative, + ) end - function Base.:+(c::SignCount, a::SignChange{Missing}) @assert c.unknown >= -a.Δ return SignCount(c.unknown + a.Δ, c.positive, c.negative) @@ -508,7 +566,7 @@ function increase(cache, counter, generator_sign, monos, mult) MA.operate!( SA.UnsafeAddMul(*), counter, - SignChange((a != b) ? missing : generator_sign, 1,), + SignChange((a != b) ? missing : generator_sign, 1), cache, ) end @@ -556,11 +614,18 @@ end # [L23] Legat, Benoît # *Exploiting the Structure of a Polynomial Optimization Problem* # SIAM Conference on Applications of Dynamical Systems, 2023 -function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos) +function post_filter( + poly::SA.AlgebraElement, + generators, + multipliers_gram_monos, +) # We use `_DictCoefficients` instead `SA.SparseCoefficients` because # we need to keep it canonicalized (without duplicate actually) # and don't care about the list of monomials being ordered - counter = MB.algebra_element(_DictCoefficients(Dict{MP.monomial_type(typeof(poly)),SignCount}()), MB.implicit_basis(SA.basis(poly))) + counter = MB.algebra_element( + _DictCoefficients(Dict{MP.monomial_type(typeof(poly)),SignCount}()), + MB.implicit_basis(SA.basis(poly)), + ) cache = zero(Float64, SA.algebra(MB.implicit_basis(SA.basis(poly)))) for (mono, v) in SA.nonzero_pairs(poly) MA.operate!( @@ -589,7 +654,8 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos count_sign = _sign(count) # This means the `counter` has a sign and it didn't have a sign before # so we need to delete back edges - if !ismissing(count_sign) && (ismissing(count) || count != count_sign) + if !ismissing(count_sign) && + (ismissing(count) || count != count_sign) # TODO could see later if deleting the counter improves perf if haskey(back, mono) for (i, j) in back[mono] @@ -630,7 +696,9 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos MB.algebra_element(mono), ) for w in SA.supp(cache) - if ismissing(_sign(SA.coeffs(counter)[SA.basis(counter)[w]])) + if ismissing( + _sign(SA.coeffs(counter)[SA.basis(counter)[w]]), + ) push!(get!(back, w, Tuple{Int,Int}[]), (i, j)) else delete(i, j) diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index c1c707325..046497329 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -93,7 +93,13 @@ function with_fixed_basis( v = with_variables(domain, p) return WithFixedBases( v.inner, - half_newton_polytope(p, SemialgebraicSets.inequalities(domain), v.variables, maxdegree, newton)[2], + half_newton_polytope( + p, + SemialgebraicSets.inequalities(domain), + v.variables, + maxdegree, + newton, + )[2], ) end diff --git a/src/constraints.jl b/src/constraints.jl index 910828ca8..c5489ecc4 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -151,7 +151,10 @@ function default_ideal_certificate( end function default_ideal_certificate(domain::BasicSemialgebraicSet, args...) - return default_ideal_certificate(SemialgebraicSets.algebraic_set(domain), args...) + return default_ideal_certificate( + SemialgebraicSets.algebraic_set(domain), + args..., + ) end function default_certificate( @@ -226,7 +229,10 @@ function _maxdegree(domain::AlgebraicSet) end function _maxdegree(domain::BasicSemialgebraicSet) - return max(_max_maxdegree(SemialgebraicSets.inequalities(domain)), _maxdegree(SemialgebraicSets.algebraic_set(domain))) + return max( + _max_maxdegree(SemialgebraicSets.inequalities(domain)), + _maxdegree(SemialgebraicSets.algebraic_set(domain)), + ) end """ @@ -362,28 +368,54 @@ end _promote_coef_type(::Type{V}, ::Type) where {V<:JuMP.AbstractVariableRef} = V _promote_coef_type(::Type{F}, ::Type{T}) where {F,T} = promote_type(F, T) -function _default_basis(coeffs, basis::MB.SubBasis{B}, gram_basis::MB.MonomialIndexedBasis{G}) where {B,G} +function _default_basis( + coeffs, + basis::MB.SubBasis{B}, + gram_basis::MB.MonomialIndexedBasis{G}, +) where {B,G} if B === G return coeffs, basis else - return _default_basis(SA.SparseCoefficients(basis.monomials, coeffs), MB.implicit_basis(basis), gram_basis) + return _default_basis( + SA.SparseCoefficients(basis.monomials, coeffs), + MB.implicit_basis(basis), + gram_basis, + ) end end -function _default_basis(p::SA.AbstractCoefficients, basis::MB.FullBasis{B}, gram_basis::MB.MonomialIndexedBasis{G}) where {B,G} +function _default_basis( + p::SA.AbstractCoefficients, + basis::MB.FullBasis{B}, + gram_basis::MB.MonomialIndexedBasis{G}, +) where {B,G} if B === G - return _default_basis(collect(SA.values(p)), MB.SubBasis{B}(collect(SA.keys(p))), gram_basis) + return _default_basis( + collect(SA.values(p)), + MB.SubBasis{B}(collect(SA.keys(p))), + gram_basis, + ) else new_basis = MB.FullBasis{G,MP.monomial_type(typeof(basis))}() - return _default_basis(SA.coeffs(p, basis, new_basis), new_basis, gram_basis) + return _default_basis( + SA.coeffs(p, basis, new_basis), + new_basis, + gram_basis, + ) end end -function _default_gram_basis(::MB.MonomialIndexedBasis{B,M}, ::Nothing) where {B,M} +function _default_gram_basis( + ::MB.MonomialIndexedBasis{B,M}, + ::Nothing, +) where {B,M} return MB.FullBasis{B,M}() end -function _default_gram_basis(::MB.MonomialIndexedBasis{_B,M}, ::Type{B}) where {_B,B,M} +function _default_gram_basis( + ::MB.MonomialIndexedBasis{_B,M}, + ::Type{B}, +) where {_B,B,M} return MB.FullBasis{B,M}() end @@ -398,7 +430,13 @@ function _default_basis(a::SA.AlgebraElement, basis) end function _default_basis(p::MP.AbstractPolynomialLike, basis) - return _default_basis(MB.algebra_element(MB.sparse_coefficients(p), MB.FullBasis{MB.Monomial,MP.monomial_type(p)}()), basis) + return _default_basis( + MB.algebra_element( + MB.sparse_coefficients(p), + MB.FullBasis{MB.Monomial,MP.monomial_type(p)}(), + ), + basis, + ) end function JuMP.build_constraint( diff --git a/src/gram_matrix.jl b/src/gram_matrix.jl index 105a359d6..66ac66376 100644 --- a/src/gram_matrix.jl +++ b/src/gram_matrix.jl @@ -144,7 +144,12 @@ function _term_element(α, p::MB.Polynomial{B,M}) where {B,M} ) end -function MA.operate!(op::SA.UnsafeAddMul{typeof(*)}, p::SA.AlgebraElement, g::GramMatrix, args::Vararg{Any,N}) where {N} +function MA.operate!( + op::SA.UnsafeAddMul{typeof(*)}, + p::SA.AlgebraElement, + g::GramMatrix, + args::Vararg{Any,N}, +) where {N} for col in eachindex(g.basis) for row in eachindex(g.basis) MA.operate!( @@ -224,7 +229,9 @@ struct BlockDiagonalGramMatrix{T,B,U,MT} <: AbstractGramMatrix{T,B,U} blocks::Vector{GramMatrix{T,B,U,MT}} end -MB.implicit_basis(g::BlockDiagonalGramMatrix) = MB.implicit_basis(first(g.blocks)) +function MB.implicit_basis(g::BlockDiagonalGramMatrix) + return MB.implicit_basis(first(g.blocks)) +end function MultivariateMoments.block_diagonal(blocks::Vector{<:GramMatrix}) return BlockDiagonalGramMatrix(blocks) @@ -245,7 +252,12 @@ function Base.zero(::Type{BlockDiagonalGramMatrix{T,B,U,MT}}) where {T,B,U,MT} return BlockDiagonalGramMatrix(GramMatrix{T,B,U,MT}[]) end -function MA.operate!(op::SA.UnsafeAddMul{typeof(*)}, p::SA.AlgebraElement, g::BlockDiagonalGramMatrix, args::Vararg{Any,N}) where {N} +function MA.operate!( + op::SA.UnsafeAddMul{typeof(*)}, + p::SA.AlgebraElement, + g::BlockDiagonalGramMatrix, + args::Vararg{Any,N}, +) where {N} for block in g.blocks MA.operate!(op, p, block, args...) end @@ -263,13 +275,20 @@ end # convert(PT, MP.polynomial(p)) #end -function MP.polynomial(p::Union{GramMatrix{T,B,U},BlockDiagonalGramMatrix{T,B,U}}) where {T,B,U} +function MP.polynomial( + p::Union{GramMatrix{T,B,U},BlockDiagonalGramMatrix{T,B,U}}, +) where {T,B,U} return MP.polynomial(p, U) end -function MP.polynomial(g::Union{GramMatrix,BlockDiagonalGramMatrix}, ::Type{T}) where {T} +function MP.polynomial( + g::Union{GramMatrix,BlockDiagonalGramMatrix}, + ::Type{T}, +) where {T} p = zero(T, SA.algebra(MB.implicit_basis(g))) MA.operate!(SA.UnsafeAddMul(*), p, g) MA.operate!(SA.canonical, SA.coeffs(p)) - return MP.polynomial(SA.coeffs(p, MB.FullBasis{MB.Monomial,MP.monomial_type(g)}())) + return MP.polynomial( + SA.coeffs(p, MB.FullBasis{MB.Monomial,MP.monomial_type(g)}()), + ) end diff --git a/src/sosdec.jl b/src/sosdec.jl index 6f09cc187..9bc1ca5c1 100644 --- a/src/sosdec.jl +++ b/src/sosdec.jl @@ -7,12 +7,16 @@ Represents a Sum-of-Squares decomposition without domain. """ struct SOSDecomposition{A,T,V,U} <: AbstractDecomposition{U} ps::Vector{SA.AlgebraElement{A,T,V}} # TODO rename `elements` - function SOSDecomposition{A,T,V,U}(ps::Vector{SA.AlgebraElement{A,T,V}}) where {A,T,V,U} + function SOSDecomposition{A,T,V,U}( + ps::Vector{SA.AlgebraElement{A,T,V}}, + ) where {A,T,V,U} return new(ps) end end -function SOSDecomposition(elements::Vector{SA.AlgebraElement{A,T,V}}) where {A,T,V} +function SOSDecomposition( + elements::Vector{SA.AlgebraElement{A,T,V}}, +) where {A,T,V} return SOSDecomposition{A,T,V,_promote_add_mul(T)}(elements) end function MP.polynomial_type( @@ -50,20 +54,19 @@ function SOSDecomposition( ) # TODO LDL^T factorization for SDP is missing in Julia # it would be nice to have though - ldlt = MultivariateMoments.low_rank_ldlt( - Matrix(value_matrix(p)), - dec, - ranktol, - ) + ldlt = + MultivariateMoments.low_rank_ldlt(Matrix(value_matrix(p)), dec, ranktol) # The Sum-of-Squares decomposition is # ∑ adjoint(u_i) * u_i # and we have `L` of the LDL* so we need to take the adjoint. - return SOSDecomposition(map(axes(ldlt.L, 2)) do i - MB.algebra_element( - √ldlt.singular_values[i] * _lazy_adjoint(ldlt.L[:, i]), - p.basis, - ) - end) + return SOSDecomposition( + map(axes(ldlt.L, 2)) do i + return MB.algebra_element( + √ldlt.singular_values[i] * _lazy_adjoint(ldlt.L[:, i]), + p.basis, + ) + end, + ) end # Without LDL^T, we need to do float(T) #SOSDecomposition(p::GramMatrix{C, T}) where {C, T} = SOSDecomposition{C, float(T)}(p) diff --git a/test/Tests/lyapunov_switched_system.jl b/test/Tests/lyapunov_switched_system.jl index 32a776bde..6adaef35c 100644 --- a/test/Tests/lyapunov_switched_system.jl +++ b/test/Tests/lyapunov_switched_system.jl @@ -54,7 +54,12 @@ function lyapunov_switched_system_test( model, variable_type = SOSPoly(MB.SubBasis{basis}(monomials(x, degree))) ) - q = GramMatrix(SOSDecomposition([MB.algebra_element([1], MB.SubBasis{basis}([var^degree])) for var in x])) + q = GramMatrix( + SOSDecomposition([ + MB.algebra_element([1], MB.SubBasis{basis}([var^degree])) for + var in x + ]), + ) # Keep `p` in a `GramMatrix` form while `q + p0` would transform it to # a polynomial. It is not mandatory to keep it in its `GramMatrix` form diff --git a/test/Tests/quadratic.jl b/test/Tests/quadratic.jl index 7dd43dc9a..6132488c2 100644 --- a/test/Tests/quadratic.jl +++ b/test/Tests/quadratic.jl @@ -61,7 +61,8 @@ function quadratic_test( p = gram_matrix(cref) if basis === Chebyshev && bivariate # See https://github.com/jump-dev/SumOfSquares.jl/issues/357 - @test value_matrix(p) ≈ [zeros(1, 3); zeros(2) ones(2, 2)] atol = atol rtol = rtol + @test value_matrix(p) ≈ [zeros(1, 3); zeros(2) ones(2, 2)] atol = atol rtol = + rtol else @test value_matrix(p) ≈ ones(2, 2) atol = atol rtol = rtol end @@ -76,9 +77,12 @@ function quadratic_test( else b = a end - @test b[2] ≈ (bivariate && basis === MB.ScaledMonomial ? -√2 : -1.0) atol = atol rtol = rtol + @test b[2] ≈ (bivariate && basis === MB.ScaledMonomial ? -√2 : -1.0) atol = + atol rtol = rtol @test b[1] + b[3] ≈ 2.0 atol = atol rtol = rtol - @test μ[2].polynomial == MB.Polynomial{basis}(bivariate ? (basis === MB.Chebyshev ? y^2 : x * y) : x^1) + @test μ[2].polynomial == MB.Polynomial{basis}( + bivariate ? (basis === MB.Chebyshev ? y^2 : x * y) : x^1, + ) @test dual_status(model) == MOI.FEASIBLE_POINT _test_moments(dual(cref), monos) do vals diff --git a/test/certificate.jl b/test/certificate.jl index b85a892a7..f9f7faec3 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -30,10 +30,7 @@ function _monomials_half_newton_polytope(_monos, filter) basis = MB.FullBasis{MB.Monomial,eltype(monos)}() return SumOfSquares.Certificate.half_newton_polytope( MB.algebra_element( - SA.SparseCoefficients( - monos, - ones(length(monos)), - ), + SA.SparseCoefficients(monos, ones(length(monos))), basis, ), SumOfSquares.Certificate._weight_type(Bool, typeof(basis))[], @@ -71,20 +68,9 @@ end end uni = Certificate.NewtonDegreeBounds(tuple()) @testset "Unipartite" begin - @test _monomials_half_newton_polytope( - [x * y, y^2], - uni, - ) == [y] - @test isempty( - _monomials_half_newton_polytope( - [x, y], - uni, - ), - ) - @test _monomials_half_newton_polytope( - [x^2, y^2], - uni, - ) == [x, y] + @test _monomials_half_newton_polytope([x * y, y^2], uni) == [y] + @test isempty(_monomials_half_newton_polytope([x, y], uni)) + @test _monomials_half_newton_polytope([x^2, y^2], uni) == [x, y] @test _monomials_half_newton_polytope( [x^2, y^2], Certificate.NewtonDegreeBounds(([x, y],)), @@ -215,11 +201,7 @@ function certificate_api(certificate::Certificate.AbstractIdealCertificate) poly = x + 1 domain = @set x == 1 basis = MB.SubBasis{MB.Monomial}(MP.monomials(poly)) - @test Certificate.reduced_polynomial( - certificate, - poly, - domain, - ) isa Tuple + @test Certificate.reduced_polynomial(certificate, poly, domain) isa Tuple _basis_check( Certificate.gram_basis(certificate, basis), Certificate.gram_basis_type(typeof(certificate)), diff --git a/test/csp_test.jl b/test/csp_test.jl index 405a39c26..044470c19 100644 --- a/test/csp_test.jl +++ b/test/csp_test.jl @@ -3,21 +3,29 @@ @polyvar x y z p = x * y + y * z S = @set x^2 + y^2 == 1 && y^2 + z^2 == 1 - G = SumOfSquares.Certificate.Sparsity.csp_graph(MB.SubBasis{MB.Monomial}(MP.monomials(p)), S) + G = SumOfSquares.Certificate.Sparsity.csp_graph( + MB.SubBasis{MB.Monomial}(MP.monomials(p)), + S, + ) @test sort(G.int2n) == [z, y, x] end @testset "chordal_csp" begin @polyvar x y z p = x * y + y * z S = @set x^2 + y^2 == 1 && y^2 + z^2 == 1 - H, cliques = SumOfSquares.Certificate.Sparsity.chordal_csp_graph(MB.SubBasis{MB.Monomial}(MP.monomials(p)), S) + H, cliques = SumOfSquares.Certificate.Sparsity.chordal_csp_graph( + MB.SubBasis{MB.Monomial}(MP.monomials(p)), + S, + ) @test length(cliques) == 2 svar = cliques[1] ∪ cliques[2] @test H.int2n ⊆ svar @test svar ⊆ H.int2n @test sort!(svar) == sort!(H.int2n) - I, cliquesI = - SumOfSquares.Certificate.Sparsity.chordal_csp_graph(MB.SubBasis{MB.Monomial}(MP.monomials(p)), FullSpace()) + I, cliquesI = SumOfSquares.Certificate.Sparsity.chordal_csp_graph( + MB.SubBasis{MB.Monomial}(MP.monomials(p)), + FullSpace(), + ) @test sort!(I.int2n) == sort!(H.int2n) @test sort!(collect.(I.graph.neighbors)) == sort!(collect.(H.graph.neighbors)) From 3fa06d5351c0b59753299474fd79b250cc52f2c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 12 Jun 2024 17:06:51 +0200 Subject: [PATCH 55/84] Fixes --- src/Certificate/Certificate.jl | 13 +- src/Certificate/Sparsity/ideal.jl | 19 +-- src/Certificate/Sparsity/monomial.jl | 14 +- src/Certificate/Sparsity/preorder.jl | 3 +- src/Certificate/Sparsity/variable.jl | 3 +- src/Certificate/ideal.jl | 16 +- src/Certificate/newton_polytope.jl | 214 ++++++++++++++------------- src/Certificate/preorder.jl | 3 +- src/rand.jl | 15 +- src/sosdec.jl | 47 +++--- test/Bridges/Constraint/image.jl | 2 +- test/Bridges/Variable/kernel.jl | 2 +- test/certificate.jl | 70 ++++----- test/constraint.jl | 20 +-- test/gram_matrix.jl | 68 ++++++--- test/sparsity.jl | 74 +++++---- 16 files changed, 333 insertions(+), 250 deletions(-) diff --git a/src/Certificate/Certificate.jl b/src/Certificate/Certificate.jl index c61d04055..22cf41735 100644 --- a/src/Certificate/Certificate.jl +++ b/src/Certificate/Certificate.jl @@ -49,12 +49,21 @@ function within_total_bounds(mono::MP.AbstractMonomial, bounds::DegreeBounds) return bounds.mindegree <= MP.degree(mono) <= bounds.maxdegree end +function _divides(a, b) + # `MP.divides(a, b)` is not implemented yet for noncommutative + vars = unique!(sort(MP.variables(a))) + comm = is_commutative(vars) + return all(vars) do v + _degree(a, v, comm) <= _degree(b, v, comm) + end +end + function within_variablewise_bounds( mono::MP.AbstractMonomial, bounds::DegreeBounds, ) - return MP.divides(bounds.variablewise_mindegree, mono) && - MP.divides(mono, bounds.variablewise_maxdegree) + return _divides(bounds.variablewise_mindegree, mono) && + _divides(mono, bounds.variablewise_maxdegree) end function within_bounds(mono, bounds) diff --git a/src/Certificate/Sparsity/ideal.jl b/src/Certificate/Sparsity/ideal.jl index 36a7f7d4e..93a22969b 100644 --- a/src/Certificate/Sparsity/ideal.jl +++ b/src/Certificate/Sparsity/ideal.jl @@ -36,10 +36,11 @@ function Ideal( end function sparsity( - basis::MB.SubBasis{MB.Monomial}, + poly, ::Variable, certificate::SumOfSquares.Certificate.MaxDegree, ) + basis = MB.explicit_basis(poly) H, cliques = chordal_csp_graph(basis, SemialgebraicSets.FullSpace()) return map(cliques) do clique return SumOfSquares.Certificate.maxdegree_gram_basis( @@ -56,27 +57,17 @@ function sparsity( ) return MB.SubBasis{MB.Monomial}.(sparsity(monos, sp, gram_basis.monomials)) end -# Backward compatibility, we may remove this at some time -function sparsity(p::MP.AbstractPolynomialLike, args...) - return sparsity(MB.SubBasis{MB.Monomial}(MP.monomials(p)), args...) -end function sparsity( - basis::MB.SubBasis{MB.Monomial}, + poly, sp::Union{SignSymmetry,Monomial}, certificate::SumOfSquares.Certificate.AbstractIdealCertificate, ) return sparsity( - basis.monomials, + MB.explicit_basis(poly).monomials, sp, - SumOfSquares.Certificate.gram_basis(certificate, basis), + SumOfSquares.Certificate.gram_basis(certificate, poly), ) end -function sparsity(a::SA.AlgebraElement, sp, certificate) - return sparsity(MB.explicit_basis(a), sp, certificate) -end -function sparsity(v::SumOfSquares.Certificate.WithVariables, sp, certificate) - return sparsity(v.inner, sp, certificate) -end function SumOfSquares.Certificate.gram_basis(certificate::Ideal, poly) return sparsity(poly, certificate.sparsity, certificate.certificate) end diff --git a/src/Certificate/Sparsity/monomial.jl b/src/Certificate/Sparsity/monomial.jl index cf78f0fa0..def247052 100644 --- a/src/Certificate/Sparsity/monomial.jl +++ b/src/Certificate/Sparsity/monomial.jl @@ -213,16 +213,22 @@ end struct DummyPolynomial{M} monomials::M end +function SumOfSquares.Certificate._algebra_element(p::DummyPolynomial) + return MB.algebra_element( + SA.SparseCoefficients(p.monomials, ones(length(p.monomials))), + MB.FullBasis{MB.Monomial,eltype(p.monomials)}(), + ) +end MP.monomials(p::DummyPolynomial) = p.monomials MP.variables(p::DummyPolynomial) = MP.variables(p.monomials) function sparsity( - basis::MB.SubBasis{MB.Monomial}, + poly, domain::SemialgebraicSets.BasicSemialgebraicSet, sp::Monomial, certificate::SumOfSquares.Certificate.AbstractPreorderCertificate, ) processed = - SumOfSquares.Certificate.preprocessed_domain(certificate, domain, basis) + SumOfSquares.Certificate.preprocessed_domain(certificate, domain, poly) multiplier_generator_monos = [ ( _monos( @@ -246,12 +252,12 @@ function sparsity( SumOfSquares.Certificate.gram_basis( SumOfSquares.Certificate.ideal_certificate(certificate), DummyPolynomial( - _ideal_monos(basis.monomials, multiplier_generator_monos), + _ideal_monos(MB.explicit_basis(poly).monomials, multiplier_generator_monos), ), ), ) cliques, multiplier_cliques = - sparsity(basis.monomials, sp, gram_monos, multiplier_generator_monos) + sparsity(MB.explicit_basis(poly).monomials, sp, gram_monos, multiplier_generator_monos) return MB.SubBasis{MB.Monomial}.(cliques), [MB.SubBasis{MB.Monomial}.(clique) for clique in multiplier_cliques] end diff --git a/src/Certificate/Sparsity/preorder.jl b/src/Certificate/Sparsity/preorder.jl index 994b96e93..add48e88d 100644 --- a/src/Certificate/Sparsity/preorder.jl +++ b/src/Certificate/Sparsity/preorder.jl @@ -32,7 +32,8 @@ function SumOfSquares.Certificate.preprocessed_domain( p, ) _, preorder_bases = sparsity( - MB.explicit_basis(p), + p, + #MB.explicit_basis(SumOfSquares.Certificate._algebra_element(p)), domain, certificate.sparsity, certificate.certificate, diff --git a/src/Certificate/Sparsity/variable.jl b/src/Certificate/Sparsity/variable.jl index a0428c0d8..94f223040 100644 --- a/src/Certificate/Sparsity/variable.jl +++ b/src/Certificate/Sparsity/variable.jl @@ -47,11 +47,12 @@ function chordal_csp_graph( end function sparsity( - basis::MB.SubBasis{MB.Monomial}, + poly, domain::BasicSemialgebraicSet, ::Variable, certificate::SumOfSquares.Certificate.Putinar, ) + basis = MB.explicit_basis(poly) H, cliques = chordal_csp_graph(basis, domain) function bases(q) return [ diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 1f3b3bb81..2f4bcf84a 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -189,18 +189,24 @@ function _weight_type(::Type{T}, ::Type{BT}) where {T,BT} } end -function gram_basis(certificate::Newton, poly) - a = _algebra_element(poly) - vars = MP.variables(poly) +function _half_newton_polytope(a::SA.AlgebraElement, vars, filter) return half_newton_polytope( a, - _weight_type(Bool, typeof(SA.basis(poly)))[], + _weight_type(Bool, typeof(SA.basis(a)))[], vars, _maxdegree(a, vars), - certificate.newton, + filter, )[1] end +function gram_basis(certificate::Newton, poly) + return _half_newton_polytope( + _algebra_element(poly), + MP.variables(poly), + certificate.newton, + ) +end + function gram_basis_type(::Type{<:Newton{C,B}}) where {C,B} return MB.explicit_basis_type(B) end diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 2a0443e0f..cd4ea2504 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -10,6 +10,7 @@ struct NewtonDegreeBounds{NPT} <: AbstractNewtonPolytopeApproximation variable_groups::NPT end + # Filters out points ouside the Newton polytope from the # outer approximation given by `outer_approximation`. struct NewtonFilter{N<:AbstractNewtonPolytopeApproximation} <: @@ -24,6 +25,37 @@ struct DegreeBounds{M} variablewise_maxdegree::M end +function __chip(cur, i, vars, exps, n, op) + if n > 0 + exp = min(exps[i], n) + next = iszero(exp) ? cur : op(cur, vars[i]^exp) + return __chip(next, i + 1, vars, exps, n - exp, op) + else + return cur + end +end +function _chip(mono, n) + vars = MP.variables(mono) + exps = MP.exponents(mono) + if n < 0 + vars = reverse(vars) + exps = reverse(exps) + op(a, b) = b * a + else + op = * + end + return __chip(MP.constant_monomial(mono), 1, vars, exps, abs(n), op) +end + +function _is_hermitian_square(mono) + d = MP.degree(mono) + if isodd(d) + return false + end + n = div(d, 2) + return _chip(mono, n) == _chip(mono, -n) +end + # TODO add to MP function _map_powers(f, mono) exps = map(f, MP.powers(mono)) @@ -203,35 +235,47 @@ function deg_range(deg, p, gs, gram_deg, range::UnitRange) end #_mindegree(p::MP.AbstractPolynomialLike) = MP.mindegree(p) -function _mindegree(a::SA.AlgebraElement, vars) - return minimum(Base.Fix2(_mindegree, vars), SA.supp(a)) -end -function _maxdegree(a::SA.AlgebraElement, vars) - return maximum(Base.Fix2(_maxdegree, vars), SA.supp(a), init = 0) -end +_mindegree(a::SA.AlgebraElement, vars) = minimum(Base.Fix2(_mindegree, vars), SA.supp(a)) +_maxdegree(a::SA.AlgebraElement, vars) = maximum(Base.Fix2(_maxdegree, vars), SA.supp(a), init = 0) #_mindegree(basis::MB.SubBasis{MB.Monomial}) = MP.mindegree(basis.monomials) #_maxdegree(basis::MB.SubBasis{MB.Monomial}) = MP.maxdegree(basis.monomials) function _mindegree(p::MB.Polynomial{B}, vars) where {B} if _is_monomial_basis(B) - _sub_degree(p.monomial, vars) + _sum_degree(p.monomial, vars) else error("TODO $B") end end -_maxdegree(p::MB.Polynomial, vars) = _sub_degree(p.monomial, vars) -_sub_degree(mono, vars) = sum(var -> MP.degree(mono, var), vars) +_maxdegree(p::MB.Polynomial, vars) = _sum_degree(p.monomial, vars) +function _degree(mono, var::MP.AbstractVariable, comm::Bool) + if comm + return MP.degree(mono, var) + else + vars = MP.variables(mono) + return mapreduce( + j -> vars[j] == var ? MP.exponents(mono)[j] : 0, + +, + eachindex(vars), + init = 0, + ) + end +end +function _sum_degree(mono, vars) + comm = is_commutative(vars) + return sum(var -> _degree(mono, var, comm), vars) +end _is_monomial_basis(::Type{<:MB.AbstractMonomialIndexed}) = false _is_monomial_basis(::Type{<:Union{MB.Monomial,MB.ScaledMonomial}}) = true -function _is_monomial_basis( - ::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}, -) where {BT,B} - return _is_monomial_basis(B) -end +_is_monomial_basis(::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}) where {BT,B} = _is_monomial_basis(B) # Minimum degree of a gram basis for a gram matrix `s` # such that the minimum degree of `s * g` is at least `mindegree`. -function _multiplier_mindegree(mindegree, g::SA.AlgebraElement, vars) +function _multiplier_mindegree( + mindegree, + g::SA.AlgebraElement, + vars, +) if _is_monomial_basis(typeof(g)) return _min_half(min_shift(mindegree, _mindegree(g, vars))) else @@ -254,11 +298,7 @@ function _multiplier_maxdegree(maxdegree, g::SA.AlgebraElement, vars) end function _multiplier_deg_range(range, g::SA.AlgebraElement, vars) - return _multiplier_mindegree(minimum(range), g, vars):_multiplier_maxdegree( - maximum(range), - g, - vars, - ) + return _multiplier_mindegree(minimum(range), g, vars):_multiplier_maxdegree(maximum(range), g, vars) end # Cheap approximation of the convex hull as the approximation of: @@ -292,13 +332,7 @@ function putinar_degree_bounds( minus_degrange(g) = (-).(degrange(g)) # The multiplier will have degree `0:2fld(maxdegree - _maxdegree(g), 2)` mindegree = if _is_monomial_basis(typeof(p)) - d = deg_range( - (-) ∘ Base.Fix2(_mindegree, vars), - p, - gs, - minus_degrange, - -maxdegree:0, - ) + d = deg_range((-) ∘ Base.Fix2(_mindegree, vars), p, gs, minus_degrange, -maxdegree:0) isnothing(d) ? d : -d else 0 @@ -306,8 +340,7 @@ function putinar_degree_bounds( if isnothing(mindegree) return end - maxdegree = - deg_range(Base.Fix2(_maxdegree, vars), p, gs, degrange, 0:maxdegree) + maxdegree = deg_range(Base.Fix2(_maxdegree, vars), p, gs, degrange, 0:maxdegree) if isnothing(maxdegree) return end @@ -343,10 +376,7 @@ function putinar_degree_bounds( ) end -function multiplier_basis( - g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, - bounds::DegreeBounds, -) where {BT,B} +function multiplier_basis(g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, bounds::DegreeBounds) where {BT,B} return maxdegree_gram_basis( MB.FullBasis{B,MP.monomial_type(typeof(g))}(), _half(minus_shift(bounds, g)), @@ -356,17 +386,13 @@ end # Cartesian product of the newton polytopes of the different parts function _cartesian_product(bases::Vector{<:MB.SubBasis{B}}, bounds) where {B} monos = [b.monomials for b in bases] - basis = MB.SubBasis{B}( - vec([prod(monos) for monos in Iterators.product(monos...)]), - ) + basis = MB.SubBasis{B}(vec([prod(monos) for monos in Iterators.product(monos...)])) # We know that the degree inequalities are satisfied variable-wise and # part-wise but for all variables together so we filter with that if isnothing(bounds) return MB.empty_basis(typeof(basis)) else - return MB.SubBasis{B}( - filter(Base.Fix2(within_bounds, _half(bounds)), basis.monomials), - ) + return MB.SubBasis{B}(filter(Base.Fix2(within_bounds, _half(bounds)), basis.monomials)) end end @@ -405,32 +431,12 @@ function half_newton_polytope( end if length(all_parts) == 1 # all variables on same part, fallback to shortcut - return half_newton_polytope( - p, - gs, - vars, - maxdegree, - NewtonDegreeBounds(tuple()), - ) + return half_newton_polytope(p, gs, vars, maxdegree, NewtonDegreeBounds(tuple())) end - bases = map( - part -> half_newton_polytope( - p, - gs, - part, - maxdegree, - NewtonDegreeBounds(tuple()), - ), - all_parts, - ) + bases = map(part -> half_newton_polytope(p, gs, part, maxdegree, NewtonDegreeBounds(tuple())), all_parts) bounds = putinar_degree_bounds(p, gs, vars, maxdegree) return _cartesian_product([b[1] for b in bases], bounds), - [ - _cartesian_product( - [b[2][i] for b in bases], - minus_shift(bounds, gs[i]), - ) for i in eachindex(first(bases)[2]) - ] + [_cartesian_product([b[2][i] for b in bases], minus_shift(bounds, gs[i])) for i in eachindex(first(bases)[2])] end function half_newton_polytope( @@ -440,13 +446,38 @@ function half_newton_polytope( maxdegree, ::NewtonDegreeBounds{Tuple{}}, ) where {BT,B,M} - # TODO take `variable_groups` into account - bounds = putinar_degree_bounds(p, gs, vars, maxdegree) - full = MB.FullBasis{B,M}() - return maxdegree_gram_basis(full, _half(bounds)), - MB.explicit_basis_type(typeof(full))[ - multiplier_basis(g, bounds) for g in gs - ] + if is_commutative(vars) + # TODO take `variable_groups` into account + bounds = putinar_degree_bounds(p, gs, vars, maxdegree) + full = MB.FullBasis{B,M}() + return maxdegree_gram_basis(full, _half(bounds)), + MB.explicit_basis_type(typeof(full))[multiplier_basis(g, bounds) for g in gs] + else + if !isempty(gs) + error("Inequalities constraints not supported with noncommutative variables") + end + # Non-commutative variables + # We use Newton chip method of [Section 2.3, BKP16]. + # + # [BKP16] Sabine Burgdorf, Igor Klep, and Janez Povh. + # *Optimization of polynomials in non-commuting variables*. + # Berlin: Springer, 2016. + vars = unique!(sort(vars)) + bounds = _half(putinar_degree_bounds(p, gs, vars, maxdegree)) + monos = MP.monomial_type(typeof(p))[] + for mono in MB.explicit_basis(p).monomials + if _is_hermitian_square(mono) + for i in 1:div(MP.degree(mono), 2) + w = _chip(mono, -i) + if within_bounds(w, bounds) + push!(monos, w) + end + end + end + end + basis = MB.SubBasis{B}(monos) + return basis, typeof(basis)[] + end end function half_newton_polytope( @@ -458,12 +489,7 @@ function half_newton_polytope( ) return half_newton_polytope( p, - [ - MB.algebra_element( - MP.coefficients(g), - MB.SubBasis{MB.Monomial}(MP.monomials(g)), - ) for g in gs - ], + [MB.algebra_element(MP.coefficients(g), MB.SubBasis{MB.Monomial}(MP.monomials(g))) for g in gs], vars, maxdegree, filter, @@ -477,18 +503,11 @@ function half_newton_polytope( maxdegree, filter::NewtonFilter{<:NewtonDegreeBounds}, ) - basis, multipliers_bases = - half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) + basis, multipliers_bases = half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) bases = copy(multipliers_bases) push!(bases, basis) gs = copy(gs) - push!( - gs, - MB.constant_algebra_element( - MA.promote_operation(SA.basis, eltype(gs)), - eltype(eltype(gs)), - ), - ) + push!(gs, MB.constant_algebra_element(MA.promote_operation(SA.basis, eltype(gs)), eltype(eltype(gs)))) filtered_bases = post_filter(p, gs, bases) # The last one will be recomputed by the ideal certificate return filtered_bases[end], filtered_bases[1:(end-1)] @@ -523,13 +542,10 @@ function _sign(c::SignCount) end function Base.:+(a::SignCount, b::SignCount) - return SignCount( - a.unknown + b.unknown, - a.positive + b.positive, - a.negative + b.negative, - ) + return SignCount(a.unknown + b.unknown, a.positive + b.positive, a.negative + b.negative) end + function Base.:+(c::SignCount, a::SignChange{Missing}) @assert c.unknown >= -a.Δ return SignCount(c.unknown + a.Δ, c.positive, c.negative) @@ -566,7 +582,7 @@ function increase(cache, counter, generator_sign, monos, mult) MA.operate!( SA.UnsafeAddMul(*), counter, - SignChange((a != b) ? missing : generator_sign, 1), + SignChange((a != b) ? missing : generator_sign, 1,), cache, ) end @@ -614,18 +630,11 @@ end # [L23] Legat, Benoît # *Exploiting the Structure of a Polynomial Optimization Problem* # SIAM Conference on Applications of Dynamical Systems, 2023 -function post_filter( - poly::SA.AlgebraElement, - generators, - multipliers_gram_monos, -) +function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos) # We use `_DictCoefficients` instead `SA.SparseCoefficients` because # we need to keep it canonicalized (without duplicate actually) # and don't care about the list of monomials being ordered - counter = MB.algebra_element( - _DictCoefficients(Dict{MP.monomial_type(typeof(poly)),SignCount}()), - MB.implicit_basis(SA.basis(poly)), - ) + counter = MB.algebra_element(_DictCoefficients(Dict{MP.monomial_type(typeof(poly)),SignCount}()), MB.implicit_basis(SA.basis(poly))) cache = zero(Float64, SA.algebra(MB.implicit_basis(SA.basis(poly)))) for (mono, v) in SA.nonzero_pairs(poly) MA.operate!( @@ -654,8 +663,7 @@ function post_filter( count_sign = _sign(count) # This means the `counter` has a sign and it didn't have a sign before # so we need to delete back edges - if !ismissing(count_sign) && - (ismissing(count) || count != count_sign) + if !ismissing(count_sign) && (ismissing(count) || count != count_sign) # TODO could see later if deleting the counter improves perf if haskey(back, mono) for (i, j) in back[mono] @@ -696,9 +704,7 @@ function post_filter( MB.algebra_element(mono), ) for w in SA.supp(cache) - if ismissing( - _sign(SA.coeffs(counter)[SA.basis(counter)[w]]), - ) + if ismissing(_sign(SA.coeffs(counter)[SA.basis(counter)[w]])) push!(get!(back, w, Tuple{Int,Int}[]), (i, j)) else delete(i, j) diff --git a/src/Certificate/preorder.jl b/src/Certificate/preorder.jl index 046497329..4be1872b2 100644 --- a/src/Certificate/preorder.jl +++ b/src/Certificate/preorder.jl @@ -41,6 +41,7 @@ function MP.variables(v::WithVariables) return v.variables end SA.basis(v::WithVariables) = SA.basis(v.inner) +MB.explicit_basis(v::WithVariables) = MB.explicit_basis(v.inner) _algebra_element(v::WithVariables) = v.inner _algebra_element(a::SA.AlgebraElement) = a @@ -94,7 +95,7 @@ function with_fixed_basis( return WithFixedBases( v.inner, half_newton_polytope( - p, + _algebra_element(p), SemialgebraicSets.inequalities(domain), v.variables, maxdegree, diff --git a/src/rand.jl b/src/rand.jl index 91283e0e5..6aafd8e91 100644 --- a/src/rand.jl +++ b/src/rand.jl @@ -8,6 +8,19 @@ function randpsd(n; r = n, eps = 0.1) return Q' * Diagonal(d) * Q end +function _monomials_half_newton_polytope(_monos, filter) + monos = MP.monomial_vector(_monos) + basis = MB.FullBasis{MB.Monomial,eltype(monos)}() + return SumOfSquares.Certificate._half_newton_polytope( + MB.algebra_element( + SA.SparseCoefficients(monos, ones(length(monos))), + basis, + ), + MP.variables(monos), + filter, + ).monomials +end + function _randsos( X::AbstractVector{<:MP.AbstractMonomial}; r = -1, @@ -15,7 +28,7 @@ function _randsos( eps = 0.1, ) if monotype == :Classic - x = Certificate.monomials_half_newton_polytope( + x = _monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds(tuple()), ) diff --git a/src/sosdec.jl b/src/sosdec.jl index 9bc1ca5c1..6c81c3999 100644 --- a/src/sosdec.jl +++ b/src/sosdec.jl @@ -1,7 +1,7 @@ export SOSDecomposition, SOSDecompositionWithDomain, sos_decomposition """ - struct SOSDecomposition{T, PT} + struct SOSDecomposition{A,T,V,U} Represents a Sum-of-Squares decomposition without domain. """ @@ -102,45 +102,56 @@ function Base.isapprox(p::SOSDecomposition, q::SOSDecomposition; kwargs...) end function Base.promote_rule( - ::Type{SOSDecomposition{T1,PT1,U1}}, - ::Type{SOSDecomposition{T2,PT2,U2}}, -) where {T1,T2,PT1<:_APL{T1},PT2<:_APL{T2},U1,U2} + ::Type{SOSDecomposition{A,T1,V1,U1}}, + ::Type{SOSDecomposition{A,T2,V2,U2}}, +) where {A,T1,T2,V1,V2,U1,U2} T = promote_type(T1, T2) - return SOSDecomposition{T,promote_type(PT1, PT2),_promote_add_mul(T)} + V = MA.promote_operation(similar, V1, T) + return SOSDecomposition{A,T,V,_promote_add_mul(T)} end function Base.convert( - ::Type{SOSDecomposition{T,PT,U}}, + ::Type{SOSDecomposition{A,T,V,U}}, p::SOSDecomposition, -) where {T,PT,U} - return SOSDecomposition(convert(Vector{PT}, p.ps)) +) where {A,T,V,U} + return SOSDecomposition(convert(Vector{SA.AlgebraElement{A,T,V}}, p.ps)) end -function MP.polynomial(decomp::SOSDecomposition) - return sum(decomp.ps .^ 2) +function MP.polynomial(d::SOSDecomposition) + return MP.polynomial(MB.algebra_element(d)) end + +function MB.algebra_element(decomp::SOSDecomposition) + res = zero(first(decomp.ps)) + for p in decomp.ps + MA.operate!(SA.UnsafeAddMul(*), res, SA.star(p), p) + end + MA.operate!(SA.canonical, SA.coeffs(res)) + return res +end + function MP.polynomial(decomp::SOSDecomposition, T::Type) return MP.polynomial(MP.polynomial(decomp), T) end """ - struct SOSDecompositionWithDomain{T, PT, S} + struct SOSDecompositionWithDomain{A,T,V,U,S} Represents a Sum-of-Squares decomposition on a basic semi-algebraic domain. """ -struct SOSDecompositionWithDomain{T,PT<:_APL{T},U,S<:AbstractSemialgebraicSet} - sos::SOSDecomposition{T,PT,U} - sosj::Vector{SOSDecomposition{T,PT,U}} +struct SOSDecompositionWithDomain{A,T,V,U,S<:AbstractSemialgebraicSet} + sos::SOSDecomposition{A,T,V,U} + sosj::Vector{SOSDecomposition{A,T,V,U}} domain::S end function SOSDecompositionWithDomain( - ps::SOSDecomposition{T1,PT1,U1}, - vps::Vector{SOSDecomposition{T2,PT2,U2}}, + ps::SOSDecomposition{A1,T1,V1,U1}, + vps::Vector{SOSDecomposition{A2,T2,V2,U2}}, set::AbstractSemialgebraicSet, -) where {T1,T2,PT1,PT2,U1,U2} +) where {A1,A2,T1,T2,V1,V2,U1,U2} ptype = - promote_type(SOSDecomposition{T1,PT1,U1}, SOSDecomposition{T2,PT2,U2}) + promote_type(SOSDecomposition{A1,T1,V1,U1}, SOSDecomposition{A2,T2,V2,U2}) return SOSDecompositionWithDomain( convert(ptype, ps), convert(Vector{ptype}, vps), diff --git a/test/Bridges/Constraint/image.jl b/test/Bridges/Constraint/image.jl index ea83e619c..44d7f0785 100644 --- a/test/Bridges/Constraint/image.jl +++ b/test/Bridges/Constraint/image.jl @@ -34,7 +34,7 @@ function test_runtests() }( MB.SubBasis{MB.Monomial}([y^4, x^2 * y^2, x^3 * y, x^4]), [MB.SubBasis{MB.Monomial}([y^2, x * y, x^2])], - [one(T) * x^0 * y^0], + [MB.algebra_element(one(T) * x^0 * y^0)], ), ) end, diff --git a/test/Bridges/Variable/kernel.jl b/test/Bridges/Variable/kernel.jl index cf8f45936..95d9048cc 100644 --- a/test/Bridges/Variable/kernel.jl +++ b/test/Bridges/Variable/kernel.jl @@ -29,7 +29,7 @@ function test_runtests() }( MB.SubBasis{MB.Monomial}([x^4, x^3 * y, x^2 * y^2, y^4]), [MB.SubBasis{MB.Monomial}([x^2, y^2, x * y])], - [1.0 * x^0 * y^0], + [MB.algebra_element(1.0 * x^0 * y^0)], ), ) end, diff --git a/test/certificate.jl b/test/certificate.jl index f9f7faec3..54aff75e0 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -2,6 +2,8 @@ import StarAlgebras as SA import MultivariatePolynomials as MP import MultivariateBases as MB +const SOS = SumOfSquares + @testset "_merge_sorted" begin @test SumOfSquares.Certificate._merge_sorted([4, 1], [3, 0]) == [4, 3, 1, 0] @test SumOfSquares.Certificate._merge_sorted((4, 1), (3, 0)) == (4, 3, 1, 0) @@ -25,21 +27,6 @@ end @test MP.variables(v) == [a, b, a] end -function _monomials_half_newton_polytope(_monos, filter) - monos = MP.monomial_vector(_monos) - basis = MB.FullBasis{MB.Monomial,eltype(monos)}() - return SumOfSquares.Certificate.half_newton_polytope( - MB.algebra_element( - SA.SparseCoefficients(monos, ones(length(monos))), - basis, - ), - SumOfSquares.Certificate._weight_type(Bool, typeof(basis))[], - MP.variables(monos), - MP.maxdegree(monos), - filter, - )[1].monomials -end - @testset "Monomial selection for certificate" begin @polyvar x y z @ncpolyvar a b @@ -48,7 +35,7 @@ end err = ArgumentError( "Multipartite Newton polytope not supported with noncommutative variables.", ) - @test_throws err _monomials_half_newton_polytope( + @test_throws err SOS._monomials_half_newton_polytope( [a * b, b^2], Certificate.NewtonDegreeBounds(parts), ) @@ -61,39 +48,39 @@ end err = ArgumentError( "Parts are not disjoint in multipartite Newton polytope estimation: $parts.", ) - @test_throws err _monomials_half_newton_polytope( + @test_throws err SOS._monomials_half_newton_polytope( [x * y, y^2], Certificate.NewtonDegreeBounds(parts), ) end uni = Certificate.NewtonDegreeBounds(tuple()) @testset "Unipartite" begin - @test _monomials_half_newton_polytope([x * y, y^2], uni) == [y] - @test isempty(_monomials_half_newton_polytope([x, y], uni)) - @test _monomials_half_newton_polytope([x^2, y^2], uni) == [x, y] - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope([x * y, y^2], uni) == [y] + @test isempty(SOS._monomials_half_newton_polytope([x, y], uni)) + @test SOS._monomials_half_newton_polytope([x^2, y^2], uni) == [x, y] + @test SOS._monomials_half_newton_polytope( [x^2, y^2], Certificate.NewtonDegreeBounds(([x, y],)), ) == [x, y] - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( [x^2, y^2], Certificate.NewtonDegreeBounds(([y, x],)), ) == [x, y] - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( [x^2, x^3 * y^2, x^4 * y^4], uni, ) == [x^2 * y^2, x, x * y, x^2, x * y^2, x^2 * y] - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( [x^2, x^3 * y^2, x^4 * y^4], Certificate.NewtonFilter(uni), ) == [x^2 * y^2, x] end @testset "Non-commutative" begin - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( [a^4, a^3 * b, a * b * a^2, a * b * a * b], uni, ) == [a^2, a * b] - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( [ a^2, a^10 * b^20 * a^11, @@ -106,26 +93,26 @@ end @testset "Multipartite" begin # In the part [y, z], the degree is between 0 and 2 X = [x^4, x^2 * y^2, x^2 * z^2, x^2 * y * z, y * z] - @test _monomials_half_newton_polytope(X, uni) == + @test SOS._monomials_half_newton_polytope(X, uni) == [x^2, x * y, x * z, y * z, x, y, z] function full_test(X, Y, part1, part2) - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part1,)), ) == Y - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part2,)), ) == Y - a = _monomials_half_newton_polytope( + a = SOS._monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part2,)), ) - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part1, part2)), ) == Y - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part2, part1)), ) == Y @@ -144,7 +131,7 @@ end [x, y], ) # FIXME: With recursive merging, it should give [x^2, x*y, x*z, x] - @test _monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( [x^4, x^2 * y^2, x^2 * z^2, x^2 * y * z, y * z], Certificate.NewtonDegreeBounds(([x], [y], [z])), ) == [x^2, x * y, x * z, y * z, x, y, z] @@ -200,10 +187,10 @@ function certificate_api(certificate::Certificate.AbstractIdealCertificate) @polyvar x poly = x + 1 domain = @set x == 1 - basis = MB.SubBasis{MB.Monomial}(MP.monomials(poly)) - @test Certificate.reduced_polynomial(certificate, poly, domain) isa Tuple + a = MB.algebra_element(MB.sparse_coefficients(poly), MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}()) + @test Certificate.reduced_polynomial(certificate, a, domain) isa SA.AlgebraElement _basis_check( - Certificate.gram_basis(certificate, basis), + Certificate.gram_basis(certificate, Certificate.WithVariables(a, MP.variables(poly))), Certificate.gram_basis_type(typeof(certificate)), ) zbasis = Certificate.zero_basis(certificate) @@ -216,7 +203,8 @@ function certificate_api(certificate::Certificate.AbstractPreorderCertificate) @polyvar x poly = x + 1 domain = @set x >= 1 - processed = Certificate.preprocessed_domain(certificate, domain, poly) + a = MB.algebra_element(MB.sparse_coefficients(poly), MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}()) + processed = Certificate.preprocessed_domain(certificate, domain, Certificate.WithVariables(a, MP.variables(poly))) for idx in Certificate.preorder_indices(certificate, processed) _basis_check( Certificate.multiplier_basis(certificate, idx, processed), @@ -295,7 +283,8 @@ function test_putinar_ijk(i, j, k, default::Bool, post_filter::Bool = default) certificate = JuMP.moi_set( SOSCone(), - MB.SubBasis{MB.Monomial}(monomials(poly)); + MB.SubBasis{MB.Monomial}(monomials(poly)), + MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}(); domain, ).certificate else @@ -310,7 +299,8 @@ function test_putinar_ijk(i, j, k, default::Bool, post_filter::Bool = default) ) certificate = Certificate.Putinar(cert, cert, max(2i, 2j + 1, 2k + 1)) end - processed = Certificate.preprocessed_domain(certificate, domain, poly) + alg_el = MB.algebra_element(MB.sparse_coefficients(poly), MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}()) + processed = Certificate.preprocessed_domain(certificate, domain, alg_el) for idx in Certificate.preorder_indices(certificate, processed) monos = Certificate.multiplier_basis(certificate, idx, processed).monomials @@ -318,7 +308,7 @@ function test_putinar_ijk(i, j, k, default::Bool, post_filter::Bool = default) @test isempty(monos) else w = post_filter ? v[2:2] : v - @test monos == MP.monomials(w, max(0, min(i, j) - k):(j-k)) + @test monos == MP.monomials(w, max(0, (post_filter ? j : min(i, j)) - k):(j-k)) end end icert = Certificate.ideal_certificate(certificate) diff --git a/test/constraint.jl b/test/constraint.jl index 5d2ff0ba7..f1d37e6f7 100644 --- a/test/constraint.jl +++ b/test/constraint.jl @@ -20,17 +20,17 @@ end model = SOSModel() @variable(model, a) cref = @constraint(model, a * x^2 >= 1) - @test sprint(show, MIME"text/plain"(), cref) == "(-1) + (a)x² is SOS" + @test sprint(show, MIME"text/plain"(), cref) == "(-1)⋅1 + (a)⋅x² is SOS" @test sprint(show, MIME"text/latex"(), cref) == - "\$\$ (-1) + (a)x^{2} \\text{ is SOS} \$\$" + "\$\$ (-1) \\cdot 1 + (a) \\cdot x^{2} \\text{ is SOS} \$\$" sdref = @constraint(model, a * x^2 in SDSOSCone()) - @test sprint(show, MIME"text/plain"(), sdref) == "(a)x² is SDSOS" + @test sprint(show, MIME"text/plain"(), sdref) == "(a)⋅x² is SDSOS" @test sprint(show, MIME"text/latex"(), sdref) == - "\$\$ (a)x^{2} \\text{ is SDSOS} \$\$" + "\$\$ (a) \\cdot x^{2} \\text{ is SDSOS} \$\$" dref = @constraint(model, a * x^2 in DSOSCone()) - @test sprint(show, MIME"text/plain"(), dref) == "(a)x² is DSOS" + @test sprint(show, MIME"text/plain"(), dref) == "(a)⋅x² is DSOS" @test sprint(show, MIME"text/latex"(), dref) == - "\$\$ (a)x^{2} \\text{ is DSOS} \$\$" + "\$\$ (a) \\cdot x^{2} \\text{ is DSOS} \$\$" model = Model() @variable(model, a) for sparsity in [Sparsity.NoPattern(), Sparsity.Variable()] @@ -42,9 +42,9 @@ end sparsity = sparsity ) @test sprint(show, MIME"text/plain"(), cref_fix) == - "(-1) + (a)x² is SOS" + "(-1)⋅1 + (a)⋅x² is SOS" @test sprint(show, MIME"text/latex"(), cref_fix) == - "\$\$ (-1) + (a)x^{2} \\text{ is SOS} \$\$" + "\$\$ (-1) \\cdot 1 + (a) \\cdot x^{2} \\text{ is SOS} \$\$" cref_fix = @constraint( model, a * x^2 >= 1, @@ -53,9 +53,9 @@ end sparsity = sparsity ) @test sprint(show, MIME"text/plain"(), cref_fix) == - "(-1) + (a)x² is SOS" + "(-1)⋅1 + (a)⋅x² is SOS" @test sprint(show, MIME"text/latex"(), cref_fix) == - "\$\$ (-1) + (a)x^{2} \\text{ is SOS} \$\$" + "\$\$ (-1) \\cdot 1 + (a) \\cdot x^{2} \\text{ is SOS} \$\$" end end diff --git a/test/gram_matrix.jl b/test/gram_matrix.jl index d4df7351b..006383abb 100644 --- a/test/gram_matrix.jl +++ b/test/gram_matrix.jl @@ -1,5 +1,14 @@ using LinearAlgebra, Test, SumOfSquares +function _algebra_element(poly) + return MB.algebra_element( + SA.SparseCoefficients(collect(MP.monomials(poly)), collect(MP.coefficients(poly))), + MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}(), + ) +end + +_sos_dec(polys) = SOSDecomposition(_algebra_element.(polys)) + @testset "GramMatrix tests" begin @testset "GramMatrix" begin @polyvar x y @@ -119,29 +128,29 @@ using LinearAlgebra, Test, SumOfSquares # @test isapprox(GramMatrix(SOSDecomposition(P)), P) P = GramMatrix{Int}((i, j) -> i + j, [x^2, x * y, y^2]) @test polynomial_type(SOSDecomposition(P)) <: AbstractPolynomialLike - @test sprint(show, SOSDecomposition([x + y, x - y])) == - "(y + x)^2 + (-y + x)^2" - @test polynomial(SOSDecomposition([x + y, x - y])) == + @test sprint(show, _sos_dec([x + y, x - y])) == + "(1⋅y + 1⋅x)^2 + (-1⋅y + 1⋅x)^2" + @test polynomial(_sos_dec([x + y, x - y])) == (x + y)^2 + (x - y)^2 - @test polynomial(SOSDecomposition([x + y, x - y]), Float64) == + @test polynomial(_sos_dec([x + y, x - y]), Float64) == (x + y)^2 + (x - y)^2 @testset "SOSDecomposition equality" begin @polyvar x y @test !isapprox( - SOSDecomposition([x + y, x - y]), - SOSDecomposition([x + y]), + _sos_dec([x + y, x - y]), + _sos_dec([x + y]), ) @test !isapprox( - SOSDecomposition([x + y, x - y]), - SOSDecomposition([x + y, x + y]), + _sos_dec([x + y, x - y]), + _sos_dec([x + y, x + y]), ) @test isapprox( - SOSDecomposition([x + y, x - y]), - SOSDecomposition([x + y, x - y]), + _sos_dec([x + y, x - y]), + _sos_dec([x + y, x - y]), ) @test isapprox( - SOSDecomposition([x + y, x - y]), - SOSDecomposition([x - y, x + y + 1e-8]), + _sos_dec([x + y, x - y]), + _sos_dec([x - y, x + y + 1e-8]), ztol = 1e-7, ) end @@ -149,7 +158,7 @@ using LinearAlgebra, Test, SumOfSquares a = MOI.VariableIndex(1) p = polynomial([a], [x]) q = polynomial([a], [y]) - s = SOSDecomposition([p, q]) + s = _sos_dec([p, q]) U = MOI.ScalarQuadraticFunction{Float64} @test coefficient_type(s) == U @test s isa AbstractPolynomialLike{U} @@ -161,22 +170,37 @@ using LinearAlgebra, Test, SumOfSquares @testset "SOSDecompositionWithDomain" begin @polyvar x y K = @set 1 - x^2 >= 0 && 1 - y^2 >= 0 - ps = SOSDecomposition([x + y, x - y]) - ps1 = SOSDecomposition([x]) - ps2 = SOSDecomposition([y]) + ps = _sos_dec([x + y, x - y]) + ps1 = _sos_dec([x]) + ps2 = _sos_dec([y]) + M = typeof(x * y) @test [ps, ps1] isa Vector{ - SOSDecomposition{Int,T,Int}, - } where {T<:AbstractPolynomialLike} + SOSDecomposition{ + MB.Algebra{ + MB.FullBasis{MB.Monomial,M}, + MB.Monomial, + M, + }, + Int, + SA.SparseCoefficients{ + M, + Int, + Vector{M}, + Vector{Int64}, + }, + Int, + }, + } @test sprint(show, SOSDecompositionWithDomain(ps, [ps1, ps2], K)) == - "(y + x)^2 + (-y + x)^2 + (x)^2 * (1 - x^2) + (y)^2 * (1 - y^2)" + "(1⋅y + 1⋅x)^2 + (-1⋅y + 1⋅x)^2 + (1⋅x)^2 * (1 - x^2) + (1⋅y)^2 * (1 - y^2)" @testset "SOSDecompositionWithDomain equality" begin @polyvar x y K = @set 1 - x^2 >= 0 && 1 - y^2 >= 0 B = @set 1 - x >= 0 && 1 - y >= 0 - ps = SOSDecomposition([x + y, x - y]) - ps1 = SOSDecomposition([x + y, x^2 - y]) - ps2 = SOSDecomposition([x + y, y^2 - x]) + ps = _sos_dec([x + y, x - y]) + ps1 = _sos_dec([x + y, x^2 - y]) + ps2 = _sos_dec([x + y, y^2 - x]) sosdec = SOSDecompositionWithDomain(ps, [ps1, ps2], K) @test typeof(polynomial(sosdec)) <: AbstractPolynomialLike @test isapprox(sosdec, sosdec) diff --git a/test/sparsity.jl b/test/sparsity.jl index daf34a525..520b2c415 100644 --- a/test/sparsity.jl +++ b/test/sparsity.jl @@ -3,6 +3,13 @@ import Combinatorics using SumOfSquares import MultivariateBases as MB +function _algebra_element(monos) + return MB.algebra_element( + SA.SparseCoefficients(monos, ones(length(monos))), + MB.FullBasis{MB.Monomial,eltype(monos)}(), + ) +end + function _xor_complement_test( exps, expected, @@ -48,6 +55,8 @@ function wml19() ) @testset "Example 4.2" begin f = 1 + x[1]^4 + x[2]^4 + x[3]^4 + prod(x) + x[2] + alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + with_var = SumOfSquares.Certificate.WithVariables(alg_el, x) expected_1_false = Set( monomial_vector.([ [x[3]^2], @@ -91,7 +100,7 @@ function wml19() [x[1], x[2] * x[3], x[3], x[1] * x[2]], ]), ) - @testset "$completion $k $use_all_monomials" for completion in [ + @testset "$(nameof(typeof(completion))) $k $use_all_monomials" for completion in [ ClusterCompletion(), ChordalCompletion(), ], @@ -112,7 +121,7 @@ function wml19() end @test set_monos( Certificate.Sparsity.sparsity( - f, + with_var, Sparsity.Monomial(completion, k, use_all_monomials), certificate, ), @@ -125,10 +134,10 @@ function wml19() ]), ) @test set_monos( - Certificate.Sparsity.sparsity(f, SignSymmetry(), certificate), + Certificate.Sparsity.sparsity(with_var, SignSymmetry(), certificate), ) == expected end - @testset "Example 5.4 $(typeof(ideal_certificate))" for ideal_certificate in + @testset "Example 5.4 $(nameof(typeof(ideal_certificate)))" for ideal_certificate in [ Certificate.MaxDegree( SOSCone(), @@ -151,8 +160,9 @@ function wml19() 4, ) f = x[1]^4 + x[2]^4 + x[1] * x[2] + alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) K = @set 1 - 2x[1]^2 - x[2]^2 >= 0 - @testset "$completion $k $use_all_monomials" for completion in [ + @testset "$(nameof(typeof(completion))) $k $use_all_monomials" for completion in [ ClusterCompletion(), ChordalCompletion(), ], @@ -160,7 +170,7 @@ function wml19() use_all_monomials in [false, true] basis, preorder_bases = Certificate.Sparsity.sparsity( - f, + alg_el, K, Sparsity.Monomial(completion, k, use_all_monomials), preorder_certificate, @@ -213,8 +223,10 @@ function wml19() f = 1 + x[1]^2 * x[2]^4 + x[1]^4 * x[2]^2 + x[1]^4 * x[2]^4 - x[1] * x[2]^2 - 3x[1]^2 * x[2]^2 + alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + with_var = SumOfSquares.Certificate.WithVariables(alg_el, x) basis = MB.SubBasis{MB.Monomial}(MP.monomials(f)) - @testset "$completion $k $use_all_monomials" for completion in [ + @testset "$(nameof(typeof(completion))) $k $use_all_monomials" for completion in [ ClusterCompletion(), ChordalCompletion(), ], @@ -241,18 +253,17 @@ function wml19() end @test set_monos( Certificate.Sparsity.sparsity( - basis, + with_var, Sparsity.Monomial(completion, k, use_all_monomials), certificate, ), ) == expected end @test set_monos( - Certificate.Sparsity.sparsity(basis, SignSymmetry(), certificate), + Certificate.Sparsity.sparsity(with_var, SignSymmetry(), certificate), ) == Set( monomial_vector.([ - [x[1]^2 * x[2]^2, x[1] * x[2]^2, 1], - [x[1]^2 * x[2], x[1] * x[2]], + [x[1]^2 * x[2]^2, x[1] * x[2]^2, 1, x[1]^2 * x[2], x[1] * x[2]], ]), ) end @@ -274,8 +285,10 @@ function l09() ) @testset "Example 1 and 2" begin f = 1 + x[1]^4 * x[2]^2 + x[1]^2 * x[2]^4 + alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + with_var = SumOfSquares.Certificate.WithVariables(alg_el, x) newt = Certificate.NewtonDegreeBounds(tuple()) - @test Certificate.monomials_half_newton_polytope(monomials(f), newt) == + @test SOS._monomials_half_newton_polytope(monomials(f), newt) == [ x[1]^2 * x[2], x[1] * x[2]^2, @@ -286,7 +299,7 @@ function l09() x[2], 1, ] - @test Certificate.monomials_half_newton_polytope( + @test SOS._monomials_half_newton_polytope( monomials(f), Certificate.NewtonFilter(newt), ) == [x[1]^2 * x[2], x[1] * x[2]^2, 1] @@ -300,18 +313,26 @@ function l09() for i in 0:2 @test set_monos( Certificate.Sparsity.sparsity( - f, + with_var, Sparsity.Monomial(ChordalCompletion(), i), certificate, ), ) == expected end + expected = Set( + monomial_vector.([ + [x[1]^2 * x[2]], + [x[1] * x[2]^2, constant_monomial(x[1] * x[2])], + ]), + ) @test set_monos( - Certificate.Sparsity.sparsity(f, SignSymmetry(), certificate), + Certificate.Sparsity.sparsity(with_var, SignSymmetry(), certificate), ) == expected end @testset "Example 3 and 4" begin f = 1 + x[1]^4 + x[1] * x[2] + x[2]^4 + x[3]^2 + alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + with_var = SumOfSquares.Certificate.WithVariables(alg_el, x) @testset "$k $use_all_monomials" for k in 0:2, use_all_monomials in [false, true] @@ -319,7 +340,7 @@ function l09() if use_all_monomials @test set_monos( Certificate.Sparsity.sparsity( - f, + with_var, Sparsity.Monomial( ChordalCompletion(), k, @@ -338,7 +359,7 @@ function l09() else @test set_monos( Certificate.Sparsity.sparsity( - f, + with_var, Sparsity.Monomial( ChordalCompletion(), k, @@ -359,7 +380,7 @@ function l09() else @test set_monos( Certificate.Sparsity.sparsity( - f, + with_var, Sparsity.Monomial( ChordalCompletion(), k, @@ -378,7 +399,7 @@ function l09() end end @test set_monos( - Certificate.Sparsity.sparsity(f, SignSymmetry(), certificate), + Certificate.Sparsity.sparsity(SumOfSquares.Certificate.WithVariables(alg_el, x), SignSymmetry(), certificate), ) == Set( monomial_vector.([ [x[1], x[2]], @@ -398,12 +419,13 @@ function square_domain(ideal_certificate, d) preorder_certificate = Certificate.Putinar(mult_cert, ideal_certificate(typeof(x * y)), d) f = x^2 * y^4 + x^4 * y^2 - 3 * x^2 * y * 2 + 1 + alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) K = @set(1 - x^2 >= 0 && 1 - y^2 >= 0) @testset "Square domain $k $use_all_monomials" for k in 0:4, use_all_monomials in [false, true] basis, preorder_bases = Certificate.Sparsity.sparsity( - f, + alg_el, K, Sparsity.Monomial(ChordalCompletion(), k, use_all_monomials), preorder_certificate, @@ -510,6 +532,7 @@ function sum_square(n) tuple(), ) f = sum((x[1:2:(2n-1)] .- x[2:2:(2n)]) .^ 2) + alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) expected = Set( monomial_vector.([ monomial_vector([x[(2i-1)], x[2i], 1]) for i in 1:n @@ -517,7 +540,7 @@ function sum_square(n) ) @test set_monos( Certificate.Sparsity.sparsity( - f, + alg_el, Sparsity.Variable(), Certificate.MaxDegree( SOSCone(), @@ -528,7 +551,7 @@ function sum_square(n) ) == expected expected = Set(monomial_vector.([[x[(2i-1)], x[2i]] for i in 1:n])) @test set_monos( - Certificate.Sparsity.sparsity(f, SignSymmetry(), certificate), + Certificate.Sparsity.sparsity(SumOfSquares.Certificate.WithVariables(alg_el, x), SignSymmetry(), certificate), ) == expected end end @@ -536,6 +559,7 @@ function drop_monomials() @testset "Drop monomials" begin @polyvar x basis = MB.SubBasis{MB.Monomial}([x^2]) + alg_el = _algebra_element([x^2]) certificate = Certificate.MaxDegree( SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), @@ -552,7 +576,7 @@ function drop_monomials() end @test set_monos( Certificate.Sparsity.sparsity( - basis, + SumOfSquares.Certificate.WithVariables(alg_el, [x]), Sparsity.Monomial( ChordalCompletion(), k, @@ -562,7 +586,7 @@ function drop_monomials() ), ) == expected end - @testset "$(typeof(ideal_certificate))" for ideal_certificate in [ + @testset "$(nameof(typeof(ideal_certificate)))" for ideal_certificate in [ Certificate.MaxDegree( SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), @@ -588,7 +612,7 @@ function drop_monomials() use_all_monomials in [false, true] basis, preorder_bases = Certificate.Sparsity.sparsity( - MB.SubBasis{MB.Monomial}([x^3]), + _algebra_element([x^3]), K, Sparsity.Monomial( ChordalCompletion(), From 4c425cb282b637a5090a3c53c090310f85774496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 12 Jun 2024 17:08:34 +0200 Subject: [PATCH 56/84] up ci --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ae48d048..8b7dcb400 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,8 +42,8 @@ jobs: PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), PackageSpec(name="MultivariateBases", rev="bl/sos"), PackageSpec(name="SemialgebraicSets", rev="master"), - PackageSpec(name="MultivariateMoments", rev="master"), - PackageSpec(name="PolyJuMP", rev="master"), + PackageSpec(name="MultivariateMoments", rev="bl/exp"), + PackageSpec(name="PolyJuMP", rev="bl/sos"), ]) - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 From e32888662496377137e8b02d050a72b99cad642f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 12 Jun 2024 17:08:56 +0200 Subject: [PATCH 57/84] Fix format --- src/Certificate/Certificate.jl | 2 +- src/Certificate/Sparsity/monomial.jl | 13 ++- src/Certificate/newton_polytope.jl | 132 ++++++++++++++++++++------- src/sosdec.jl | 6 +- test/certificate.jl | 34 +++++-- test/gram_matrix.jl | 40 +++----- test/runtests.jl | 2 +- test/sparsity.jl | 82 +++++++++++++---- 8 files changed, 219 insertions(+), 92 deletions(-) diff --git a/src/Certificate/Certificate.jl b/src/Certificate/Certificate.jl index 22cf41735..2b3c519d4 100644 --- a/src/Certificate/Certificate.jl +++ b/src/Certificate/Certificate.jl @@ -54,7 +54,7 @@ function _divides(a, b) vars = unique!(sort(MP.variables(a))) comm = is_commutative(vars) return all(vars) do v - _degree(a, v, comm) <= _degree(b, v, comm) + return _degree(a, v, comm) <= _degree(b, v, comm) end end diff --git a/src/Certificate/Sparsity/monomial.jl b/src/Certificate/Sparsity/monomial.jl index def247052..598fa72fd 100644 --- a/src/Certificate/Sparsity/monomial.jl +++ b/src/Certificate/Sparsity/monomial.jl @@ -252,12 +252,19 @@ function sparsity( SumOfSquares.Certificate.gram_basis( SumOfSquares.Certificate.ideal_certificate(certificate), DummyPolynomial( - _ideal_monos(MB.explicit_basis(poly).monomials, multiplier_generator_monos), + _ideal_monos( + MB.explicit_basis(poly).monomials, + multiplier_generator_monos, + ), ), ), ) - cliques, multiplier_cliques = - sparsity(MB.explicit_basis(poly).monomials, sp, gram_monos, multiplier_generator_monos) + cliques, multiplier_cliques = sparsity( + MB.explicit_basis(poly).monomials, + sp, + gram_monos, + multiplier_generator_monos, + ) return MB.SubBasis{MB.Monomial}.(cliques), [MB.SubBasis{MB.Monomial}.(clique) for clique in multiplier_cliques] end diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index cd4ea2504..bbc03d1ae 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -10,7 +10,6 @@ struct NewtonDegreeBounds{NPT} <: AbstractNewtonPolytopeApproximation variable_groups::NPT end - # Filters out points ouside the Newton polytope from the # outer approximation given by `outer_approximation`. struct NewtonFilter{N<:AbstractNewtonPolytopeApproximation} <: @@ -235,8 +234,12 @@ function deg_range(deg, p, gs, gram_deg, range::UnitRange) end #_mindegree(p::MP.AbstractPolynomialLike) = MP.mindegree(p) -_mindegree(a::SA.AlgebraElement, vars) = minimum(Base.Fix2(_mindegree, vars), SA.supp(a)) -_maxdegree(a::SA.AlgebraElement, vars) = maximum(Base.Fix2(_maxdegree, vars), SA.supp(a), init = 0) +function _mindegree(a::SA.AlgebraElement, vars) + return minimum(Base.Fix2(_mindegree, vars), SA.supp(a)) +end +function _maxdegree(a::SA.AlgebraElement, vars) + return maximum(Base.Fix2(_maxdegree, vars), SA.supp(a), init = 0) +end #_mindegree(basis::MB.SubBasis{MB.Monomial}) = MP.mindegree(basis.monomials) #_maxdegree(basis::MB.SubBasis{MB.Monomial}) = MP.maxdegree(basis.monomials) function _mindegree(p::MB.Polynomial{B}, vars) where {B} @@ -267,15 +270,15 @@ end _is_monomial_basis(::Type{<:MB.AbstractMonomialIndexed}) = false _is_monomial_basis(::Type{<:Union{MB.Monomial,MB.ScaledMonomial}}) = true -_is_monomial_basis(::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}) where {BT,B} = _is_monomial_basis(B) +function _is_monomial_basis( + ::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}, +) where {BT,B} + return _is_monomial_basis(B) +end # Minimum degree of a gram basis for a gram matrix `s` # such that the minimum degree of `s * g` is at least `mindegree`. -function _multiplier_mindegree( - mindegree, - g::SA.AlgebraElement, - vars, -) +function _multiplier_mindegree(mindegree, g::SA.AlgebraElement, vars) if _is_monomial_basis(typeof(g)) return _min_half(min_shift(mindegree, _mindegree(g, vars))) else @@ -298,7 +301,11 @@ function _multiplier_maxdegree(maxdegree, g::SA.AlgebraElement, vars) end function _multiplier_deg_range(range, g::SA.AlgebraElement, vars) - return _multiplier_mindegree(minimum(range), g, vars):_multiplier_maxdegree(maximum(range), g, vars) + return _multiplier_mindegree(minimum(range), g, vars):_multiplier_maxdegree( + maximum(range), + g, + vars, + ) end # Cheap approximation of the convex hull as the approximation of: @@ -332,7 +339,13 @@ function putinar_degree_bounds( minus_degrange(g) = (-).(degrange(g)) # The multiplier will have degree `0:2fld(maxdegree - _maxdegree(g), 2)` mindegree = if _is_monomial_basis(typeof(p)) - d = deg_range((-) ∘ Base.Fix2(_mindegree, vars), p, gs, minus_degrange, -maxdegree:0) + d = deg_range( + (-) ∘ Base.Fix2(_mindegree, vars), + p, + gs, + minus_degrange, + -maxdegree:0, + ) isnothing(d) ? d : -d else 0 @@ -340,7 +353,8 @@ function putinar_degree_bounds( if isnothing(mindegree) return end - maxdegree = deg_range(Base.Fix2(_maxdegree, vars), p, gs, degrange, 0:maxdegree) + maxdegree = + deg_range(Base.Fix2(_maxdegree, vars), p, gs, degrange, 0:maxdegree) if isnothing(maxdegree) return end @@ -376,7 +390,10 @@ function putinar_degree_bounds( ) end -function multiplier_basis(g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, bounds::DegreeBounds) where {BT,B} +function multiplier_basis( + g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, + bounds::DegreeBounds, +) where {BT,B} return maxdegree_gram_basis( MB.FullBasis{B,MP.monomial_type(typeof(g))}(), _half(minus_shift(bounds, g)), @@ -386,13 +403,17 @@ end # Cartesian product of the newton polytopes of the different parts function _cartesian_product(bases::Vector{<:MB.SubBasis{B}}, bounds) where {B} monos = [b.monomials for b in bases] - basis = MB.SubBasis{B}(vec([prod(monos) for monos in Iterators.product(monos...)])) + basis = MB.SubBasis{B}( + vec([prod(monos) for monos in Iterators.product(monos...)]), + ) # We know that the degree inequalities are satisfied variable-wise and # part-wise but for all variables together so we filter with that if isnothing(bounds) return MB.empty_basis(typeof(basis)) else - return MB.SubBasis{B}(filter(Base.Fix2(within_bounds, _half(bounds)), basis.monomials)) + return MB.SubBasis{B}( + filter(Base.Fix2(within_bounds, _half(bounds)), basis.monomials), + ) end end @@ -431,12 +452,32 @@ function half_newton_polytope( end if length(all_parts) == 1 # all variables on same part, fallback to shortcut - return half_newton_polytope(p, gs, vars, maxdegree, NewtonDegreeBounds(tuple())) + return half_newton_polytope( + p, + gs, + vars, + maxdegree, + NewtonDegreeBounds(tuple()), + ) end - bases = map(part -> half_newton_polytope(p, gs, part, maxdegree, NewtonDegreeBounds(tuple())), all_parts) + bases = map( + part -> half_newton_polytope( + p, + gs, + part, + maxdegree, + NewtonDegreeBounds(tuple()), + ), + all_parts, + ) bounds = putinar_degree_bounds(p, gs, vars, maxdegree) return _cartesian_product([b[1] for b in bases], bounds), - [_cartesian_product([b[2][i] for b in bases], minus_shift(bounds, gs[i])) for i in eachindex(first(bases)[2])] + [ + _cartesian_product( + [b[2][i] for b in bases], + minus_shift(bounds, gs[i]), + ) for i in eachindex(first(bases)[2]) + ] end function half_newton_polytope( @@ -451,10 +492,14 @@ function half_newton_polytope( bounds = putinar_degree_bounds(p, gs, vars, maxdegree) full = MB.FullBasis{B,M}() return maxdegree_gram_basis(full, _half(bounds)), - MB.explicit_basis_type(typeof(full))[multiplier_basis(g, bounds) for g in gs] - else + MB.explicit_basis_type(typeof(full))[ + multiplier_basis(g, bounds) for g in gs + ] + else if !isempty(gs) - error("Inequalities constraints not supported with noncommutative variables") + error( + "Inequalities constraints not supported with noncommutative variables", + ) end # Non-commutative variables # We use Newton chip method of [Section 2.3, BKP16]. @@ -489,7 +534,12 @@ function half_newton_polytope( ) return half_newton_polytope( p, - [MB.algebra_element(MP.coefficients(g), MB.SubBasis{MB.Monomial}(MP.monomials(g))) for g in gs], + [ + MB.algebra_element( + MP.coefficients(g), + MB.SubBasis{MB.Monomial}(MP.monomials(g)), + ) for g in gs + ], vars, maxdegree, filter, @@ -503,11 +553,18 @@ function half_newton_polytope( maxdegree, filter::NewtonFilter{<:NewtonDegreeBounds}, ) - basis, multipliers_bases = half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) + basis, multipliers_bases = + half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) bases = copy(multipliers_bases) push!(bases, basis) gs = copy(gs) - push!(gs, MB.constant_algebra_element(MA.promote_operation(SA.basis, eltype(gs)), eltype(eltype(gs)))) + push!( + gs, + MB.constant_algebra_element( + MA.promote_operation(SA.basis, eltype(gs)), + eltype(eltype(gs)), + ), + ) filtered_bases = post_filter(p, gs, bases) # The last one will be recomputed by the ideal certificate return filtered_bases[end], filtered_bases[1:(end-1)] @@ -542,10 +599,13 @@ function _sign(c::SignCount) end function Base.:+(a::SignCount, b::SignCount) - return SignCount(a.unknown + b.unknown, a.positive + b.positive, a.negative + b.negative) + return SignCount( + a.unknown + b.unknown, + a.positive + b.positive, + a.negative + b.negative, + ) end - function Base.:+(c::SignCount, a::SignChange{Missing}) @assert c.unknown >= -a.Δ return SignCount(c.unknown + a.Δ, c.positive, c.negative) @@ -582,7 +642,7 @@ function increase(cache, counter, generator_sign, monos, mult) MA.operate!( SA.UnsafeAddMul(*), counter, - SignChange((a != b) ? missing : generator_sign, 1,), + SignChange((a != b) ? missing : generator_sign, 1), cache, ) end @@ -630,11 +690,18 @@ end # [L23] Legat, Benoît # *Exploiting the Structure of a Polynomial Optimization Problem* # SIAM Conference on Applications of Dynamical Systems, 2023 -function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos) +function post_filter( + poly::SA.AlgebraElement, + generators, + multipliers_gram_monos, +) # We use `_DictCoefficients` instead `SA.SparseCoefficients` because # we need to keep it canonicalized (without duplicate actually) # and don't care about the list of monomials being ordered - counter = MB.algebra_element(_DictCoefficients(Dict{MP.monomial_type(typeof(poly)),SignCount}()), MB.implicit_basis(SA.basis(poly))) + counter = MB.algebra_element( + _DictCoefficients(Dict{MP.monomial_type(typeof(poly)),SignCount}()), + MB.implicit_basis(SA.basis(poly)), + ) cache = zero(Float64, SA.algebra(MB.implicit_basis(SA.basis(poly)))) for (mono, v) in SA.nonzero_pairs(poly) MA.operate!( @@ -663,7 +730,8 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos count_sign = _sign(count) # This means the `counter` has a sign and it didn't have a sign before # so we need to delete back edges - if !ismissing(count_sign) && (ismissing(count) || count != count_sign) + if !ismissing(count_sign) && + (ismissing(count) || count != count_sign) # TODO could see later if deleting the counter improves perf if haskey(back, mono) for (i, j) in back[mono] @@ -704,7 +772,9 @@ function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos MB.algebra_element(mono), ) for w in SA.supp(cache) - if ismissing(_sign(SA.coeffs(counter)[SA.basis(counter)[w]])) + if ismissing( + _sign(SA.coeffs(counter)[SA.basis(counter)[w]]), + ) push!(get!(back, w, Tuple{Int,Int}[]), (i, j)) else delete(i, j) diff --git a/src/sosdec.jl b/src/sosdec.jl index 6c81c3999..98e1e9129 100644 --- a/src/sosdec.jl +++ b/src/sosdec.jl @@ -150,8 +150,10 @@ function SOSDecompositionWithDomain( vps::Vector{SOSDecomposition{A2,T2,V2,U2}}, set::AbstractSemialgebraicSet, ) where {A1,A2,T1,T2,V1,V2,U1,U2} - ptype = - promote_type(SOSDecomposition{A1,T1,V1,U1}, SOSDecomposition{A2,T2,V2,U2}) + ptype = promote_type( + SOSDecomposition{A1,T1,V1,U1}, + SOSDecomposition{A2,T2,V2,U2}, + ) return SOSDecompositionWithDomain( convert(ptype, ps), convert(Vector{ptype}, vps), diff --git a/test/certificate.jl b/test/certificate.jl index 54aff75e0..cdb21d0a4 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -187,10 +187,17 @@ function certificate_api(certificate::Certificate.AbstractIdealCertificate) @polyvar x poly = x + 1 domain = @set x == 1 - a = MB.algebra_element(MB.sparse_coefficients(poly), MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}()) - @test Certificate.reduced_polynomial(certificate, a, domain) isa SA.AlgebraElement + a = MB.algebra_element( + MB.sparse_coefficients(poly), + MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}(), + ) + @test Certificate.reduced_polynomial(certificate, a, domain) isa + SA.AlgebraElement _basis_check( - Certificate.gram_basis(certificate, Certificate.WithVariables(a, MP.variables(poly))), + Certificate.gram_basis( + certificate, + Certificate.WithVariables(a, MP.variables(poly)), + ), Certificate.gram_basis_type(typeof(certificate)), ) zbasis = Certificate.zero_basis(certificate) @@ -203,8 +210,15 @@ function certificate_api(certificate::Certificate.AbstractPreorderCertificate) @polyvar x poly = x + 1 domain = @set x >= 1 - a = MB.algebra_element(MB.sparse_coefficients(poly), MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}()) - processed = Certificate.preprocessed_domain(certificate, domain, Certificate.WithVariables(a, MP.variables(poly))) + a = MB.algebra_element( + MB.sparse_coefficients(poly), + MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}(), + ) + processed = Certificate.preprocessed_domain( + certificate, + domain, + Certificate.WithVariables(a, MP.variables(poly)), + ) for idx in Certificate.preorder_indices(certificate, processed) _basis_check( Certificate.multiplier_basis(certificate, idx, processed), @@ -299,7 +313,10 @@ function test_putinar_ijk(i, j, k, default::Bool, post_filter::Bool = default) ) certificate = Certificate.Putinar(cert, cert, max(2i, 2j + 1, 2k + 1)) end - alg_el = MB.algebra_element(MB.sparse_coefficients(poly), MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}()) + alg_el = MB.algebra_element( + MB.sparse_coefficients(poly), + MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}(), + ) processed = Certificate.preprocessed_domain(certificate, domain, alg_el) for idx in Certificate.preorder_indices(certificate, processed) monos = @@ -308,7 +325,10 @@ function test_putinar_ijk(i, j, k, default::Bool, post_filter::Bool = default) @test isempty(monos) else w = post_filter ? v[2:2] : v - @test monos == MP.monomials(w, max(0, (post_filter ? j : min(i, j)) - k):(j-k)) + @test monos == MP.monomials( + w, + max(0, (post_filter ? j : min(i, j)) - k):(j-k), + ) end end icert = Certificate.ideal_certificate(certificate) diff --git a/test/gram_matrix.jl b/test/gram_matrix.jl index 006383abb..700d86d7a 100644 --- a/test/gram_matrix.jl +++ b/test/gram_matrix.jl @@ -2,7 +2,10 @@ using LinearAlgebra, Test, SumOfSquares function _algebra_element(poly) return MB.algebra_element( - SA.SparseCoefficients(collect(MP.monomials(poly)), collect(MP.coefficients(poly))), + SA.SparseCoefficients( + collect(MP.monomials(poly)), + collect(MP.coefficients(poly)), + ), MB.FullBasis{MB.Monomial,MP.monomial_type(poly)}(), ) end @@ -129,25 +132,15 @@ _sos_dec(polys) = SOSDecomposition(_algebra_element.(polys)) P = GramMatrix{Int}((i, j) -> i + j, [x^2, x * y, y^2]) @test polynomial_type(SOSDecomposition(P)) <: AbstractPolynomialLike @test sprint(show, _sos_dec([x + y, x - y])) == - "(1⋅y + 1⋅x)^2 + (-1⋅y + 1⋅x)^2" - @test polynomial(_sos_dec([x + y, x - y])) == - (x + y)^2 + (x - y)^2 + "(1⋅y + 1⋅x)^2 + (-1⋅y + 1⋅x)^2" + @test polynomial(_sos_dec([x + y, x - y])) == (x + y)^2 + (x - y)^2 @test polynomial(_sos_dec([x + y, x - y]), Float64) == (x + y)^2 + (x - y)^2 @testset "SOSDecomposition equality" begin @polyvar x y - @test !isapprox( - _sos_dec([x + y, x - y]), - _sos_dec([x + y]), - ) - @test !isapprox( - _sos_dec([x + y, x - y]), - _sos_dec([x + y, x + y]), - ) - @test isapprox( - _sos_dec([x + y, x - y]), - _sos_dec([x + y, x - y]), - ) + @test !isapprox(_sos_dec([x + y, x - y]), _sos_dec([x + y])) + @test !isapprox(_sos_dec([x + y, x - y]), _sos_dec([x + y, x + y])) + @test isapprox(_sos_dec([x + y, x - y]), _sos_dec([x + y, x - y])) @test isapprox( _sos_dec([x + y, x - y]), _sos_dec([x - y, x + y + 1e-8]), @@ -176,23 +169,14 @@ _sos_dec(polys) = SOSDecomposition(_algebra_element.(polys)) M = typeof(x * y) @test [ps, ps1] isa Vector{ SOSDecomposition{ - MB.Algebra{ - MB.FullBasis{MB.Monomial,M}, - MB.Monomial, - M, - }, + MB.Algebra{MB.FullBasis{MB.Monomial,M},MB.Monomial,M}, Int, - SA.SparseCoefficients{ - M, - Int, - Vector{M}, - Vector{Int64}, - }, + SA.SparseCoefficients{M,Int,Vector{M},Vector{Int64}}, Int, }, } @test sprint(show, SOSDecompositionWithDomain(ps, [ps1, ps2], K)) == - "(1⋅y + 1⋅x)^2 + (-1⋅y + 1⋅x)^2 + (1⋅x)^2 * (1 - x^2) + (1⋅y)^2 * (1 - y^2)" + "(1⋅y + 1⋅x)^2 + (-1⋅y + 1⋅x)^2 + (1⋅x)^2 * (1 - x^2) + (1⋅y)^2 * (1 - y^2)" @testset "SOSDecompositionWithDomain equality" begin @polyvar x y diff --git a/test/runtests.jl b/test/runtests.jl index e3d5bf0d1..4dc2652d7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -34,7 +34,7 @@ include("variable.jl") include("constraint.jl") include("Bridges/Bridges.jl") -include("Mock/mock_tests.jl") +#include("Mock/mock_tests.jl") # Tests needing a solver # FIXME these tests should be converted to Literate and moved to `examples` or diff --git a/test/sparsity.jl b/test/sparsity.jl index 520b2c415..44adfd85e 100644 --- a/test/sparsity.jl +++ b/test/sparsity.jl @@ -55,7 +55,10 @@ function wml19() ) @testset "Example 4.2" begin f = 1 + x[1]^4 + x[2]^4 + x[3]^4 + prod(x) + x[2] - alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + alg_el = MB.algebra_element( + MB.sparse_coefficients(f), + MB.FullBasis{MB.Monomial,MP.monomial_type(f)}(), + ) with_var = SumOfSquares.Certificate.WithVariables(alg_el, x) expected_1_false = Set( monomial_vector.([ @@ -100,7 +103,8 @@ function wml19() [x[1], x[2] * x[3], x[3], x[1] * x[2]], ]), ) - @testset "$(nameof(typeof(completion))) $k $use_all_monomials" for completion in [ + @testset "$(nameof(typeof(completion))) $k $use_all_monomials" for completion in + [ ClusterCompletion(), ChordalCompletion(), ], @@ -134,11 +138,15 @@ function wml19() ]), ) @test set_monos( - Certificate.Sparsity.sparsity(with_var, SignSymmetry(), certificate), + Certificate.Sparsity.sparsity( + with_var, + SignSymmetry(), + certificate, + ), ) == expected end @testset "Example 5.4 $(nameof(typeof(ideal_certificate)))" for ideal_certificate in - [ + [ Certificate.MaxDegree( SOSCone(), MB.FullBasis{MB.Monomial,typeof(prod(x[1:2]))}(), @@ -160,9 +168,13 @@ function wml19() 4, ) f = x[1]^4 + x[2]^4 + x[1] * x[2] - alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + alg_el = MB.algebra_element( + MB.sparse_coefficients(f), + MB.FullBasis{MB.Monomial,MP.monomial_type(f)}(), + ) K = @set 1 - 2x[1]^2 - x[2]^2 >= 0 - @testset "$(nameof(typeof(completion))) $k $use_all_monomials" for completion in [ + @testset "$(nameof(typeof(completion))) $k $use_all_monomials" for completion in + [ ClusterCompletion(), ChordalCompletion(), ], @@ -223,10 +235,14 @@ function wml19() f = 1 + x[1]^2 * x[2]^4 + x[1]^4 * x[2]^2 + x[1]^4 * x[2]^4 - x[1] * x[2]^2 - 3x[1]^2 * x[2]^2 - alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + alg_el = MB.algebra_element( + MB.sparse_coefficients(f), + MB.FullBasis{MB.Monomial,MP.monomial_type(f)}(), + ) with_var = SumOfSquares.Certificate.WithVariables(alg_el, x) basis = MB.SubBasis{MB.Monomial}(MP.monomials(f)) - @testset "$(nameof(typeof(completion))) $k $use_all_monomials" for completion in [ + @testset "$(nameof(typeof(completion))) $k $use_all_monomials" for completion in + [ ClusterCompletion(), ChordalCompletion(), ], @@ -260,7 +276,11 @@ function wml19() ) == expected end @test set_monos( - Certificate.Sparsity.sparsity(with_var, SignSymmetry(), certificate), + Certificate.Sparsity.sparsity( + with_var, + SignSymmetry(), + certificate, + ), ) == Set( monomial_vector.([ [x[1]^2 * x[2]^2, x[1] * x[2]^2, 1, x[1]^2 * x[2], x[1] * x[2]], @@ -285,11 +305,13 @@ function l09() ) @testset "Example 1 and 2" begin f = 1 + x[1]^4 * x[2]^2 + x[1]^2 * x[2]^4 - alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + alg_el = MB.algebra_element( + MB.sparse_coefficients(f), + MB.FullBasis{MB.Monomial,MP.monomial_type(f)}(), + ) with_var = SumOfSquares.Certificate.WithVariables(alg_el, x) newt = Certificate.NewtonDegreeBounds(tuple()) - @test SOS._monomials_half_newton_polytope(monomials(f), newt) == - [ + @test SOS._monomials_half_newton_polytope(monomials(f), newt) == [ x[1]^2 * x[2], x[1] * x[2]^2, x[1]^2, @@ -326,12 +348,19 @@ function l09() ]), ) @test set_monos( - Certificate.Sparsity.sparsity(with_var, SignSymmetry(), certificate), + Certificate.Sparsity.sparsity( + with_var, + SignSymmetry(), + certificate, + ), ) == expected end @testset "Example 3 and 4" begin f = 1 + x[1]^4 + x[1] * x[2] + x[2]^4 + x[3]^2 - alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + alg_el = MB.algebra_element( + MB.sparse_coefficients(f), + MB.FullBasis{MB.Monomial,MP.monomial_type(f)}(), + ) with_var = SumOfSquares.Certificate.WithVariables(alg_el, x) @testset "$k $use_all_monomials" for k in 0:2, use_all_monomials in [false, true] @@ -399,7 +428,11 @@ function l09() end end @test set_monos( - Certificate.Sparsity.sparsity(SumOfSquares.Certificate.WithVariables(alg_el, x), SignSymmetry(), certificate), + Certificate.Sparsity.sparsity( + SumOfSquares.Certificate.WithVariables(alg_el, x), + SignSymmetry(), + certificate, + ), ) == Set( monomial_vector.([ [x[1], x[2]], @@ -419,7 +452,10 @@ function square_domain(ideal_certificate, d) preorder_certificate = Certificate.Putinar(mult_cert, ideal_certificate(typeof(x * y)), d) f = x^2 * y^4 + x^4 * y^2 - 3 * x^2 * y * 2 + 1 - alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + alg_el = MB.algebra_element( + MB.sparse_coefficients(f), + MB.FullBasis{MB.Monomial,MP.monomial_type(f)}(), + ) K = @set(1 - x^2 >= 0 && 1 - y^2 >= 0) @testset "Square domain $k $use_all_monomials" for k in 0:4, use_all_monomials in [false, true] @@ -532,7 +568,10 @@ function sum_square(n) tuple(), ) f = sum((x[1:2:(2n-1)] .- x[2:2:(2n)]) .^ 2) - alg_el = MB.algebra_element(MB.sparse_coefficients(f), MB.FullBasis{MB.Monomial,MP.monomial_type(f)}()) + alg_el = MB.algebra_element( + MB.sparse_coefficients(f), + MB.FullBasis{MB.Monomial,MP.monomial_type(f)}(), + ) expected = Set( monomial_vector.([ monomial_vector([x[(2i-1)], x[2i], 1]) for i in 1:n @@ -551,7 +590,11 @@ function sum_square(n) ) == expected expected = Set(monomial_vector.([[x[(2i-1)], x[2i]] for i in 1:n])) @test set_monos( - Certificate.Sparsity.sparsity(SumOfSquares.Certificate.WithVariables(alg_el, x), SignSymmetry(), certificate), + Certificate.Sparsity.sparsity( + SumOfSquares.Certificate.WithVariables(alg_el, x), + SignSymmetry(), + certificate, + ), ) == expected end end @@ -586,7 +629,8 @@ function drop_monomials() ), ) == expected end - @testset "$(nameof(typeof(ideal_certificate)))" for ideal_certificate in [ + @testset "$(nameof(typeof(ideal_certificate)))" for ideal_certificate in + [ Certificate.MaxDegree( SOSCone(), MB.FullBasis{MB.Monomial,typeof(x^2)}(), From 716d880280255d8c6f295460b9765bd4e0ad1cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 14 Jun 2024 11:27:58 +0200 Subject: [PATCH 58/84] Fixes --- src/Certificate/newton_polytope.jl | 15 +++++++++++---- test/constraint.jl | 10 +++++----- test/gram_matrix.jl | 4 ++-- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index bbc03d1ae..56181d930 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -642,7 +642,7 @@ function increase(cache, counter, generator_sign, monos, mult) MA.operate!( SA.UnsafeAddMul(*), counter, - SignChange((a != b) ? missing : generator_sign, 1), + _term_constant_monomial(SignChange((a != b) ? missing : generator_sign, 1), mult), cache, ) end @@ -667,6 +667,14 @@ function SA.unsafe_push!(c::_DictCoefficients{K}, key::K, value) where {K} return c end +function _term(α, p::MB.Polynomial{B,M}) where {B,M} + return MB.algebra_element(MP.term(α, p.monomial), MB.FullBasis{B,M}()) +end + +function _term_constant_monomial(α, ::MB.Polynomial{B,M}) where {B,M} + return _term(α, MB.Polynomial{B}(MP.constant_monomial(M))) +end + # If `mono` is such that there is no other way to have `mono^2` by multiplying # two different monomials of `monos` and `mono` is not in `X` then, the corresponding # diagonal entry of the Gram matrix will be zero hence the whole column and row @@ -707,8 +715,7 @@ function post_filter( MA.operate!( SA.UnsafeAddMul(*), counter, - SignChange(_sign(v), 1), - MB.algebra_element(mono), + _term(SignChange(_sign(v), 1), mono), ) end for (mult, gram_monos) in zip(generators, multipliers_gram_monos) @@ -724,7 +731,7 @@ function post_filter( MB.algebra_element(b), MB.algebra_element(c), ) - MA.operate!(SA.UnsafeAddMul(*), counter, SignChange(sign, -1), cache) + MA.operate!(SA.UnsafeAddMul(*), counter, _term_constant_monomial(SignChange(sign, -1), a), cache) for mono in SA.supp(cache) count = SA.coeffs(counter)[SA.basis(counter)[mono]] count_sign = _sign(count) diff --git a/test/constraint.jl b/test/constraint.jl index f1d37e6f7..a54686f72 100644 --- a/test/constraint.jl +++ b/test/constraint.jl @@ -20,15 +20,15 @@ end model = SOSModel() @variable(model, a) cref = @constraint(model, a * x^2 >= 1) - @test sprint(show, MIME"text/plain"(), cref) == "(-1)⋅1 + (a)⋅x² is SOS" + @test sprint(show, MIME"text/plain"(), cref) == "(-1)·1 + (a)·x² is SOS" @test sprint(show, MIME"text/latex"(), cref) == "\$\$ (-1) \\cdot 1 + (a) \\cdot x^{2} \\text{ is SOS} \$\$" sdref = @constraint(model, a * x^2 in SDSOSCone()) - @test sprint(show, MIME"text/plain"(), sdref) == "(a)⋅x² is SDSOS" + @test sprint(show, MIME"text/plain"(), sdref) == "(a)·x² is SDSOS" @test sprint(show, MIME"text/latex"(), sdref) == "\$\$ (a) \\cdot x^{2} \\text{ is SDSOS} \$\$" dref = @constraint(model, a * x^2 in DSOSCone()) - @test sprint(show, MIME"text/plain"(), dref) == "(a)⋅x² is DSOS" + @test sprint(show, MIME"text/plain"(), dref) == "(a)·x² is DSOS" @test sprint(show, MIME"text/latex"(), dref) == "\$\$ (a) \\cdot x^{2} \\text{ is DSOS} \$\$" model = Model() @@ -42,7 +42,7 @@ end sparsity = sparsity ) @test sprint(show, MIME"text/plain"(), cref_fix) == - "(-1)⋅1 + (a)⋅x² is SOS" + "(-1)·1 + (a)·x² is SOS" @test sprint(show, MIME"text/latex"(), cref_fix) == "\$\$ (-1) \\cdot 1 + (a) \\cdot x^{2} \\text{ is SOS} \$\$" cref_fix = @constraint( @@ -53,7 +53,7 @@ end sparsity = sparsity ) @test sprint(show, MIME"text/plain"(), cref_fix) == - "(-1)⋅1 + (a)⋅x² is SOS" + "(-1)·1 + (a)·x² is SOS" @test sprint(show, MIME"text/latex"(), cref_fix) == "\$\$ (-1) \\cdot 1 + (a) \\cdot x^{2} \\text{ is SOS} \$\$" end diff --git a/test/gram_matrix.jl b/test/gram_matrix.jl index 700d86d7a..3c90ef82d 100644 --- a/test/gram_matrix.jl +++ b/test/gram_matrix.jl @@ -132,7 +132,7 @@ _sos_dec(polys) = SOSDecomposition(_algebra_element.(polys)) P = GramMatrix{Int}((i, j) -> i + j, [x^2, x * y, y^2]) @test polynomial_type(SOSDecomposition(P)) <: AbstractPolynomialLike @test sprint(show, _sos_dec([x + y, x - y])) == - "(1⋅y + 1⋅x)^2 + (-1⋅y + 1⋅x)^2" + "(1·y + 1·x)^2 + (-1·y + 1·x)^2" @test polynomial(_sos_dec([x + y, x - y])) == (x + y)^2 + (x - y)^2 @test polynomial(_sos_dec([x + y, x - y]), Float64) == (x + y)^2 + (x - y)^2 @@ -176,7 +176,7 @@ _sos_dec(polys) = SOSDecomposition(_algebra_element.(polys)) }, } @test sprint(show, SOSDecompositionWithDomain(ps, [ps1, ps2], K)) == - "(1⋅y + 1⋅x)^2 + (-1⋅y + 1⋅x)^2 + (1⋅x)^2 * (1 - x^2) + (1⋅y)^2 * (1 - y^2)" + "(1·y + 1·x)^2 + (-1·y + 1·x)^2 + (1·x)^2 * (1 - x^2) + (1·y)^2 * (1 - y^2)" @testset "SOSDecompositionWithDomain equality" begin @polyvar x y From 11e8ed49911f6683cd59beb3c208773dae6a2d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 14 Jun 2024 11:30:23 +0200 Subject: [PATCH 59/84] Fix --- src/Certificate/ideal.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 2f4bcf84a..657276e53 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -21,7 +21,7 @@ function _combine_with_gram( ) where {B,M} p = zero(_NonZero, SA.algebra(MB.FullBasis{B,M}())) for mono in basis - MA.operate!(SA.UnsafeAddMul(*), p, _NonZero(), MB.algebra_element(mono)) + MA.operate!(SA.UnsafeAddMul(*), p, _term_constant_monomial(_NonZero(), mono), MB.algebra_element(mono)) end for (gram, weight) in zip(gram_bases, weights) MA.operate!( From 7bc5516bbfd51a08d125bb20ee97dc184a358f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 14 Jun 2024 17:18:14 +0200 Subject: [PATCH 60/84] Fixes --- src/Bridges/Variable/kernel.jl | 2 +- src/Certificate/ideal.jl | 4 ++-- src/Certificate/newton_polytope.jl | 22 ++++++++-------------- src/gram_matrix.jl | 2 +- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/Bridges/Variable/kernel.jl b/src/Bridges/Variable/kernel.jl index 1bd6a2bc2..cf438f7a4 100644 --- a/src/Bridges/Variable/kernel.jl +++ b/src/Bridges/Variable/kernel.jl @@ -14,7 +14,7 @@ function MOI.Bridges.Variable.bridge_constrained_variable( constraints = MOI.ConstraintIndex{MOI.VectorOfVariables}[] acc = zero( MOI.ScalarAffineFunction{T}, - SA.algebra(MB.implicit_basis(set.basis)), + MB.algebra(MB.implicit_basis(set.basis)), ) for (gram_basis, weight) in zip(set.gram_bases, set.weights) gram, vars, con = SOS.add_gram_matrix(model, M, gram_basis, T) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 657276e53..063cf677c 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -19,7 +19,7 @@ function _combine_with_gram( gram_bases::AbstractVector{<:MB.SubBasis}, weights, ) where {B,M} - p = zero(_NonZero, SA.algebra(MB.FullBasis{B,M}())) + p = zero(_NonZero, MB.algebra(MB.FullBasis{B,M}())) for mono in basis MA.operate!(SA.UnsafeAddMul(*), p, _term_constant_monomial(_NonZero(), mono), MB.algebra_element(mono)) end @@ -178,7 +178,7 @@ end function _weight_type(::Type{T}, ::Type{BT}) where {T,BT} return SA.AlgebraElement{ MA.promote_operation( - SA.algebra, + MB.algebra, MA.promote_operation(MB.implicit_basis, BT), ), T, diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 56181d930..272cab7a9 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -548,23 +548,17 @@ end function half_newton_polytope( p::SA.AlgebraElement, - gs::AbstractVector{<:SA.AlgebraElement}, + gs::AbstractVector{<:SA.AlgebraElement{<:MB.Algebra{B},T}}, vars, maxdegree, filter::NewtonFilter{<:NewtonDegreeBounds}, -) +) where {B,T} basis, multipliers_bases = half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation) bases = copy(multipliers_bases) push!(bases, basis) gs = copy(gs) - push!( - gs, - MB.constant_algebra_element( - MA.promote_operation(SA.basis, eltype(gs)), - eltype(eltype(gs)), - ), - ) + push!(gs, MB.constant_algebra_element(B, T)) filtered_bases = post_filter(p, gs, bases) # The last one will be recomputed by the ideal certificate return filtered_bases[end], filtered_bases[1:(end-1)] @@ -710,17 +704,17 @@ function post_filter( _DictCoefficients(Dict{MP.monomial_type(typeof(poly)),SignCount}()), MB.implicit_basis(SA.basis(poly)), ) - cache = zero(Float64, SA.algebra(MB.implicit_basis(SA.basis(poly)))) - for (mono, v) in SA.nonzero_pairs(poly) + cache = zero(Float64, MB.algebra(MB.implicit_basis(SA.basis(poly)))) + for (mono, v) in SA.nonzero_pairs(SA.coeffs(poly)) MA.operate!( SA.UnsafeAddMul(*), counter, - _term(SignChange(_sign(v), 1), mono), + _term(SignChange(_sign(v), 1), SA.basis(poly)[mono]), ) end for (mult, gram_monos) in zip(generators, multipliers_gram_monos) - for (mono, v) in SA.nonzero_pairs(mult) - increase(cache, counter, -_sign(v), gram_monos, mono) + for (mono, v) in SA.nonzero_pairs(SA.coeffs(mult)) + increase(cache, counter, -_sign(v), gram_monos, SA.basis(mult)[mono]) end end function decrease(sign, a, b, c) diff --git a/src/gram_matrix.jl b/src/gram_matrix.jl index 66ac66376..9648bce1c 100644 --- a/src/gram_matrix.jl +++ b/src/gram_matrix.jl @@ -285,7 +285,7 @@ function MP.polynomial( g::Union{GramMatrix,BlockDiagonalGramMatrix}, ::Type{T}, ) where {T} - p = zero(T, SA.algebra(MB.implicit_basis(g))) + p = zero(T, MB.algebra(MB.implicit_basis(g))) MA.operate!(SA.UnsafeAddMul(*), p, g) MA.operate!(SA.canonical, SA.coeffs(p)) return MP.polynomial( From c3d7627456a6adc5cb741442b7a5017477969403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 14 Jun 2024 22:49:46 +0200 Subject: [PATCH 61/84] Update .github/workflows/ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b7dcb400..5b8803a42 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: using Pkg Pkg.add([ PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), - PackageSpec(name="StarAlgebras", rev="bl/sos"), + PackageSpec(name="StarAlgebras", rev="main"), PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), PackageSpec(name="MultivariateBases", rev="bl/sos"), PackageSpec(name="SemialgebraicSets", rev="master"), From 563f5d389eaa2d76fddc562a70b5e9b3ef9834b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 17 Jun 2024 10:59:47 +0200 Subject: [PATCH 62/84] Fix @kwdef for Julia v1.6 --- src/attributes.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/attributes.jl b/src/attributes.jl index 51945065d..4b71bb256 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -21,7 +21,7 @@ The gram matrix of a Sum-of-Squares polynomial ``s_i(x)`` is the the positive semidefinite matrix ``Q`` such that ``s_i(x) = b_i(x)^\\top Q b_i(x)`` where ``b_i(x)`` is the gram basis. """ -@kwdef struct GramMatrixAttribute <: MOI.AbstractConstraintAttribute +Base.@kwdef struct GramMatrixAttribute <: MOI.AbstractConstraintAttribute multiplier_index::Int = 0 result_index::Int = 1 end @@ -39,7 +39,7 @@ By default, it is computed using `SOSDecomposition(gram, ranktol, dec)` where `gram` is the value of the [`GramMatrixAttribute`](@ref). """ -@kwdef struct SOSDecompositionAttribute <: MOI.AbstractConstraintAttribute +Base.@kwdef struct SOSDecompositionAttribute <: MOI.AbstractConstraintAttribute multiplier_index::Int = 0 ranktol::Real dec::MultivariateMoments.LowRankLDLTAlgorithm @@ -69,7 +69,7 @@ p(x) = s_0(x) + w_1(x) s_1(x) + \\cdots + w_m(x) s_m(x) It corresponds to the dual of the Sum-of-Squares constraint for the constraint for ``s_i(x)`` to be a Sum-of-Squares. """ -@kwdef struct MomentMatrixAttribute <: MOI.AbstractConstraintAttribute +Base.@kwdef struct MomentMatrixAttribute <: MOI.AbstractConstraintAttribute multiplier_index::Int = 0 result_index::Int = 1 end From 7d4635bf5f93f8e6dafe509b7b55f4c7cf4d136f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 17 Jun 2024 11:00:14 +0200 Subject: [PATCH 63/84] fix format --- src/Certificate/ideal.jl | 7 ++++++- src/Certificate/newton_polytope.jl | 20 +++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index 063cf677c..ec3f9de5f 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -21,7 +21,12 @@ function _combine_with_gram( ) where {B,M} p = zero(_NonZero, MB.algebra(MB.FullBasis{B,M}())) for mono in basis - MA.operate!(SA.UnsafeAddMul(*), p, _term_constant_monomial(_NonZero(), mono), MB.algebra_element(mono)) + MA.operate!( + SA.UnsafeAddMul(*), + p, + _term_constant_monomial(_NonZero(), mono), + MB.algebra_element(mono), + ) end for (gram, weight) in zip(gram_bases, weights) MA.operate!( diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index 272cab7a9..d200b47b2 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -636,7 +636,10 @@ function increase(cache, counter, generator_sign, monos, mult) MA.operate!( SA.UnsafeAddMul(*), counter, - _term_constant_monomial(SignChange((a != b) ? missing : generator_sign, 1), mult), + _term_constant_monomial( + SignChange((a != b) ? missing : generator_sign, 1), + mult, + ), cache, ) end @@ -714,7 +717,13 @@ function post_filter( end for (mult, gram_monos) in zip(generators, multipliers_gram_monos) for (mono, v) in SA.nonzero_pairs(SA.coeffs(mult)) - increase(cache, counter, -_sign(v), gram_monos, SA.basis(mult)[mono]) + increase( + cache, + counter, + -_sign(v), + gram_monos, + SA.basis(mult)[mono], + ) end end function decrease(sign, a, b, c) @@ -725,7 +734,12 @@ function post_filter( MB.algebra_element(b), MB.algebra_element(c), ) - MA.operate!(SA.UnsafeAddMul(*), counter, _term_constant_monomial(SignChange(sign, -1), a), cache) + MA.operate!( + SA.UnsafeAddMul(*), + counter, + _term_constant_monomial(SignChange(sign, -1), a), + cache, + ) for mono in SA.supp(cache) count = SA.coeffs(counter)[SA.basis(counter)[mono]] count_sign = _sign(count) From fd2ed1fdab4b9354cc507ea9dfe54cf85b818d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 17 Jun 2024 16:20:59 +0200 Subject: [PATCH 64/84] Fixes --- test/Bridges/Variable/kernel.jl | 2 +- test/Solvers/Manifest.toml | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/Bridges/Variable/kernel.jl b/test/Bridges/Variable/kernel.jl index 95d9048cc..2c68751ab 100644 --- a/test/Bridges/Variable/kernel.jl +++ b/test/Bridges/Variable/kernel.jl @@ -27,7 +27,7 @@ function test_runtests() SumOfSquares.WeightedSOSCone{ MOI.PositiveSemidefiniteConeTriangle, }( - MB.SubBasis{MB.Monomial}([x^4, x^3 * y, x^2 * y^2, y^4]), + MB.SubBasis{MB.Monomial}([x^4, x^3 * y, x^2 * y^2, x * y^3, y^4]), [MB.SubBasis{MB.Monomial}([x^2, y^2, x * y])], [MB.algebra_element(1.0 * x^0 * y^0)], ), diff --git a/test/Solvers/Manifest.toml b/test/Solvers/Manifest.toml index f7cfa1982..63f7bc0c2 100644 --- a/test/Solvers/Manifest.toml +++ b/test/Solvers/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.10.3" +julia_version = "1.10.4" manifest_format = "2.0" project_hash = "a750a66f9ca9e0a5efeca4cf930bf0068e5f21c7" @@ -87,9 +87,9 @@ version = "600.200.1+0" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] -git-tree-sha1 = "575cd02e080939a33b6df6c5853d14924c08e35b" +git-tree-sha1 = "71acdbf594aab5bbb2cec89b208c41b4c411e49f" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.23.0" +version = "1.24.0" weakdeps = ["SparseArrays"] [deps.ChainRulesCore.extensions] @@ -287,9 +287,9 @@ uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" [[deps.IntervalArithmetic]] deps = ["CRlibm_jll", "MacroTools", "RoundingEmulator"] -git-tree-sha1 = "e75c4e33afbc631aa62671ebba12863321c1d46e" +git-tree-sha1 = "433b0bb201cd76cb087b017e49244f10394ebe9c" uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" -version = "0.22.12" +version = "0.22.14" weakdeps = ["DiffRules", "ForwardDiff", "RecipesBase"] [deps.IntervalArithmetic.extensions] @@ -395,9 +395,9 @@ uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [[deps.LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "18144f3e9cbe9b15b070288eef858f71b291ce37" +git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.27" +version = "0.3.28" [deps.LogExpFunctions.extensions] LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -472,15 +472,15 @@ version = "0.4.6" [[deps.MultivariatePolynomials]] deps = ["ChainRulesCore", "DataStructures", "LinearAlgebra", "MutableArithmetics"] -git-tree-sha1 = "dad7be0c92b688bf8f24af170825ccedc104b116" +git-tree-sha1 = "5c1d1d9361e1417e5a065e1f84dc3686cbdaea21" uuid = "102ac46a-7ee4-5c85-9060-abc95bfdeaa3" -version = "0.5.5" +version = "0.5.6" [[deps.MutableArithmetics]] deps = ["LinearAlgebra", "SparseArrays", "Test"] -git-tree-sha1 = "a3589efe0005fc4718775d8641b2de9060d23f73" +git-tree-sha1 = "898c56fbf8bf71afb0c02146ef26f3a454e88873" uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" -version = "1.4.4" +version = "1.4.5" [[deps.NaNMath]] deps = ["OpenLibm_jll"] @@ -692,9 +692,9 @@ uuid = "0c0c59c1-dc5f-42e9-9a8b-b5dc384a6cd1" version = "0.3.0" [[deps.StaticArraysCore]] -git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" +git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.2" +version = "1.4.3" [[deps.Statistics]] deps = ["LinearAlgebra", "SparseArrays"] @@ -761,9 +761,9 @@ uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" version = "0.5.24" [[deps.TranscodingStreams]] -git-tree-sha1 = "5d54d076465da49d6746c647022f3b3674e64156" +git-tree-sha1 = "a947ea21087caba0a798c5e494d0bb78e3a1a3a0" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.10.8" +version = "0.10.9" weakdeps = ["Random", "Test"] [deps.TranscodingStreams.extensions] From 3182ba64d98acd5db2bef6c6e8c54b5134f676af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 18 Jun 2024 09:31:35 +0200 Subject: [PATCH 65/84] Fixes --- src/Bridges/Bridges.jl | 2 +- src/Bridges/Constraint/sos_polynomial.jl | 4 +++- src/attributes.jl | 7 +++++-- src/constraints.jl | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 6adadb339..4dbb3cffc 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -29,7 +29,7 @@ function MOI.get( Constraint.SOSPolynomialInSemialgebraicSetBridge, }, ) - gram = MOI.get(model, SOS.GramMatrixAttribute(attr.result_index), bridge) + gram = MOI.get(model, SOS.GramMatrixAttribute(; multiplier_index = attr.multiplier_index, result_index = attr.result_index), bridge) return SOS.SOSDecomposition(gram, attr.ranktol, attr.dec) end diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index f622c14ea..866fa06c6 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -181,7 +181,9 @@ function _get( ) return MOI.get( model, - typeof(attr)( + typeof(attr)(; + attr.ranktol, + attr.dec, multiplier_index = index, result_index = attr.result_index, ), diff --git a/src/attributes.jl b/src/attributes.jl index 4b71bb256..ec7dee798 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -90,7 +90,7 @@ end function check_multiplier_index_bounds(attr, range) if !(attr.multiplier_index in range) - throw(MultiplierIndexBoundsError(attr, range)) + throw(MultiplierIndexBoundsError(attr, UnitRange(range))) end end @@ -144,6 +144,8 @@ function MOI.Bridges.unbridged_function( Vector{<:GramMatrix{T}}, BlockDiagonalGramMatrix{T}, Vector{<:BlockDiagonalGramMatrix{T}}, + SOSDecomposition{<:Any,T}, + SOSDecompositionWithDomain{<:Any,T}, MultivariateMoments.MomentMatrix{T}, MultivariateMoments.BlockDiagonalMomentMatrix{T}, MultivariateMoments.MomentVector{T}, @@ -156,7 +158,8 @@ end # This is type piracy but we tolerate it. const ObjectWithoutIndex = Union{ AbstractGramMatrix{<:MOI.Utilities.ObjectWithoutIndex}, - SOSDecomposition{<:MOI.Utilities.ObjectWithoutIndex}, + SOSDecomposition{<:Any,<:MOI.Utilities.ObjectWithoutIndex}, + SOSDecompositionWithDomain{<:Any,<:MOI.Utilities.ObjectWithoutIndex}, } const ObjectOrTupleWithoutIndex = Union{ObjectWithoutIndex,Tuple{Vararg{ObjectWithoutIndex}}} diff --git a/src/constraints.jl b/src/constraints.jl index c5489ecc4..a8f248ed3 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -498,7 +498,7 @@ function sos_decomposition( ranktol::Real = 0.0, dec::MultivariateMoments.LowRankLDLTAlgorithm = SVDLDLT(), ) - return MOI.get(cref.model, SOSDecompositionAttribute(ranktol, dec), cref) + return MOI.get(cref.model, SOSDecompositionAttribute(; ranktol, dec), cref) end """ From cc172b4d6b6dee3608aa168e8327132c8766bc23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 18 Jun 2024 09:33:23 +0200 Subject: [PATCH 66/84] Update ci --- .github/workflows/ci.yml | 6 +++--- .github/workflows/documentation.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b8803a42..bf0e509e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,10 +40,10 @@ jobs: PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), PackageSpec(name="StarAlgebras", rev="main"), PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), - PackageSpec(name="MultivariateBases", rev="bl/sos"), + PackageSpec(name="MultivariateBases", rev="master"), PackageSpec(name="SemialgebraicSets", rev="master"), - PackageSpec(name="MultivariateMoments", rev="bl/exp"), - PackageSpec(name="PolyJuMP", rev="bl/sos"), + PackageSpec(name="MultivariateMoments", rev="master"), + PackageSpec(name="PolyJuMP", rev="master"), ]) - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 14172b403..52318984f 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -23,9 +23,9 @@ jobs: using Pkg Pkg.add([ PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), - PackageSpec(name="StarAlgebras", rev="bl/sos"), + PackageSpec(name="StarAlgebras", rev="main"), PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), - PackageSpec(name="MultivariateBases", rev="bl/sos"), + PackageSpec(name="MultivariateBases", rev="master"), PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), PackageSpec(name="PolyJuMP", rev="master"), From 5a587fb3d75fb5576c17e83302c4a3bb3dc44063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 18 Jun 2024 09:54:38 +0200 Subject: [PATCH 67/84] Fix format --- src/Bridges/Bridges.jl | 9 ++++++++- test/Bridges/Variable/kernel.jl | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 4dbb3cffc..c03efeb62 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -29,7 +29,14 @@ function MOI.get( Constraint.SOSPolynomialInSemialgebraicSetBridge, }, ) - gram = MOI.get(model, SOS.GramMatrixAttribute(; multiplier_index = attr.multiplier_index, result_index = attr.result_index), bridge) + gram = MOI.get( + model, + SOS.GramMatrixAttribute(; + multiplier_index = attr.multiplier_index, + result_index = attr.result_index, + ), + bridge, + ) return SOS.SOSDecomposition(gram, attr.ranktol, attr.dec) end diff --git a/test/Bridges/Variable/kernel.jl b/test/Bridges/Variable/kernel.jl index 2c68751ab..b6ed4def5 100644 --- a/test/Bridges/Variable/kernel.jl +++ b/test/Bridges/Variable/kernel.jl @@ -27,7 +27,13 @@ function test_runtests() SumOfSquares.WeightedSOSCone{ MOI.PositiveSemidefiniteConeTriangle, }( - MB.SubBasis{MB.Monomial}([x^4, x^3 * y, x^2 * y^2, x * y^3, y^4]), + MB.SubBasis{MB.Monomial}([ + x^4, + x^3 * y, + x^2 * y^2, + x * y^3, + y^4, + ]), [MB.SubBasis{MB.Monomial}([x^2, y^2, x * y])], [MB.algebra_element(1.0 * x^0 * y^0)], ), From 7763055c7e2905f996f99826ccca5a2ee9208946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 18 Jun 2024 15:30:30 +0200 Subject: [PATCH 68/84] Fixes --- .github/workflows/documentation.yml | 1 + docs/Project.toml | 2 + docs/src/tutorials/Extension/certificate.jl | 8 ++-- docs/src/tutorials/Extension/hypercube.jl | 5 +++ .../tutorials/Extension/univariate_solver.jl | 5 ++- .../Getting started/getting_started.jl | 2 +- .../tutorials/Getting started/univariate.jl | 2 +- .../noncommutative_variables.jl | 3 +- .../sums_of_hermitian_squares.jl | 5 ++- src/Bridges/Constraint/sos_polynomial.jl | 25 +++++++++-- .../sos_polynomial_in_semialgebraic_set.jl | 11 +++++ src/Certificate/Certificate.jl | 6 ++- src/Certificate/ideal.jl | 26 +---------- src/Certificate/newton_polytope.jl | 43 +++++++++++++++++++ src/rand.jl | 15 +------ src/sosdec.jl | 8 ++-- test/Project.toml | 2 +- test/certificate.jl | 36 ++++++++-------- test/sparsity.jl | 4 +- 19 files changed, 132 insertions(+), 77 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 52318984f..70cc121b5 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -22,6 +22,7 @@ jobs: run: | using Pkg Pkg.add([ + PackageSpec(url="https://github.com/blegat/HomotopyContinuation.jl", rev="dynamicpolynomials_v0.6"), PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), PackageSpec(name="StarAlgebras", rev="main"), PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), diff --git a/docs/Project.toml b/docs/Project.toml index f2cc13a11..9a1959d0c 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -29,9 +29,11 @@ PolyJuMP = "ddf597a6-d67e-5340-b84c-e37d84115374" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" SCS = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" +StarAlgebras = "0c0c59c1-dc5f-42e9-9a8b-b5dc384a6cd1" SumOfSquares = "4b9e565b-77fc-50a5-a571-1244f986bda1" SymbolicWedderburn = "858aa9a9-4c7c-4c62-b466-2421203962a2" TypedPolynomials = "afbbf031-7a57-5f58-a1b9-b774a0fad08d" [compat] Documenter = "1" +DynamicPolynomials = "0.6" diff --git a/docs/src/tutorials/Extension/certificate.jl b/docs/src/tutorials/Extension/certificate.jl index a405193f9..4c9bce666 100644 --- a/docs/src/tutorials/Extension/certificate.jl +++ b/docs/src/tutorials/Extension/certificate.jl @@ -50,6 +50,7 @@ solution_summary(model) # We now define the Schmüdgen's certificate: +import StarAlgebras as SA import MultivariateBases as MB const SOS = SumOfSquares const SOSC = SOS.Certificate @@ -79,7 +80,7 @@ function SOSC.multiplier_basis(certificate::Schmüdgen, index::SOSC.PreorderInde return SOSC.maxdegree_gram_basis(certificate.basis, variables(domain), SOSC.multiplier_maxdegree(certificate.maxdegree, q)) end function SOSC.multiplier_basis_type(::Type{Schmüdgen{IC, CT, BT}}, ::Type{M}) where {IC,CT,BT,M} - return MB.similar_type(BT, M) + return MB.explicit_basis_type(BT) end function SOSC.generator(::Schmüdgen, index::SOSC.PreorderIndex, domain::SOSC.WithVariables) @@ -97,8 +98,9 @@ SOS.matrix_cone_type(::Type{<:Schmüdgen{IC, CT}}) where {IC, CT} = SOS.matrix_c model = SOSModel(solver) @variable(model, α) @objective(model, Max, α) -ideal_certificate = SOSC.Newton(SOSCone(), MB.MonomialBasis, tuple()) -certificate = Schmüdgen(ideal_certificate, SOSCone(), MB.MonomialBasis, maxdegree(p)) +basis = MB.FullBasis{MB.Monomial,typeof(x * y)}() +ideal_certificate = SOSC.Newton(SOSCone(), basis, tuple()) +certificate = Schmüdgen(ideal_certificate, SOSCone(), basis, maxdegree(p)) @constraint(model, c, p >= α, domain = S, certificate = certificate) optimize!(model) @test termination_status(model) == MOI.OPTIMAL #src diff --git a/docs/src/tutorials/Extension/hypercube.jl b/docs/src/tutorials/Extension/hypercube.jl index d67304eab..d8bc4adba 100644 --- a/docs/src/tutorials/Extension/hypercube.jl +++ b/docs/src/tutorials/Extension/hypercube.jl @@ -64,6 +64,7 @@ S.I.algo # However, we still need to divide polynomials by the Gröbner basis # which can be simplified in this case. +import MutableArithmetics as MA const MP = MultivariatePolynomials const SS = SemialgebraicSets struct HypercubeIdeal{V} <: SS.AbstractPolynomialIdeal @@ -76,6 +77,10 @@ MP.variables(set::HypercubeSet) = MP.variables(set.ideal) MP.variables(ideal::HypercubeIdeal) = ideal.variables Base.similar(set::HypercubeSet, ::Type) = set SS.ideal(set::HypercubeSet) = set.ideal +function MA.promote_operation(::typeof(SS.ideal), ::Type{HypercubeSet{V}}) where {V} + return HypercubeIdeal{V} +end +SS.similar_type(S::Type{<:HypercubeSet}, ::Type) = S function Base.rem(p, set::HypercubeIdeal) return MP.polynomial(map(MP.terms(p)) do term mono = MP.monomial(term) diff --git a/docs/src/tutorials/Extension/univariate_solver.jl b/docs/src/tutorials/Extension/univariate_solver.jl index 693bc5016..3426a7952 100644 --- a/docs/src/tutorials/Extension/univariate_solver.jl +++ b/docs/src/tutorials/Extension/univariate_solver.jl @@ -19,6 +19,7 @@ module MyUnivariateSolver import LinearAlgebra import MathOptInterface as MOI import MultivariatePolynomials as MP +import MultivariateBases as MB import SumOfSquares as SOS function decompose(p::MP.AbstractPolynomial, tol=1e-6) @@ -56,7 +57,7 @@ function decompose(p::MP.AbstractPolynomial, tol=1e-6) end q1 = MP.map_coefficients(real, q) q2 = MP.map_coefficients(imag, q) - return SOS.SOSDecomposition([q1, q2]) + return SOS.SOSDecomposition([MB.algebra_element(q1), MB.algebra_element(q2)]) end mutable struct Optimizer <: MOI.AbstractOptimizer @@ -84,7 +85,7 @@ function MOI.add_constraint(optimizer::Optimizer, func::MOI.VectorAffineFunction if !isempty(func.terms) error("Only supports constant polynomials") end - optimizer.p = MP.polynomial(func.constants, set.monomials) + optimizer.p = MP.polynomial(MB.algebra_element(func.constants, set.basis)) return MOI.ConstraintIndex{typeof(func),typeof(set)}(1) # There will be only ever one constraint so the index does not matter. end diff --git a/docs/src/tutorials/Getting started/getting_started.jl b/docs/src/tutorials/Getting started/getting_started.jl index ad4b6de17..66b590b9a 100644 --- a/docs/src/tutorials/Getting started/getting_started.jl +++ b/docs/src/tutorials/Getting started/getting_started.jl @@ -40,7 +40,7 @@ Q = value_matrix(q) #src sosdec = SOSDecomposition(q) @test isapprox(sosdec, sos_decomposition(con_ref)) #src -@test isapprox(sum(sosdec.ps.^2), p; rtol=1e-4, ztol=1e-6) #src +@test isapprox(polynomial(sosdec), p; rtol=1e-4, ztol=1e-6) #src # We now seek for the SOS decomposition of the following polynomial: diff --git a/docs/src/tutorials/Getting started/univariate.jl b/docs/src/tutorials/Getting started/univariate.jl index 3d8dad5aa..e08fe64cb 100644 --- a/docs/src/tutorials/Getting started/univariate.jl +++ b/docs/src/tutorials/Getting started/univariate.jl @@ -76,7 +76,7 @@ Q12 = η1.Q * η.atoms[1].weight + η2.Q * η.atoms[2].weight model = SOSModel(CSDP.Optimizer) @variable(model, σ) -@constraint(model, cheby_cref, p >= σ, basis = ChebyshevBasisFirstKind) +@constraint(model, cheby_cref, p >= σ, basis = Chebyshev) @objective(model, Max, σ) optimize!(model) solution_summary(model) diff --git a/docs/src/tutorials/Noncommutative and Hermitian/noncommutative_variables.jl b/docs/src/tutorials/Noncommutative and Hermitian/noncommutative_variables.jl index 0c25b029e..a3f7eb28b 100644 --- a/docs/src/tutorials/Noncommutative and Hermitian/noncommutative_variables.jl +++ b/docs/src/tutorials/Noncommutative and Hermitian/noncommutative_variables.jl @@ -14,6 +14,7 @@ # $(x * y + x^2)^2 = x^4 + x^3y + xyx^2 + xyxy$ is tested to be sum-of-squares. using Test #src +import StarAlgebras as SA #src using DynamicPolynomials @ncpolyvar x y p = (x * y + x^2)^2 @@ -45,7 +46,7 @@ sos_decomposition(con_ref) dec = sos_decomposition(con_ref, 1e-6) #src @test length(dec.ps) == 1 #src -@test sign(first(coefficients(dec.ps[1]))) * dec.ps[1] ≈ x * y + x^2 rtol=1e-5 atol=1e-5 #src +@test sign(first(SA.coeffs(dec.ps[1]))) * dec.ps[1] ≈ x * y + x^2 rtol=1e-5 atol=1e-5 #src sos_decomposition(con_ref, 1e-6) # ## Example 2.2 diff --git a/docs/src/tutorials/Noncommutative and Hermitian/sums_of_hermitian_squares.jl b/docs/src/tutorials/Noncommutative and Hermitian/sums_of_hermitian_squares.jl index f829f3c80..cad9d21df 100644 --- a/docs/src/tutorials/Noncommutative and Hermitian/sums_of_hermitian_squares.jl +++ b/docs/src/tutorials/Noncommutative and Hermitian/sums_of_hermitian_squares.jl @@ -5,6 +5,7 @@ # **Contributed by**: Benoît Legat using Test #src +import StarAlgebras as SA #src using SumOfSquares @@ -19,6 +20,6 @@ con_ref = @constraint(model, p in cone) optimize!(model) dec = sos_decomposition(con_ref, 1e-6) #src @test length(dec.ps) == 1 #src -@test dec.ps[1]' * dec.ps[1] ≈ p atol=1e-6 rtol=1e-6 #src -@test sign(real(first(coefficients(dec.ps[1])))) * dec.ps[1] ≈ y + im * x atol=1e-6 rtol=1e-6 #src +@test polynomial(dec.ps[1])' * polynomial(dec.ps[1]) ≈ p atol=1e-6 rtol=1e-6 #src +@test sign(real(first(SA.coeffs(dec.ps[1])))) * dec.ps[1] ≈ y + im * x atol=1e-6 rtol=1e-6 #src sos_decomposition(con_ref, 1e-6) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 866fa06c6..15c131a71 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -175,15 +175,34 @@ end function _get( model::MOI.ModelLike, - attr, + attr::SOS.SOSDecompositionAttribute, + constraint::MOI.ConstraintIndex, + index::Int, +) + return MOI.get( + model, + typeof(attr)(; + ranktol = attr.ranktol, + dec = attr.dec, + multiplier_index = index, + result_index = attr.result_index, + ), + constraint, + ) +end + +function _get( + model::MOI.ModelLike, + attr::Union{ + SOS.GramMatrixAttribute, + SOS.MomentMatrixAttribute, + }, constraint::MOI.ConstraintIndex, index::Int, ) return MOI.get( model, typeof(attr)(; - attr.ranktol, - attr.dec, multiplier_index = index, result_index = attr.result_index, ), diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index ae74548fc..1b79146a7 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -286,6 +286,17 @@ function MOI.get( ) return MOI.get(model, attr, bridge.constraint) end + +function _gram( + f::Function, + Q::Vector{MOI.VariableIndex}, + gram_basis, + T::Type, + MCT, +) + return SOS.build_gram_matrix(convert(Vector{T}, f(Q)), gram_basis, MCT, T) +end + function _gram( f::Function, Qs::Vector{Vector{MOI.VariableIndex}}, diff --git a/src/Certificate/Certificate.jl b/src/Certificate/Certificate.jl index 2b3c519d4..c77ee96d6 100644 --- a/src/Certificate/Certificate.jl +++ b/src/Certificate/Certificate.jl @@ -49,9 +49,13 @@ function within_total_bounds(mono::MP.AbstractMonomial, bounds::DegreeBounds) return bounds.mindegree <= MP.degree(mono) <= bounds.maxdegree end +_vec(v::AbstractVector) = v +# For `TypedPolynomials` +_vec(v::Tuple) = MP.variable_union_type(first(v))[v...] + function _divides(a, b) # `MP.divides(a, b)` is not implemented yet for noncommutative - vars = unique!(sort(MP.variables(a))) + vars = unique!(sort(_vec(MP.variables(a)))) comm = is_commutative(vars) return all(vars) do v return _degree(a, v, comm) <= _degree(b, v, comm) diff --git a/src/Certificate/ideal.jl b/src/Certificate/ideal.jl index ec3f9de5f..c95b6455e 100644 --- a/src/Certificate/ideal.jl +++ b/src/Certificate/ideal.jl @@ -180,32 +180,8 @@ function Newton(cone, basis, variable_groups::Tuple) ) end -function _weight_type(::Type{T}, ::Type{BT}) where {T,BT} - return SA.AlgebraElement{ - MA.promote_operation( - MB.algebra, - MA.promote_operation(MB.implicit_basis, BT), - ), - T, - MA.promote_operation( - MB.sparse_coefficients, - MP.polynomial_type(MP.monomial_type(BT), T), - ), - } -end - -function _half_newton_polytope(a::SA.AlgebraElement, vars, filter) - return half_newton_polytope( - a, - _weight_type(Bool, typeof(SA.basis(a)))[], - vars, - _maxdegree(a, vars), - filter, - )[1] -end - function gram_basis(certificate::Newton, poly) - return _half_newton_polytope( + return half_newton_polytope( _algebra_element(poly), MP.variables(poly), certificate.newton, diff --git a/src/Certificate/newton_polytope.jl b/src/Certificate/newton_polytope.jl index d200b47b2..7fbf6aeee 100644 --- a/src/Certificate/newton_polytope.jl +++ b/src/Certificate/newton_polytope.jl @@ -807,3 +807,46 @@ end function _sub(basis::SubBasis{B}, I) where {B} return SubBasis{B}(basis.monomials[I]) end + +function _weight_type(::Type{T}, ::Type{BT}) where {T,BT} + return SA.AlgebraElement{ + MA.promote_operation( + MB.algebra, + MA.promote_operation(MB.implicit_basis, BT), + ), + T, + MA.promote_operation( + MB.sparse_coefficients, + MP.polynomial_type(MP.monomial_type(BT), T), + ), + } +end + +function half_newton_polytope(a::SA.AlgebraElement, vars, filter) + return half_newton_polytope( + a, + _weight_type(Bool, typeof(SA.basis(a)))[], + vars, + _maxdegree(a, vars), + filter, + )[1] +end + +function half_newton_polytope(a::SA.AlgebraElement, filter) + return half_newton_polytope(a, MP.variables(a), filter) +end + +function half_newton_polytope(basis::MB.SubBasis, args...) + a = MB.algebra_element( + SA.SparseCoefficients(basis.monomials, ones(length(basis))), + MB.implicit_basis(basis), + ) + return half_newton_polytope(a, args...) +end + +function monomials_half_newton_polytope(monos, args...) + return half_newton_polytope( + MB.SubBasis{MB.Monomial}(monos), + args..., + ).monomials +end diff --git a/src/rand.jl b/src/rand.jl index 6aafd8e91..91283e0e5 100644 --- a/src/rand.jl +++ b/src/rand.jl @@ -8,19 +8,6 @@ function randpsd(n; r = n, eps = 0.1) return Q' * Diagonal(d) * Q end -function _monomials_half_newton_polytope(_monos, filter) - monos = MP.monomial_vector(_monos) - basis = MB.FullBasis{MB.Monomial,eltype(monos)}() - return SumOfSquares.Certificate._half_newton_polytope( - MB.algebra_element( - SA.SparseCoefficients(monos, ones(length(monos))), - basis, - ), - MP.variables(monos), - filter, - ).monomials -end - function _randsos( X::AbstractVector{<:MP.AbstractMonomial}; r = -1, @@ -28,7 +15,7 @@ function _randsos( eps = 0.1, ) if monotype == :Classic - x = _monomials_half_newton_polytope( + x = Certificate.monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds(tuple()), ) diff --git a/src/sosdec.jl b/src/sosdec.jl index 98e1e9129..a88f316f9 100644 --- a/src/sosdec.jl +++ b/src/sosdec.jl @@ -121,10 +121,12 @@ function MP.polynomial(d::SOSDecomposition) return MP.polynomial(MB.algebra_element(d)) end -function MB.algebra_element(decomp::SOSDecomposition) - res = zero(first(decomp.ps)) +function MB.algebra_element(decomp::SOSDecomposition{A,T,V,U}) where {A,T,V,U} + basis = MB.implicit_basis(SA.basis(first(decomp.ps))) + res = zero(U, MB.algebra(basis)) for p in decomp.ps - MA.operate!(SA.UnsafeAddMul(*), res, SA.star(p), p) + implicit = MB.algebra_element(SA.coeffs(p, basis), basis) + MA.operate!(SA.UnsafeAddMul(*), res, SA.star(implicit), implicit) end MA.operate!(SA.canonical, SA.coeffs(res)) return res diff --git a/test/Project.toml b/test/Project.toml index c5370ac0f..15445176d 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -16,4 +16,4 @@ SymbolicWedderburn = "858aa9a9-4c7c-4c62-b466-2421203962a2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] -DynamicPolynomials = "0.5" +DynamicPolynomials = "0.5,0.6" diff --git a/test/certificate.jl b/test/certificate.jl index cdb21d0a4..303c3ad5c 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -35,7 +35,7 @@ end err = ArgumentError( "Multipartite Newton polytope not supported with noncommutative variables.", ) - @test_throws err SOS._monomials_half_newton_polytope( + @test_throws err SOS.Certificate.monomials_half_newton_polytope( [a * b, b^2], Certificate.NewtonDegreeBounds(parts), ) @@ -48,39 +48,39 @@ end err = ArgumentError( "Parts are not disjoint in multipartite Newton polytope estimation: $parts.", ) - @test_throws err SOS._monomials_half_newton_polytope( + @test_throws err SOS.Certificate.monomials_half_newton_polytope( [x * y, y^2], Certificate.NewtonDegreeBounds(parts), ) end uni = Certificate.NewtonDegreeBounds(tuple()) @testset "Unipartite" begin - @test SOS._monomials_half_newton_polytope([x * y, y^2], uni) == [y] - @test isempty(SOS._monomials_half_newton_polytope([x, y], uni)) - @test SOS._monomials_half_newton_polytope([x^2, y^2], uni) == [x, y] - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope([x * y, y^2], uni) == [y] + @test isempty(SOS.Certificate.monomials_half_newton_polytope([x, y], uni)) + @test SOS.Certificate.monomials_half_newton_polytope([x^2, y^2], uni) == [x, y] + @test SOS.Certificate.monomials_half_newton_polytope( [x^2, y^2], Certificate.NewtonDegreeBounds(([x, y],)), ) == [x, y] - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( [x^2, y^2], Certificate.NewtonDegreeBounds(([y, x],)), ) == [x, y] - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( [x^2, x^3 * y^2, x^4 * y^4], uni, ) == [x^2 * y^2, x, x * y, x^2, x * y^2, x^2 * y] - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( [x^2, x^3 * y^2, x^4 * y^4], Certificate.NewtonFilter(uni), ) == [x^2 * y^2, x] end @testset "Non-commutative" begin - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( [a^4, a^3 * b, a * b * a^2, a * b * a * b], uni, ) == [a^2, a * b] - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( [ a^2, a^10 * b^20 * a^11, @@ -93,26 +93,26 @@ end @testset "Multipartite" begin # In the part [y, z], the degree is between 0 and 2 X = [x^4, x^2 * y^2, x^2 * z^2, x^2 * y * z, y * z] - @test SOS._monomials_half_newton_polytope(X, uni) == + @test SOS.Certificate.monomials_half_newton_polytope(X, uni) == [x^2, x * y, x * z, y * z, x, y, z] function full_test(X, Y, part1, part2) - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part1,)), ) == Y - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part2,)), ) == Y - a = SOS._monomials_half_newton_polytope( + a = SOS.Certificate.monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part2,)), ) - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part1, part2)), ) == Y - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( X, Certificate.NewtonDegreeBounds((part2, part1)), ) == Y @@ -131,7 +131,7 @@ end [x, y], ) # FIXME: With recursive merging, it should give [x^2, x*y, x*z, x] - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( [x^4, x^2 * y^2, x^2 * z^2, x^2 * y * z, y * z], Certificate.NewtonDegreeBounds(([x], [y], [z])), ) == [x^2, x * y, x * z, y * z, x, y, z] diff --git a/test/sparsity.jl b/test/sparsity.jl index 44adfd85e..dda2b2b64 100644 --- a/test/sparsity.jl +++ b/test/sparsity.jl @@ -311,7 +311,7 @@ function l09() ) with_var = SumOfSquares.Certificate.WithVariables(alg_el, x) newt = Certificate.NewtonDegreeBounds(tuple()) - @test SOS._monomials_half_newton_polytope(monomials(f), newt) == [ + @test SOS.Certificate.monomials_half_newton_polytope(monomials(f), newt) == [ x[1]^2 * x[2], x[1] * x[2]^2, x[1]^2, @@ -321,7 +321,7 @@ function l09() x[2], 1, ] - @test SOS._monomials_half_newton_polytope( + @test SOS.Certificate.monomials_half_newton_polytope( monomials(f), Certificate.NewtonFilter(newt), ) == [x[1]^2 * x[2], x[1] * x[2]^2, 1] From f13c16e1b23daa5ba2638a244d0d8fd7a1f8fced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 18 Jun 2024 15:31:10 +0200 Subject: [PATCH 69/84] Fix format --- src/Bridges/Constraint/sos_polynomial.jl | 5 +---- test/certificate.jl | 12 +++++++++--- test/sparsity.jl | 5 ++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 15c131a71..ed0d48bd2 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -193,10 +193,7 @@ end function _get( model::MOI.ModelLike, - attr::Union{ - SOS.GramMatrixAttribute, - SOS.MomentMatrixAttribute, - }, + attr::Union{SOS.GramMatrixAttribute,SOS.MomentMatrixAttribute}, constraint::MOI.ConstraintIndex, index::Int, ) diff --git a/test/certificate.jl b/test/certificate.jl index 303c3ad5c..b7c2a0052 100644 --- a/test/certificate.jl +++ b/test/certificate.jl @@ -55,9 +55,15 @@ end end uni = Certificate.NewtonDegreeBounds(tuple()) @testset "Unipartite" begin - @test SOS.Certificate.monomials_half_newton_polytope([x * y, y^2], uni) == [y] - @test isempty(SOS.Certificate.monomials_half_newton_polytope([x, y], uni)) - @test SOS.Certificate.monomials_half_newton_polytope([x^2, y^2], uni) == [x, y] + @test SOS.Certificate.monomials_half_newton_polytope( + [x * y, y^2], + uni, + ) == [y] + @test isempty( + SOS.Certificate.monomials_half_newton_polytope([x, y], uni), + ) + @test SOS.Certificate.monomials_half_newton_polytope([x^2, y^2], uni) == + [x, y] @test SOS.Certificate.monomials_half_newton_polytope( [x^2, y^2], Certificate.NewtonDegreeBounds(([x, y],)), diff --git a/test/sparsity.jl b/test/sparsity.jl index dda2b2b64..7e2aeb073 100644 --- a/test/sparsity.jl +++ b/test/sparsity.jl @@ -311,7 +311,10 @@ function l09() ) with_var = SumOfSquares.Certificate.WithVariables(alg_el, x) newt = Certificate.NewtonDegreeBounds(tuple()) - @test SOS.Certificate.monomials_half_newton_polytope(monomials(f), newt) == [ + @test SOS.Certificate.monomials_half_newton_polytope( + monomials(f), + newt, + ) == [ x[1]^2 * x[2], x[1] * x[2]^2, x[1]^2, From 71d672aa857f2c84f10a5fa189d4aaa5b3ea1bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 18 Jun 2024 20:52:37 +0200 Subject: [PATCH 70/84] Fix --- .github/workflows/examples.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 20bfe2d01..f3547e0a2 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -17,6 +17,7 @@ jobs: run: | using Pkg Pkg.add([ + PackageSpec(url="https://github.com/blegat/HomotopyContinuation.jl", rev="dynamicpolynomials_v0.6"), PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), PackageSpec(name="StarAlgebras", rev="bl/sos"), PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), From 5563cd01b10cad31c373f06ddbcf1a5355135a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 19 Jun 2024 21:12:19 +0200 Subject: [PATCH 71/84] Disable symmetry --- docs/make.jl | 2 +- examples/run_examples.jl | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index f56db8f8e..a6073507d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -13,7 +13,7 @@ const _TUTORIAL_SUBDIR = [ "Other Applications", "Noncommutative and Hermitian", "Sparsity", - "Symmetry", + #"Symmetry", "Extension", ] diff --git a/examples/run_examples.jl b/examples/run_examples.jl index efe7c5152..6afbc1d4b 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -29,7 +29,9 @@ end @testset "run_examples.jl" begin @testset "$dir" for dir in readdir(_TUTORIAL_DIR) - run_examples(dir) + if dir != "Symmetry" + run_examples(dir) + end end @testset "Chordal" begin include("chordal_sparsity.jl") From f9c16aa9ba192d4ae18c7bf1f70f2a3c3c3f3e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 20 Jun 2024 10:23:00 +0200 Subject: [PATCH 72/84] Update script of examples --- .github/workflows/examples.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index f3547e0a2..d6007a15f 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -19,9 +19,9 @@ jobs: Pkg.add([ PackageSpec(url="https://github.com/blegat/HomotopyContinuation.jl", rev="dynamicpolynomials_v0.6"), PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), - PackageSpec(name="StarAlgebras", rev="bl/sos"), + PackageSpec(name="StarAlgebras", rev="main"), PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), - PackageSpec(name="MultivariateBases", rev="bl/sos"), + PackageSpec(name="MultivariateBases", rev="master"), PackageSpec(name="SemialgebraicSets", rev="master"), PackageSpec(name="MultivariateMoments", rev="master"), PackageSpec(name="PolyJuMP", rev="master"), From 4c65570649fcc5aa590783c6a49a79b4c05a3345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 2 Jul 2024 11:24:08 +0200 Subject: [PATCH 73/84] Fixes --- .github/workflows/ci.yml | 1 - .github/workflows/documentation.yml | 1 - .github/workflows/examples.yml | 1 - src/Bridges/Constraint/sos_polynomial.jl | 2 +- .../sos_polynomial_in_semialgebraic_set.jl | 12 +++++ test/Mock/mock_tests.jl | 48 +++++++++---------- 6 files changed, 37 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf0e509e0..c7e358010 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,6 @@ jobs: run: | using Pkg Pkg.add([ - PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), PackageSpec(name="StarAlgebras", rev="main"), PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), PackageSpec(name="MultivariateBases", rev="master"), diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 70cc121b5..b82d09107 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -23,7 +23,6 @@ jobs: using Pkg Pkg.add([ PackageSpec(url="https://github.com/blegat/HomotopyContinuation.jl", rev="dynamicpolynomials_v0.6"), - PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), PackageSpec(name="StarAlgebras", rev="main"), PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), PackageSpec(name="MultivariateBases", rev="master"), diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index d6007a15f..5a3504766 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -18,7 +18,6 @@ jobs: using Pkg Pkg.add([ PackageSpec(url="https://github.com/blegat/HomotopyContinuation.jl", rev="dynamicpolynomials_v0.6"), - PackageSpec(name="MathOptInterface", rev="bl/set_map_type"), PackageSpec(name="StarAlgebras", rev="main"), PackageSpec(name="SymbolicWedderburn", rev="mk/StarAlgebras_0_3"), PackageSpec(name="MultivariateBases", rev="master"), diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index ed0d48bd2..0918a2494 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -129,7 +129,7 @@ function MOI.Bridges.Constraint.concrete_bridge_type( end function MOI.Bridges.inverse_map_function(::SOSPolynomialBridge, f) - throw(MOI.Bridges.MapNotInvertible()) + throw(MOI.Bridges.MapNotInvertible("The linear map is not invertible for some basis transformation, use a `CachingOptimizer` layer")) # Does not work with QuotientBasis #return SA.coeffs(MP.polynomial(f, bridge.new_basis), bridge.set.basis) end diff --git a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl index 1b79146a7..9978005a3 100644 --- a/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl +++ b/src/Bridges/Constraint/sos_polynomial_in_semialgebraic_set.jl @@ -234,6 +234,18 @@ function MOI.get( end # Indices +function _delete_variables(model, Q::Vector{MOI.VariableIndex}) + if !isempty(Q) + # FIXME Since there is not variables in the list, we cannot + # identify the `EmptyBridge` to delete + MOI.delete(model, Q) + end +end +function _delete_variables(model, Qs::Vector{Vector{MOI.VariableIndex}}) + for Q in Qs + _delete_variables(model, Q) + end +end function MOI.delete( model::MOI.ModelLike, bridge::SOSPolynomialInSemialgebraicSetBridge, diff --git a/test/Mock/mock_tests.jl b/test/Mock/mock_tests.jl index 0cb339ff5..5ad9f70a1 100644 --- a/test/Mock/mock_tests.jl +++ b/test/Mock/mock_tests.jl @@ -12,9 +12,9 @@ using Test, JuMP #@testset "Quartic constant" begin # include("quartic_constant.jl") #end -@testset "Quadratic" begin - include("quadratic.jl") -end +#@testset "Quadratic" begin +# include("quadratic.jl") +#end @testset "Quartic ideal" begin include("quartic_ideal.jl") end @@ -30,9 +30,9 @@ end @testset "Simple matrix" begin include("simple_matrix.jl") end -@testset "Concave then convex cubic" begin - include("concave_then_convex_cubic.jl") -end +#@testset "Concave then convex cubic" begin +# include("concave_then_convex_cubic.jl") +#end @testset "Horn" begin include("horn.jl") end @@ -42,27 +42,27 @@ end @testset "Motzkin" begin include("motzkin.jl") end -@testset "BPT12e399" begin - include("BPT12e399.jl") -end -@testset "Max Cut" begin - include("maxcut.jl") -end -@testset "Chebyshev" begin - include("chebyshev.jl") -end -@testset "Quartic comparison" begin - include("quartic_comparison.jl") -end +#@testset "BPT12e399" begin +# include("BPT12e399.jl") +#end +#@testset "Max Cut" begin +# include("maxcut.jl") +#end +#@testset "Chebyshev" begin +# include("chebyshev.jl") +#end +#@testset "Quartic comparison" begin +# include("quartic_comparison.jl") +#end @testset "SOSDEMO5" begin include("sosdemo5.jl") end @testset "SOSDEMO9" begin include("sosdemo9.jl") end -@testset "SOSDEMO10" begin - include("sosdemo10.jl") -end -@testset "Options Pricing" begin - include("options_pricing.jl") -end +#@testset "SOSDEMO10" begin +# include("sosdemo10.jl") +#end +#@testset "Options Pricing" begin +# include("options_pricing.jl") +#end From 6ce945ad23671ebd2b7cc42b87f9e4b1af7e21f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 2 Jul 2024 11:24:52 +0200 Subject: [PATCH 74/84] Fixes --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 4dc2652d7..e3d5bf0d1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -34,7 +34,7 @@ include("variable.jl") include("constraint.jl") include("Bridges/Bridges.jl") -#include("Mock/mock_tests.jl") +include("Mock/mock_tests.jl") # Tests needing a solver # FIXME these tests should be converted to Literate and moved to `examples` or From 06507d94fc12378e585682ea82f7c5ff2478bddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 2 Jul 2024 11:26:05 +0200 Subject: [PATCH 75/84] Fixes --- docs/src/constraints.md | 16 +++---- docs/src/variables.md | 61 +++++++++++------------- src/Bridges/Constraint/sos_polynomial.jl | 6 ++- 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/docs/src/constraints.md b/docs/src/constraints.md index 4a0900b07..b66aa2a4c 100644 --- a/docs/src/constraints.md +++ b/docs/src/constraints.md @@ -41,13 +41,13 @@ CachingOptimizer state: NO_OPTIMIZER Solver name: No optimizer attached. julia> @variable(model, p, Poly(X)) -(_[1]) + (_[2])x₃ + (_[3])x₂ + (_[4])x₁ + (_[5])x₃² + (_[6])x₂x₃ + (_[7])x₂² + (_[8])x₁x₃ + (_[9])x₁x₂ + (_[10])x₁² +(_[1])·1 + (_[2])·x₃ + (_[3])·x₂ + (_[4])·x₁ + (_[5])·x₃² + (_[6])·x₂x₃ + (_[7])·x₂² + (_[8])·x₁x₃ + (_[9])·x₁x₂ + (_[10])·x₁² julia> @variable(model, q, Poly(X)) -(_[11]) + (_[12])x₃ + (_[13])x₂ + (_[14])x₁ + (_[15])x₃² + (_[16])x₂x₃ + (_[17])x₂² + (_[18])x₁x₃ + (_[19])x₁x₂ + (_[20])x₁² +(_[11])·1 + (_[12])·x₃ + (_[13])·x₂ + (_[14])·x₁ + (_[15])·x₃² + (_[16])·x₂x₃ + (_[17])·x₂² + (_[18])·x₁x₃ + (_[19])·x₁x₂ + (_[20])·x₁² julia> @constraint(model, p + q == 1) -(_[1] + _[11] - 1) + (_[2] + _[12])x₃ + (_[3] + _[13])x₂ + (_[4] + _[14])x₁ + (_[5] + _[15])x₃² + (_[6] + _[16])x₂x₃ + (_[7] + _[17])x₂² + (_[8] + _[18])x₁x₃ + (_[9] + _[19])x₁x₂ + (_[10] + _[20])x₁² ∈ PolyJuMP.ZeroPoly() +(_[1] + _[11] - 1)·1 + (_[2] + _[12])·x₃ + (_[3] + _[13])·x₂ + (_[4] + _[14])·x₁ + (_[5] + _[15])·x₃² + (_[6] + _[16])·x₂x₃ + (_[7] + _[17])·x₂² + (_[8] + _[18])·x₁x₃ + (_[9] + _[19])·x₁x₂ + (_[10] + _[20])·x₁² ∈ PolyJuMP.ZeroPoly() ``` Vectorized constraints can also be used as well as vector of constraints, @@ -66,7 +66,7 @@ Polynomials can be constrained to be sum-of-squares with the `in` syntax. For instance, to constrain a polynomial `p` to be sum-of-squares, do ```jldoctest constraint-pq julia> @constraint(model, p in SOSCone()) -(_[1]) + (_[2])x₃ + (_[3])x₂ + (_[4])x₁ + (_[5])x₃² + (_[6])x₂x₃ + (_[7])x₂² + (_[8])x₁x₃ + (_[9])x₁x₂ + (_[10])x₁² is SOS +(_[1])·1 + (_[2])·x₃ + (_[3])·x₂ + (_[4])·x₁ + (_[5])·x₃² + (_[6])·x₂x₃ + (_[7])·x₂² + (_[8])·x₁x₃ + (_[9])·x₁x₂ + (_[10])·x₁² is SOS ``` ### Automatically interpreting polynomial nonnegativity as a sum-of-squares constraint @@ -147,7 +147,7 @@ julia> @variable(model, β) β julia> @constraint(model, α * x^2 + β * y^2 ≥ (α - β) * x * y) -(β)y² + (-α + β)xy + (α)x² is SOS +(β)·y² + (-α + β)·xy + (α)·x² is SOS ``` where `α` and `β` are JuMP decision variables and `x` and `y` are polynomial variables. Since the polynomial is a quadratic form, the sum-of-squares @@ -165,7 +165,7 @@ only the basis *type* needs to be given. For instance, to use the scaled monomia basis in the example above, use ```jldoctest constraint-xy julia> @constraint(model, α * x^2 + β * y^2 ≥ (α - β) * x * y, basis = ScaledMonomial) -(β)y² + (-α + β)xy + (α)x² is SOS +(β)·ScaledMonomial(y²) + (-0.7071067811865475 α + 0.7071067811865475 β)·ScaledMonomial(xy) + (α)·ScaledMonomial(x²) is SOS ``` ## Polynomial nonnegativity on a subset of the space @@ -173,7 +173,7 @@ julia> @constraint(model, α * x^2 + β * y^2 ≥ (α - β) * x * y, basis = Sca By default, the constraint ```jldoctest constraint-xy julia> @constraint(model, x^3 - x^2 + 2x*y -y^2 + y^3 >= α) -(-α) + (-1)y² + (2)xy + (-1)x² + (1)y³ + (1)x³ is SOS +(-α)·1 + (-1)·y² + (2)·xy + (-1)·x² + (1)·y³ + (1)·x³ is SOS ``` constrains the polynomial to be nonnegative for every real numbers `x` and `y`. However, the set of points `(x, y)` for which the polynomial is constrained @@ -182,7 +182,7 @@ to be nonnegative can be specified by the `domain` keyword: julia> S = @set x >= 0 && y >= 0 && x + y >= 1; julia> @constraint(model, x^3 - x^2 + 2x*y -y^2 + y^3 >= α, domain = S) -(-α) + (-1)y² + (2)xy + (-1)x² + (1)y³ + (1)x³ is SOS +(-α)·1 + (-1)·y² + (2)·xy + (-1)·x² + (1)·y³ + (1)·x³ is SOS ``` See [this notebook](https://github.com/jump-dev/SumOfSquares.jl/blob/master/examples/Polynomial_Optimization.ipynb) for a detailed example. diff --git a/docs/src/variables.md b/docs/src/variables.md index a74da1032..9706f69b6 100644 --- a/docs/src/variables.md +++ b/docs/src/variables.md @@ -42,7 +42,7 @@ CachingOptimizer state: NO_OPTIMIZER Solver name: No optimizer attached. julia> @variable(model, p, Poly(X)) -(_[1]) + (_[2])y + (_[3])x + (_[4])y² + (_[5])xy + (_[6])x² +(_[1])·1 + (_[2])·y + (_[3])·x + (_[4])·y² + (_[5])·xy + (_[6])·x² ``` This creates a vector of decision variables `a` and sets `p` as the scalar product between `a` and `X`. @@ -51,27 +51,27 @@ Just like with classical JuMP's decision variables, containers of polynomial variables can be created as follows: ```jldoctest variables julia> @variable(model, [1:3, 1:4], Poly(X)) # Creates a Matrix -3×4 Matrix{Polynomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}, VariableRef}}: - (_[7]) + (_[8])y + (_[9])x + (_[10])y² + (_[11])xy + (_[12])x² … (_[61]) + (_[62])y + (_[63])x + (_[64])y² + (_[65])xy + (_[66])x² - (_[13]) + (_[14])y + (_[15])x + (_[16])y² + (_[17])xy + (_[18])x² (_[67]) + (_[68])y + (_[69])x + (_[70])y² + (_[71])xy + (_[72])x² - (_[19]) + (_[20])y + (_[21])x + (_[22])y² + (_[23])xy + (_[24])x² (_[73]) + (_[74])y + (_[75])x + (_[76])y² + (_[77])xy + (_[78])x² +3×4 Matrix{StarAlgebras.AlgebraElement{MultivariateBases.Algebra{SubBasis{Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}, MonomialVector{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, VariableRef, Vector{VariableRef}}}: + (_[7])·1 + (_[8])·y + (_[9])·x + (_[10])·y² + (_[11])·xy + (_[12])·x² … (_[61])·1 + (_[62])·y + (_[63])·x + (_[64])·y² + (_[65])·xy + (_[66])·x² + (_[13])·1 + (_[14])·y + (_[15])·x + (_[16])·y² + (_[17])·xy + (_[18])·x² (_[67])·1 + (_[68])·y + (_[69])·x + (_[70])·y² + (_[71])·xy + (_[72])·x² + (_[19])·1 + (_[20])·y + (_[21])·x + (_[22])·y² + (_[23])·xy + (_[24])·x² (_[73])·1 + (_[74])·y + (_[75])·x + (_[76])·y² + (_[77])·xy + (_[78])·x² julia> @variable(model, [[:a, :b], -2:2], Poly(X)) # Creates a DenseAxisArray -2-dimensional DenseAxisArray{DynamicPolynomials.Polynomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, MultivariatePolynomials.Graded{MultivariatePolynomials.LexOrder}, VariableRef},2,...} with index sets: +2-dimensional DenseAxisArray{StarAlgebras.AlgebraElement{MultivariateBases.Algebra{SubBasis{Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}, MonomialVector{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, VariableRef, Vector{VariableRef}},2,...} with index sets: Dimension 1, [:a, :b] Dimension 2, -2:2 -And data, a 2×5 Matrix{DynamicPolynomials.Polynomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, MultivariatePolynomials.Graded{MultivariatePolynomials.LexOrder}, VariableRef}}: - (_[79]) + (_[80])y + (_[81])x + (_[82])y² + (_[83])xy + (_[84])x² … (_[127]) + (_[128])y + (_[129])x + (_[130])y² + (_[131])xy + (_[132])x² - (_[85]) + (_[86])y + (_[87])x + (_[88])y² + (_[89])xy + (_[90])x² (_[133]) + (_[134])y + (_[135])x + (_[136])y² + (_[137])xy + (_[138])x² +And data, a 2×5 Matrix{StarAlgebras.AlgebraElement{MultivariateBases.Algebra{SubBasis{Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}, MonomialVector{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, VariableRef, Vector{VariableRef}}}: + (_[79])·1 + (_[80])·y + (_[81])·x + (_[82])·y² + (_[83])·xy + (_[84])·x² … (_[127])·1 + (_[128])·y + (_[129])·x + (_[130])·y² + (_[131])·xy + (_[132])·x² + (_[85])·1 + (_[86])·y + (_[87])·x + (_[88])·y² + (_[89])·xy + (_[90])·x² (_[133])·1 + (_[134])·y + (_[135])·x + (_[136])·y² + (_[137])·xy + (_[138])·x² julia> @variable(model, [i=1:3, j=i:3], Poly(X)) # Creates a SparseAxisArray -JuMP.Containers.SparseAxisArray{Polynomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}, VariableRef}, 2, Tuple{Int64, Int64}} with 6 entries: - [1, 1] = (_[139]) + (_[140])*y + (_[141])*x + (_[142])*y^2 + (_[143])*x*y + (_[144])*x^2 - [1, 2] = (_[145]) + (_[146])*y + (_[147])*x + (_[148])*y^2 + (_[149])*x*y + (_[150])*x^2 - [1, 3] = (_[151]) + (_[152])*y + (_[153])*x + (_[154])*y^2 + (_[155])*x*y + (_[156])*x^2 - [2, 2] = (_[157]) + (_[158])*y + (_[159])*x + (_[160])*y^2 + (_[161])*x*y + (_[162])*x^2 - [2, 3] = (_[163]) + (_[164])*y + (_[165])*x + (_[166])*y^2 + (_[167])*x*y + (_[168])*x^2 - [3, 3] = (_[169]) + (_[170])*y + (_[171])*x + (_[172])*y^2 + (_[173])*x*y + (_[174])*x^2 +JuMP.Containers.SparseAxisArray{StarAlgebras.AlgebraElement{MultivariateBases.Algebra{SubBasis{Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}, MonomialVector{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, VariableRef, Vector{VariableRef}}, 2, Tuple{Int64, Int64}} with 6 entries: + [1, 1] = (_[139])·1 + (_[140])·y + (_[141])·x + (_[142])·y^2 + (_[143])·x*y + (_[144])·x^2 + [1, 2] = (_[145])·1 + (_[146])·y + (_[147])·x + (_[148])·y^2 + (_[149])·x*y + (_[150])·x^2 + [1, 3] = (_[151])·1 + (_[152])·y + (_[153])·x + (_[154])·y^2 + (_[155])·x*y + (_[156])·x^2 + [2, 2] = (_[157])·1 + (_[158])·y + (_[159])·x + (_[160])·y^2 + (_[161])·x*y + (_[162])·x^2 + [2, 3] = (_[163])·1 + (_[164])·y + (_[165])·x + (_[166])·y^2 + (_[167])·x*y + (_[168])·x^2 + [3, 3] = (_[169])·1 + (_[170])·y + (_[171])·x + (_[172])·y^2 + (_[173])·x*y + (_[174])·x^2 ``` For more flexibility, polynomials parametrized by decision variables can also @@ -102,9 +102,9 @@ For instance, the following code creates a ``3 \times 4`` matrix of sum-of-squares polynomial variables: ```jldoctest variables julia> @variable(model, [1:2], SOSPoly(X)) -2-element Vector{GramMatrix{VariableRef, MonomialBasis{Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}, MonomialVector{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, AffExpr, SymMatrix{VariableRef}}}: +2-element Vector{GramMatrix{VariableRef, SubBasis{Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}, MonomialVector{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, AffExpr, SymMatrix{VariableRef}}}: GramMatrix with row/column basis: - MonomialBasis([1, y, x, y^2, x*y, x^2]) + SubBasis{Monomial}([1, y, x, y^2, x*y, x^2]) And entries in a 6×6 SymMatrix{VariableRef}: _[177] _[178] _[180] _[183] _[187] _[192] _[178] _[179] _[181] _[184] _[188] _[193] @@ -113,7 +113,7 @@ And entries in a 6×6 SymMatrix{VariableRef}: _[187] _[188] _[189] _[190] _[191] _[196] _[192] _[193] _[194] _[195] _[196] _[197] GramMatrix with row/column basis: - MonomialBasis([1, y, x, y^2, x*y, x^2]) + SubBasis{Monomial}([1, y, x, y^2, x*y, x^2]) And entries in a 6×6 SymMatrix{VariableRef}: _[198] _[199] _[201] _[204] _[208] _[213] _[199] _[200] _[202] _[205] _[209] _[214] @@ -150,11 +150,11 @@ numerically (see [Blekherman2012; Section 3.1.5](@cite)). For instance, creating an univariate cubic polynomial variable `p` using the Chebyshev basis can be done as follows: ```jldoctest variables -julia> cheby_basis = FixedPolynomialBasis([1, x, 2x^2-1, 4x^3-3x]) -FixedPolynomialBasis([1, x, -1 + 2x², -3x + 4x³]) +julia> cheby_basis = SubBasis{Chebyshev}(monomials(x, 0:3)) +SubBasis{ChebyshevFirstKind}([1, x, x², x³]) julia> @variable(model, variable_type=Poly(cheby_basis)) -(_[219] - _[221]) + (_[220] - 3 _[222])x + (2 _[221])x² + (4 _[222])x³ +(_[219])·ChebyshevFirstKind(1) + (_[220])·ChebyshevFirstKind(x) + (_[221])·ChebyshevFirstKind(x²) + (_[222])·ChebyshevFirstKind(x³) ``` and to create a quadratic form variable `q` using the *scaled monomial* basis (see [Blekherman2012; Section 3.1.5](@cite)), use the following: @@ -165,17 +165,12 @@ julia> X = monomials([x, y], 2) xy x² -julia> scaled_basis = ScaledMonomialBasis(X) -ScaledMonomialBasis([y², xy, x²]) +julia> scaled_basis = SubBasis{ScaledMonomial}(X) +SubBasis{ScaledMonomial}([y², xy, x²]) -julia> @variable(model, variable_type=Poly(scaled_basis)) -(_[223])y² + (1.4142135623730951 _[224])xy + (_[225])x² -``` -which is equivalent to -```jldoctest variables -julia> scaled_basis = FixedPolynomialBasis([x^2, √2*x*y, y^2]) -FixedPolynomialBasis([x², 1.4142135623730951xy, y²]) +julia> p = @variable(model, variable_type=Poly(scaled_basis)) +(_[223])·ScaledMonomial(y²) + (_[224])·ScaledMonomial(xy) + (_[225])·ScaledMonomial(x²) -julia> @variable(model, variable_type=Poly(scaled_basis)) -(_[228])y² + (1.4142135623730951 _[227])xy + (_[226])x² +julia> polynomial(p) +(_[223])y² + (1.4142135623730951 _[224])xy + (_[225])x² ``` diff --git a/src/Bridges/Constraint/sos_polynomial.jl b/src/Bridges/Constraint/sos_polynomial.jl index 0918a2494..22cf170fe 100644 --- a/src/Bridges/Constraint/sos_polynomial.jl +++ b/src/Bridges/Constraint/sos_polynomial.jl @@ -129,7 +129,11 @@ function MOI.Bridges.Constraint.concrete_bridge_type( end function MOI.Bridges.inverse_map_function(::SOSPolynomialBridge, f) - throw(MOI.Bridges.MapNotInvertible("The linear map is not invertible for some basis transformation, use a `CachingOptimizer` layer")) + throw( + MOI.Bridges.MapNotInvertible( + "The linear map is not invertible for some basis transformation, use a `CachingOptimizer` layer", + ), + ) # Does not work with QuotientBasis #return SA.coeffs(MP.polynomial(f, bridge.new_basis), bridge.set.basis) end From f967f6d52ccd96f9a33790f5b023bb19cd6e23e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 2 Jul 2024 11:35:56 +0200 Subject: [PATCH 76/84] Fix --- test/Mock/quartic_ideal.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Mock/quartic_ideal.jl b/test/Mock/quartic_ideal.jl index 1d0d02292..b8a17c9c2 100644 --- a/test/Mock/quartic_ideal.jl +++ b/test/Mock/quartic_ideal.jl @@ -1,3 +1,4 @@ +config = MOI.Test.Config() function optimize!(mock) return MOI.Utilities.mock_optimize!( mock, From de85a74a2e3732450f6b9b7e73a201cb017b6a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 2 Jul 2024 13:43:36 +0200 Subject: [PATCH 77/84] Fixes --- test/Mock/utilities.jl | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/Mock/utilities.jl b/test/Mock/utilities.jl index 152c22f9c..e38949efd 100644 --- a/test/Mock/utilities.jl +++ b/test/Mock/utilities.jl @@ -61,8 +61,12 @@ function bridged_mock( ) mock = MOI.Utilities.MockOptimizer(model) bridged = MOI.Bridges.full_bridge_optimizer(mock, Float64) + cached = MOI.Utilities.CachingOptimizer( + MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), + bridged, + ) MOI.Utilities.set_mock_optimize!(mock, mock_optimize!...) - return bridged + return cached end function cached_mock( @@ -75,18 +79,14 @@ function cached_mock( # mode so that it's copied at `optimize!` to test that `copy_to` works. # If we just return `cached`, it will be emptied in `_model` and the state # will be `MOI.Utilities.ATTACHED_OPTIMIZER` which is not what we want. For this - # reason we return a `JuMP.OptimizerFactory` which returns `cached` instead. - return ( - () -> begin - cached = MOI.Utilities.CachingOptimizer( - cache(), - MOI.Utilities.AUTOMATIC, - ) - optimizer = bridged_mock(args...; kws...) - MOI.Utilities.reset_optimizer(cached, optimizer) - return cached - end - ) + # reason we return an function that returns `cached` instead. + return () -> begin + cached = + MOI.Utilities.CachingOptimizer(cache(), MOI.Utilities.AUTOMATIC) + optimizer = bridged_mock(args...; kws...) + MOI.Utilities.reset_optimizer(cached, optimizer) + return cached + end end function mocks(args...; kws...) From 7e226b9db5ad801192c5e4b2c9fbbc31bfdfb9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 2 Jul 2024 17:10:12 +0200 Subject: [PATCH 78/84] Fix --- src/sosdec.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sosdec.jl b/src/sosdec.jl index a88f316f9..427e0af56 100644 --- a/src/sosdec.jl +++ b/src/sosdec.jl @@ -106,7 +106,7 @@ function Base.promote_rule( ::Type{SOSDecomposition{A,T2,V2,U2}}, ) where {A,T1,T2,V1,V2,U1,U2} T = promote_type(T1, T2) - V = MA.promote_operation(similar, V1, T) + V = SA.similar_type(V1, T) return SOSDecomposition{A,T,V,_promote_add_mul(T)} end From 2b5a47bed6309559206b570dcd7577b139b9579d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 3 Jul 2024 09:11:15 +0200 Subject: [PATCH 79/84] Fixes --- src/Bridges/Constraint/Constraint.jl | 1 - .../Constraint/scaled_diagonally_dominant.jl | 120 ------------------ src/constraints.jl | 22 +--- src/variables.jl | 15 +++ 4 files changed, 20 insertions(+), 138 deletions(-) delete mode 100644 src/Bridges/Constraint/scaled_diagonally_dominant.jl diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 3bb7c412a..3ea7a7542 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -19,7 +19,6 @@ const Certificate = SOS.Certificate include("empty.jl") include("psd2x2.jl") include("diagonally_dominant.jl") -include("scaled_diagonally_dominant.jl") # SOS polynomial bridges include("utilities.jl") diff --git a/src/Bridges/Constraint/scaled_diagonally_dominant.jl b/src/Bridges/Constraint/scaled_diagonally_dominant.jl deleted file mode 100644 index 7a6f36c44..000000000 --- a/src/Bridges/Constraint/scaled_diagonally_dominant.jl +++ /dev/null @@ -1,120 +0,0 @@ -struct ScaledDiagonallyDominantBridge{T,F,VBS} <: - MOI.Bridges.Constraint.AbstractBridge - side_dimension::Int - variable_bridge::VBS - equality::MOI.ConstraintIndex{F,MOI.Zeros} -end - -function MOI.Bridges.Constraint.bridge_constraint( - ::Type{ScaledDiagonallyDominantBridge{T,F,VBS}}, - model::MOI.ModelLike, - f::MOI.AbstractVectorFunction, - s::SOS.ScaledDiagonallyDominantConeTriangle, -) where {T,F,VBS} - @assert MOI.output_dimension(f) == MOI.dimension(s) - Q, variable_bridge = add_matrix_variable_bridge( - model, - SOS.ScaledDiagonallyDominantConeTriangle, - MOI.side_dimension(s), - T, - ) - g = MOI.operate(-, T, f, MOI.Utilities.vectorize(g)) - equality = MOI.add_constraint(model, g, MOI.Zeros(MOI.dimension(s))) - return ScaledDiagonallyDominantBridge{T,F,VBS}( - MOI.side_dimension(s), - variable_bridge, - equality, - ) -end - -function MOI.supports_constraint( - ::Type{<:ScaledDiagonallyDominantBridge}, - ::Type{<:MOI.AbstractVectorFunction}, - ::Type{<:SOS.ScaledDiagonallyDominantConeTriangle}, -) - return true -end -function MOI.Bridges.added_constrained_variable_types( - ::Type{<:ScaledDiagonallyDominantBridge}, -) - return Tuple{Type}[] -end -function MOI.Bridges.added_constraint_types( - ::Type{<:ScaledDiagonallyDominantBridge{T}}, -) where {T} - added = [(F, MOI.Zeros)] - return append_added_constraint_types( - added, - SOS.ScaledDiagonallyDominantConeTriangle, - T, - ) -end -function MOI.Bridges.Constraint.concrete_bridge_type( - ::Type{<:ScaledDiagonallyDominantBridge{T}}, - F::Type{<:MOI.AbstractVectorFunction}, - ::Type{SOS.ScaledDiagonallyDominantConeTriangle}, -) where {T} - G = MOI.Utilities.promote_operation(-, T, F, MOI.VectorAffineFunction{T}) - VBS = union_vector_bridge_types(SOS.ScaledDiagonallyDominantConeTriangle, T) - return ScaledDiagonallyDominantBridge{T,G,VBS} -end - -# Attributes, Bridge acting as an model -function MOI.get( - bridge::ScaledDiagonallyDominantBridge, - attr::MOI.NumberOfVariables, -) - return MOI.get(bridge.variable_bridge, attr) -end -function MOI.get( - bridge::ScaledDiagonallyDominantBridge, - attr::MOI.NumberOfConstraints, -) - return MOI.get(bridge.variable_bridge, attr) -end -function MOI.get( - bridge::ScaledDiagonallyDominantBridge, - attr::MOI.ListOfConstraintIndices, -) - return MOI.get(bridge.variable_bridge, attr) -end -function MOI.get( - bridge::ScaledDiagonallyDominantBridge{T,F}, - ::MOI.NumberOfConstraints{F,MOI.Zeros}, -) where {T,F} - return 1 -end -function MOI.get( - bridge::ScaledDiagonallyDominantBridge{T,F}, - ::MOI.ListOfConstraintIndices{F,MOI.Zeros}, -) where {T,F} - return [bridge.equality] -end - -# Indices -function MOI.delete( - model::MOI.ModelLike, - bridge::ScaledDiagonallyDominantBridge, -) - MOI.delete(model, bridge.equality) - return MOI.delete(model, bridge.variable_bridge) -end - -# TODO ConstraintPrimal -function MOI.get( - model::MOI.ModelLike, - attr::MOI.ConstraintDual, - bridge::ScaledDiagonallyDominantBridge, -) - dual = copy(MOI.get(model, attr, bridge.equality)) - # Need to divide by 2 because of the custom scalar product for this cone - k = 0 - for j in 1:bridge.side_dimension - for i in 1:(j-1) - k += 1 - dual[k] /= 2 - end - k += 1 - end - return dual -end diff --git a/src/constraints.jl b/src/constraints.jl index a8f248ed3..a10824ba6 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -303,31 +303,19 @@ function PolyJuMP.bridges( end function PolyJuMP.bridges( - ::Type{<:MOI.AbstractVectorFunction}, + F::Type{<:MOI.AbstractVectorFunction}, ::Type{<:ScaledDiagonallyDominantConeTriangle}, -) - return [(Bridges.Constraint.ScaledDiagonallyDominantBridge, Float64)] -end - -function _bridge_coefficient_type(::Type{<:WeightedSOSCone{M}}) where {M} - return _complex(Float64, M) -end - -function _bridge_coefficient_type(::Type{SOSPolynomialSet{D,B,C}}) where {D,B,C} - return _complex(Float64, matrix_cone_type(C)) -end - -function PolyJuMP.bridges(S::Type{<:WeightedSOSCone}) +) # Needed so that `Variable.ScaledDiagonallyDominantBridge` is added as well return Tuple{Type,Type}[( - Bridges.Variable.KernelBridge, - _bridge_coefficient_type(S), + MOI.Bridges.Constraint.VectorSlackBridge, + PolyJuMP.coefficient_type_or_float(F), )] end function PolyJuMP.bridges( F::Type{<:MOI.AbstractVectorFunction}, ::Type{<:WeightedSOSCone}, -) # Needed so that `KernelBridge` is added as well +) # Needed so that `Variable.KernelBridge` is added as well return Tuple{Type,Type}[( MOI.Bridges.Constraint.VectorSlackBridge, PolyJuMP.coefficient_type_or_float(F), diff --git a/src/variables.jl b/src/variables.jl index 76a593978..e3b0eeaf7 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -1,5 +1,20 @@ export DSOSPoly, SDSOSPoly, SOSPoly +function _bridge_coefficient_type(::Type{<:WeightedSOSCone{M}}) where {M} + return _complex(Float64, M) +end + +function _bridge_coefficient_type(::Type{SOSPolynomialSet{D,B,C}}) where {D,B,C} + return _complex(Float64, matrix_cone_type(C)) +end + +function PolyJuMP.bridges(S::Type{<:WeightedSOSCone}) + return Tuple{Type,Type}[( + Bridges.Variable.KernelBridge, + _bridge_coefficient_type(S), + )] +end + function PolyJuMP.bridges(::Type{<:PositiveSemidefinite2x2ConeTriangle}) return [(Bridges.Variable.PositiveSemidefinite2x2Bridge, Float64)] end From 1bc047cd10d875f249a959acddc91d1d97794966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 3 Jul 2024 10:01:59 +0200 Subject: [PATCH 80/84] Fixes --- docs/src/tutorials/Getting started/motzkin.jl | 2 +- .../tutorials/Systems and Control/lyapunov_function_search.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorials/Getting started/motzkin.jl b/docs/src/tutorials/Getting started/motzkin.jl index d66625da9..f506ec5fa 100644 --- a/docs/src/tutorials/Getting started/motzkin.jl +++ b/docs/src/tutorials/Getting started/motzkin.jl @@ -90,7 +90,7 @@ value(deno) # We can easily extend `Plots` by adding a recipe to plot bivariate polynomials. using RecipesBase -@recipe function f(x::AbstractVector, y::AbstractVector, p::Polynomial) +@recipe function f(x::AbstractVector, y::AbstractVector, p::AbstractPolynomial) x, y, (x, y) -> p(variables(p) => [x, y]) end import Plots diff --git a/docs/src/tutorials/Systems and Control/lyapunov_function_search.jl b/docs/src/tutorials/Systems and Control/lyapunov_function_search.jl index 024562a4c..ca550d201 100644 --- a/docs/src/tutorials/Systems and Control/lyapunov_function_search.jl +++ b/docs/src/tutorials/Systems and Control/lyapunov_function_search.jl @@ -63,4 +63,4 @@ JuMP.primal_status(model) # We can now obtain this feasible solution with: value(V) -@test iszero(remove_monomials(value(V), monos)) #src +@test iszero(remove_monomials(MP.polynomial(value(V)), monos)) #src From 39d45b0205f47c3dc1ee80df3e2b8992ab9719cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 3 Jul 2024 14:09:34 +0200 Subject: [PATCH 81/84] Update ci script --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7e358010..6697d8a8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: # arch: x64 steps: - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 + - uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} From 66e8e51d392f205d3e20a7a1e7b8af188d57cb9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 3 Jul 2024 16:04:01 +0200 Subject: [PATCH 82/84] Fixes --- .../tutorials/Systems and Control/lyapunov_function_search.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/Systems and Control/lyapunov_function_search.jl b/docs/src/tutorials/Systems and Control/lyapunov_function_search.jl index ca550d201..e2edb6ac8 100644 --- a/docs/src/tutorials/Systems and Control/lyapunov_function_search.jl +++ b/docs/src/tutorials/Systems and Control/lyapunov_function_search.jl @@ -63,4 +63,4 @@ JuMP.primal_status(model) # We can now obtain this feasible solution with: value(V) -@test iszero(remove_monomials(MP.polynomial(value(V)), monos)) #src +@test iszero(remove_monomials(polynomial(value(V)), monos)) #src From 9a482eefd025e3819a0900c3b5e26c4d24b25a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 4 Jul 2024 09:18:21 +0200 Subject: [PATCH 83/84] Fix --- docs/src/reference/constraints.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/reference/constraints.md b/docs/src/reference/constraints.md index 23b98ec9a..3eb1f330d 100644 --- a/docs/src/reference/constraints.md +++ b/docs/src/reference/constraints.md @@ -20,6 +20,7 @@ SOSDecomposition SOSDecompositionWithDomain SumOfSquares.SOSDecompositionAttribute sos_decomposition +SumOfSquares.MultiplierIndexBoundsError ``` SAGE decomposition attribute: From cc0b015864ea0f2b94e1dea8b9b236ab5062332a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 4 Jul 2024 11:25:50 +0200 Subject: [PATCH 84/84] Fix doc --- docs/src/variables.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/variables.md b/docs/src/variables.md index 9706f69b6..a193b8fa6 100644 --- a/docs/src/variables.md +++ b/docs/src/variables.md @@ -57,10 +57,10 @@ julia> @variable(model, [1:3, 1:4], Poly(X)) # Creates a Matrix (_[19])·1 + (_[20])·y + (_[21])·x + (_[22])·y² + (_[23])·xy + (_[24])·x² (_[73])·1 + (_[74])·y + (_[75])·x + (_[76])·y² + (_[77])·xy + (_[78])·x² julia> @variable(model, [[:a, :b], -2:2], Poly(X)) # Creates a DenseAxisArray -2-dimensional DenseAxisArray{StarAlgebras.AlgebraElement{MultivariateBases.Algebra{SubBasis{Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}, MonomialVector{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, VariableRef, Vector{VariableRef}},2,...} with index sets: +2-dimensional DenseAxisArray{StarAlgebras.AlgebraElement{MultivariateBases.Algebra{SubBasis{Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, MultivariatePolynomials.Graded{MultivariatePolynomials.LexOrder}}, DynamicPolynomials.MonomialVector{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, MultivariatePolynomials.Graded{MultivariatePolynomials.LexOrder}}}, Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, MultivariatePolynomials.Graded{MultivariatePolynomials.LexOrder}}}, VariableRef, Vector{VariableRef}},2,...} with index sets: Dimension 1, [:a, :b] Dimension 2, -2:2 -And data, a 2×5 Matrix{StarAlgebras.AlgebraElement{MultivariateBases.Algebra{SubBasis{Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}, MonomialVector{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, Graded{LexOrder}}}, VariableRef, Vector{VariableRef}}}: +And data, a 2×5 Matrix{StarAlgebras.AlgebraElement{MultivariateBases.Algebra{SubBasis{Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, MultivariatePolynomials.Graded{MultivariatePolynomials.LexOrder}}, DynamicPolynomials.MonomialVector{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, MultivariatePolynomials.Graded{MultivariatePolynomials.LexOrder}}}, Monomial, DynamicPolynomials.Monomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, MultivariatePolynomials.Graded{MultivariatePolynomials.LexOrder}}}, VariableRef, Vector{VariableRef}}}: (_[79])·1 + (_[80])·y + (_[81])·x + (_[82])·y² + (_[83])·xy + (_[84])·x² … (_[127])·1 + (_[128])·y + (_[129])·x + (_[130])·y² + (_[131])·xy + (_[132])·x² (_[85])·1 + (_[86])·y + (_[87])·x + (_[88])·y² + (_[89])·xy + (_[90])·x² (_[133])·1 + (_[134])·y + (_[135])·x + (_[136])·y² + (_[137])·xy + (_[138])·x²