Licensing Issues with Gurobi.jl on Cluster

TL;DR: Is there a way to use Gurobi.jl and distributed algorithms from StochasticPrograms.jl in a way that works on a cluster, given that StochasticPrograms.jl requires JuMP.jl version 1.2.1?

Hi everyone,

I’m planning to use StochasticPrograms.jl to solve a large two-stage stochastic optimization problem. I’d like to use a decomposition method such as L-Shaped or Progressive Hedging (both are implemented in StochasticPrograms) to solve this on my institution’s cluster.

However, I run into what, on the surface, seems like a licensing issue regarding Gurobi.jl:

Set parameter WLSAccessID
Set parameter WLSSecret
Set parameter LicenseID
ERROR: LoadError: Gurobi Error 10024: Web license service only available for container environments
Stacktrace:
[1] _check_ret
@ ~/.julia/packages/Gurobi/VPomg/src/MOI_wrapper/MOI_wrapper.jl:393 [inlined]
[2] Gurobi.Env(; output_flag::Int64, memory_limit::Nothing, started::Bool)
@ Gurobi ~/.julia/packages/Gurobi/VPomg/src/MOI_wrapper/MOI_wrapper.jl:122
[3] Env
@ ~/.julia/packages/Gurobi/VPomg/src/MOI_wrapper/MOI_wrapper.jl:95 [inlined]
[4] Gurobi.Optimizer(env::Nothing; enable_interrupts::Bool)
@ Gurobi ~/.julia/packages/Gurobi/VPomg/src/MOI_wrapper/MOI_wrapper.jl:324
[5] Optimizer (repeats 2 times)
@ ~/.julia/packages/Gurobi/VPomg/src/MOI_wrapper/MOI_wrapper.jl:318 [inlined]
[6] #53
@ ~/ScenarioSelect/src/stoch_prog_examples/farmer_problem.jl:57 [inlined]
[7] bridge_type(optimizer::var"#53#54")
@ StochasticPrograms ~/.julia/packages/StochasticPrograms/pIScW/src/types/optimizer.jl:99
[8] StochasticPrograms.StochasticProgramOptimizer(optimizer_constructor::Function)
@ StochasticPrograms ~/.julia/packages/StochasticPrograms/pIScW/src/types/optimizer.jl:91
[9] StochasticProgram(stages::Tuple{StochasticPrograms.Stage{@NamedTuple{Crops::Vector{Symbol}, Cost::Dict{Symbol, Int64}, Budget::Int64}}, StochasticPrograms.Stage{@NamedTuple{Crops::Vector{Symbol}, Required::Dict{Symbol, Int64}, PurchasePrice::Dict{Symbol, Int64}, SellPrice::Dict{Symbol, Int64}}}}, scenarios::Tuple{Vector{Scenario{JuMP.Containers.DenseAxisArray{Float64, 1, Tuple{Vector{Symbol}}, Tuple{JuMP.Containers._AxisLookup{Dict{Symbol, Int64}}}}}}}, instantiation::UnspecifiedInstantiation, optimizer_constructor::Function)
@ StochasticPrograms ~/.julia/packages/StochasticPrograms/pIScW/src/types/stochasticprogram.jl:65
[10] StochasticProgram(first_stage_params::@NamedTuple{Crops::Vector{Symbol}, Cost::Dict{Symbol, Int64}, Budget::Int64}, second_stage_params::@NamedTuple{Crops::Vector{Symbol}, Required::Dict{Symbol, Int64}, PurchasePrice::Dict{Symbol, Int64}, SellPrice::Dict{Symbol, Int64}}, scenarios::Vector{Scenario{JuMP.Containers.DenseAxisArray{Float64, 1, Tuple{Vector{Symbol}}, Tuple{JuMP.Containers._AxisLookup{Dict{Symbol, Int64}}}}}}, instantiation::UnspecifiedInstantiation, optimizer_constructor::Function)
@ StochasticPrograms ~/.julia/packages/StochasticPrograms/pIScW/src/types/stochasticprogram.jl:143
[11] instantiate(sm::StochasticModel{2, Tuple{StageParameters{@NamedTuple{Crops::Vector{Symbol}, Cost::Dict{Symbol, Int64}, Budget::Int64}}, StageParameters{@NamedTuple{Crops::Vector{Symbol}, Required::Dict{Symbol, Int64}, PurchasePrice::Dict{Symbol, Int64}, SellPrice::Dict{Symbol, Int64}}}}}, scenarios::Vector{Scenario{JuMP.Containers.DenseAxisArray{Float64, 1, Tuple{Vector{Symbol}}, Tuple{JuMP.Containers._AxisLookup{Dict{Symbol, Int64}}}}}}; instantiation::UnspecifiedInstantiation, optimizer::var"#53#54", defer::Bool, direct_model::Bool, kw::@Kwargs{})
@ StochasticPrograms ~/.julia/packages/StochasticPrograms/pIScW/src/methods/api.jl:42
[12] top-level scope
@ ~/ScenarioSelect/src/stoch_prog_examples/farmer_problem.jl:55
in expression starting at /global/home/users/sunashsharma/ScenarioSelect/src/stoch_prog_examples/farmer_problem.jl:55
Running with 8 processes:

