Usage

The public entry point is sedumipy.sedumi(), matching original SeDuMi’s own signature:

x, y, info = sedumipy.sedumi(A, b, c, K, pars=None, **pars_kwargs)

which solves the primal problem

\[\text{(P)} \qquad \min_x\ c^T x \quad \text{such that } Ax = b,\ x \in K\]

together with its dual

\[\text{(D)} \qquad \max_{y,s}\ b^T y \quad \text{such that } A^T y + s = c,\ s \in K^*\]

One call solves both: sedumi() returns the primal optimum x and the dual optimum y (the dual slack s = c - A^T y is not returned, but is one line to recompute). \(K\) is the cone the primal variable lives in and \(K^*\) its dual cone, both defined block by block just below. This primal-dual pair, and the meaning of each K field, follow original SeDuMi’s own convention; see the Addendum to the SeDuMi User Guide (Pólik, 2005), Section 2.

A may be given either as \(m \times n\) or its transpose \(n \times m\) (SeDuMi disambiguates by shape, matching original SeDuMi); dense NumPy arrays and SciPy sparse matrices are both accepted.

The cone structure K

K is a plain dict describing how the columns of x are carved up into cones, in the same field-name convention as original SeDuMi:

K["f"]

number of free (unrestricted) variables

K["l"]

number of variables constrained to the nonnegative orthant (x >= 0)

K["q"]

sizes of Lorentz (second-order) cone blocks

K["r"]

sizes of rotated Lorentz cone blocks

K["s"]

sizes of positive semidefinite (PSD) cone blocks

Spelled out, a block of size \(n\) is constrained to one of:

\[\begin{split}\begin{aligned} \texttt{K.f} \;&:\; \mathbb{R}^{n} && \text{unrestricted} \\[2pt] \texttt{K.l} \;&:\; \mathbb{R}^{n}_{+} = \bigl\{\, x : x_i \ge 0 \,\bigr\} && \text{nonnegative orthant} \\[2pt] \texttt{K.q} \;&:\; \mathcal{Q}^{n} = \bigl\{\, x : x_1 \ge \lVert (x_2, \ldots, x_n) \rVert \,\bigr\} && \text{Lorentz (second-order) cone} \\[2pt] \texttt{K.r} \;&:\; \mathcal{R}^{n} = \bigl\{\, x : 2 x_1 x_2 \ge \lVert (x_3, \ldots, x_n) \rVert^2,\; x_1, x_2 \ge 0 \,\bigr\} && \text{rotated Lorentz cone} \\[2pt] \texttt{K.s} \;&:\; \mathcal{S}^{n}_{+} = \bigl\{\, X \in \mathbb{R}^{n \times n} : X = X^T,\; X \succeq 0 \,\bigr\} && \text{PSD cone} \end{aligned}\end{split}\]

and \(K\) is the product of all its blocks. Two conventions are worth pinning down, because they differ between solvers: a Lorentz block puts its norm bound in the first entry, and a rotated block carries a factor of 2 on the \(x_1 x_2\) product. All of these are self-dual (\(K^* = K\)) except K.f, whose dual is \(\{0\}\) – so a free block’s dual-slack entries are pinned to zero.

Only the fields you need have to be present; omitted fields default to none of that cone type. Blocks appear in x in f, l, q, r, s order, each block occupying that many consecutive entries – except an s block of size \(n\), which occupies \(n^2\) entries, since its matrix is stored flattened column-major (see step 2 of the worked example below).

A pure-LP problem needs only K.l. For

\[\begin{split}\min_x\ 7x_1 + 4x_2 + 10x_3 \quad \text{such that}\quad \begin{aligned} 3x_1 + x_2 + 2x_3 &= 9, \\ x_1 + 2x_2 + 4x_3 &= 8, \end{aligned} \quad x_1, x_2, x_3 \ge 0\end{split}\]

the objective coefficients are c, the two constraint rows are A, their right-hand sides are b, and K says all three variables are nonnegative:

import numpy as np
import sedumipy

A = np.array([[3.0, 1.0, 2.0],
              [1.0, 2.0, 4.0]])
b = np.array([9.0, 8.0])
c = np.array([7.0, 4.0, 10.0])

x, y, info = sedumipy.sedumi(A, b, c, {"l": 3})

Here x = [2, 3, 0] and y = [2, 1], the optima of (P) and (D) respectively: both objectives agree at c @ x == b @ y == 26, and the dual slack c - A.T @ y == [0, 0, 2] is zero exactly where x is positive.

Worked example: turning a written model into A, b, c, K

