Skip to content

Commit ea29705

Browse files
authored
Rewrite IPM data structures (#62)
* rename directory * Util function for generating vectors of given type * Add an IPMData data structure and update IPM algorithm * Add detailed timing within IPM optimization * Rename files * Conversion ProblemData --> IPMData and solution extraction * Fix stopping criterion for Primal infeasibility * Remove arithmetic for KKT solvers overview * Update style convention in docstrings * Update form of internal LP representation in docs * Add toy example tutorial * Bind IPMData to IPM optimizer and simplify dispatch rules
1 parent 76e22bf commit ea29705

39 files changed

+1475
-1433
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ QPSReader = "10f199a5-22af-520b-b891-7ce84a7b1bd0"
1414
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1515
SuiteSparse = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9"
1616
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
17+
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
1718

1819
[compat]
1920
Krylov = "0.5.2"
2021
LDLFactorizations = "0.6"
2122
MathOptInterface = "0.9.5"
2223
QPSReader = "0.2"
24+
TimerOutputs = "0.5.6"
2325
julia = "1.3"
2426

2527
[extras]

docs/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
[deps]
22
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
3+
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
4+
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
35
Tulip = "6dd1b50a-3aae-11e9-10b5-ef983d2400fa"

docs/make.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ makedocs(
66
format = Documenter.HTML(prettyurls = get(ENV, "CI", nothing) == "true"),
77
pages = [
88
"Home" => "index.md",
9+
"Tutorials" => Any[
10+
"tutorials/lp_example.md",
11+
],
912
"User manual" => Any[
1013
"Problem formulation" => "manual/formulation.md",
1114
"Solving linear systems" => "manual/linear_systems.md"

docs/src/manual/formulation.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@ Internally, Tulip solves LPs of the form
2626
& c^{T} x + \ c_{0}\\
2727
s.t.
2828
& A x = b\\
29-
& x \leq u\\
30-
& x \geq 0
29+
& l \leq x \leq u\\
3130
\end{array}
3231
```
33-
where ``x, c, u \in \mathbb{R}^{n}``, ``A \in \mathbb{R}^{m \times n}`` and ``b \in \mathbb{R}^{m}``.
34-
Some ``u_{j}`` may may take infinite value, i.e., the corresponding variable ``x_{j}`` has no upper bound.
32+
where ``x, c \in \mathbb{R}^{n}``, ``A \in \mathbb{R}^{m \times n}``, ``b \in \mathbb{R}^{m}``,
33+
and ``l, u \in (\mathbb{R} \cup \{-\infty, +\infty \})^{n}``, i.e., some bounds may be infinite.
3534

3635
The original problem is automatically reformulated into standard form before the optimization is performed.
3736
This transformation is transparent to the user.

docs/src/manual/linear_systems.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ To enable the use of fast external libraries and/or specialized routines, the re
4747

4848
Here is a list of currently supported linear solvers:
4949

50-
| Linear solver type | `Tv` | System | Backend | Method |
51-
|:-------------------|:-----|:-------|:--------|:-------|
52-
| [`Dense_SymPosDef`](@ref) | `Real` | Normal equations | Dense / LAPACK | Cholesky
53-
| [`Cholmod_SymQuasDef`](@ref) | `Float64` | Augmented system | CHOLMOD | LDLᵀ
54-
| [`Cholmod_SymPosDef`](@ref) | `Float64` | Normal equations | CHOLMOD | Cholesky
55-
| [`LDLFact_SymQuasDef`](@ref) | `Real` | Augmented system | [LDLFactorizations.jl](https://github.com/JuliaSmoothOptimizers/LDLFactorizations.jl) | LDLᵀ
56-
| [`KrylovSPDSolver`](@ref) | `Real` | Normal equations | [Krylov.jl](https://github.com/JuliaSmoothOptimizers/Krylov.jl) | Krylov
57-
| [`KrylovSIDSolver`](@ref) | `Real` | Augmented system[^1] | [Krylov.jl](https://github.com/JuliaSmoothOptimizers/Krylov.jl) | Krylov
58-
| [`KrylovSQDSolver`](@ref) | `Real` | Augmented system[^1] | [Krylov.jl](https://github.com/JuliaSmoothOptimizers/Krylov.jl) | Krylov
50+
| Linear solver type | System | Backend | Method |
51+
|:-------------------|:-------|:--------|:-------|
52+
| [`Dense_SymPosDef`](@ref) | Normal equations | Dense / LAPACK | Cholesky
53+
| [`Cholmod_SymQuasDef`](@ref) | Augmented system | CHOLMOD | LDLᵀ
54+
| [`Cholmod_SymPosDef`](@ref) | Normal equations | CHOLMOD | Cholesky
55+
| [`LDLFact_SymQuasDef`](@ref) | Augmented system | [LDLFactorizations.jl](https://github.com/JuliaSmoothOptimizers/LDLFactorizations.jl) | LDLᵀ
56+
| [`KrylovSPDSolver`](@ref) | Normal equations | [Krylov.jl](https://github.com/JuliaSmoothOptimizers/Krylov.jl) | Krylov
57+
| [`KrylovSIDSolver`](@ref) | Augmented system[^1] | [Krylov.jl](https://github.com/JuliaSmoothOptimizers/Krylov.jl) | Krylov
58+
| [`KrylovSQDSolver`](@ref) | Augmented system[^1] | [Krylov.jl](https://github.com/JuliaSmoothOptimizers/Krylov.jl) | Krylov
5959

6060
[^1]: [`KrylovSIDSolver`](@ref)s view the augmented system as a symmetric indefinite system,
6161
while [`KrylovSQDSolver`](@ref)s exploit its 2x2 structure and quasi-definite property.

docs/src/reference/attributes.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ Attributes are queried using [`get_attribute`](@ref) and set using [`set_attribu
1313
| [`ModelName`](@ref) | `String` | Name of the model
1414
| [`NumberOfConstraints`](@ref) | `Int` | Number of constraints in the model
1515
| [`NumberOfVariables`](@ref) | `Int` | Number of variables in the model
16-
| [`ObjectiveValue`](@ref) | `Tv` | Objective value of the current primal solution
17-
| [`DualObjectiveValue`](@ref) | `Tv` | Objective value of the current dual solution
18-
| [`ObjectiveConstant`](@ref) | `Tv` | Value of the objective constant
16+
| [`ObjectiveValue`](@ref) | `T` | Objective value of the current primal solution
17+
| [`DualObjectiveValue`](@ref) | `T` | Objective value of the current dual solution
18+
| [`ObjectiveConstant`](@ref) | `T` | Value of the objective constant
1919
| [`ObjectiveSense`](@ref) | | Optimization sense
2020
| [`Status`](@ref) | | Model status
2121
| [`BarrierIterations`](@ref) | `Int` | Number of barrier iterations
@@ -25,17 +25,17 @@ Attributes are queried using [`get_attribute`](@ref) and set using [`set_attribu
2525

2626
| Name | Type | Description
2727
|:-----------------------------------|:---------|:------------------------------
28-
| [`VariableLowerBound`](@ref) | `Tv` | Variable lower bound
29-
| [`VariableUpperBound`](@ref) | `Tv` | Variable upper bound
30-
| [`VariableObjectiveCoeff`](@ref) | `Tv` | Variable objective coefficient
28+
| [`VariableLowerBound`](@ref) | `T` | Variable lower bound
29+
| [`VariableUpperBound`](@ref) | `T` | Variable upper bound
30+
| [`VariableObjectiveCoeff`](@ref) | `T` | Variable objective coefficient
3131
| [`VariableName`](@ref) | `String` | Variable name
3232

3333
## Constraint attributes
3434

3535
| Name | Type | Description
3636
|:-----------------------------------|:---------|:------------------------------
37-
| [`ConstraintLowerBound`](@ref) | `Tv` | Constraint lower bound
38-
| [`ConstraintUpperBound`](@ref) | `Tv` | Constraint upper bound
37+
| [`ConstraintLowerBound`](@ref) | `T` | Constraint lower bound
38+
| [`ConstraintUpperBound`](@ref) | `T` | Constraint upper bound
3939
| [`ConstraintName`](@ref) | `String` | Constraint name
4040

4141

@@ -46,22 +46,22 @@ Attributes are queried using [`get_attribute`](@ref) and set using [`set_attribu
4646

4747
```@autodocs
4848
Modules = [Tulip]
49-
Pages = ["src/Core/attributes.jl"]
49+
Pages = ["src/attributes.jl"]
5050
Filter = t -> typeof(t) === DataType && t <: Tulip.AbstractModelAttribute
5151
```
5252

5353
### Variable attributes
5454

5555
```@autodocs
5656
Modules = [Tulip]
57-
Pages = ["src/Core/attributes.jl"]
57+
Pages = ["src/attributes.jl"]
5858
Filter = t -> typeof(t) === DataType && t <: Tulip.AbstractVariableAttribute
5959
```
6060

6161
### Constraint attributes
6262

6363
```@autodocs
6464
Modules = [Tulip]
65-
Pages = ["src/Core/attributes.jl"]
65+
Pages = ["src/attributes.jl"]
6666
Filter = t -> typeof(t) === DataType && t <: Tulip.AbstractConstraintAttribute
6767
```

docs/src/tutorials/lp_example.md

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# Toy example
2+
3+
Tulip can be accessed in 3 ways:
4+
through [JuMP](https://github.com/jump-dev/JuMP.jl),
5+
through [MathOptInterface](https://github.com/jump-dev/MathOptInterface.jl),
6+
or directly.
7+
8+
This tutorial illustrates, for each case, how to build a model, solve it,
9+
and query the solution value.
10+
In all cases, we consider the small LP
11+
```math
12+
\begin{array}{rrrl}
13+
(LP) \ \ \
14+
\displaystyle Z^{*} = \min_{x, y} & -2x & - y\\
15+
s.t.
16+
& x & - y & \geq -2\\
17+
& 2x &- y & \leq 4\\
18+
& x &+ 2y & \leq 7\\
19+
& x,& y & \geq 0\\
20+
\end{array}
21+
```
22+
whose optimal value and solution are ``Z^{*} = -8`` and ``(x^{*}, y^{*}) = (3, 2)``.
23+
24+
## JuMP
25+
26+
```jldoctest; output = false
27+
using Printf
28+
using JuMP
29+
import Tulip
30+
31+
# Instantiate JuMP model
32+
lp = Model(Tulip.Optimizer)
33+
34+
# Create variables
35+
@variable(lp, x >= 0)
36+
@variable(lp, y >= 0)
37+
38+
# Add constraints
39+
@constraint(lp, row1, x - y >= -2)
40+
@constraint(lp, row2, 2*x - y <= 4)
41+
@constraint(lp, row3, x + 2*y <= 7)
42+
43+
# Set the objective
44+
@objective(lp, Min, -2*x - y)
45+
46+
# Set some parameters
47+
set_optimizer_attribute(lp, "OutputLevel", 0) # disable output
48+
set_optimizer_attribute(lp, "Presolve", 0) # disable presolve
49+
50+
# Solve the problem
51+
optimize!(lp)
52+
53+
# Check termination status
54+
st = termination_status(lp)
55+
println("Termination status: $st")
56+
57+
# Query solution value
58+
objval = objective_value(lp)
59+
x_ = value(x)
60+
y_ = value(y)
61+
62+
@printf "Z* = %.4f\n" objval
63+
@printf "x* = %.4f\n" x_
64+
@printf "y* = %.4f\n" y_
65+
66+
# output
67+
68+
Termination status: OPTIMAL
69+
Z* = -8.0000
70+
x* = 3.0000
71+
y* = 2.0000
72+
```
73+
74+
## MOI
75+
76+
```jldoctest; output = false
77+
using Printf
78+
79+
import MathOptInterface
80+
const MOI = MathOptInterface
81+
82+
import Tulip
83+
84+
lp = Tulip.Optimizer{Float64}()
85+
86+
# Create variables
87+
x = MOI.add_variable(lp)
88+
y = MOI.add_variable(lp)
89+
90+
# Set variable bounds
91+
MOI.add_constraint(lp, MOI.SingleVariable(x), MOI.GreaterThan(0.0)) # x >= 0
92+
MOI.add_constraint(lp, MOI.SingleVariable(y), MOI.GreaterThan(0.0)) # y >= 0
93+
94+
# Add constraints
95+
row1 = MOI.add_constraint(lp,
96+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, -1.0], [x, y]), 0.0),
97+
MOI.GreaterThan(-2.0)
98+
)
99+
row2 = MOI.add_constraint(lp,
100+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, -1.0], [x, y]), 0.0),
101+
MOI.LessThan(4.0)
102+
)
103+
row3 = MOI.add_constraint(lp,
104+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], [x, y]), 0.0),
105+
MOI.LessThan(7.0)
106+
)
107+
108+
# Set the objective
109+
MOI.set(lp,
110+
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
111+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([-2.0, -1.0], [x, y]), 0.0)
112+
)
113+
MOI.set(lp, MOI.ObjectiveSense(), MOI.MIN_SENSE)
114+
115+
# Set some parameters
116+
MOI.set(lp, MOI.Silent(), true) # disable output
117+
MOI.set(lp, MOI.RawParameter("Presolve"), 0) # disable presolve
118+
119+
# Solve the problem
120+
MOI.optimize!(lp)
121+
122+
# Check status
123+
st = MOI.get(lp, MOI.TerminationStatus())
124+
println("Termination status: $st")
125+
126+
# Query solution value
127+
objval = MOI.get(lp, MOI.ObjectiveValue())
128+
x_ = MOI.get(lp, MOI.VariablePrimal(), x)
129+
y_ = MOI.get(lp, MOI.VariablePrimal(), y)
130+
131+
@printf "Z* = %.4f\n" objval
132+
@printf "x* = %.4f\n" x_
133+
@printf "y* = %.4f\n" y_
134+
135+
# output
136+
137+
Termination status: OPTIMAL
138+
Z* = -8.0000
139+
x* = 3.0000
140+
y* = 2.0000
141+
```
142+
143+
## Tulip
144+
145+
!!! warning
146+
Tulip's low-level API should not be considered stable nor complete.
147+
The recommended way to use Tulip is through JuMP/MOI as shown above.
148+
149+
150+
```jldoctest; output = false
151+
using Printf
152+
import Tulip
153+
154+
# Instantiate Tulip object
155+
lp = Tulip.Model{Float64}()
156+
pb = lp.pbdata # Internal problem data
157+
158+
# Create variables
159+
x = Tulip.add_variable!(pb, Int[], Float64[], -2.0, 0.0, Inf, "x")
160+
y = Tulip.add_variable!(pb, Int[], Float64[], -1.0, 0.0, Inf, "y")
161+
162+
# Add constraints
163+
row1 = Tulip.add_constraint!(pb, [x, y], [1.0, -1.0], -2.0, Inf, "row1")
164+
row2 = Tulip.add_constraint!(pb, [x, y], [2.0, -1.0], -Inf, 4.0, "row2")
165+
row3 = Tulip.add_constraint!(pb, [x, y], [1.0, 2.0], -Inf, 7.0, "row3")
166+
167+
# Set the objective
168+
# Nothing to do here as objective is already declared
169+
170+
# Set some parameters
171+
Tulip.set_parameter(lp, "OutputLevel", 0) # disable output
172+
Tulip.set_parameter(lp, "Presolve", 0) # disable presolve
173+
174+
# Solve the problem
175+
Tulip.optimize!(lp)
176+
177+
# Check termination status
178+
st = Tulip.get_attribute(lp, Tulip.Status())
179+
println("Termination status: $st")
180+
181+
# Query solution value
182+
objval = Tulip.get_attribute(lp, Tulip.ObjectiveValue())
183+
x_ = lp.solution.x[x]
184+
y_ = lp.solution.x[y]
185+
186+
@printf "Z* = %.4f\n" objval
187+
@printf "x* = %.4f\n" x_
188+
@printf "y* = %.4f\n" y_
189+
190+
# output
191+
192+
Termination status: Trm_Optimal
193+
Z* = -8.0000
194+
x* = 3.0000
195+
y* = 2.0000
196+
```

src/Core/utils.jl

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)