After some prodding, I think this is due to StochasticPrograms.jl requiring an old version of JuMP.jl (JuMP 1.2.1): This error occurs when I run the following SLURM script:

#!/bin/bash
#SBATCH --job-name=test
#SBATCH --partition=lr3
#SBATCH --qos=lr_normal
#SBATCH --account=pc_psidml
#SBATCH -N 1
#SBATCH --ntasks=20
#SBATCH -o %x-%j.out
#SBATCH --export=ALL
#SBATCH --time=00:05:00

module load gurobi/10.0.0
export GRB_LICENSE_FILE="/global/home/users/sunashsharma/gurobi.lic"

julia --project=/global/home/users/sunashsharma/ScenarioSelect farmer_problem.jl

farmer_problem.jl is an example that comes with StochasticPrograms.jl, which I have modified to run using Gurobi on a cluster:

using Distributed
import Pkg
project = Pkg.project();
# Get the number of processes from the SLURM environment
nprocs = parse(Int, get(ENV, "SLURM_NTASKS", "4"))
workers = addprocs(nprocs; exeflags = "--project=$(project.path)")
println("Running with $(nprocs) processes:")

@everywhere begin
    using StochasticPrograms
    import Gurobi
end

Crops = [:wheat, :corn, :beets]
@stochastic_model farmer_model begin
    @stage 1 begin
        @parameters begin
            Crops = Crops
            Cost = Dict(:wheat=>150, :corn=>230, :beets=>260)
            Budget = 500
        end
        @decision(farmer_model, x[c in Crops] >= 0)
        @objective(farmer_model, Min, sum(Cost[c]*x[c] for c in Crops))
        @constraint(farmer_model, sum(x[c] for c in Crops) <= Budget)
    end
    @stage 2 begin
        @parameters begin
            Crops = Crops
            Required = Dict(:wheat=>200, :corn=>240, :beets=>0)
            PurchasePrice = Dict(:wheat=>238, :corn=>210)
            SellPrice = Dict(:wheat=>170, :corn=>150, :beets=>36, :extra_beets=>10)
        end
        @uncertain ξ[c in Crops]
        @recourse(farmer_model, y[p in setdiff(Crops, [:beets])] >= 0)
        @recourse(farmer_model, w[s in Crops ∪ [:extra_beets]] >= 0)
        @objective(farmer_model, Min, sum(PurchasePrice[p] * y[p] for p in setdiff(Crops, [:beets]))
                    - sum(SellPrice[s] * w[s] for s in Crops ∪ [:extra_beets]))
        @constraint(farmer_model, minimum_requirement[p in setdiff(Crops, [:beets])],
            ξ[p] * x[p] + y[p] - w[p] >= Required[p])
        @constraint(farmer_model, minimum_requirement_beets,
            ξ[:beets] * x[:beets] - w[:beets] - w[:extra_beets] >= Required[:beets])
        @constraint(farmer_model, beets_quota, w[:beets] <= 6000)
    end
