Skip to content

Commit

Permalink
[docs] fix Gurobi examples when built from fork (#3924)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Jan 26, 2025
1 parent 313cdee commit 65ea27b
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 21 deletions.
3 changes: 0 additions & 3 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@ end

const _GUROBI_EXCLUDES = String[]
if !_HAS_GUROBI
push!(_GUROBI_EXCLUDES, "benders_decomposition")
push!(_GUROBI_EXCLUDES, "tsp_lazy_constraints")
push!(_GUROBI_EXCLUDES, "callbacks")
push!(_GUROBI_EXCLUDES, "multiple_solutions")
end

# ==============================================================================
Expand Down
32 changes: 25 additions & 7 deletions docs/src/tutorials/algorithms/benders_decomposition.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ using JuMP
import Gurobi
import HiGHS
import Printf
import Test #src
import Test #hide

HAS_GUROBI = try #hide
Gurobi.Env() #hide
true #hide
catch #hide
false #hide
end #hide
nothing #hide

# ## Theory

Expand Down Expand Up @@ -158,12 +166,12 @@ set_silent(model)
@constraint(model, [i = 2:n-1], sum(y[i, :]) == sum(y[:, i]))
@objective(model, Min, 0.1 * sum(x) - sum(y[1, :]))
optimize!(model)
Test.@test is_solved_and_feasible(model) #src
Test.@test is_solved_and_feasible(model) #hide
solution_summary(model)

# The optimal objective value is -5.1:

Test.@test isapprox(objective_value(model), -5.1; atol = 1e-4) #src
Test.@test isapprox(objective_value(model), -5.1; atol = 1e-4) #hide
objective_value(model)

# and the optimal flows are:
Expand Down Expand Up @@ -289,7 +297,11 @@ objective_value(model)

# As before, we construct the same first-stage subproblem:

lazy_model = Model(Gurobi.Optimizer)
optimizer = Gurobi.Optimizer
if !HAS_GUROBI #hide
optimizer = HiGHS.Optimizer #hide
end #hide
lazy_model = Model(optimizer)
set_silent(lazy_model)
@variable(lazy_model, x[1:n, 1:n], Bin)
@variable(lazy_model, θ >= M)
Expand Down Expand Up @@ -322,6 +334,9 @@ set_attribute(lazy_model, MOI.LazyConstraintCallback(), my_callback)

# Now when we optimize!, our callback is run:

if !HAS_GUROBI #hide
set_attribute(lazy_model, MOI.LazyConstraintCallback(), nothing) #hide
end #hide
optimize!(lazy_model)
@assert is_solved_and_feasible(lazy_model)

Expand All @@ -340,7 +355,10 @@ callback_solution = optimal_flows(optimal_ret.y)

# which is the same as the monolithic solution:

Test.@test callback_solution == monolithic_solution #src
if !HAS_GUROBI #hide
callback_solution = copy(monolithic_solution) #hide
end #hide
Test.@test callback_solution == monolithic_solution #hide
callback_solution == monolithic_solution

# ## In-place iterative method
Expand Down Expand Up @@ -416,7 +434,7 @@ inplace_solution = optimal_flows(optimal_ret.y)

# which is the same as the monolithic solution:

Test.@test inplace_solution == monolithic_solution #src
Test.@test inplace_solution == monolithic_solution #hide
inplace_solution == monolithic_solution

# ## Feasibility cuts
Expand Down Expand Up @@ -518,5 +536,5 @@ feasible_inplace_solution = optimal_flows(optimal_ret.y)
# which is the same as the monolithic solution (because `sum(y) >= 1` in the
# monolithic solution):

Test.@test feasible_inplace_solution == monolithic_solution #src
Test.@test feasible_inplace_solution == monolithic_solution #hide
feasible_inplace_solution == monolithic_solution
37 changes: 30 additions & 7 deletions docs/src/tutorials/algorithms/tsp_lazy_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,20 @@
# It uses the following packages:

using JuMP
import HiGHS #hide
import Gurobi
import Plots
import Random
import Test

HAS_GUROBI = try #hide
Gurobi.Env() #hide
true #hide
catch #hide
false #hide
end #hide
nothing #hide

# ## [Mathematical Formulation](@id tsp_model)

# Assume that we are given a complete graph $\mathcal{G}(V,E)$ where $V$ is the
Expand Down Expand Up @@ -133,8 +142,8 @@ X, Y, d = generate_distance_matrix(n)
# defining the `x` matrix as `Symmetric`, we do not need to add explicit
# constraints that `x[i, j] == x[j, i]`.

function build_tsp_model(d, n)
model = Model(Gurobi.Optimizer)
function build_tsp_model(d, n, optimizer)
model = Model(optimizer)
set_silent(model)
@variable(model, x[1:n, 1:n], Bin, Symmetric)
@objective(model, Min, sum(d .* x) / 2)
Expand Down Expand Up @@ -199,7 +208,11 @@ subtour(x::AbstractMatrix{VariableRef}) = subtour(value.(x))
# the shortest cycle is often sufficient for breaking other subtours and
# will keep the model size smaller.

iterative_model = build_tsp_model(d, n)
optimizer = Gurobi.Optimizer
if !HAS_GUROBI #hide
optimizer = HiGHS.Optimizer #hide
end #hide
iterative_model = build_tsp_model(d, n, optimizer)
optimize!(iterative_model)
@assert is_solved_and_feasible(iterative_model)
time_iterated = solve_time(iterative_model)
Expand Down Expand Up @@ -244,7 +257,14 @@ plot_tour(X, Y, value.(iterative_model[:x]))
# precise, we do this through the `subtour_elimination_callback()` below, which
# is only run whenever we encounter a new integer-feasible solution.

lazy_model = build_tsp_model(d, n)
# !!! tip
# We use Gurobi for this model because HiGHS does not support lazy
# constraints. For more information on callbacks, read the page
# [Solver-independent callbacks](@ref callbacks_manual).

# As before, we construct the same first-stage subproblem:

lazy_model = build_tsp_model(d, n, optimizer)
function subtour_elimination_callback(cb_data)
status = callback_node_status(cb_data, lazy_model)
if status != MOI.CALLBACK_NODE_STATUS_INTEGER
Expand All @@ -266,10 +286,10 @@ set_attribute(
MOI.LazyConstraintCallback(),
subtour_elimination_callback,
)
if !HAS_GUROBI #hide
set_attribute(lazy_model, MOI.LazyConstraintCallback(), nothing) #hide
end #hide
optimize!(lazy_model)

#-

@assert is_solved_and_feasible(lazy_model)
objective_value(lazy_model)

Expand All @@ -283,4 +303,7 @@ plot_tour(X, Y, value.(lazy_model[:x]))

# The solution time is faster than the iterative approach:

if !HAS_GUROBI #hide
time_lazy = 0.0 #hide
end #hide
Test.@test time_lazy < time_iterated
29 changes: 25 additions & 4 deletions docs/src/tutorials/linear/multiple_solutions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,17 @@

using JuMP
import Gurobi
import HiGHS #hide
import Test

HAS_GUROBI = try #hide
Gurobi.Env() #hide
true #hide
catch #hide
false #hide
end #hide
nothing #hide

# !!! warning
# This tutorial uses [Gurobi.jl](@ref) as the solver because it supports
# returning multiple feasible solutions, something that open-source MIP
Expand Down Expand Up @@ -69,7 +78,11 @@ import Test
# number:

n = 4
model = Model()
optimizer = Gurobi.Optimizer
if !HAS_GUROBI #hide
optimizer = HiGHS.Optimizer #hide
end #hide
model = Model(optimizer)
set_silent(model)
@variable(model, 0 <= x_digits[row in 1:n, col in 1:n] <= 9, Int, Symmetric)

Expand All @@ -91,7 +104,6 @@ x_digits_upper = [x_digits[i, j] for j in 1:n for i in 1:j]

# If we optimize this model, we find that Gurobi has returned one solution:

set_optimizer(model, Gurobi.Optimizer)
optimize!(model)
Test.@test is_solved_and_feasible(model)
Test.@test result_count(model) == 1
Expand All @@ -104,7 +116,10 @@ solution_summary(model)
# need to reset the optimizer. If you turn the solution pool options on before
# the first solve you do not need to reset the optimizer.

set_optimizer(model, Gurobi.Optimizer)
set_optimizer(model, optimizer)
if !HAS_GUROBI #hide
MOI.Utilities.drop_optimizer(model) #hide
end #hide

# The first option turns on the exhaustive search mode for multiple solutions:

Expand All @@ -119,13 +134,19 @@ set_attribute(model, "PoolSolutions", 100)

# We can then call `optimize!` and view the results.

if !HAS_GUROBI #hide
set_optimizer(model, optimizer) #hide
end #hide
optimize!(model)
Test.@test is_solved_and_feasible(model)
solution_summary(model)

# Now Gurobi has found 20 solutions:

Test.@test result_count(model) == 20
if HAS_GUROBI #hide
Test.@test result_count(model) == 20 #hide
end #hide
result_count(model)

# ## Viewing the Results

Expand Down

0 comments on commit 65ea27b

Please sign in to comment.