summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pyproject.toml2
-rw-r--r--sumofsquares/error.py7
-rw-r--r--sumofsquares/solver/cvxopt.py15
-rw-r--r--sumofsquares/solver/mosek.py57
4 files changed, 77 insertions, 4 deletions
diff --git a/pyproject.toml b/pyproject.toml
index def9c86..cb9203d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,7 +8,9 @@ readme = "README.md"
[tool.poetry.dependencies]
python = ">=3.10"
polymatrix = {git = "https://gitlab.ethz.ch/npross/polymatrix.git"}
+python = ">=3.10,<3.13"
cvxopt = "^1.3.2"
+mosek = "^10.1.31"
[tool.poetry.group.notebook.dependencies]
diff --git a/sumofsquares/error.py b/sumofsquares/error.py
new file mode 100644
index 0000000..34ea6d8
--- /dev/null
+++ b/sumofsquares/error.py
@@ -0,0 +1,7 @@
+"""
+Errors and exceptions raised by the sum of squares package.
+"""
+
+class NotSupportedBySolver(Exception):
+ """ The chosen solver cannot solve this problem. """
+
diff --git a/sumofsquares/solver/cvxopt.py b/sumofsquares/solver/cvxopt.py
index 0de8dea..0e5ce37 100644
--- a/sumofsquares/solver/cvxopt.py
+++ b/sumofsquares/solver/cvxopt.py
@@ -1,3 +1,6 @@
+"""
+Solve sumofsquares problems using CVXOPT
+"""
import cvxopt
import numpy as np
@@ -12,6 +15,7 @@ from polymatrix.polymatrix.index import MonomialIndex, VariableIndex
from ..abc import Problem, SolverInfo
from ..constraints import NonNegative, EqualToZero, PositiveSemiDefinite, ExponentialCone
+from ..error import NotSupportedBySolver
from ..variable import OptVariable
@@ -25,11 +29,14 @@ class CVXOPTInfo(SolverInfo, UserDict):
return self[key]
-def solve_sos_cone(prob: Problem, verbose: bool = False, *args, **kwargs) -> tuple[dict[OptVariable, float], CVXOPTInfo]:
+def solve_sos_cone(prob: Problem, verbose: bool = False,
+ *args, **kwargs) -> tuple[dict[OptVariable, float], CVXOPTInfo]:
r"""
- Solve a conic problem in the cone of SOS polynomials :math:`\mathbf{\Sigma}_d(x)`.
+ Solve a conic problem in the cone of SOS polynomials
+ :math:`\mathbf{\Sigma}_d(x)` using CVXOPT.
- Any `*args` and `**kwargs` other than `prob` are passed to the CVXOPT solver.
+ Any `*args` and `**kwargs` other than `prob` are passed to the CVXOPT
+ solver.
"""
# Check that the problem can be solved by CVXOpt, i.e.
# - Cost function is at most quadratic
@@ -88,7 +95,7 @@ def solve_sos_cone(prob: Problem, verbose: bool = False, *args, **kwargs) -> tup
raise NotImplementedError
elif isinstance(c, ExponentialCone):
- raise NotImplementedError
+ raise NotSupportedBySolver("CVXOpt cannot solve problems with the exponential cone.")
elif isinstance(c, EqualToZero | NonNegative):
# Convert constraints to affine expression in decision variables
diff --git a/sumofsquares/solver/mosek.py b/sumofsquares/solver/mosek.py
new file mode 100644
index 0000000..a454e68
--- /dev/null
+++ b/sumofsquares/solver/mosek.py
@@ -0,0 +1,57 @@
+"""
+Solve sumofsquares problems using MOSEK
+"""
+from __future__ import annotations
+
+import sys
+import mosek
+
+from pathlib import Path
+
+from ..abc import Problem, SolverInfo
+from ..variable import OptVariable
+
+
+class MOSEKInfo(SolverInfo):
+ pass
+
+
+def _streamprinter(text):
+ # Helper function
+ sys.stdout.write(text)
+ sys.stdout.flush()
+
+
+MOSEK_ENV: mosek.Env | None = None
+
+def setup(license_file: Path | str | None = None):
+ """
+ Setup the MOSEK solver. If `license_file` is left unspecified, according to
+ the official documentation MOSEK will search for a license file in one of
+ the following directories depending on the operating system
+
+ ::
+ $HOME/mosek/mosek.lic
+ %USERPROFILE%\mosek\mosek.lic
+ """
+ global MOSEK_ENV
+ MOSEK_ENV = mosek.Env(licensefile=str(license_file))
+
+
+def solve_sos_cone(prob: Problem, verbose: bool = False,
+ *args, **kwargs) -> tuple[dict[OptVariable, float], MOSEKInfo]:
+ r"""
+ Solve a conic problem in the cone of SOS polynomials
+ :math:`\mathbf{\Sigma}_d(x)` using MOSEK.
+ """
+ global MOSEK_ENV
+
+ if not MOSEK_ENV:
+ raise RuntimeError("You forgot to call `sumofsquares.solvers.mosek.setup(license)`!")
+
+ with MOSEK_ENV as env:
+ with env.Task() as task:
+ if verbose:
+ task.set_Stream(mosek.streamtype.log, _streamprinter)
+
+ raise NotImplementedError