Models rarely arrive already in the shape of (P). Far more often you have a handful of decision variables and a list of constraints, each asking some affine function of those variables to be nonnegative, or to have bounded norm, or to be positive semidefinite. That is precisely the shape of the dual (D), so that is the side to map onto. Take this problem, which mixes an LP block, two second-order-cone blocks, and a 3x3 PSD block:

\[\begin{split}\begin{aligned} \max_{y \in \mathbb{R}^3} \quad & 6y_1 + 4y_2 + 5y_3 \\[4pt] \text{s.t.} \quad & 16y_1 - 14y_2 + 5y_3 \le -3, \qquad 7y_1 + 2y_2 \le 5, \\[6pt] & \left\| \begin{pmatrix} 8y_1 + 13y_2 - 12y_3 - 2 \\ -8y_1 + 18y_2 + 6y_3 - 14 \\ y_1 - 3y_2 - 17y_3 - 13 \end{pmatrix} \right\| \le -24y_1 - 7y_2 + 15y_3 + 12, \qquad \left\| \begin{pmatrix} y_1 \\ y_2 \\ y_3 \end{pmatrix} \right\| \le 10, \\[6pt] & \begin{pmatrix} 7y_1+3y_2+9y_3 & -5y_1+13y_2+6y_3 & y_1-6y_2-6y_3 \\ -5y_1+13y_2+6y_3 & y_1+12y_2-7y_3 & -7y_1-10y_2-7y_3 \\ y_1-6y_2-6y_3 & -7y_1-10y_2-7y_3 & -4y_1-28y_2-11y_3 \end{pmatrix} \preceq \begin{pmatrix} 68 & -30 & -19 \\ -30 & 99 & 23 \\ -19 & 23 & 10 \end{pmatrix} \end{aligned}\end{split}\]

The cone and the objective vector can be read straight off: there are \(m = 3\) variables, so b is the objective’s coefficient vector, and the five constraints group into four cone blocks.

b = np.array([6.0, 4.0, 5.0])
K = {"l": 2, "q": [4, 4], "s": [3]}

That leaves A and c.

Step 1: rewrite every constraint as “constant minus linear”

(D)’s constraint is \(s = c - A^T y \in K^*\), so each block’s job is to express its own slack \(s\) as a constant minus something linear in \(y\). Those constants become that block’s slice of c, and the coefficients of \(y_k\) become its slice of column \(k\) of \(A^T\). Working block by block:

The LP block (K.l = 2). Move each <= to a nonnegativity:

\[16y_1 - 14y_2 + 5y_3 \le -3 \;\Longleftrightarrow\; \underbrace{(-3)}_{c_1} - \underbrace{(16y_1 - 14y_2 + 5y_3)}_{\text{row of } A^T} \ge 0\]

and likewise \(5 - (7y_1 + 2y_2) \ge 0\), giving \(c = (-3, 5)\) and rows \((16, -14, 5)\), \((7, 2, 0)\).

The Lorentz blocks (K.q = [4, 4]). A K.q block of size 4 is one 4-vector \(s = (s_0; \bar{s})\) constrained by \(s_0 \ge \|\bar{s}\|\) – the bound comes first, then the three components whose norm it bounds. So stack the right-hand side on top of the norm’s argument, then split each of those four entries the same way as above:

\[\begin{split}s = \begin{pmatrix} \;12 - (24y_1 + 7y_2 - 15y_3) \\ -2 - (-8y_1 - 13y_2 + 12y_3) \\ -14 - (8y_1 - 18y_2 - 6y_3) \\ -13 - (-y_1 + 3y_2 + 17y_3) \end{pmatrix} \quad\Longrightarrow\quad c = \begin{pmatrix} 12 \\ -2 \\ -14 \\ -13 \end{pmatrix},\qquad \text{rows of } A^T = \begin{pmatrix} 24 & 7 & -15 \\ -8 & -13 & 12 \\ 8 & -18 & -6 \\ -1 & 3 & 17 \end{pmatrix}\end{split}\]

The second block, \(\|(y_1, y_2, y_3)\| \le 10\), is the same idea with nothing to rearrange: \(c = (10, 0, 0, 0)\) and the rows \((0,0,0)\), \((-1,0,0)\), \((0,-1,0)\), \((0,0,-1)\).

The PSD block (K.s = [3]). Rearranged the same way, \(M(y) \preceq C\) is \(C - M(y) \succeq 0\), where the matrix on the left splits into one coefficient matrix per variable, \(M(y) = y_1 A_1 + y_2 A_2 + y_3 A_3\):

