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
together with its dual
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:
|
number of free (unrestricted) variables |
|
number of variables constrained to the nonnegative
orthant ( |
|
sizes of Lorentz (second-order) cone blocks |
|
sizes of rotated Lorentz cone blocks |
|
sizes of positive semidefinite (PSD) cone blocks |
Spelled out, a block of size \(n\) is constrained to one of:
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
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:
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:
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:
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\):
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:
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 |
|---|---|---|
|
|
coefficients of \(y_k\) in the two LP rows |
|
|
norm bound first, then its three components |
|
|
norm bound first, then its three components |
|
|
|
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):
epsDesired accuracy (default
1e-8).bigepsAccuracy still considered a usable (“numerically OK, but not to
eps”) solution.maxiterMaximum interior-point iterations (default
150).fidSet to
0to 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:
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 thehinf*family is only given to 2-3 significant figures, andtests/test_benchmarks.py’s tolerance for this family is widened by hand accordingly (see that file’sSDPLIB_PARAMS).qssp30old(reldiff~1.4e-2): the original Octave/MEX build itself returnsnumerr=2on this instance (a genuine solver limitation, not something this port introduced) – see Contributing’s DEVLOG reference onnb_L2/nql30old/qssp30oldfor 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.