Getting Started

This tutorial introduces ConicIP.jl's problem formulation, core API, and solution interpretation. By the end you will have solved your first optimization problem in under a minute.

Problem Formulation

ConicIP solves optimization problems of the form

minimize    ½ yᵀQy - cᵀy
subject to  Ay ≥_K b
            Gy  = d

where ≥_K denotes a generalized inequality with respect to a cone K.

Arguments at a Glance

ArgumentSizeDescription
Qn × nPositive semidefinite Hessian (use spzeros(n,n) for LPs)
cnLinear objective term (vector)
Am × nInequality constraint matrix
bmInequality right-hand side (vector)
cone_dimsVector of (String,Int)Cone specification for rows of A
Gp × nEquality constraint matrix (optional)
dpEquality right-hand side (vector, optional)

Cone Specification

The cone_dims argument is a vector of (type, dimension) tuples describing how the rows of A and b are partitioned into cone constraints:

  • ("R", n) – nonnegative orthant: the first n rows satisfy Ay - b ≥ 0
  • ("Q", m) – second-order cone: the next m rows satisfy ‖(Ay-b)[2:end]‖ ≤ (Ay-b)[1]
  • ("S", k) – semidefinite cone (experimental): the next k rows represent a vectorized symmetric matrix that must be positive semidefinite, where k = n(n+1)/2

For example, [("R", 3), ("Q", 5)] means the first 3 rows of Ay ≥ b are nonnegative constraints, and the next 5 rows form a second-order cone constraint.

Example: Box-Constrained QP

Let's solve a box-constrained quadratic program: minimize ½ yᵀQy - cᵀy subject to 0 ≤ y ≤ 1.

We encode the box constraints 0 ≤ y ≤ 1 as [I; -I] y ≥ [0; -1].

using ConicIP, SparseArrays, LinearAlgebra, Random
Random.seed!(42)

n = 10
Q = sparse(Diagonal(rand(n) .+ 0.1))
c = randn(n)

# Encode 0 ≤ y ≤ 1 as [I; -I] y ≥ [0; -1]
A = sparse([I(n); -I(n)])
b = [zeros(n); -ones(n)]
cone_dims = [("R", 2n)]

sol = conicIP(Q, c, A, b, cone_dims; verbose=false)
sol.status
:Optimal

The primal solution:

round.(sol.y, digits=4)
10-element Vector{Float64}:
 0.0
 0.0
 1.0
 0.0313
 0.0
 0.9955
 0.0
 0.0
 0.0
 0.0

Interpreting the Solution

The solver returns a Solution struct with these key fields:

FieldDescription
sol.yPrimal variables (vector)
sol.vDual variables for inequality constraints
sol.wDual variables for equality constraints
sol.status:Optimal, :Infeasible, :Unbounded, :Abandoned, or :Error
sol.pobj, sol.dobjPrimal and dual objective values
sol.prFeasPrimal feasibility residual
sol.duFeasDual feasibility residual
sol.muFeasComplementarity residual
sol.IterNumber of iterations

Let's inspect the convergence residuals:

(prFeas=sol.prFeas, duFeas=sol.duFeas, muFeas=sol.muFeas)
(prFeas = 1.0331927562766044e-16, duFeas = 1.0246771995780095e-16, muFeas = 6.672332164139473e-8)

All residuals are well below the default tolerance (optTol = 1e-6).

Using JuMP Instead

You can also model problems via JuMP instead of the direct conicIP API. Here's the same box-constrained QP:

using JuMP

model = Model(() -> ConicIP.Optimizer(verbose=false))

@variable(model, 0 <= x[i=1:n] <= 1)
@objective(model, Min, 0.5 * sum(Q[i,i] * x[i]^2 for i in 1:n) - sum(c[i] * x[i] for i in 1:n))

optimize!(model)
termination_status(model)
OPTIMAL::TerminationStatusCode = 1

The JuMP solution matches the direct API:

round.(value.(x), digits=4)
10-element Vector{Float64}:
 0.0
 0.0
 1.0
 0.0313
 0.0
 0.9953
 0.0
 0.0
 0.0
 0.0

Next Steps