\[\begin{split}A_1 = \begin{pmatrix} 7 & -5 & 1 \\ -5 & 1 & -7 \\ 1 & -7 & -4 \end{pmatrix},\; A_2 = \begin{pmatrix} 3 & 13 & -6 \\ 13 & 12 & -10 \\ -6 & -10 & -28 \end{pmatrix},\; A_3 = \begin{pmatrix} 9 & 6 & -6 \\ 6 & -7 & -7 \\ -6 & -7 & -11 \end{pmatrix}\end{split}\]

So the “constant” is the matrix \(C\) and the “coefficient of \(y_k\)” is the matrix \(A_k\). Both are matrices, and neither c nor \(A^T\) has anywhere to put a matrix – which brings us to the one genuinely fiddly part.

Step 2: flatten the PSD block’s matrices

sedumipy has no matrix-valued slots. An n-by-n PSD block is stored as n**2 consecutive scalar entries of x, c, and s, in column-major (vec) order:

\[\begin{split}\operatorname{vec} \begin{pmatrix} S_{11} & S_{12} & S_{13} \\ S_{21} & S_{22} & S_{23} \\ S_{31} & S_{32} & S_{33} \end{pmatrix} = (S_{11}, S_{21}, S_{31},\; S_{12}, S_{22}, S_{32},\; S_{13}, S_{23}, S_{33})^T\end{split}\]

which is exactly NumPy’s M.flatten(order="F"), inverted by v.reshape(3, 3, order="F"). Note that this is the full n**2 vectorization: all nine entries, with the off-diagonal ones genuinely written twice, and no \(\sqrt{2}\) scaling anywhere. (Some solvers instead take a half-vectorized, \(\sqrt{2}\)-scaled svec of length n(n+1)/2; SeDuMi does not.)

Apply vec to the matrices from step 1 and the PSD block stops being special: vec(C) is that block’s slice of c, and vec(A_k) is that block’s slice of column k of \(A^T\) – nine rows, just as the LP block contributed two and each Lorentz block four.

Step 3: stack the blocks

Blocks occupy consecutive entries in f, l, q, r, s order, so c is the blocks’ constants concatenated, and \(A^T\) is the blocks’ coefficients stacked vertically – one row per cone coordinate, one column per variable \(y_k\):

block

rows of \(A^T\)

what those rows hold

K.l = 2

0:2

coefficients of \(y_k\) in the two LP rows

K.q[0] = 4

2:6

norm bound first, then its three components

K.q[1] = 4

6:10

norm bound first, then its three components

K.s = [3]

10:19

vec(A_k), column-major, 9 = 3**2 entries

That totals \(n = 2 + 4 + 4 + 9 = 19\) rows against \(m = 3\) columns. Since sedumi() accepts A in either orientation and \(19 \neq 3\), the stacked \(A^T\) can be passed as-is:

import numpy as np
import sedumipy

# K.l block: 2 rows
At_l = np.array([[16.0, -14.0, 5.0],
                 [7.0, 2.0, 0.0]])
c_l = np.array([-3.0, 5.0])

# K.q blocks: 4 rows each, norm bound first
At_q1 = np.array([[24.0, 7.0, -15.0],
                  [-8.0, -13.0, 12.0],
                  [8.0, -18.0, -6.0],
                  [-1.0, 3.0, 17.0]])
c_q1 = np.array([12.0, -2.0, -14.0, -13.0])

At_q2 = np.array([[0.0, 0.0, 0.0],
                  [-1.0, 0.0, 0.0],
                  [0.0, -1.0, 0.0],
                  [0.0, 0.0, -1.0]])
c_q2 = np.array([10.0, 0.0, 0.0, 0.0])

# K.s block: 9 rows, one vec() per variable
A1 = np.array([[7.0, -5.0, 1.0], [-5.0, 1.0, -7.0], [1.0, -7.0, -4.0]])
A2 = np.array([[3.0, 13.0, -6.0], [13.0, 12.0, -10.0], [-6.0, -10.0, -28.0]])
A3 = np.array([[9.0, 6.0, -6.0], [6.0, -7.0, -7.0], [-6.0, -7.0, -11.0]])
C = np.array([[68.0, -30.0, -19.0], [-30.0, 99.0, 23.0], [-19.0, 23.0, 10.0]])

At_s = np.column_stack([M.flatten(order="F") for M in (A1, A2, A3)])
c_s = C.flatten(order="F")

At = np.vstack([At_l, At_q1, At_q2, At_s])        # (19, 3)
c = np.concatenate([c_l, c_q1, c_q2, c_s])        # (19,)
b = np.array([6.0, 4.0, 5.0])
K = {"l": 2, "q": [4, 4], "s": [3]}

x, y, info = sedumipy.sedumi(At, b, c, K, fid=0)

