Toy example

Tulip can be accessed in 3 ways: through JuMP, through MathOptInterface, or directly.

This tutorial illustrates, for each case, how to build a model, solve it, and query the solution value. In all cases, we consider the small LP

\[\begin{array}{rrrl} (LP) \ \ \ \displaystyle Z^{*} = \min_{x, y} & -2x & - y\\ s.t. & x & - y & \geq -2\\ & 2x &- y & \leq 4\\ & x &+ 2y & \leq 7\\ & x,& y & \geq 0\\ \end{array}\]

whose optimal value and solution are $Z^{*} = -8$ and $(x^{*}, y^{*}) = (3, 2)$.

JuMP

using Printf
using JuMP
import Tulip

# Instantiate JuMP model
lp = Model(Tulip.Optimizer)

# Create variables
@variable(lp, x >= 0)
@variable(lp, y >= 0)

# Add constraints
@constraint(lp, row1, x - y >= -2)
@constraint(lp, row2, 2*x - y <= 4)
@constraint(lp, row3, x + 2*y <= 7)

# Set the objective
@objective(lp, Min, -2*x - y)

# Set some parameters
set_optimizer_attribute(lp, "OutputLevel", 0)  # disable output
set_optimizer_attribute(lp, "Presolve_Level", 0)     # disable presolve

# Solve the problem
optimize!(lp)

# Check termination status
st = termination_status(lp)
println("Termination status: $st")

# Query solution value
objval = objective_value(lp)
x_ = value(x)
y_ = value(y)

@printf "Z* = %.4f\n" objval
@printf "x* = %.4f\n" x_
@printf "y* = %.4f\n" y_

MOI

using Printf

import MathOptInterface
const MOI = MathOptInterface

import Tulip

lp = Tulip.Optimizer{Float64}()

# Create variables
x = MOI.add_variable(lp)
y = MOI.add_variable(lp)

# Set variable bounds
MOI.add_constraint(lp, x, MOI.GreaterThan(0.0))  # x >= 0
MOI.add_constraint(lp, y, MOI.GreaterThan(0.0))  # y >= 0

# Add constraints
row1 = MOI.add_constraint(lp,
    MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, -1.0], [x, y]), 0.0),
    MOI.GreaterThan(-2.0)
)
row2 = MOI.add_constraint(lp,
    MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, -1.0], [x, y]), 0.0),
    MOI.LessThan(4.0)
)
row3 = MOI.add_constraint(lp,
    MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0,  2.0], [x, y]), 0.0),
    MOI.LessThan(7.0)
) 

# Set the objective
MOI.set(lp,
    MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
    MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([-2.0, -1.0], [x, y]), 0.0)
)
MOI.set(lp, MOI.ObjectiveSense(), MOI.MIN_SENSE)

# Set some parameters
MOI.set(lp, MOI.Silent(), true)               # disable output
MOI.set(lp, MOI.RawOptimizerAttribute("Presolve_Level"), 0)  # disable presolve

# Solve the problem
MOI.optimize!(lp)

# Check status
st = MOI.get(lp, MOI.TerminationStatus())
println("Termination status: $st")

# Query solution value
objval = MOI.get(lp, MOI.ObjectiveValue())
x_ = MOI.get(lp, MOI.VariablePrimal(), x)
y_ = MOI.get(lp, MOI.VariablePrimal(), y)

@printf "Z* = %.4f\n" objval
@printf "x* = %.4f\n" x_
@printf "y* = %.4f\n" y_

Tulip

Warning

Tulip's low-level API should not be considered stable nor complete. The recommended way to use Tulip is through JuMP/MOI as shown above.

using Printf
import Tulip

# Instantiate Tulip object
lp = Tulip.Model{Float64}()
pb = lp.pbdata  # Internal problem data

# Create variables
x = Tulip.add_variable!(pb, Int[], Float64[], -2.0, 0.0, Inf, "x")
y = Tulip.add_variable!(pb, Int[], Float64[], -1.0, 0.0, Inf, "y")

# Add constraints
row1 = Tulip.add_constraint!(pb, [x, y], [1.0, -1.0], -2.0, Inf, "row1")
row2 = Tulip.add_constraint!(pb, [x, y], [2.0, -1.0], -Inf, 4.0, "row2")
row3 = Tulip.add_constraint!(pb, [x, y], [1.0,  2.0], -Inf, 7.0, "row3")

# Set the objective
# Nothing to do here as objective is already declared

# Set some parameters
Tulip.set_parameter(lp, "OutputLevel", 0)  # disable output
Tulip.set_parameter(lp, "Presolve_Level", 0)     # disable presolve

# Solve the problem
Tulip.optimize!(lp)

# Check termination status
st = Tulip.get_attribute(lp, Tulip.Status())
println("Termination status: $st")

# Query solution value
objval = Tulip.get_attribute(lp, Tulip.ObjectiveValue())
x_ = lp.solution.x[x]
y_ = lp.solution.x[y]

@printf "Z* = %.4f\n" objval
@printf "x* = %.4f\n" x_
@printf "y* = %.4f\n" y_