A Gentle Start
SDPA for Python solves Conic-form Linear Optimization Problems (CLPs). A CLP is of the form:
\[\begin{aligned} (\mathcal{P}) \min_{x}. \quad & c^T x\\ \textrm{s.t.} \quad & x \succeq_K 0\\ & A x - b \succeq_J 0, \end{aligned}\]where \(\mathcal{ J }\) and \(\mathcal{ K }\) represents cones, that will be defined in the next subsection.
As an example, we will solve the following specific CLP in this section as a gentle begin, the problems is defined as \((\mathcal{P})\) with
\[\begin{aligned} &\boldsymbol{A}=\left(\begin{array}{cccc} -10 & -4 & -4 & 0 \\ 0 & 0 & 0 & 8 \\ 0 & 8 & 8 & 2 \end{array}\right), \quad \boldsymbol{b}=\left(\begin{array}{c} -48 \\ 8 \\ -20 \end{array}\right), \quad \boldsymbol{c}=\left(\begin{array}{c} 11 \\ 0 \\ 0 \\ -23 \end{array}\right) \\ &\text { J.f }=3, \quad \mathrm{~K} . \mathrm{s}=2, \end{aligned}\]where J.f
and K.s
represents the data format of the corresponding cones \(\mathcal{ J }\) and \(\mathcal{ K }\).
Data Format in SDPAP
As seen above, the problem data is read into Python variables A
, b
, c
, K
and J
.
A
, b
and c
may be defined as numpy.ndarray
or one of the sparse array formats (e.g. csr_array
or csc_array
) in scipy.sparse
.
K
and J
are SymCone
objects, a data structure provided in sdpa-python
. They can be initialized using K = sdpap.SymCone()
and J = sdpap.SymCone()
respectively. We need to set their parameters according to the problem.
In the CLP problem form shown above, the generalized inequality \(x \succeq_{\mathcal{ K }} 0\) means \(x\in \mathcal{ K }\) for cone \(\mathcal{ K}\). The cones \(\mathcal{ J }\) and \(\mathcal{ K }\) are each defined as Cartesian product of three different type of cones:
\(\mathcal{ K } = \mathcal{ K }_{f}\times\mathcal{ K }_{l}\times\mathcal{ K }_{s}\) and \(\mathcal{ J } = \mathcal{ J }_{f}\times\mathcal{ J }_{l}\times\mathcal{ J }_{s}\)
Corresponding to each type, we will specify for cone \(\mathcal{ K }\), the parameters K.f
, K.l
, K.s
and similarly for cone \(\mathcal{ J }\), the parameters J.f
, J.l
, J.s
These parameters specify the type of constraints, which will be discussed in the following.
\(f \colon\) Equality Constraints
The cone with subscript \(f\) represents \(x\) that satisfies the equality constraint. In code, we use J.f
or K.f
to represent the number of equality constraint. For example, for
We have three equality constraints. Hence, J.f = 3
.
\(l \colon\) LP Cones
The cone with subscript \(l\) represents the LP cone, i.e., \(x\) such that \(\left\{x\colon x \succeq_{\mathbb{ R }_{+}^{n}} 0\right\}\) In code, we use J.l
or K.l
to represent the size of variables in the LP cone constraints.
For example, for
\[\left(\begin{array}{cccc} -10 & -4 & -4 & 0 \\ 0 & 0 & 0 & 8 \\ 0 & 8 & 8 & 2 \end{array}\right) x-\left(\begin{array}{c} -48 \\ 8 \\ -20 \end{array}\right)\succeq_{\mathbb{ R }_{+}^{n}} 0,\]We have three inequality constraints. Thus, J.l = 3
.
\(s\colon\) SDP Cones
The cone with subscript \(s\) represents the SDP cones. One SDP cone is defined as the set
\[\left\{\boldsymbol{x}_{s}: \text{vec}^{-1}(\boldsymbol{x}_{s})\succeq_{S_{+}} \mathbf{0}\right\}\]where \(S_{+}\) represents positive-semidefinite cone. The size of \(\boldsymbol{x}_{s}\) must be a square of an integer, i.e., \(\boldsymbol{x}_{s} \in \mathbb{ R }^{n^2}\) and \(\text{vec}^{-1}(\boldsymbol{x}_{s}) \in \mathbb{ R }^{n\times n}\) The symbol \(\text{vec}^{-1}(x)\) means the matrix formed by the vector \(x\). That is, if
\[\boldsymbol{x}_{s}=\left[\begin{array}{l} x_{1} \\ x_{2} \\ x_{3} \\ x_{4} \end{array}\right]\]then
\[\text{vec}^{-1}\left(\boldsymbol{x}_{s}\right)=\left(\begin{array}{ll} x_{1} & x_{3} \\ x_{2} & x_{4} \end{array}\right)\]In code, we use J.s
or K.s
as a tuple to represents the number of SDP cone constraints and the number of the rows or columns of the matrix in each SDP cone. For example, if we have three SDP cone constraints \(\boldsymbol{x}_{1}\succeq_{S_{+}} 0,\boldsymbol{x}_{2}\succeq_{S_{+}} 0\) and \(\boldsymbol{x}_{3} \succeq_{S_{+}} 0\) and \(\boldsymbol{x}_{i}\in \mathbb{ R }^{i^2}\) for any \(i = 1,2,3\). Then K.s = (1,2,3)
.
Solving the Sample CLP
Now, we can define and solve the sample CLP given in the beginning of this section by the following code:
import sdpap
import numpy as np
A = np.array([
[-10, -4, -4, 0],
[0, 0, 0, 8],
[0, 8, 8, 2]
])
b = np.array([
[-48],
[8],
[-20]
])
c = np.array([
[11],
[0],
[0],
[-23]
])
K = sdpap.SymCone(s=(2,))
J = sdpap.SymCone(f=3)
x, y, sdpapinfo , timeinfo , sdpainfo = sdpap.solve(A,b,c,K,J)
The output will be as below:
---------- SDPAP Start ----------
SDPA start at [Sun Jan 26 20:41:26 2025]
NumThreads is set as 12
Schur computation : DENSE
Converted to SDPA internal data / Starting SDPA main loop
mu thetaP thetaD objP objD alphaP alphaD beta
0 1.0e+04 1.0e+00 1.0e+00 -0.00e+00 +1.20e+03 1.0e+00 9.1e-01 2.00e-01
1 1.6e+03 0.0e+00 9.4e-02 +8.39e+02 +7.51e+01 2.3e+00 9.6e-01 2.00e-01
2 1.7e+02 2.3e-16 3.6e-03 +1.96e+02 -3.74e+01 1.3e+00 1.0e+00 2.00e-01
3 1.8e+01 2.9e-16 2.2e-17 -6.84e+00 -4.19e+01 9.9e-01 9.0e+01 1.00e-01
4 1.9e+00 2.6e-16 1.8e-15 -3.81e+01 -4.19e+01 1.0e+00 1.0e+00 1.00e-01
5 1.9e-01 2.7e-16 7.5e-18 -4.15e+01 -4.19e+01 1.0e+00 1.0e+00 1.00e-01
6 1.9e-02 2.8e-16 7.5e-18 -4.19e+01 -4.19e+01 1.0e+00 9.0e+01 1.00e-01
7 1.9e-03 2.9e-16 1.2e-15 -4.19e+01 -4.19e+01 1.0e+00 1.0e+00 1.00e-01
8 1.9e-04 2.8e-16 2.2e-17 -4.19e+01 -4.19e+01 1.0e+00 1.0e+00 1.00e-01
9 1.9e-05 2.7e-16 7.5e-18 -4.19e+01 -4.19e+01 1.0e+00 1.0e+00 1.00e-01
10 1.9e-06 2.9e-16 3.7e-18 -4.19e+01 -4.19e+01 1.0e+00 1.0e+00 1.00e-01
phase.value = pdOPT
Iteration = 10
mu = +1.9180668442023158e-06
relative gap = +9.1554505917001858e-08
gap = +3.8361336223147191e-06
digits = +7.0383202767393023e+00
objValPrimal = -4.1899996163866383e+01
objValDual = -4.1900000000000006e+01
p.feas.error = +3.5690251616723664e-14
d.feas.error = +3.5527136788005009e-15
total time = 0.003271
main loop time = 0.003268
total time = 0.003271
file check time = 0.000000
file change time = 0.000003
file read time = 0.000000
Converting optimal solution to CLP format
SDPA end at [Sun Jan 26 20:41:26 2025]
Start: getCLPresult
Making result infomation...
(Re)calculating feasibility errors for CLP converted solution.
/opt/anaconda3/lib/python3.12/site-packages/sdpap/sdpaputils.py:114: RuntimeWarning: k >= N - 1 for N * N square matrix. Attempting to use scipy.linalg.eig instead.
eig = eigs(mat.toarray(), k=1, which='SM',
========================================
SDPAP: Result
========================================
SDPA.phase = pdOPT
iteration = 10
convMethod = LMI
frvMethod = split
domainMethod = none
rangeMethod = none
primalObj = +4.1900000000000006e+01
dualObj = +4.1899996163866383e+01
dualityGap = +9.1554505917001858e-08
primalError = +3.5527136788005009e-15
dualError = +0.0000000000000000e+00
convertTime = 0.000166
solveTime = 0.011680
retrievingTime = 0.000002
totalTime = 0.011902
---------- SDPAP End ----------
The dictionary sdpainfo
will contain the output of the backend solver. The dictionary sdpapinfo
will contain the output of SDPA for Python (for the original CLP).