This converges (info["numerr"] == 0) to y = [-1.2209, 0.0966, 3.5775] with optimal value b @ y = 10.9485.

Reading the answer back out

y is the model’s \((y_1, y_2, y_3)\) directly. The slacks come from the same s = c - A^T y, sliced by the table above – and un-flattened, for the PSD block, by the inverse of step 2:

s = c - At @ y

s_l = s[0:2]                            # K.l -- nonnegative
q1, q2 = s[2:6], s[6:10]                # K.q -- each is (bound, *components)
S = s[10:19].reshape(3, 3, order="F")   # K.s -- back to a 3x3 matrix

q1[0] >= np.linalg.norm(q1[1:])         # the Lorentz condition, per block
np.linalg.eigvalsh(S)                   # all >= 0

Here that gives s_l = [0, 13.35], both Lorentz blocks within their norm bounds (the first one tight, 94.288 on both sides), and eigvals(S) = [0, 50.16, 165.18] – so the LP block’s first row, the first cone constraint, and the PSD constraint are all active at the optimum.

Solver options (pars)

Options are passed either as a pars dict or as individual keyword arguments, which override the same key in pars when both are given:

x, y, info = sedumipy.sedumi(A, b, c, K, eps=1e-9)

Commonly used options (see checkpars.py for the full list and defaults, ported line-for-line from checkpars.m):

eps

Desired accuracy (default 1e-8).

bigeps

Accuracy still considered a usable (“numerically OK, but not to eps”) solution.

maxiter

Maximum interior-point iterations (default 150).

fid

Set to 0 to suppress solver progress output.

The info dict

info reports the outcome: info["iter"] (iteration count), info["pinf"]/info["dinf"] (primal/dual infeasibility flags), and info["numerr"] (0 = solved to eps, 1 = solved only to bigeps, 2 = failed).

Reading and writing problem files

Two file formats are supported for problem/solution I/O, both under sedumipy directly:

# SeDuMi-style .mat problem/solution files
At, b, c, K, pars = sedumipy.read_mat("problem.mat")
sedumipy.write_solution_mat("solution.mat", x, y, info)

# sparse SDPA format (.dat-s), as used by SDPLIB/DIMACS
At, b, c, K = sedumipy.read_sdpa("problem.dat-s")
sedumipy.write_sdpa("problem.dat-s", At, b, c, K)  # K.q/K.r not supported

See API reference for the full signatures.

Using sedumipy from cvxpy

cvxpy can drive sedumipy directly, so a problem can be written in cvxpy’s modeling language instead of as (A, b, c, K) by hand. Install cvxpy alongside sedumipy (pip install sedumipy[cvxpy]) and pass an instance of the solver class to solve():

import cvxpy as cp
import numpy as np
from sedumipy.cvxpy_interface import SEDUMIPY

A = np.array([[3.0, 1.0, 2.0], [1.0, 2.0, 4.0]])
b = np.array([9.0, 8.0])
c = np.array([7.0, 4.0, 10.0])

x = cp.Variable(3)
problem = cp.Problem(cp.Minimize(c @ x), [A @ x == b, x >= 0])
problem.solve(solver=SEDUMIPY())        # 26.0

x.value                                 # array([2., 3., 0.])
problem.status                          # 'optimal'
problem.solver_stats.num_iters          # iterations sedumi() took
problem.solver_stats.extra_stats        # sedumi()'s own `info` dict

This needs no patched or forked cvxpy: a stock install accepts a solver instance it has never heard of (cvxpy’s “custom solver” path), which is what SEDUMIPY() is. Note that this is why the solver is passed as an object rather than by name – solver="SEDUMIPY" and cp.SEDUMIPY do not exist, since cvxpy resolves names only against its own built-in solvers.

Dual values come back on the constraints as usual (constraint.dual_value), and keyword arguments to solve() are passed through as sedumi()’s pars:

problem.solve(solver=SEDUMIPY(), eps=1e-10, maxiter=250)

verbose=True has no effect, and neither does warm starting. The fid option described above isn’t wired to verbose either – it defaults to 1, so solve() prints SeDuMi’s progress table to stdout regardless of verbose; pass fid=0 to silence it:

problem.solve(solver=SEDUMIPY(), fid=0)

Scope. The interface covers the cones sedumipy itself supports: equalities, inequalities, second-order cone and PSD constraints – so LP, SOCP, SDP and any mix of them. Exponential- and power-cone problems (cp.exp, cp.log, cp.entr, fractional cp.power, …) and mixed-integer problems are out of scope; cvxpy raises its own SolverError for those rather than returning a wrong answer.

Benchmarks

tests/test_benchmarks.py solves published SDPLIB and DIMACS problems and checks the result against each collection’s own official optimal-value table:

