Python Quick Start¶
PDHCG provides a user-friendly Python interface for quadratic and quadratic conic problems using familiar NumPy and SciPy data structures.
Basic Usage¶
import numpy as np
import scipy.sparse as sp
from pdhcg import Model
# Example: minimize 0.5 * x'(Q + R^T D R)x + c'x
# subject to l <= A x <= u, lb <= x <= ub
# (D defaults to identity, i.e. 0.5 * x'(Q + R^T R)x + c'x.)
# 1. Define Standard QP terms
Q = sp.csc_matrix([[1.0, -1.0], [-1.0, 2.0]])
c = np.array([-2.0, -6.0])
# 2. Define Low-Rank Matrix R
# This adds 0.5 * ||Rx||^2 to the objective
R = sp.csc_matrix([[1.0, 0.0]])
# 3. (Optional) Middle matrix D for R^T D R; 1-D = diag, 2-D = dense, sparse OK.
# D = np.array([2.5])
# 4. Define Constraints
A = sp.csc_matrix([[1.0, 1.0], [-1.0, 2.0], [2.0, 1.0]])
l = np.array([-np.inf, -np.inf, -np.inf])
u = np.array([2.0, 2.0, 3.0])
lb = np.zeros(2)
ub = np.array([np.inf, np.inf])
# 5. Create QP model with Low-Rank term (R) and optional middle D
m = Model(objective_matrix=Q,
objective_matrix_low_rank=R,
# objective_matrix_low_rank_middle=D,
objective_vector=c,
constraint_matrix=A,
constraint_lower_bound=l,
constraint_upper_bound=u,
variable_lower_bound=lb,
variable_upper_bound=ub)
# 5. Set solver parameters (0=Silent, 1=Summary, 2=Detailed)
m.setParams(LogLevel=2)
# Solve
m.optimize()
# Print results
print(f"Status: {m.Status}")
print(f"Objective: {m.ObjVal:.4f}")
if m.X is not None:
print(f"Primal Solution: {m.X}")
Quick start with cone constraints¶
Conic constraints use a columnar ConeSpec, whose arrays can describe many
blocks without allocating one Python object per cone. See
model.md.
import numpy as np
import scipy.sparse as sp
from pdhcg import ConeSpec, ConeType, Model
# min z s.t. v = 3, w = 4, (v, w, z) in K_soc => z = sqrt(v^2 + w^2) = 5
A = sp.csr_matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
model = Model(
objective_vector=np.array([0.0, 0.0, 1.0]),
constraint_matrix=A,
constraint_lower_bound=np.array([3.0, 4.0]),
constraint_upper_bound=np.array([3.0, 4.0]),
variable_cones=ConeSpec(
ConeType.SOC,
np.array([0], dtype=np.int32),
v_dims=1,
),
)
model.optimize()
print(model.Status, model.X)
CVXPY¶
Install the optional integration with pip install "pdhcg[cvxpy]", then
import the backend once in each process:
import cvxpy as cp
import pdhcg.cvxpy_backend # Registers solver="PDHCG".
x = cp.Variable()
problem = cp.Problem(cp.Minimize(x), [x >= 1])
value = problem.solve(solver="PDHCG", eps=1e-6)
print(problem.status, value, x.value)
The backend preserves CVXPY's primal and dual conventions. It supports quadratic objectives and Zero, NonNeg, SOC, ExpCone, and PowCone3D constraints. PSD and mixed-integer models are not supported.
Model Creation¶
The Model class is the core interface for defining quadratic conic problems. The problem formulation is:
Required Parameters¶
objective_vector(\(c\)): Linear coefficients of the objective function
Optional Parameters¶
objective_matrix(\(Q\)): Sparse quadratic coefficientsobjective_matrix_low_rank(\(R\)): Low-rank quadratic factor of shape \((k, n)\)objective_matrix_low_rank_middle(\(D\), \(k\times k\)): Middle matrix in \(R^\top D R\). 1-D array → diagonal \(D\); 2-D array → dense symmetric \(D\); scipy sparse → sparse \(D\). May be indefinite. Defaults to identityconstraint_matrix(\(A\)): Linear constraint matrixconstraint_lower_bound(\(\ell_c\)): Constraint lower boundsconstraint_upper_bound(\(u_c\)): Constraint upper boundsaffine_cone_matrix(\(F\)): Matrix in the native affine-cone constraint \(Fx + g \in \mathcal{K}_a\)affine_cone_offset(\(g\)): Affine-cone offset; defaults to zeroaffine_cones:ConeSpeccovering every row of \(F\)variable_cones:ConeSpecdescribing cone blocks embedded in \(x\)variable_lower_bound(\(\ell_v\)): Variable lower bounds (default: \(-\infty\))variable_upper_bound(\(u_v\)): Variable upper bounds (default: \(+\infty\))objective_constant: Constant term in objective
Setting Parameters¶
Solver parameters can be set individually or in batch:
# Set individual parameter
m.setParam("TimeLimit", 3600)
# Set multiple parameters
m.setParams(
TimeLimit=3600,
IterationLimit=100000,
LogLevel=1
)
# Or use the Params view
m.Params.TimeLimit = 3600
Warm Starting¶
Provide initial solutions to speed up convergence:
Accessing Results¶
After calling optimize(), results are available through properties:
m.optimize()
# Solution
print(m.X) # Primal solution
print(m.Pi) # Dual solution
# Objective
print(m.ObjVal) # Primal objective value
print(m.DualObj) # Dual objective value
print(m.Gap) # Objective gap
print(m.RelGap) # Relative gap
# Status
print(m.Status) # Solution status string
print(m.StatusCode) # Solution status code
print(m.IterCount) # Number of iterations
print(m.Runtime) # Runtime in seconds
# Residuals
print(m.RelPrimalResidual) # Relative primal residual
print(m.RelDualResidual) # Relative dual residual