end
ξ₁ = @scenario ξ[c in Crops] = [3.0, 3.6, 24.0] probability = 1/2
ξ₂ = @scenario ξ[c in Crops] = [2.5, 3.0, 20.0] probability = 1/3
ξ₃ = @scenario ξ[c in Crops] = [2.0, 2.4, 16.0] probability = 1/3
farmer = instantiate(
    farmer_model, [ξ₁,ξ₂,ξ₃],
    optimizer = () -> LShaped.Optimizer(master_optimizer=() -> Gurobi.Optimizer()))
optimize!(farmer)
x = optimal_decision(farmer)
x = farmer[1,:x]
println("Wheat: $(value(x[:wheat]))")
println("Corn: $(value(x[:corn]))")
println("Beets: $(value(x[:beets]))")
println("Profit: $(objective_value(farmer))")
rmprocs(workers)

The main reason I think this is actually a StochasticPrograms.jl issue is that I can run a simple distributed example with the latest version of JuMP.jl just fine on my cluster:

using Distributed, ClusterManagers
import Pkg
project = Pkg.project();
# Get the number of processes from the SLURM environment
nprocs = parse(Int, get(ENV, "SLURM_NTASKS", "1"))
workers = addprocs(nprocs; exeflags = "--project=$(project.path)")
println("Running with $(nprocs) processes:")

@everywhere begin
    using JuMP
    import Gurobi
    import MathOptInterface
end
@everywhere begin
    function solve_model_with_right_hand_side(i)
        model = Model(Gurobi.Optimizer)
        set_attribute(model, MathOptInterface.NumberOfThreads(), 4)
        set_silent(model)
        @variable(model, x)
        @objective(model, Min, x)
        set_lower_bound(x, i)
        optimize!(model)
        @assert is_solved_and_feasible(model)
        return objective_value(model)
    end
end
solutions = pmap(solve_model_with_right_hand_side, 1:80)
println(solutions)

For this the SLURM script was:

#!/bin/bash
#SBATCH --job-name=test
#SBATCH --partition=lr3
#SBATCH --qos=lr_normal
#SBATCH --account=pc_psidml
#SBATCH -N 2
#SBATCH --ntasks=40
#SBATCH -o %x-%j.out
#SBATCH --export=ALL
#SBATCH --time=00:05:00

module load gurobi/10.0.0
export GRB_LICENSE_FILE="/global/home/users/sunashsharma/gurobi.lic"

julia --project=/global/home/users/sunashsharma/JuMPSandbox test_parallel.jl

StochasticPrograms.jl requires JuMP.jl 1.2.1, and holds back the version of Gurobi.jl to 0.11.3 as a result. I’m posting here though to see if I’m missing anything regarding Gurobi.jl or StochasticPrograms.jl usage that could be causing this error, or if there is a quick fix for the StochasticPrograms.jl dependency issue that anyone has found.

Thanks a lot for any help!

Sunash

1 Like

I think that this downgrade in Gurobi.jl does not allow you to use Gurobi v10 (as it wasn’t released at that time and the package will look elsewhere for an installation). Gurobi version >= v10 is required to use this license feature which was supported from Gurobi.jl v0.11.4 (unless I missed something).
Which I think explains why you are able to use it with your alternative approach.

2 Likes

Hi @Sunash_Sharma, welcome to the forum :smile:

Is there a way to use Gurobi.jl and distributed algorithms from StochasticPrograms.jl in a way that works on a cluster, given that StochasticPrograms.jl requires JuMP.jl version 1.2.1?

Your real question is “is there a way to use Gurobi 10 or later and StochasticPrograms.jl”, and the answer is “no”.

See StochasticPrograms requires an old version of MathOptInterface · Issue #49 · martinbiel/StochasticPrograms.jl · GitHub

1 Like

Hi @odow, great to be here! Thanks to you and @torressa for your responses. Looks like I’ll need to find alternatives to StochasticPrograms.jl, so I’ll take a look at SDDP.jl as you suggested in a different thread.

1 Like