git submodule update --init --recursive   # if not already done
.venv/bin/python -m pytest tests/test_benchmarks.py -v          # everything, ~101 problems (~10 min)
.venv/bin/python -m pytest tests/test_benchmarks.py -v -m mini  # fastest subset only (~35s)

Measured comparison against original Octave/MEX SeDuMi

The table below is a timing and optimal-value comparison of this port against a from-source build of the original Octave/MEX SeDuMi (vendor/sedumi-upstream, built via install_sedumi -rebuild), solving all 105 problems tests/test_benchmarks.py covers (both solvable and infeasible SDPLIB/DIMACS/TORUS instances), measured 2026-09-04 on Octave 8.4.0 / a 4-core Linux container. Absolute numbers are environment-dependent (CPU/core count/thermal state) – an earlier pass in this same environment, with the two suites run under different background load (Octave/MEX measured right after a heavy mkoctfile rebuild, Python measured on an otherwise-idle machine), showed Octave/MEX ~22% faster in aggregate purely from that noise. The numbers below are from both suites run back-to-back on an otherwise- idle machine (Python immediately followed by Octave/MEX, no rebuild or install in between) to remove that skew; treat the ratio column as indicative, not a tight bound. See Contributing (DEVLOG’s Phase 5 entry) for an earlier, 5-problem version of this same comparison.

Timing, by problem size (bucketed by the original Octave/MEX solve time):

Bucket

Problems

Python total

Octave/MEX total

Ratio (Python / Octave)

< 2 s

61

51.1 s

44.5 s

1.15

2-20 s

34

261.1 s

278.1 s

0.94

20 s+

10

501.4 s

478.3 s

1.05

All 105

105

813.6 s

800.9 s

1.02

Under matched conditions, this port and original Octave/MEX SeDuMi run within a few percent of each other in aggregate – close enough that the remaining gap is within this environment’s own run-to-run noise, not a clear, reproducible slowdown in either direction. Individual problems still vary more (see the full table below): DIMACS’s small ANTENNA-family SOCP problems (nb, nb_L2, nql30old, …) consistently show this port running 2-3x slower, plausibly Python interpreter/NumPy- allocation/ctypes-crossing overhead dominating on problems this small, while several others (qssp30, hinf13, truss6, …) run faster than the original Octave/MEX – see the ratio_py_over_oct column.

Optimal value (pobj) agreement. Both solvers’ final primal objective values, formatted in scientific notation to the same number of significant digits so they line up for a direct compare:

pobj (7 significant digits) and timing, all 105 problems

source

name

python_pobj

octave_pobj

reldiff

python_iter

octave_iter

python_numerr

octave_numerr

python_s

octave_s

ratio_py_over_oct

DIMACS

copo14

-6.645059e-09

-6.646434e-09

2.07e-04

17

17

0

0

1.20

1.07

1.12

DIMACS

copo23

-1.893947e-08

-1.893850e-08

5.12e-05

20

20

0

0

68.09

54.09

1.26

DIMACS

filter48_socp

1.416129e+00

1.416129e+00

1.60e-07

35

37

0

0

1.76

1.12

1.58

DIMACS

filtinf1

0.000000e+00

0.000000e+00

0.00e+00

31

30

0

0

1.61

0.89

1.81

DIMACS

hamming_7_5_6

-4.266667e+01

-4.266667e+01

1.37e-10

17

17

0

0

10.99

9.49

1.16

DIMACS

hamming_9_8

-2.240000e+02

-2.240000e+02

3.16e-08

25

25

0

0

55.10

46.09

1.20

DIMACS

minphase

5.981158e+00

5.980187e+00

1.62e-04

33

30

1

1

0.67

0.60

1.11

DIMACS

nb

-5.070309e-02

-5.070309e-02

5.81e-11

20

20

0

0

1.37

0.62

2.21

DIMACS

nb_L1

-1.301227e+01

-1.301227e+01

4.71e-11

18

18

0

0

1.99

2.94

0.68

DIMACS

nb_L2

-1.628972e+00

-1.628972e+00

3.62e-10

16

16

0

0

2.25

0.78

2.89

DIMACS

nb_L2_bessel

-1.025695e-01

-1.025695e-01

3.95e-08

16

16

0

0

1.24

0.62

2.00

DIMACS

nql30

-9.460279e-01

-9.460279e-01

1.91e-11

15

15

0

0

0.29

0.42

0.68

DIMACS

nql30old

9.460509e-01

9.460480e-01

3.01e-06

18

18

1

1

4.73

1.50

3.15

DIMACS

qssp30

-6.496669e+00

