Tutorial
Input
RipQP uses the package QuadraticModels.jl to model convex quadratic problems.
Here is a basic example:
using QuadraticModels, LinearAlgebra, SparseMatricesCOO
Q = [6. 2. 1.
2. 5. 2.
1. 2. 4.]
c = [-8.; -3; -3]
A = [1. 0. 1.
0. 2. 1.]
b = [0.; 3]
l = [-1.0;0;0]
u = [Inf; Inf; Inf]
QM = QuadraticModel(
c,
SparseMatrixCOO(tril(Q)),
A=SparseMatrixCOO(A),
lcon=b,
ucon=b,
lvar=l,
uvar=u,
c0=0.,
name="QM"
)
QuadraticModels.QuadraticModel{Float64, Vector{Float64}, SparseMatricesCOO.SparseMatrixCOO{Float64, Int64}, SparseMatricesCOO.SparseMatrixCOO{Float64, Int64}}
Problem name: QM
All variables: ████████████████████ 3 All constraints: ████████████████████ 2
free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
lower: ████████████████████ 3 lower: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
upper: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 upper: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
low/upp: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 low/upp: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
fixed: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 fixed: ████████████████████ 2
infeas: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 infeas: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
nnzh: ( 0.00% sparsity) 6 linear: ████████████████████ 2
nonlinear: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
nnzj: ( 33.33% sparsity) 4
Counters:
obj: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 grad: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 cons: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
cons_lin: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 cons_nln: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jcon: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
jgrad: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jac: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jac_lin: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
jac_nln: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jprod: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jprod_lin: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
jprod_nln: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jtprod: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jtprod_lin: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
jtprod_nln: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 hess: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 hprod: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
jhess: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jhprod: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
Once your QuadraticModel
is loaded, you can simply solve it RipQP:
using RipQP
stats = ripqp(QM)
println(stats)
[ Info: Solving in Float64 using K2LDL with LDLFactorizationData
[ Info: iter obj rgap ‖rb‖ ‖rc‖ α_pri α_du μ ρ δ
[ Info: 0 3.6e+00 9.9e-01 1.4e+00 3.8e+00 0.0e+00 0.0e+00 5.9e+00 1.5e-03 1.5e-03
[ Info: 1 1.6e+00 2.7e-01 4.1e-03 1.4e-03 1.0e+00 1.0e+00 2.4e-01 1.5e-03 1.5e-03
[ Info: 2 1.2e+00 2.1e-02 5.5e-05 4.5e-05 1.0e+00 1.0e+00 1.5e-02 1.5e-04 1.5e-04
[ Info: 3 1.1e+00 1.0e-04 6.9e-07 1.9e-05 1.0e+00 1.0e+00 6.1e-05 1.5e-05 1.5e-05
[ Info: 4 1.1e+00 1.0e-07 1.0e-09 1.9e-08 1.0e+00 1.0e+00 6.1e-08 1.5e-06 1.5e-06
[ Info: 5 1.1e+00 1.0e-10 1.1e-12 1.8e-11 1.0e+00 1.0e+00 6.1e-11 1.5e-07 1.5e-07
Generic Execution stats
status: first-order stationary
objective value: 1.1250000001819647
primal feasibility: 5.253818735984856e-13
dual feasibility: 4.5240388878182354e-11
solution: [-6.602556960437742e-11 1.499999999967075 6.550018773077893e-11]
multipliers: [-5.000000000443094 2.249999999907261]
multipliers_L: [1.3498983056074131e-12 9.350004367856938e-13 2.7500000006802963]
multipliers_U: [0.0 0.0 0.0]
iterations: 5
elapsed time: 13.493134021759033
solver specific:
iters_sp: 5
pdd: 1.002449365261327e-10
psoperations: ∅
iters_sp3: 0
iters_sp2: 0
relative_iter_cnt: 20
The stats
output is a GenericExecutionStats.
It is also possible to use the package QPSReader.jl in order to read convex quadratic problems in MPS or SIF formats: (download QAFIRO)
using QPSReader, QuadraticModels
QM = QuadraticModel(readqps("assets/QAFIRO.SIF"))
QuadraticModels.QuadraticModel{Float64, Vector{Float64}, SparseMatricesCOO.SparseMatrixCOO{Float64, Int64}, SparseMatricesCOO.SparseMatrixCOO{Float64, Int64}}
Problem name: Generic
All variables: ████████████████████ 32 All constraints: ████████████████████ 27
free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
lower: ████████████████████ 32 lower: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
upper: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 upper: ███████████████⋅⋅⋅⋅⋅ 19
low/upp: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 low/upp: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
fixed: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 fixed: ██████⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 8
infeas: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 infeas: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
nnzh: ( 98.86% sparsity) 6 linear: ████████████████████ 27
nonlinear: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
nnzj: ( 90.39% sparsity) 83
Counters:
obj: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 grad: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 cons: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
cons_lin: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 cons_nln: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jcon: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
jgrad: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jac: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jac_lin: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
jac_nln: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jprod: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jprod_lin: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
jprod_nln: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jtprod: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jtprod_lin: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
jtprod_nln: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 hess: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 hprod: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
jhess: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 jhprod: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
Logging
RipQP displays some logs at each iterate.
stats = ripqp(QM)
println(stats)
[ Info: Solving in Float64 using K2LDL with LDLFactorizationData
[ Info: iter obj rgap ‖rb‖ ‖rc‖ α_pri α_du μ ρ δ
[ Info: 0 5.1e+03 5.2e+00 5.3e+02 6.2e+01 0.0e+00 0.0e+00 8.2e+02 1.5e-03 1.5e-03
[ Info: 1 2.8e+02 4.7e+01 7.7e+01 8.7e+00 8.6e-01 2.9e-01 3.4e+02 1.5e-03 1.5e-03
[ Info: 2 2.8e+02 3.7e+01 4.3e+01 7.3e+00 4.4e-01 2.8e-01 2.6e+02 1.5e-04 1.5e-04
[ Info: 3 2.2e+02 3.0e+00 2.5e+00 5.4e-01 9.4e-01 9.9e-01 1.5e+01 1.5e-05 1.5e-05
[ Info: 4 3.6e+01 2.1e+00 8.2e-03 6.4e-02 1.0e+00 9.9e-01 1.6e+00 1.5e-06 1.5e-06
[ Info: 5 4.7e+00 1.9e+00 3.5e-06 1.4e-06 1.0e+00 1.0e+00 2.2e-01 1.5e-07 1.5e-07
[ Info: 6 -4.3e-01 7.7e-01 1.1e-07 3.1e-01 1.0e+00 7.7e-01 3.5e-02 1.5e-08 1.5e-08
[ Info: 7 -1.5e+00 5.3e-02 1.1e-08 1.1e-02 9.1e-01 9.8e-01 3.1e-03 1.5e-09 1.5e-09
[ Info: 8 -1.6e+00 6.1e-05 1.0e-10 1.3e-05 1.0e+00 1.0e+00 3.7e-06 1.5e-10 1.5e-09
[ Info: 9 -1.6e+00 6.1e-08 2.6e-13 1.3e-08 1.0e+00 1.0e+00 3.7e-09 1.5e-11 1.5e-09
[ Info: 10 -1.6e+00 6.1e-11 7.1e-15 1.3e-11 1.0e+00 1.0e+00 3.7e-12 1.5e-12 1.5e-09
Generic Execution stats
status: first-order stationary
objective value: -1.590781793787462
primal feasibility: 7.105427357601002e-15
dual feasibility: 4.0220152292944574e-11
solution: [0.38028479685706695 8.806189977065276e-13 0.3802847968561864 0.40310188466849095 ⋯ 1.6759073619030825e-13]
multipliers: [-4.466932765449747 -2.2377603513257897e-12 -1.980617639140097e-14 -3.642178610385085 ⋯ -6.781571768245936e-15]
multipliers_L: [3.0441783478982717e-12 1.185323748792536 2.340230871153934e-12 2.242879722664324e-12 ⋯ 10.000000000000178]
multipliers_U: [0.0 0.0 0.0 0.0 ⋯ 0.0]
iterations: 10
elapsed time: 0.09997987747192383
solver specific:
iters_sp: 10
pdd: 6.100201586626379e-11
psoperations: [QuadraticModels.SingletonRow{Float64, Vector{Float64}}(3, 1, 1.0, false, true) QuadraticModels.SingletonRow{Float64, Vector{Float64}}(13, 16, 1.0, false, true)]
iters_sp3: 0
iters_sp2: 0
relative_iter_cnt: 40
You can deactivate logging with
stats = ripqp(QM, display = false)
println(stats)
Generic Execution stats
status: first-order stationary
objective value: -1.590781793787462
primal feasibility: 7.105427357601002e-15
dual feasibility: 4.0220152292944574e-11
solution: [0.38028479685706695 8.806189977065276e-13 0.3802847968561864 0.40310188466849095 ⋯ 1.6759073619030825e-13]
multipliers: [-4.466932765449747 -2.2377603513257897e-12 -1.980617639140097e-14 -3.642178610385085 ⋯ -6.781571768245936e-15]
multipliers_L: [3.0441783478982717e-12 1.185323748792536 2.340230871153934e-12 2.242879722664324e-12 ⋯ 10.000000000000178]
multipliers_U: [0.0 0.0 0.0 0.0 ⋯ 0.0]
iterations: 10
elapsed time: 0.0003490447998046875
solver specific:
iters_sp: 10
pdd: 6.100201586626379e-11
psoperations: [QuadraticModels.SingletonRow{Float64, Vector{Float64}}(3, 1, 1.0, false, true) QuadraticModels.SingletonRow{Float64, Vector{Float64}}(13, 16, 1.0, false, true)]
iters_sp3: 0
iters_sp2: 0
relative_iter_cnt: 40
It is also possible to get a history of several quantities such as the primal and dual residuals and the relative primal-dual gap. These quantites are available in the dictionary solver_specific
of the stats
.
stats = ripqp(QM, display = false, history = true)
pddH = stats.solver_specific[:pddH]
11-element Vector{Float64}:
5.202001221940632
47.09291657152063
37.04715154649735
2.9705628772130837
2.0982950800102707
1.949512880288423
0.7749630402562204
0.05312630164594008
6.100127510228456e-5
6.100081602192295e-8
6.100201586626379e-11
Change configuration and tolerances
You can use RipQP
without presolve and scaling with:
stats = ripqp(QM, ps=false, scaling = false)
You can also change the RipQP.InputTol
type to change the tolerances for the stopping criteria:
stats = ripqp(QM, itol = InputTol(max_iter = 5))
println(stats)
[ Info: Solving in Float64 using K2LDL with LDLFactorizationData
[ Info: iter obj rgap ‖rb‖ ‖rc‖ α_pri α_du μ ρ δ
[ Info: 0 5.1e+03 5.2e+00 5.3e+02 6.2e+01 0.0e+00 0.0e+00 8.2e+02 1.5e-03 1.5e-03
[ Info: 1 2.8e+02 4.7e+01 7.7e+01 8.7e+00 8.6e-01 2.9e-01 3.4e+02 1.5e-03 1.5e-03
[ Info: 2 2.8e+02 3.7e+01 4.3e+01 7.3e+00 4.4e-01 2.8e-01 2.6e+02 1.5e-04 1.5e-04
[ Info: 3 2.2e+02 3.0e+00 2.5e+00 5.4e-01 9.4e-01 9.9e-01 1.5e+01 1.5e-05 1.5e-05
[ Info: 4 3.6e+01 2.1e+00 8.2e-03 6.4e-02 1.0e+00 9.9e-01 1.6e+00 1.5e-06 1.5e-06
[ Info: 5 4.7e+00 1.9e+00 3.5e-06 1.4e-06 1.0e+00 1.0e+00 2.2e-01 1.5e-07 1.5e-07
Generic Execution stats
status: maximum iteration
objective value: 4.658954498519296
primal feasibility: 3.4981491245367202e-6
dual feasibility: 1.6824715908247967e-6
solution: [0.743878138254691 0.004674605650346285 0.7392033550578112 0.78851071023687 ⋯ 0.002243926732604866]
multipliers: [-7.826615009307647 -0.44355741689044687 -0.0002826585049596877 -7.445204284037653 ⋯ -9.745279751756744e-5]
multipliers_L: [0.1452930232869297 1.5112382757139278 0.6014690171102718 0.44363007256944875 ⋯ 10.002732536283734]
multipliers_U: [0.0 0.0 0.0 0.0 ⋯ 0.0]
iterations: 5
elapsed time: 0.007070064544677734
solver specific:
iters_sp: 5
pdd: 1.949512880288423
psoperations: [QuadraticModels.SingletonRow{Float64, Vector{Float64}}(3, 1, 1.0, false, true) QuadraticModels.SingletonRow{Float64, Vector{Float64}}(13, 16, 1.0, false, true)]
iters_sp3: 0
iters_sp2: 0
relative_iter_cnt: 20
Save the Interior-Point system
At every iteration, RipQP solves two linear systems with the default Predictor-Corrector method (the affine system and the corrector-centering system), or one linear system with the Infeasible Path-Following method.
To save these systems, you can use:
w = SystemWrite(write = true, name="test_", kfirst = 4, kgap=3)
stats = ripqp(QM, w = w)
This will save one matrix and the associated two right hand sides of the PC method every three iterations starting at iteration four. Then, you can read the saved files with:
using DelimitedFiles, MatrixMarket
K = MatrixMarket.mmread("test_K_iter4.mtx")
rhs_aff = readdlm("test_rhs_iter4_aff.rhs", Float64)[:]
rhs_cc = readdlm("test_rhs_iter4_cc.rhs", Float64)[:];
Timers
You can see the elapsed time with:
stats.elapsed_time
For more advance timers you can use TimerOutputs.jl:
using TimerOutputs
TimerOutputs.enable_debug_timings(RipQP)
reset_timer!(RipQP.to)
stats = ripqp(QM)
TimerOutputs.complement!(RipQP.to) # print complement of timed sections
show(RipQP.to, sortby = :firstexec)