Gettings Started
SOSOpt is dedicated to solving the sums-of-squares (SOS) optimization problem, offering advanced support for multiple evaluations and substitutions of decision variables. The SOS optimization problem under consideration is formulated as:
where \(c(\theta)\), \(q(\theta)\), \(p_k(x; \theta)\), and \(l(\theta)\) are polynomial expression in polynomial variable \(x\) and decision variable \(\theta\). When solving the SOS problem, an exception is raised when one of these polynomial expression does not depend linearly on the decision variable \(\theta\).
Compared to other SOS libraries, SOSOpt defines standalone constraints, not requiring a link to a concrete SOS problem.
In SOSTOOLS, a Program
object is used to define the SOS constraint sosineq(Program, r);
.
In SumOfSqaures.jl, a model
object is used to define the SOS constraint @constraint(model, r >= 0)
.
The state
object takes a comparable role of such an object as a container of shared data.
However, in SOSOpt, the usage of an SOS constraint returned by the function sosopt.sos_constraint
is not restricted to a single SOS program, making this approach more modular when working with different SOS problems.
The state object is created as follows.
Variables
The polynomial variable \(x\) and decision variable \(\theta\) are both build within the PolyMat ecosystem.
By convention, native PolyMat variables -- created via polymat.define_variable
-- are interpretated as polynomial variables.
In contrast, decision variables are introduced by extending the PolyMat framework with a new variable type.
The following example defines a polynomial variable \(x\) and a decision variable \(\theta\) consisting of three components and uses them to construct a polynomial expression.
import sosopt
import polymat
# define a polynomial variable
x = polymat.define_variable('x')
# define three decision variables
theta_0 = sosopt.define_variable('theta_0')
theta_1 = sosopt.define_variable('theta_1')
theta_2 = sosopt.define_variable('theta_2')
# parametrized polynomial containing both polynomial and decision variable
r = theta_0 + theta_1 * x + theta_2 * x**2
Alternatively, the construction of a fully parametrized polynomial -- involving the specification of a decision variable for each coefficient of the polynomial -- is automated using the sosopt.define_polynomial
function:
# creates a monomial vector [1 x x^2]
monomials = x.combinations(degrees=range(3))
# creates a parametrized polynomial r = r_0 + r_1 x + r_2 x^2
state, r = sosopt.define_polynomial(name='r', monomials=monomials).apply(state)
# returns a polymat vector [r_0, r_1, r_2] containing the coefficients
r.coefficient
Furthermore, a parametrized polynomial matrix can be created by additionally specifying the row
and col
arguments:
Polynomial Constraints
The polynomial constraints in the SOS problem can be defined in SOSOpt as follows:
Zero Polynomial Constraint
This polynomial constraint ensures that a polynomial expression is zero as a polynomial: All coefficients of the polynomials must be zero.
The following constraint:
state, r_zero_constraint = sosopt.zero_polynomial_constraint(
name='r_zero',
equal_to_zero=r,
).apply(state)
enforces the coefficients of \(r(x)\) to be zero:
SOS Constraint
This polynomial constraint ensures that a scalar polynomial expression belongs to the SOS Cone.
The following constraint:
enforces the SOS constraint:SOS Matrix Constraint
This polynomial constraint ensures a polynomial matrix expression belongs to the SOS Matrix Cone.
The following constraint:
state, q_sos_constraint = sosopt.sos_matrix_constraint(
name='q_sos',
greater_than_zero=q,
).apply(state)
Quadratic Module Constraint
This polynomial constraint defines a non-negativity condition on a subset of the states space using a quadratic module construction following Putinar's Positivstellensatz.
The following constraint:
state, r_qm_constraint = sosopt.quadratic_module_constraint(
name='r_qm',
greater_than_zero=r,
domain=sosopt.set_(
smaller_than_zero={'w': w},
)
).apply(state)
Defining an SOS Problem
An SOS problem is defined using the sosopt.sos_problem
function taking as arguments:
- lin_cost
: Scalar expression \(c(\theta)\) defining the linear cost.
- quad_cost
: Vector expression \(q(\theta)\) defining the quadratic cost \(q(\theta)^\top q(\theta)\).
- constraints
: SOS and equality constraints
- solver
: SDP solver selection (CVXOPT or MOSEK)
problem = sosopt.sos_problem(
lin_cost=Q.trace(),
quad_cost=Q.diag(),
constraints=(r_sos_constraint,),
solver=polymat.cvxopt_solver,
)
# solve SOS problem
state, result = problem.solve().apply(state)
The solve
method converts the SOS problem to an SDP, solves the SDP using the provided solver, and maps the result to a dictionary result.symbol_values
.
See examples/boxconstraints for a full example on how to define and solve an SOS problem.