-6.496669e+00

1.81e-11

20

20

0

0

0.45

0.86

0.52

DIMACS

qssp30old

6.496687e+00

6.589279e+00

1.41e-02

19

15

1

2

6.08

2.90

2.10

DIMACS

sched_100_100_scaled

2.733079e+01

2.733079e+01

1.30e-10

60

60

0

0

4.97

2.99

1.66

DIMACS

sched_100_50_orig

1.818899e+05

1.818899e+05

1.93e-10

41

40

0

1

4.49

1.79

2.50

DIMACS

sched_50_50_orig

2.667300e+04

2.667300e+04

1.17e-10

44

44

0

0

1.77

0.93

1.91

DIMACS

sched_50_50_scaled

7.852038e+00

7.852038e+00

1.47e-11

31

31

0

0

0.88

0.58

1.52

DIMACS

torusg3-8

-4.573582e+02

-4.573582e+02

3.82e-11

19

19

0

0

12.26

12.09

1.01

DIMACS

toruspm3-8-50

-5.278087e+02

-5.278087e+02

9.28e-11

18

18

0

0

11.53

11.64

0.99

DIMACS

truss5

1.326357e+02

1.326357e+02

3.00e-10

23

23

0

0

0.86

1.13

0.76

DIMACS

truss8

1.331146e+02

1.331146e+02

1.20e-10

24

24

0

0

1.89

2.17

0.87

SDPLIB

arch0

-5.665173e-01

-5.665173e-01

8.20e-10

31

32

0

0

1.95

1.90

1.03

SDPLIB

arch2

-6.715154e-01

-6.715154e-01

3.52e-11

28

28

0

0

1.55

1.45

1.07

SDPLIB

arch4

-9.726274e-01

-9.726274e-01

5.18e-11

29

29

0

0

1.63

1.53

1.06

SDPLIB

arch8

-7.056980e+00

-7.056980e+00

8.78e-11

34

34

0

0

1.85

1.85

1.00

SDPLIB

control1

-1.778463e+01

-1.778463e+01

6.73e-11

30

30

0

0

0.17

0.22

0.77

SDPLIB

control10

-3.853310e+01

-3.853310e+01

1.67e-07

43

43

1

1

34.69

33.80

1.03

SDPLIB

control11

-3.195870e+01

-3.195870e+01

9.66e-08

47

47

1

1

60.19

58.01

1.04

SDPLIB

control2

-8.300000e+00

-8.300000e+00

2.78e-09

31

31

0

0

0.26

0.33

0.79

SDPLIB

control3

-1.363327e+01

-1.363327e+01

4.32e-10

34

34

1

0

1.13

1.25

0.91

SDPLIB

control4

-1.979423e+01

-1.979423e+01

2.53e-10

35

35

1

1

1.13

1.24

0.91

SDPLIB

control5

-1.688361e+01

-1.688361e+01

6.89e-08

35

35

1

1

2.51

2.48

1.01

SDPLIB

control6

-3.730448e+01

-3.730448e+01

3.80e-09

38

38

1

1

3.78

3.81

0.99

SDPLIB

control7

-2.062509e+01

-2.062509e+01

2.42e-10

40

40

1

1

6.69

6.89

0.97

SDPLIB

control8

-2.028637e+01

-2.028638e+01

2.69e-08

40

40

1

1

11.50

11.72

0.98

SDPLIB

control9

-1.467543e+01

-1.467543e+01

7.53e-08

40

40

1

1

20.18

19.97

1.01

SDPLIB

equalG11

-6.291540e+02

-6.291540e+02

2.28e-08

21

21

1

1

50.96

56.64

0.90

SDPLIB

gpp100

4.494351e+01

4.494352e+01

1.26e-07

22

22

1

1

0.54

0.68

0.80

SDPLIB

gpp124-1

7.343042e+00

7.343045e+00

4.18e-07

23

24

1

1

0.72

0.94

0.77

SDPLIB

gpp124-2

4.686232e+01

4.686244e+01

2.72e-06

23

21

1

1

0.90

0.83

1.08

SDPLIB

gpp124-3

1.530142e+02

1.530141e+02

5.24e-07

21

22

1

1

0.70

0.83

0.84

SDPLIB

gpp124-4

4.189873e+02

4.189873e+02

9.79e-08

25

25

1

1

0.89

1.04

0.85

SDPLIB

gpp250-1

1.544489e+01

1.544488e+01

6.56e-07

23

25

1

1

3.13

3.65

0.86

SDPLIB

gpp250-2

8.186916e+01

8.186904e+01

1.40e-06

24

27

1

1

3.50

3.95

0.89

SDPLIB

gpp250-3

