2 Guide
2.1 The problem SCS solves
SCS solves the convex quadratic cone program
minimize ½ xᵀPx + cᵀx
subject to Ax + s = b, s ∈ K
over the variable x ∈ ℝⁿ and slack s ∈ ℝᵐ, where P is symmetric positive semidefinite, A is m×n, and K is a closed convex cone. SCS simultaneously produces a solution to the dual problem; the dual variable is returned as scs-result-y. See the upstream algorithm notes for the precise primal/dual pair.
In Racket you supply A and the optional P as compressed-sparse-column matrices (scs:matrix / scs:sparse-matrix), b and c as vectors or lists, and K as a make-cone value, then call solve.
2.2 Cones
The cone K is a Cartesian product of primitive cones. The rows of A (and the entries of b and s) must appear in this exact order, which is also the order of the make-cone keywords:
second-order (Lorentz) cones —
#:soc, a list of block sizes; each block (t, u) satisfies ‖u‖₂ ≤ t. positive semidefinite cones —
#:psd, a list of matrix dimensions; see Semidefinite cones. exponential cones —
#:exp-primal / #:exp-dual counts of triples; the primal cone is the closure of {(x,y,z) : y·e^(x/y) ≤ z, y > 0}. power cones —
#:power, a list of parameters in [-1, 1] (negative values select the dual power cone).
This mirrors the cone ordering documented in the SCS cones reference.
2.2.1 Box cones
The box cone is {(t, r) : t·l ≤ r ≤ t·u} with a leading scale variable t. To impose plain bounds l ≤ y ≤ u, arrange A and b so the first box row’s slack is a constant 1 (a row of zeros in A with the matching b entry equal to 1) and the remaining rows carry y. make-cone takes the bounds with #:box-lower and #:box-upper; their length is the number of box-constrained entries, and SCS’s bsize is that length plus one. Model predictive control is a worked example.
2.2.2 Semidefinite cones
For a k×k symmetric matrix, the PSD cone slack is the lower triangle stacked column-wise with the off-diagonal entries scaled by √2; the block length is k(k+1)/2. So a 2×2 matrix [[a b] [b c]] is represented by (a, √2·b, c). #:psd takes the list of k values. See the cones reference and Semidefinite program.
2.3 Building matrices and vectors
scs:matrix builds a matrix from a dense, row-major sequence of values:
(scs:matrix 3 2 ; 3 rows, 2 columns -1 1 1 0 0 1)
It also accepts a single list of rows, inferring the dimensions, which reads like a 2D literal:
(scs:matrix '((-1 1) ( 1 0) ( 0 1)))
scs:sparse-matrix builds one from explicit (row col value) triples, which is convenient for a quadratic P (supply only the upper triangle):
(scs:sparse-matrix 2 2 '(0 0 3) '(0 1 -1) '(1 1 2))
For data already in a scipy-style coordinate form, scs:coo-matrix takes three parallel sequences (rows cols vals) (vectors or lists), matching scipy.sparse.coo_matrix((data, (row, col))):
(scs:coo-matrix 2 2 '(0 0 1) '(0 1 1) '(3 -1 2))
All three return a CSC matrix suitable for #:A or #:P. The right-hand side b and objective c are passed to solve as plain vectors or lists.
2.4 Modeling cookbook
Getting a problem into SCS’s standard form Ax + s = b, s ∈ K comes down to a handful of recurring moves. Each is used by one or more of the worked Examples.
2.4.1 Laying out A in cone-ordered row-blocks
The rows of A (and the entries of b and s) must appear in the Cones order: zero rows, then positive, box, second-order, semidefinite, exponential, and power. Build A as a stack of row-blocks, one per primitive cone, in that order, and make make-cone’s keywords describe the same blocks. A slack lands in a given cone exactly when s = b − Ax restricted to that block satisfies the membership condition; a common idiom is an A = −I block with b = 0, which sets s = x on those rows so the variables themselves are constrained (Second-order cone, Exponential cone, Power cone).
2.4.2 Bounds and the absolute-value trick
A two-sided bound l ≤ aᵀx ≤ u is two positive-orthant rows, aᵀx ≤ u and −aᵀx ≤ −l. The absolute-value trick models |aᵀx|: introduce an auxiliary t, add the two rows aᵀx − t ≤ 0 and −aᵀx − t ≤ 0 (so t ≥ |aᵀx|), and use t in the objective. This is how L1 penalties become cone programs in Lasso along a regularization path and Elastic net regression.
2.4.3 Norms via the second-order cone
A Euclidean-norm bound ‖u‖₂ ≤ t is one second-order cone block over (t, u) (Second-order cone). To minimize a norm, bound it by a fresh variable t with ‖u‖₂ ≤ t and minimize t.
2.4.4 The epigraph trick for nonlinear objectives
To minimize a convex function f(x) that is not already linear or quadratic, minimize a new variable t subject to the epigraph trick constraint f(x) ≤ t, then express that constraint with a cone. For x log x the epigraph is exponential-cone membership, which is how Maximum entropy maximizes entropy.
2.4.5 Box-cone layout
The box cone carries a leading scale variable. To impose plain bounds l ≤ y ≤ u, pin that scale to 1 with a constant row (zeros in A, a 1 in b) and put y on the remaining rows; see Box cones and the worked Model predictive control.
2.4.6 Symmetric matrices and the svec scaling
A semidefinite constraint X ⪰ 0 is one #:psd block whose slack is the √2-scaled lower-triangular vectorization of X; see Semidefinite cones and Semidefinite program.
2.5 Settings
make-settings produces a settings value populated with SCS’s defaults, overridden by the keywords you pass. Unlike the C library, verbose? defaults to #f (libraries should be quiet). The most common knobs are the convergence tolerances #:eps-abs and #:eps-rel (SCS default 0.0001) and #:max-iters. See the upstream settings reference for the full list and defaults. Passing no #:settings to solve uses the defaults.
2.6 Solving and reading results
solve returns an scs-result. Check solved? (true when the exit flag is 1, SCS_SOLVED); scs-result-status is the human-readable status string and scs-result-status-val the integer exit flag. The exit flags follow the SCS exit-flag reference: 1 solved, 2 solved-inaccurate, -1 unbounded, -2 infeasible, and -3 through -7 for the indeterminate/failed/interrupted and inaccurate-certificate cases.
The primal solution is scs-result-x, the dual is scs-result-y, and the slack is scs-result-s; scs-result-pobj and scs-result-dobj are the primal and dual objectives.
2.6.1 Direct vs. indirect solver
By default solve uses the direct linear-system solver. Pass #:indirect? #t to use the indirect (conjugate-gradient) solver, which can scale better on very large sparse problems.
2.6.2 Warm starting and re-solving
When a sequence of problems differs only in b or c, build a solver once with make-solver and re-solve it instead of rebuilding from scratch. solver-solve! returns an scs-result; it cold-starts the first time and warm-starts (reusing the previous iterate) on every call after that. solver-update! swaps in a new b and/or c (the matrices and cone are fixed):
(define s (make-solver #:A A #:b b #:c c #:cone cone)) (solver-solve! s) ; cold start (solver-update! s #:b b2) ; only b changes (solver-solve! s) ; warm-started re-solve
solve is just make-solver followed by a single solver-solve!. Warm starting and re-solving, Lasso along a regularization path, and Model predictive control show the pattern. For full manual control over the workspace and solution buffers, the scs/foreign layer exposes the underlying scs-init / scs-update / scs-solve.
2.6.3 Troubleshooting and status
When solved? is false, the exit flag says why, and the result still carries useful information:
Infeasible (flag -2): no x satisfies the constraints. SCS returns a certificate of infeasibility in scs-result-y (a dual ray); scs-result-x is not meaningful.
Unbounded (flag -1): the objective can be driven to −∞ within the constraints. The certificate is a primal ray in scs-result-x.
Inaccurate (flags 2, -6, -7): SCS hit its iteration or time limit with the residuals only partially converged. The iterate is usually close; loosen #:eps-abs / #:eps-rel to accept it, or raise #:max-iters / #:time-limit-secs to push further.
Indeterminate / failed (flags -3 through -5): the problem could not be classified, or setup failed.
A few common modeling mistakes produce a spurious infeasible or unbounded result rather than an error:
Cone-order mismatch. The rows of A must follow the Cones order and match make-cone’s keyword counts exactly. A miscounted block silently re-interprets later rows.
A missing box scale row. The box cone needs its leading scale entry pinned (see Box cones); omitting it changes the feasible set.
Wrong PSD scaling. Off-diagonal entries of a semidefinite block must be √2-scaled (see Semidefinite cones).
To see SCS’s own iteration log while diagnosing, pass (make-settings #:verbose? #t).