Describing problems¶
Polynomials¶
Polynomial holds coefficients in increasing degree:
from sdpb_python import Polynomial
p = Polynomial([1, 0, "1/12"]) # 1 + x^2/12
p.degree # 2
p(2) # evaluates with Python arithmetic
Anywhere a polynomial is expected you may pass a bare coefficient list, or a single number for a constant. With sympy installed:
import sympy
x = sympy.Symbol("x")
Polynomial.from_sympy(x**4/12 + x**2 + 1) # rationals stay exact (Fraction)
Polynomial.from_sympy(sympy.sqrt(2) * x, digits=300)
Polynomial matrices¶
A PolynomialMatrix is one positivity constraint. Its
polynomials argument is a dim x dim nested list; each entry is the list of
N + 1 polynomials multiplying \(z_0, \dots, z_N\). The matrix must be
symmetric (entries [r][s] and [s][r] equal).
from sdpb_python import PolynomialMatrix, DampedRational
# 2x2 matrix, N = 1 (two polynomials per entry)
m = PolynomialMatrix(
[[[[1, 0, 1], [0, 1]], [[0, 1], [0]]],
[[[0, 1], [0]], [[2], [1, 0, 0, 1]]]],
prefactor=DampedRational.exp_minus_x(poles=[-1, -2]),
)
m.dim, m.num_vectors, m.max_degree # 2, 2, 3
Prefactors and sampling¶
SDPB samples each matrix at max_degree + 1 points chosen to minimise
interpolation error for the prefactor. You can override any part of that, using
the same optional fields as pmp.json:
Field |
Meaning |
|---|---|
|
|
|
Prefactor with fewer poles, used to choose the sample points |
|
Keep at most this many poles when deriving |
|
Explicit points \(x_k \ge 0\) |
|
Explicit prefactor values at the points |
|
Explicit |
Inspect what SDPB derived:
s = m.sampled() # SampledMatrix
s.sample_points, s.sample_scalings, s.bilinear_basis, s.bilinear_bases
Sample points must be non-negative; sdpb-python checks this because SDPB itself would crash on a negative point.
Polynomial matrix programs¶
from sdpb_python import PMP
pmp = PMP(objective=[a0, a1, ..., aN], normalization=[n0, ..., nN], matrices=[m1, m2, ...])
pmp.num_variables # N
Validation at construction: non-empty objective, normalization of the same
length, every matrix carrying N + 1 polynomials per entry, symmetric
matrices. Errors are ValueErrors raised before anything reaches C++.
normalization=None means \(z_0 = 1\) (SDPB’s trivial normalization); then
Solution.z is None and Solution.y is \(z_1, \dots, z_N\).
The SDP behind a PMP¶
pmp.to_sdp(precision=None, max_num_poles=None) returns an
SDPData: the objective constant \(b_0\), the vector \(b\),
the normalization, and per block dim, num_points, the sampled bases, c
and B. This is exactly what SDPB’s pmp2sdp writes to an sdp/ directory,
and is what the test-suite compares against SDPB’s reference data.
max_num_poles plays the role of pmp2sdp --maxNumPoles and combines with
each matrix’s own max_num_poles by taking the minimum.
Linear matrix inequalities¶
LMI takes b (length N), f (default 0) and
blocks, a list whose entries are the sequences \((M_0, M_1, \dots, M_N)\) of
symmetric matrices for each block. Matrices may be nested lists, numpy arrays,
or mpmath.matrix; symmetry and equal sizes are checked.
from sdpb_python import LMI
lmi = LMI(b=[1, 1], f=3, blocks=[(M0, M1, M2)])
To minimise, negate b and f. An LMI is equivalent to a PMP whose
polynomials are constants with a constant prefactor; the test-suite checks the
two agree.