3.035393e+02

3.035393e+02

1.71e-07

26

26

1

1

3.80

3.79

1.00

SDPLIB

gpp250-4

7.473282e+02

7.473285e+02

4.39e-07

24

24

1

1

3.50

3.49

1.00

SDPLIB

gpp500-1

2.532065e+01

2.532059e+01

2.03e-06

29

29

1

1

20.41

21.00

0.97

SDPLIB

gpp500-2

1.560608e+02

1.560608e+02

3.00e-07

27

27

1

1

17.24

19.64

0.88

SDPLIB

gpp500-3

5.130179e+02

5.130181e+02

2.96e-07

28

27

1

1

18.66

19.87

0.94

SDPLIB

gpp500-4

1.567019e+03

1.567019e+03

1.03e-08

26

26

1

1

17.46

19.44

0.90

SDPLIB

hinf1

-2.032744e+00

-2.032711e+00

1.63e-05

20

21

1

1

0.23

0.41

0.55

SDPLIB

hinf10

-1.087361e+02

-1.087389e+02

2.52e-05

26

27

1

1

0.46

0.77

0.60

SDPLIB

hinf11

-6.588885e+01

-6.589054e+01

2.57e-05

28

28

1

1

0.61

0.83

0.74

SDPLIB

hinf13

-4.503329e+01

-4.499563e+01

8.36e-04

15

16

1

1

0.59

1.11

0.53

SDPLIB

hinf14

-1.299426e+01

-1.299419e+01

5.13e-06

23

25

1

1

1.27

1.89

0.67

SDPLIB

hinf15

-2.481544e+01

-2.483998e+01

9.88e-04

17

16

1

1

0.78

0.61

1.29

SDPLIB

hinf2

-1.096721e+01

-1.096726e+01

3.69e-06

19

18

1

1

0.19

0.25

0.75

SDPLIB

hinf3

-5.694204e+01

-5.694188e+01

2.78e-06

20

19

1

1

0.19

0.26

0.70

SDPLIB

hinf4

-2.747640e+02

-2.747640e+02

2.83e-09

25

27

0

0

0.29

0.45

0.63

SDPLIB

hinf5

-3.622346e+02

-3.622170e+02

4.84e-05

19

18

1

1

0.17

0.25

0.68

SDPLIB

hinf6

-4.489387e+02

-4.489410e+02

5.22e-06

23

22

1

1

0.31

0.42

0.74

SDPLIB

hinf7

-3.894261e+02

-3.835302e+02

1.51e-02

19

18

1

1

0.16

0.21

0.74

SDPLIB

hinf8

-1.161456e+02

-1.161365e+02

7.84e-05

17

16

1

1

0.22

0.30

0.73

SDPLIB

hinf9

-2.362474e+02

-2.362474e+02

1.29e-08

26

26

0

0

0.21

0.31

0.67

SDPLIB

infd1

0.000000e+00

0.000000e+00

0.00e+00

6

6

0

0

0.04

0.06

0.73

SDPLIB

infd2

0.000000e+00

0.000000e+00

0.00e+00

6

6

0

0

0.04

0.06

0.73

SDPLIB

infp1

-1.000000e+00

-1.000000e+00

3.33e-16

7

7

0

0

0.05

0.07

0.74

SDPLIB

infp2

-1.000000e+00

-1.000000e+00

3.33e-16

11

11

0

0

0.07

0.10

0.70

SDPLIB

maxG11

-6.291648e+02

-6.291648e+02

2.36e-11

16

16

0

0

31.70

33.56

0.94

SDPLIB

maxG51

-4.006255e+03

-4.006255e+03

6.00e-12

22

22

0

0

75.14

87.07

0.86

SDPLIB

mcp100

-2.261574e+02

-2.261574e+02

1.64e-10

17

17

0

0

0.30

0.35

0.87

SDPLIB

mcp124-1

-1.419905e+02

-1.419905e+02

4.60e-11

17

17

0

0

0.42

0.49

0.86

SDPLIB

mcp124-2

-2.698802e+02

-2.698802e+02

7.18e-12

18

18

0

0

0.45

0.52

0.88

SDPLIB

mcp124-3

-4.677501e+02

-4.677501e+02

7.42e-11

18

18

0

0

0.46

0.52

0.89

SDPLIB

mcp124-4

-8.644119e+02

-8.644119e+02

5.50e-12

20

20

0

0

0.55

0.60

0.93

SDPLIB

mcp250-1

-3.172643e+02

-3.172643e+02

1.41e-10

19

19

0

0

1.85

2.12

0.87

SDPLIB

mcp250-2

-5.319301e+02

-5.319301e+02

