Erstellen Sie ein JuMP-Modell mit Multithreading in Julia
Ich habe ein Optimierungsmodell, das sich als sehr schwierig zu erstellen herausstellt. Dieses Modell hat viele Wenn-Sonst-Bedingungen und auch viele Schleifen. Daher habe ich mir überlegt, Multithreading zum Erstellen dieses einzelnen JuMP-Modellobjekts zu verwenden.
Eine sehr vereinfachte Version einer Codeschleife sieht folgendermaßen aus:
Threads.@threads for g in sets["A"]
Array_1 = [gg for gg in [sets["B"];sets["A"]] if data2[gg] == g]
Array_2 = [gg for gg in sets["B"] if data[gg] == g]
for t in STAGES
Array_3 = [gg for gg in [sets["B"];sets["A"]] if data2[gg] == g && (gg, t) in sets["C"] ]
for b in BLOCKS
name = @constraint( model, ((g, t, b) in sets["C"] ? X1[(g,t,b)] : 0)
- sum(X1[(gg,t,b)] for gg in Array_3 )
+ X2[(g,t,b)] - sum(X2[(gg,t,b)] for gg in Array_1)
- sum(data3[gg] for gg in Array_2) == data4[(g, t, b)])
end
end
a=string("con_",g,"_",t,"_",b)
JuMP.set_name(name,a)
end
Ich habe viele dieser Schleifen mit vielen Wenn-Sonst-Bedingungen im Inneren. Deshalb habe ich @Threads.threadsvor dem ersten for g in sets["A"]Ziel hinzugefügt , die Zeit für die Erstellung des Modells zu verkürzen.
Das Problem ist, dass ich ERROR: LoadError: TaskFailedException: UndefRefError: access to undefined referencebeim Umbenennen der Einschränkung eine erhalte. Gibt es ein Problem mit meinem Ansatz? Wenn ich das nicht sage, Threads.@threadsgibt es überhaupt kein Problem, es funktioniert nur sehr langsam.
Einige Informationen zur Infrastruktur:
julia> versioninfo()
Julia Version 1.4.1
Commit 381693d3df* (2020-04-14 17:20 UTC)
Platform Info:
OS: Linux (x86_64-pc-linux-gnu)
CPU: Intel(R) Xeon(R) CPU E5-2660 v3 @ 2.60GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-8.0.1 (ORCJIT, haswell)
Environment:
JULIA_NUM_THREADS = 40
und Pakete:
(@v1.4) pkg> status
Status `~/.julia/environments/v1.4/Project.toml`
[c7e460c6] ArgParse v1.1.0
[a076750e] CPLEX v0.6.6
[336ed68f] CSV v0.7.7
[e2554f3b] Clp v0.8.1
[a93c6f00] DataFrames v0.21.7
[5789e2e9] FileIO v1.4.3
[2e9cd046] Gurobi v0.8.1
[033835bb] JLD2 v0.2.1
[4076af6c] JuMP v0.21.5
[438e738f] PyCall v1.91.4
[2913bbd2] StatsBase v0.33.1
[bd369af6] Tables v1.0.5
[6dd1b50a] Tulip v0.6.2
[1a1011a3] SharedArrays
[10745b16] Statistics
Danke im Voraus!
Vollständige Stapelverfolgung:
ERROR: LoadError: TaskFailedException:
UndefRefError: access to undefined reference
Stacktrace:
[1] getindex at ./array.jl:788 [inlined]
[2] ht_keyindex2!(::Dict{MathOptInterface.ConstraintIndex,String}, ::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64},MathOptInterface.EqualTo{Float64}}) at ./dict.jl:326
[3] setindex!(::Dict{MathOptInterface.ConstraintIndex,String}, ::String, ::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64},MathOptInterface.EqualTo{Float64}}) at ./dict.jl:381
[4] set at /home/user/.julia/packages/MathOptInterface/k7UUH/src/Utilities/model.jl:349 [inlined]
[5] set at /home/user/.julia/packages/MathOptInterface/k7UUH/src/Utilities/universalfallback.jl:354 [inlined]
[6] set(::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.AbstractOptimizer,MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, ::MathOptInterface.ConstraintName, ::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64},MathOptInterface.EqualTo{Float64}}, ::String) at /home/user/.julia/packages/MathOptInterface/k7UUH/src/Utilities/cachingoptimizer.jl:646
[7] set(::Model, ::MathOptInterface.ConstraintName, ::ConstraintRef{Model,MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64},MathOptInterface.EqualTo{Float64}},ScalarShape}, ::String) at /home/user/.julia/packages/JuMP/qhoVb/src/JuMP.jl:903
[8] set_name(::ConstraintRef{Model,MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64},MathOptInterface.EqualTo{Float64}},ScalarShape}, ::String) at /home/user/.julia/packages/JuMP/qhoVb/src/constraints.jl:68
[9] macro expansion at /home/user/code/model_formulation.jl:117 [inlined]
[10] (::var"#20#threadsfor_fun#255"{Dict{Any,Any},Dict{Any,Any},JuMP.Containers.DenseAxisArray{VariableRef,1,Tuple{Array{Tuple{String,Int64,Int64},1}},Tuple{Dict{Tuple{String,Int64,Int64},Int64}}},JuMP.Containers.DenseAxisArray{VariableRef,1,Tuple{Array{Tuple{String,Int64,Int64},1}},Tuple{Dict{Tuple{String,Int64,Int64},Int64}}},Array{String,1}})(::Bool) at ./threadingconstructs.jl:61
[11] (::var"#20#threadsfor_fun#255"{Dict{Any,Any},Dict{Any,Any},JuMP.Containers.DenseAxisArray{VariableRef,1,Tuple{Array{Tuple{String,Int64,Int64},1}},Tuple{Dict{Tuple{String,Int64,Int64},Int64}}},JuMP.Containers.DenseAxisArray{VariableRef,1,Tuple{Array{Tuple{String,Int64,Int64},1}},Tuple{Dict{Tuple{String,Int64,Int64},Int64}}},Array{String,1}})() at ./threadingconstructs.jl:28
Stacktrace:
[1] wait(::Task) at ./task.jl:267
[2] macro expansion at ./threadingconstructs.jl:69 [inlined]
[3] model_formulation(::Dict{Any,Any}, ::Dict{Any,Any}, ::Dict{Any,Any}, ::Dict{String,Bool}, ::String) at /home/user/code/model_formulation.jl:102
[4] functionA(::Dict{Any,Any}, ::Dict{Any,Any}, ::Dict{Any,Any}, ::String, ::Dict{String,Bool}) at /home/user/code/functionA.jl:178
[5] top-level scope at /home/user/code/main.jl:81
[6] include(::Module, ::String) at ./Base.jl:377
[7] exec_options(::Base.JLOptions) at ./client.jl:288
[8] _start() at ./client.jl:484
in expression starting at /home/user/code/main.jl:81
Antworten
Sie haben zwei Möglichkeiten, ein JuMP-Optimierungsmodell zu parallelisieren
Führen Sie eine Multithread-Version des Solvers aus (vorausgesetzt, der Solver unterstützt ihn). In diesem Fall wird die Parallelität vollständig von der externen Solver-Bibliothek verarbeitet und Ihr Julia-Prozess bleibt Single-Threaded.
Führen Sie mehrere Single-Threaded-Solver-Prozesse in parallelen Threads aus, die von Julia gesteuert werden. In diesem Fall müssen mehrere Kopien des Modells separat erstellt werden, die Sie gleichzeitig an den Solver senden können.
# 1:
Solver unterstützen Parameter, einschließlich Multithreading-Steuerung (andererseits verwenden sie möglicherweise standardmäßig einfach alle verfügbaren Threads). Hier ist ein Beispiel mit Gurobi:
using JuMP, Gurobi
m = Model(optimizer_with_attributes(Gurobi.Optimizer, "Threads" => 2))
@variable(m, 0 <= x <= 2)
@variable(m, 0 <= y <= 30)
@objective(m, Max, 5x + 3 * y)
@constraint(m, con, 1x + 5y <= 3)
optimize!(m) # the model will be optimized using 2 threads
# 2:
Wenn Sie viele Solver-Kopien parallel ausführen, benötigen Sie separate Modellkopien. In meinem Code unterscheiden sie sich durch den Bereich für xParameter:
Threads.@threads for z in 1:4
m = Model(optimizer_with_attributes(Gurobi.Optimizer, "Threads" => 1))
@variable(m, 0 <= x <= z)
@variable(m, 0 <= y <= 30)
@objective(m, Max, 5x + 3 * y)
@constraint(m, con, 1x + 5y <= 3)
optimize!(m)
#todo collect results
end
Dies sind zwei getrennte Ansätze, die Sie nicht mischen können. Wenn Sie die Ausführung parallelisieren, muss jeder Thread eine separate Modellkopie erhalten, da JuMP das ModelObjekt mutiert .