4.4. Second-Order Cone Program¶
Second-order cone programming is the standard convex formulation for Euclidean norm constraints of the form
Together with affine equalities, this gives the standard SOCP template
Here \(x \in \mathbb{R}^n\) is the decision variable, \(f \in \mathbb{R}^n\) defines the linear objective \(f^\top x\), \(A_i \in \mathbb{R}^{n_i \times n}\), \(b_i \in \mathbb{R}^{n_i}\), \(c_i \in \mathbb{R}^n\), \(d_i \in \mathbb{R}\), and \(F \in \mathbb{R}^{p \times n}\), \(g \in \mathbb{R}^p\) define the affine equality system \(F x = g\). The left-hand side of each cone constraint is a Euclidean norm, while the right-hand side is affine.
SOCPs appear whenever “stay inside a norm cone” is the natural modeling idea. They are common in robust optimization, control, and geometric design problems.
Step 1: Generate a feasible cone instance
This example is built from a known feasible point \(x_0\). For each cone
constraint, we choose
\(d_i = \|A_i x_0 + b_i\|_2 - c_i^\top x_0\), so the inequality is satisfied at
\(x_0\) with equality. We also set g = F @ x0 so the affine equalities hold
at the same reference point.
import numpy as np
import admm
np.random.seed(1)
m = 3
n = 10
p = 5
n_i = 5
f = np.random.randn(n)
A = []
b = []
c = []
d = []
x0 = np.random.randn(n)
for i in range(m):
A.append(np.random.randn(n_i, n))
b.append(np.random.randn(n_i))
c.append(np.random.randn(n))
d.append(np.linalg.norm(A[i] @ x0 + b[i], 2) - c[i].T @ x0)
F = np.random.randn(p, n)
g = F @ x0
Step 2: Create the model and variable
This problem has one vector decision variable \(x \in \mathbb{R}^{10}\).
model = admm.Model()
x = admm.Var("x", n)
Step 3: Write the objective
The objective is the linear form \(f^\top x\), which maps directly to
f.T @ x in the symbolic API.
model.setObjective(f.T @ x)
Step 4: Add the cone constraints one by one
This is the central modeling idea in an SOCP. For each index i, the code
admm.norm(A[i] @ x + b[i], ord=2) <= c[i].T @ x + d[i]
is a literal translation of \(\|A_i x + b_i\|_2 \le c_i^\top x + d_i\). We add those constraints in a loop because the problem has several cone inequalities, not just one. After that, we add the affine equality block \(Fx = g\).
for i in range(m):
model.addConstr(admm.norm(A[i] @ x + b[i], ord=2) <= c[i].T @ x + d[i])
model.addConstr(F @ x == g)
Step 5: Solve and inspect the result
Once the cone and equality constraints are in place, we solve and print the standard solver outputs.
model.optimize()
print(" * model.ObjVal: ", model.ObjVal) # Expected: 2.06815161777782
print(" * model.StatusString: ", model.StatusString) # Expected: SOLVE_OPT_SUCCESS
Complete runnable example:
import numpy as np
import admm
np.random.seed(1)
m = 3
n = 10
p = 5
n_i = 5
f = np.random.randn(n)
A = []
b = []
c = []
d = []
x0 = np.random.randn(n)
for i in range(m):
A.append(np.random.randn(n_i, n))
b.append(np.random.randn(n_i))
c.append(np.random.randn(n))
d.append(np.linalg.norm(A[i] @ x0 + b[i], 2) - c[i].T @ x0)
F = np.random.randn(p, n)
g = F @ x0
model = admm.Model()
x = admm.Var("x", n)
model.setObjective(f.T @ x)
for i in range(m):
model.addConstr(admm.norm(A[i] @ x + b[i], ord=2) <= c[i].T @ x + d[i])
model.addConstr(F @ x == g)
model.optimize()
print(" * model.ObjVal: ", model.ObjVal) # Expected: 2.06815161777782
print(" * model.StatusString: ", model.StatusString) # Expected: SOLVE_OPT_SUCCESS
This example is available as a standalone script in the examples/ folder of the ADMM repository:
python examples/second_order_cone_program.py
Each second-order cone constraint \(\|A_i x + b_i\|_2 \le c_i^\top x + d_i\) maps to
a single addConstr(...) call. No auxiliary variables or epigraph reformulations are
needed — ADMM accepts norm constraints directly.