Buat model JuMP dengan multi-threading di Julia
Saya memiliki model pengoptimalan yang ternyata sangat sulit untuk dibuat. Model ini memiliki banyak kondisi if-else dan juga banyak loop. Jadi saya berpikir untuk menggunakan multi-threading untuk membangun objek model JuMP tunggal ini.
Versi satu loop kode yang sangat sederhana terlihat seperti ini:
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
Saya memiliki banyak loop dengan banyak kondisi if-else di dalamnya. Jadi saya menambahkan @Threads.threadssebelum yang pertama for g in sets["A"]bertujuan untuk mengurangi waktu pembuatan model.
Masalahnya adalah saya mendapatkan ERROR: LoadError: TaskFailedException: UndefRefError: access to undefined referencesaat mengganti nama kendala. Apakah ada masalah dengan pendekatan saya? Jika saya tidak meletakkan Threads.@threadstidak ada masalah sama sekali, itu hanya bekerja sangat lambat.
Beberapa informasi tentang 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
dan paket:
(@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
Terima kasih sebelumnya!
Stacktrace lengkap:
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
Jawaban
Anda memiliki dua opsi untuk memparalelkan model pengoptimalan JuMP
Jalankan versi multi-thread dari Solver (asalkan solver mendukungnya) - dalam hal ini paralelisme sepenuhnya ditangani oleh pustaka solver eksternal dan proses Julia Anda tetap single-threaded.
Jalankan beberapa proses pemecah utas tunggal dalam utas paralel yang dikendalikan oleh Julia. Dalam hal ini beberapa salinan model perlu dibuat secara terpisah yang dapat Anda coba kirim ke pemecah pada saat yang bersamaan.
# 1:
Solver mendukung parameter termasuk kontrol multi-threading (di sisi lain, mereka mungkin hanya menggunakan semua utas yang tersedia secara default). Berikut adalah contoh dengan 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:
Menjalankan banyak salinan solver secara paralel, Anda perlu memiliki salinan model terpisah. Dalam kode saya, mereka berbeda dengan kisaran untuk 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
Ini adalah dua pendekatan terpisah dan Anda tidak dapat mencampurnya. Jika Anda memparalelkan eksekusi, setiap utas perlu mendapatkan salinan model terpisah karena JuMP mengubah Modelobjek.