Metadata-Version: 2.4
Name: sdplab
Version: 0.0.1
Summary: Tools for constructing and solving regularized semidefinite programming problems.
Author: Pavlo Pelikh
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/Pavlo3P/SDPLab
Project-URL: Repository, https://github.com/Pavlo3P/SDPLab
Project-URL: Issues, https://github.com/Pavlo3P/SDPLab/issues
Project-URL: Changelog, https://github.com/Pavlo3P/SDPLab/blob/master/CHANGELOG.md
Keywords: semidefinite-programming,conic-optimization,convex-optimization,regularization,quantum,jax,numpy
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: scipy
Requires-Dist: cvxpy
Requires-Dist: spacecore<0.5,>=0.4.2
Provides-Extra: jax
Requires-Dist: spacecore[jax]; extra == "jax"
Requires-Dist: jax>=0.9.1; extra == "jax"
Requires-Dist: optax>=0.2; extra == "jax"
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == "test"
Dynamic: license-file

# SDPLab

A library for building semidefinite and conic programs and solving them
through their **smoothed dual**. Problem data, spectral regularizers, and the
dual functional live here; every first-order optimization loop is delegated to
[spacecore](https://pypi.org/project/spacecore/).

## Install

```bash
pip install sdplab
```

The JAX backend and the optax optimizer are optional:

```bash
pip install "sdplab[jax]"
```

## The problem

$$
\begin{aligned}
\min_{X \in \mathrm{dom}(\mathcal{A})}\quad
    & \langle C, X\rangle \\
\text{s.t.}\quad
    & \mathcal{A}X = b, \\
    & X \succeq 0.
\end{aligned}
$$

| symbol | code | meaning |
| --- | --- | --- |
| $\mathrm{dom}(\mathcal{A})$ | `problem.dom` | space of $X$ and $C$ |
| $\mathrm{cod}(\mathcal{A})$ | `problem.cod` | space of $\mathcal{A}X$, $b$, and the dual $y$ |
| $\mathcal{A}$ | `problem.A` | linear constraint operator, a spacecore `LinOp` |
| $\mathcal{A}^\dagger y - C$ | `problem.dual_slack(y)` | dual slack |

$X$ is a plain element of a spacecore Euclidean Jordan algebra space — a
Hermitian matrix (classic SDP), a nonnegative vector (LP), or a tree of such
blocks — and $X \succeq 0$ means a nonnegative Jordan spectrum. Solvers accept
and return raw arrays and trees; there are no wrapper objects.

```python
import numpy as np
from spacecore import Context, DenseLinOp, DenseVectorSpace, HermitianSpace, NumpyOps
from sdplab import SDPProblem

ctx = Context(NumpyOps())                   # optional; see the spacecore docs
dom = HermitianSpace(n, ctx=ctx)            # X and C live here
cod = DenseVectorSpace((m,), ctx=ctx)       # A X, b, and the dual y live here

A = DenseLinOp(A_mats, dom, cod, ctx=ctx)
problem = SDPProblem(C, A, b, ctx=ctx)
```

Solve it directly with the CVXPY reference backend:

```python
from sdplab.solvers import run_cvxpy_solver

X, y = run_cvxpy_solver(problem, solver="CLARABEL")
```

## The smoothed dual

Penalize the primal with a superlinear convex $\varphi$ at strength
$\varepsilon > 0$:

$$
\min_X\ \langle C, X\rangle + \varepsilon\,\mathrm{Tr}[\varphi(X)]
\quad\text{s.t.}\quad \mathcal{A}X = b,\ X \succeq 0.
$$

Its dual is unconstrained and differentiable:

$$
\max_{y}\ D_\varepsilon(y) = \langle b, y\rangle - \varepsilon\,
\mathrm{Tr}\!\left[\psi\!\left(\frac{\mathcal{A}^\dagger y - C}{\varepsilon}\right)\right],
$$

with $\psi$ the Legendre transform of $\varphi$. Gradient methods apply, and the
primal is read back off the eigenvalues $s_i$ of the dual slack:
$\lambda_i(X) = \psi'(s_i/\varepsilon)$.

This route **assumes a unit-trace primal** — `primal_from_dual` normalizes to
$\mathrm{Tr}\,X = 1$ — so pose the problem accordingly, hence `unit_trace=True`
below. On a problem whose feasible set has a different trace the recovered $X$
is not feasible for it.

```python
from sdplab import EntropyReg, RegularizedSDPDualFunctional, run_regularized_solver
from sdplab.examples import generate_max_cut

problem = generate_max_cut(8, seed=0, unit_trace=True)
dual = RegularizedSDPDualFunctional(problem, EntropyReg(problem.dom))

result = run_regularized_solver(dual.bind(0.1), verbose=0)
X = dual.primal_from_dual(result.dual, 0.1)

problem.primal_objective(X)     # -5.0859, against -5.0990 from CVXPY
```

$\varepsilon$ is a per-call argument, so a continuation schedule can lower it
without rebuilding anything; `bind(eps)` fixes it and yields a standard
single-argument `spacecore.Functional`.

The theoretical basis for the regularized SDP formulation — the smoothed dual,
the primal recovery map, and the $\varepsilon \downarrow 0$ limit — is developed
in [arXiv:2602.23144](https://arxiv.org/abs/2602.23144).

## Regularizers

| class | $\varphi(t)$ |
| --- | --- |
| `EntropyReg` | $t(\log t - 1)$ |
| `QuadraticReg` | $t^2/2$ |

## Examples

`sdplab.examples` ships three instances, chosen to differ in the structure of
$\mathcal{A}$:

- `generate_max_cut` — real, diagonal extraction. `unit_trace=True` rescales the
  variable so $\mathrm{Tr}\,X = 1$.
- `generate_random_qot` — quantum optimal transport: complex, with a stacked
  Hermitian-block codomain, so the dual is a tuple of matrices.
- `generate_qubit_tomography` — zero cost, so pure feasibility.

## Backends

Everything is written against spacecore's backend contract, so NumPy, JAX, and
torch contexts all work. `run_regularized_solver` picks
`spacecore.minimize_optax` on a JAX backend and `spacecore.minimize_scipy`
otherwise. 