2.81e-11

19

19

0

0

1.83

2.15

0.85

SDPLIB

mcp250-3

-9.811726e+02

-9.811726e+02

4.16e-11

19

19

0

0

1.96

2.10

0.93

SDPLIB

mcp250-4

-1.681960e+03

-1.681960e+03

1.86e-11

21

21

0

0

2.25

2.36

0.95

SDPLIB

mcp500-1

-5.981485e+02

-5.981485e+02

9.03e-12

21

21

0

0

11.38

12.14

0.94

SDPLIB

mcp500-2

-1.070057e+03

-1.070057e+03

2.39e-10

20

20

0

0

10.69

12.24

0.87

SDPLIB

mcp500-3

-1.847970e+03

-1.847970e+03

2.48e-10

20

20

0

0

10.39

12.33

0.84

SDPLIB

mcp500-4

-3.566738e+03

-3.566738e+03

6.87e-12

20

20

0

0

11.37

12.57

0.90

SDPLIB

qap10

1.092577e+03

1.092583e+03

5.22e-06

25

27

1

1

4.71

5.25

0.90

SDPLIB

qap5

4.360000e+02

4.360000e+02

2.26e-11

14

14

0

0

0.13

0.19

0.70

SDPLIB

qap6

3.814246e+02

3.814275e+02

7.42e-06

23

24

1

1

0.56

0.72

0.78

SDPLIB

qap7

4.248084e+02

4.248085e+02

2.46e-07

24

24

1

1

0.77

0.78

0.99

SDPLIB

qap8

7.569352e+02

7.569134e+02

2.89e-05

27

24

1

1

1.72

1.13

1.52

SDPLIB

qap9

1.409920e+03

1.409905e+03

1.04e-05

25

24

1

1

2.64

2.19

1.21

SDPLIB

ss30

-2.023951e+01

-2.023951e+01

1.48e-10

37

37

1

1

8.14

9.15

0.89

SDPLIB

theta1

-2.300000e+01

-2.300000e+01

4.96e-11

19

19

0

0

0.18

0.22

0.85

SDPLIB

theta2

-3.287917e+01

-3.287917e+01

1.63e-11

20

20

0

0

0.69

0.78

0.88

SDPLIB

theta3

-4.216698e+01

-4.216698e+01

1.05e-10

20

20

0

0

3.84

3.64

1.05

SDPLIB

theta4

-5.032122e+01

-5.032122e+01

3.10e-10

20

20

0

0

17.02

16.63

1.02

SDPLIB

theta5

-5.723231e+01

-5.723231e+01

2.18e-11

23

23

0

0

89.65

67.39

1.33

SDPLIB

truss1

8.999996e+00

8.999996e+00

4.61e-11

13

13

0

0

0.10

0.17

0.57

SDPLIB

truss2

1.233804e+02

1.233804e+02

9.01e-11

20

20

0

0

0.69

1.11

0.62

SDPLIB

truss3

9.109996e+00

9.109996e+00

2.84e-11

15

15

0

0

0.12

0.19

0.64

SDPLIB

truss4

9.009996e+00

9.009996e+00

4.67e-12

14

14

0

0

0.13

0.20

0.64

SDPLIB

truss5

1.326357e+02

1.326357e+02

3.00e-10

23

23

0

0

0.96

1.20

0.80

SDPLIB

truss6

9.010014e+02

9.010014e+02

4.50e-10

28

28

0

0

9.45

18.09

0.52

SDPLIB

truss7

9.000014e+02

9.000014e+02

2.27e-08

27

25

1

1

15.45

20.62

0.75

SDPLIB

truss8

1.331146e+02

1.331146e+02

1.20e-10

24

24

0

0

1.89

2.22

0.85

reldiff is |python_pobj - octave_pobj| / max(|python_pobj|, |octave_pobj|). Across all 105 problems: 59 agree to a relative difference under 1e-8, 81 under 1e-6, and 99 under 1e-4. The handful of larger outliers are already-documented cases, not porting bugs:

  • hinf7 (reldiff ~1.5e-2): SDPLIB’s own published reference value for the hinf* family is only given to 2-3 significant figures, and tests/test_benchmarks.py’s tolerance for this family is widened by hand accordingly (see that file’s SDPLIB_PARAMS).

  • qssp30old (reldiff ~1.4e-2): the original Octave/MEX build itself returns numerr=2 on this instance (a genuine solver limitation, not something this port introduced) – see Contributing’s DEVLOG reference on nb_L2/nql30old/qssp30old for the full story.

Raw per-run CSVs (Python, Octave/MEX) and the merged comparison table are downloadable for anyone who wants to slice the data differently.