mirror of
https://github.com/ANL-CEEESA/MIPLearn.jl.git
synced 2025-12-06 08:28:52 -06:00
Partial implementation of parallel_solve
This commit is contained in:
@@ -5,6 +5,7 @@ version = "0.2.0"
|
|||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d"
|
Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d"
|
||||||
|
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
|
||||||
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
|
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
|
||||||
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
|
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
|
||||||
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||||
|
|||||||
78
README.md
78
README.md
@@ -108,12 +108,12 @@ end
|
|||||||
fit!(solver, training_instances)
|
fit!(solver, training_instances)
|
||||||
|
|
||||||
# Save trained solver to disk
|
# Save trained solver to disk
|
||||||
save("solver.mls", solver)
|
save("solver.bin", solver)
|
||||||
|
|
||||||
# Application restarts...
|
# Application restarts...
|
||||||
|
|
||||||
# Load trained solver from disk
|
# Load trained solver from disk
|
||||||
solver = load("solver.mls")
|
solver = load_solver("solver.bin")
|
||||||
|
|
||||||
# Solve additional instances
|
# Solve additional instances
|
||||||
test_instances = [...]
|
test_instances = [...]
|
||||||
@@ -123,46 +123,43 @@ end
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1.5 Solving training instances in parallel
|
### 1.5 Solving instances from disk
|
||||||
```julia
|
|
||||||
using MIPLearn
|
|
||||||
using Cbc
|
|
||||||
|
|
||||||
# Solve training instances in parallel
|
In all examples above, we have assumed that instances are available as `JuMPInstance` objects, stored in memory. When problem instances are very large, or when there is a large number of problem instances, this approach may require an excessive amount of memory. To reduce memory requirements, MIPLearn.jl can also operate on instances that are stored on disk, through the `FileInstance` class, as the next example illustrates.
|
||||||
training_instances = [...]
|
|
||||||
solver = LearningSolver(Cbc.Optimizer)
|
|
||||||
parallel_solve!(solver, training_instances, n_jobs=4)
|
|
||||||
fit!(solver, training_instances)
|
|
||||||
|
|
||||||
# Solve test instances in parallel
|
|
||||||
test_instances = [...]
|
|
||||||
parallel_solve!(solver, test_instances)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.6 Solving instances from disk
|
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
using MIPLearn
|
using MIPLearn
|
||||||
using JuMP
|
using JuMP
|
||||||
using Cbc
|
using Cbc
|
||||||
|
|
||||||
# Create 600 problem instances and save them to files
|
# Create a large number of problem instances
|
||||||
for i in 1:600
|
for i in 1:600
|
||||||
m = Model()
|
|
||||||
@variable(m, x, Bin)
|
# Build JuMP model
|
||||||
@objective(m, Min, x)
|
model = Model()
|
||||||
@feature(x, [1.0])
|
@variable(...)
|
||||||
|
@objective(...)
|
||||||
|
@constraint(...)
|
||||||
|
|
||||||
|
# Add ML features and categories
|
||||||
|
@feature(...)
|
||||||
|
@category(...)
|
||||||
|
|
||||||
|
# Save instances to a file
|
||||||
instance = JuMPInstance(m)
|
instance = JuMPInstance(m)
|
||||||
save("instance-$i.bin", instance)
|
save("instance-$i.bin", instance)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Initialize instances and solver
|
# Initialize training and test instances
|
||||||
training_instances = [FileInstance("instance-$i.bin") for i in 1:500]
|
training_instances = [FileInstance("instance-$i.bin") for i in 1:500]
|
||||||
test_instances = [FileInstance("instance-$i.bin") for i in 501:600]
|
test_instances = [FileInstance("instance-$i.bin") for i in 501:600]
|
||||||
|
|
||||||
|
# Initialize solver
|
||||||
solver = LearningSolver(Cbc.Optimizer)
|
solver = LearningSolver(Cbc.Optimizer)
|
||||||
|
|
||||||
# Solve training instances
|
# Solve training instances. Files are modified in-place, and at most one
|
||||||
|
# file is loaded to memory at a time.
|
||||||
for instance in training_instances
|
for instance in training_instances
|
||||||
solve!(solver, instance)
|
solve!(solver, instance)
|
||||||
end
|
end
|
||||||
@@ -176,6 +173,39 @@ for instance in test_instances
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 1.6 Solving training instances in parallel
|
||||||
|
|
||||||
|
In many situations, instances can be solved in parallel to accelerate the training process. MIPLearn.jl provides the method `parallel_solve!(solver, instances)` to easily achieve this.
|
||||||
|
|
||||||
|
First, launch Julia in multi-process mode:
|
||||||
|
```
|
||||||
|
julia --procs 4
|
||||||
|
```
|
||||||
|
Then run the following script:
|
||||||
|
|
||||||
|
```julia
|
||||||
|
@everywhere using MIPLearn
|
||||||
|
@everywhere using Cbc
|
||||||
|
|
||||||
|
# Initialize training and test instances
|
||||||
|
training_instances = [...]
|
||||||
|
test_instances = [...]
|
||||||
|
|
||||||
|
# Initialize the solver
|
||||||
|
solver = LearningSolver(Cbc.Optimizer)
|
||||||
|
|
||||||
|
# Solve training instances in parallel. The number of instances solved
|
||||||
|
# simultaneously is the same as the `--procs` specified when running Julia.
|
||||||
|
parallel_solve!(solver, training_instances)
|
||||||
|
|
||||||
|
# Train machine learning models
|
||||||
|
fit!(solver, training_instances)
|
||||||
|
|
||||||
|
# Solve test instances in parallel
|
||||||
|
parallel_solve!(solver, test_instances)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## 2. Customization
|
## 2. Customization
|
||||||
|
|
||||||
### 2.1 Selecting solver components
|
### 2.1 Selecting solver components
|
||||||
|
|||||||
@@ -6,20 +6,16 @@ __precompile__(false)
|
|||||||
module MIPLearn
|
module MIPLearn
|
||||||
|
|
||||||
using PyCall
|
using PyCall
|
||||||
|
global miplearn = pyimport("miplearn")
|
||||||
export JuMPInstance
|
global traceback = pyimport("traceback")
|
||||||
export LearningSolver
|
|
||||||
export @feature
|
|
||||||
export @category
|
|
||||||
|
|
||||||
miplearn = pyimport("miplearn")
|
|
||||||
|
|
||||||
include("utils/log.jl")
|
include("utils/log.jl")
|
||||||
include("utils/exceptions.jl")
|
include("utils/exceptions.jl")
|
||||||
|
include("instance/abstract.jl")
|
||||||
|
include("instance/jump.jl")
|
||||||
|
include("instance/file.jl")
|
||||||
include("solvers/jump.jl")
|
include("solvers/jump.jl")
|
||||||
include("solvers/learning.jl")
|
include("solvers/learning.jl")
|
||||||
include("solvers/macros.jl")
|
include("solvers/macros.jl")
|
||||||
include("instance/jump.jl")
|
|
||||||
include("instance/file.jl")
|
|
||||||
|
|
||||||
end # module
|
end # module
|
||||||
|
|||||||
8
src/instance/abstract.jl
Normal file
8
src/instance/abstract.jl
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
||||||
|
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
|
||||||
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
|
abstract type Instance
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -55,12 +55,16 @@ end
|
|||||||
|
|
||||||
struct FileInstance <: Instance
|
struct FileInstance <: Instance
|
||||||
py::PyCall.PyObject
|
py::PyCall.PyObject
|
||||||
|
filename::AbstractString
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function FileInstance(filename)::FileInstance
|
function FileInstance(filename)::FileInstance
|
||||||
filename isa AbstractString || error("filename should be a string. Found $(typeof(filename)) instead.")
|
filename isa AbstractString || error("filename should be a string. Found $(typeof(filename)) instead.")
|
||||||
return FileInstance(PyFileInstance(filename))
|
return FileInstance(
|
||||||
|
PyFileInstance(filename),
|
||||||
|
filename,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,18 +2,21 @@
|
|||||||
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
|
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
|
||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
|
using Distributed
|
||||||
|
|
||||||
|
|
||||||
struct LearningSolver
|
struct LearningSolver
|
||||||
py::PyCall.PyObject
|
py::PyCall.PyObject
|
||||||
end
|
optimizer_factory
|
||||||
|
|
||||||
|
|
||||||
abstract type Instance
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function LearningSolver(optimizer_factory)::LearningSolver
|
function LearningSolver(optimizer_factory)::LearningSolver
|
||||||
py = miplearn.LearningSolver(solver=JuMPSolver(optimizer_factory))
|
py = miplearn.LearningSolver(solver=JuMPSolver(optimizer_factory))
|
||||||
return LearningSolver(py)
|
return LearningSolver(
|
||||||
|
py,
|
||||||
|
optimizer_factory,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -31,7 +34,19 @@ function fit!(solver::LearningSolver, instances::Vector{<:Instance})
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function parallel_solve!(solver::LearningSolver, instances::Vector{FileInstance})
|
||||||
|
filenames = [instance.filename for instance in instances]
|
||||||
|
optimizer_factory = solver.optimizer_factory
|
||||||
|
@sync @distributed for filename in filenames
|
||||||
|
s = LearningSolver(optimizer_factory)
|
||||||
|
solve!(s, FileInstance(filename))
|
||||||
|
nothing
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
export Instance,
|
export Instance,
|
||||||
LearningSolver,
|
LearningSolver,
|
||||||
solve!,
|
solve!,
|
||||||
fit!
|
fit!,
|
||||||
|
parallel_solve!
|
||||||
|
|||||||
@@ -62,3 +62,7 @@ macro category(obj, category)
|
|||||||
set_category!($(esc(obj)), $(esc(category)))
|
set_category!($(esc(obj)), $(esc(category)))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
export @feature,
|
||||||
|
@category
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
using PyCall
|
using PyCall
|
||||||
traceback = pyimport("traceback")
|
|
||||||
|
|
||||||
|
|
||||||
macro python_call(expr)
|
macro python_call(expr)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using MIPLearn
|
|||||||
using Gurobi
|
using Gurobi
|
||||||
|
|
||||||
@testset "LearningSolver" begin
|
@testset "LearningSolver" begin
|
||||||
@testset "model with annotations" begin
|
@testset "Model with annotations" begin
|
||||||
# Create standard JuMP model
|
# Create standard JuMP model
|
||||||
weights = [1.0, 2.0, 3.0]
|
weights = [1.0, 2.0, 3.0]
|
||||||
prices = [5.0, 6.0, 7.0]
|
prices = [5.0, 6.0, 7.0]
|
||||||
@@ -48,7 +48,7 @@ using Gurobi
|
|||||||
solve!(solver, instance)
|
solve!(solver, instance)
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "model without annotations" begin
|
@testset "Model without annotations" begin
|
||||||
model = Model()
|
model = Model()
|
||||||
@variable(model, x, Bin)
|
@variable(model, x, Bin)
|
||||||
@variable(model, y, Bin)
|
@variable(model, y, Bin)
|
||||||
@@ -57,5 +57,4 @@ using Gurobi
|
|||||||
instance = JuMPInstance(model)
|
instance = JuMPInstance(model)
|
||||||
stats = solve!(solver, instance)
|
stats = solve!(solver, instance)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user