SOSOpt is a library designed for solving sums-of-squares optimization problems.
Project description
SOSOpt
SOSOpt is a Python library designed for solving sums-of-squares (SOS) optimization problems.
Features
polymatintegration: Extends thepolymatecosystem by introducing a new variable type for decision variables. Nativepolymatvariables are interpretated as polynomial variables, enabling seamless creation of expressions that mix both polnyomial and decision variables.- Pythonic code structure: Designed with a programming-oriented syntax, avoiding unnatural adaptations of Python dunder methods to match mathematical notation.
- Data-oriented design: Key components such as decision variables, SOS constraints, and SOS problems are implemented as structured data types, facilitating efficient inspection and debugging.
- Stateful Computation: The sparse internal structures are computed based on a state object. This eliminates any dependency on global variables and provides control over the sparse intermediate structures stored in memory for reuse.
Installation
You can install SOSOpt using pip:
pip install sosopt
Basic example
This example illustrates how to define and solve a simple SOS optimization problem using SOSOpt.
In this example, we aim to compute the coefficients of a polynomial $r(x)$ whose zero-sublevel set contains the box-like set defined by the intersection of the zero-sublevel sets of polynomials $w_1(x)$ and $w_2(x)$:
$$\mathcal X_\text{Box} := \lbrace x \mid w_1(x) \leq 0, w_2(x) \leq 0 \rbrace$$
The polynomial $r(x)$ is parameterized by the symmetric matrix $Q_r$, and is expressed as:
$$r(x) := Z(x)^\top Q_r Z(x)$$
where $Z(x)$ is a vector of monomials in $x$.
The SOS optimization problem is formulated to find $r(x)$ that maximizes the surrogate for the volume of the zero-sublevel set of $r(x)$, represented by the trace of $Q_r$. The resulting SOS problem is defined as:
$$\begin{array}{ll} \text{find} & Q_r \in \mathbb R^{m \times m} \ \text{minimize} & \text{tr}( Q_r ) \ \text{subject to} & r(x) < 0 \quad \forall x \in \mathcal X_\text{Box} \ \end{array}$$
This formulation seeks to minimize the trace of $Q_r$ while ensuring that $r(x)$ is negative within the box-like set $\mathcal X_\text{Box}$.
import polymat
import sosopt
# Initialize the state object, which is passed through all operations related to solving
# the SOS problem
state = polymat.init_state()
# Define polynomial variables and stack them into a vector
variable_names = ("x_1", "x_2", "x_3")
x1, x2, x3 = tuple(polymat.define_variable(name) for name in variable_names)
x = polymat.v_stack((x1, x2, x3))
# Define the box-like set as the intersection of the zero-sublevel sets of two
# polynomials w1 and w2.
w1 = ((x1 + 0.3) / 0.5) ** 2 + (x2 / 20) ** 2 + (x3 / 20) ** 2 - 1
w2 = ((x1 + 0.3) / 20) ** 2 + (x2 / 1.3) ** 2 + (x3 / 1.3) ** 2 - 1
# Define a polynomial where the coefficients are decision variables in the SOS problem
r_var = sosopt.define_polynomial(
name='r',
monomials=x.combinations(degrees=(1, 2)),
)
# Fix the constant part of the polynomial to -1 to ensure numerical stability
r = r_var - 1
# Prints the symbol representation of the polynomial:
# r(x) = r_0*x_1 + r_1*x_2 + ... + r_8*x_3**2 - 1
state, sympy_repr = polymat.to_sympy(r).apply(state)
print(f'r={sympy_repr}')
# Apply Putinar's Positivstellensatz to ensure the box-like set, encoded by w1 and w2,
# is contained within the zero sublevel set of r(x).
state, constraint = sosopt.psatz_putinar_constraint(
name="rpos",
smaller_than_zero=r,
domain=sosopt.set_(
smaller_than_zero={
"w1": w1,
"w2": w2,
},
),
).apply(state)
# Minimize the volume surrogate of the zero-sublevel set of r(x)
Qr_diag = r.to_gram_matrix(x).diag()
# Define the SOS problem
problem = sosopt.sos_problem(
lin_cost=-Qr_diag.sum(),
quad_cost=Qr_diag,
constraints=(constraint,),
solver=sosopt.cvx_opt_solver, # choose solver
# solver=sosopt.mosek_solver,
)
# Solve the SOS problem
state, sos_result = problem.solve().apply(state)
# Output the result
# Prints the mapping of symbols to their correspoindg vlaues found by the solver
print(f'{sos_result.symbol_values=}')
# Display solver data such as status, iterations, and final cost.
print(f'{sos_result.solver_data.status}') # Expected output: 'optimal'
print(f'{sos_result.solver_data.iterations}') # Expected output: 6
print(f'{sos_result.solver_data.cost}') # Expected output: -1.2523582776230828
print(f'{sos_result.solver_data.solution}') # Expected output: array([ 5.44293046e-01, ...])
This figure illustrates the contour of the zero-sublevel sets of the resulting polynomial $r(x)$:
Operations
Defining Optimization Variables
- Decision variable: Use
sosopt.define_variableto create a decision variable for the SOS Problem. Any variables created withpolymat.define_variableare treated as polynomial variables. - Polynomial variable: Define a polynomial matrix variable with entries that are parametrized polynomials, where the coefficients are decision variables, using
sosopt.define_polynomial. - Matrix variable: Create a symmetric $n \times n$ polynomial matrix variable using
sosopt.define_symmetric_matrix. - Multipliers*: Given a reference polynomial, create a polynomial variable intended for multiplication with the reference polynomial, ensuring that the resulting polynomial does not exceed a specified degree using
sosopt.define_multiplier.
Defining Sets
- Semialgebraic set: Define a semialgebraic set from a collection scalar polynomial expressions with
sosopt.set_.
Defining Constraint
- Zero Polynomial*: Enforce a polynomial expression to be equal to zero using
sosopt.zero_polynomial_constraint. - Sum-of-Sqaures (SOS)*: Define a scalar polynomial expression within the SOS Cone using
sosopt.sos_constraint. - SOS Matrix*: Define a polynomial matrix expression within the SOS Matrix Cone using
sosopt.sos_matrix_constraint. - Putinar's P-satz*: Encode a positivity condition for a polynomial matrix expression on a semialgebraic set using
sosopt.psatz_putinar_constraint.
Defining the SOS Optimization Problem
- Solver Arguments*: Convert polynomial expression to their array representations, which are required for defining the SOS problem, using
sosopt.solver_args. - SOS Problem: Create an SOS Optimization problem using the solver arguments with
sosopt.sos_problem.
* These operations return a state monad object. To retrieve the actualy result, you need to call the apply method on the returned object, passing the state as an argument.
Reference
Below are some references related to this project:
- PolyMat is a Python library designed for the representation and manipulation of multivariate polynomial matrices.
- Advanced safety filter includes Jupyter notebooks that model and simulate the concept of an advanced safety filter using SOSOpt.
- SumOfSqaures.py is a simple sum-of-squares Python library built on
sympy, leading to increased computation time when converting an SOS problem into a SDP.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sosopt-0.0.7.tar.gz.
File metadata
- Download URL: sosopt-0.0.7.tar.gz
- Upload date:
- Size: 23.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9d8077b1cfba5c1b16ec7d77b6b6c686302e1f019018d561bb6122757f19263
|
|
| MD5 |
7018301e62f6e2d346e8e58f9fa86f69
|
|
| BLAKE2b-256 |
9673881561ab36c5968cfaa76867969c8f329e2a6285e99dba104635488294b1
|
File details
Details for the file sosopt-0.0.7-py2.py3-none-any.whl.
File metadata
- Download URL: sosopt-0.0.7-py2.py3-none-any.whl
- Upload date:
- Size: 39.7 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4b2947075e8cc250a88c0d9484864e18201f8f1395f83433aceac9a46570f40c
|
|
| MD5 |
2a6412f0b2a88771d9ea8c311be3d35e
|
|
| BLAKE2b-256 |
60aff43438df54e50869c824452af90728588730c8196b53e02dd853a79787ea
|