Skip to main content

SOSOpt

SOSOpt is a Python library designed for solving sums-of-squares (SOS) optimization problems.

Features

  • PolyMat integration: Extends the PolyMat ecosystem by introducing a new variable type for decision variables.
  • High-performance: While languages like Matlab (via SOSTOOLS) and Julia (via SumOfSqaures.jl) offer powerful SOS solvers, Python lack a comparable native implementation. SOSOpt fills this gap by providing a high-performance SOS optimization library.
  • Multiple Evaluations: Supports advanced workflows, including multiple evaluations of SOS problems and efficient substitutions of decision variables in a bilinear SOS formulations.

Installation

You can install SOSOpt using pip:

pip install sosopt

Documentation

Full documentation and usage examples are available at: https://michaelschneeberger.github.io/sosopt/latest/

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{diag}( Q_r )^\top \text{diag}( 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 = sosopt.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
state, r_var = sosopt.define_polynomial(
    name='r',
    monomials=x.combinations(degrees=(1, 2)),
).apply(state)
# 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.quadratic_module_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 = sosopt.gram_matrix(r, x).diag()

# Define the SOS problem
problem = sosopt.sos_problem(
    lin_cost=-Qr_diag.sum(),
    quad_cost=Qr_diag,
    constraints=(constraint,),
    solver=sosopt.cvxopt_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)$:

sos problem result

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.
  • SOSTOOLS powerful SOS solver written in $MATLAB$.
  • SumOfSqaures.jl powerful SOS solver written in $Julia$.

Metadata

Release files for sosopt 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sosopt 1.0.1
File Size Uploaded Provenance
sosopt-1.0.1.tar.gz 29.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for sosopt 1.0.1
File Interpreter ABI Platform
sosopt-1.0.1-py2.py3-none-any.whl Python 2, Python 3 none any Details

Total release size: 80.9 kB

Release files / sosopt-1.0.1.tar.gz

Download URL sosopt-1.0.1.tar.gz
Size 29.0 kB
Tags Source
SHA-256 checksum
How to use checksums
513214b665b18e856a288653e6354df9fbeec57c3f1f984d701e4368f6278a21
BLAKE2b-256 checksum
How to use checksums
672420a20f4d22a892b83c79fb5d127e90064de79dada0a98181a7148d478689
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.2

Release files / sosopt-1.0.1-py2.py3-none-any.whl

Download URL sosopt-1.0.1-py2.py3-none-any.whl
Size 51.9 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
7c3bc6d91d1b404af9456c55ab68993304f182182639c63eedd601d534980d84
BLAKE2b-256 checksum
How to use checksums
9788d10249666714a7f7a1cccd282553ffc99f45dd4c8878fefcc3972b272578
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.2

Security for sosopt 1.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page