Reorganize package; add macros

This commit is contained in:
2021-04-09 07:16:06 -05:00
parent 96f7243d4c
commit eb7f7034a9
24 changed files with 824 additions and 290 deletions

View File

@@ -1,41 +1,25 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020, 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.
__precompile__(false)
module MIPLearn
using PyCall
export JuMPInstance
export LearningSolver
export @feature
export @category
miplearn = pyimport("miplearn")
Instance = miplearn.Instance
BenchmarkRunner = miplearn.BenchmarkRunner
macro pycall(expr)
quote
err_msg = nothing
result = nothing
try
result = $(esc(expr))
catch err
args = err.val.args[1]
if (err isa PyCall.PyError) && (args isa String) && startswith(args, "Julia")
err_msg = replace(args, r"Stacktrace.*" => "")
else
rethrow(err)
end
end
if err_msg != nothing
error(err_msg)
end
result
end
end
include("log.jl")
include("jump_solver.jl")
include("learning_solver.jl")
include("instance.jl")
export Instance, BenchmarkRunner
include("utils/log.jl")
include("utils/pycall.jl")
include("modeling/jump_instance.jl")
include("modeling/jump_solver.jl")
include("modeling/learning_solver.jl")
include("modeling/macros.jl")
include("problems/knapsack.jl")
end # module

View File

@@ -1,63 +0,0 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
using JSON2
import Base: dump
to_model(instance) =
error("not implemented: to_model")
get_instance_features(instance) = [0.0]
get_variable_features(instance, varname) = [0.0]
get_variable_category(instance, varname) = "default"
find_violated_lazy_constraints(instance, model) = []
build_lazy_constraint(instance, model, v) = nothing
macro Instance(klass)
quote
@pydef mutable struct Wrapper <: Instance
function __init__(self, args...; kwargs...)
self.data = $(esc(klass))(args...; kwargs...)
self.training_data = []
self.features = miplearn.Features()
end
to_model(self) =
$(esc(:to_model))(self.data)
get_instance_features(self) =
$(esc(:get_instance_features))(self.data)
get_variable_features(self, varname) =
$(esc(:get_variable_features))(self.data, varname)
get_variable_category(self, varname) =
$(esc(:get_variable_category))(self.data, varname)
find_violated_lazy_constraints(self, model) =
find_violated_lazy_constraints(self.data, model)
build_lazy_constraint(self, model, v) =
build_lazy_constraint(self.data, model, v)
load(self) = nothing
flush(self) = nothing
end
end
end
export get_instance_features,
get_variable_features,
get_variable_category,
find_violated_lazy_constraints,
build_lazy_constraint,
to_model,
dump,
load!,
@Instance

View File

@@ -1,27 +0,0 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
struct LearningSolver
py::PyCall.PyObject
end
function LearningSolver(;
optimizer,
kwargs...,
)::LearningSolver
py = miplearn.LearningSolver(
;
kwargs...,
solver=JuMPSolver(optimizer=optimizer),
)
return LearningSolver(py)
end
solve!(solver::LearningSolver, instance; kwargs...) =
solver.py.solve(instance; kwargs...)
fit!(solver::LearningSolver, instances; kwargs...) =
solver.py.fit(instances; kwargs...)
export LearningSolver

View File

@@ -0,0 +1,52 @@
# 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.
@pydef mutable struct JuMPInstance <: miplearn.Instance
function __init__(self, model)
init_miplearn_ext(model)
features = model.ext[:miplearn][:features]
self.model = model
# Copy training data
training_data = []
for sample in self.model.ext[:miplearn][:training_samples]
pysample = miplearn.TrainingSample()
pysample.__dict__ = sample
push!(training_data, pysample)
end
self.training_data = training_data
# Copy features to data classes
self.features = miplearn.Features(
instance=miplearn.InstanceFeatures(
user_features=PyCall.array2py(
features[:instance][:user_features],
),
lazy_constraint_count=0,
),
variables=Dict(
varname => miplearn.VariableFeatures(
category=vfeatures[:category],
user_features=PyCall.array2py(
vfeatures[:user_features],
),
)
for (varname, vfeatures) in features[:variables]
),
constraints=Dict(
cname => miplearn.ConstraintFeatures(
category=cfeat[:category],
user_features=PyCall.array2py(
cfeat[:user_features],
),
)
for (cname, cfeat) in features[:constraints]
),
)
end
function to_model(self)
return self.model
end
end

View File

@@ -1,5 +1,5 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020, 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.
using JuMP

View File

@@ -0,0 +1,32 @@
# 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.
struct LearningSolver
py::PyCall.PyObject
end
function LearningSolver(
;
optimizer,
)::LearningSolver
py = miplearn.LearningSolver(solver=JuMPSolver(optimizer=optimizer))
return LearningSolver(py)
end
function solve!(solver::LearningSolver, model::Model)
instance = JuMPInstance(model)
mip_stats = solver.py.solve(instance)
push!(
model.ext[:miplearn][:training_samples],
instance.training_data[1].__dict__,
)
return mip_stats
end
function fit!(solver::LearningSolver, models::Array{Model})
instances = [JuMPInstance(m) for m in models]
solver.py.fit(instances)
end
export LearningSolver

84
src/modeling/macros.jl Normal file
View File

@@ -0,0 +1,84 @@
# 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.
function init_miplearn_ext(model)::Dict
if :miplearn keys(model.ext)
model.ext[:miplearn] = Dict{Symbol,Any}(
:features => Dict(
:variables => Dict{String,Dict}(),
:constraints => Dict{String,Dict}(),
:instance => Dict{Symbol,Any}(),
),
:training_samples => [],
)
end
return model.ext[:miplearn]
end
function init_miplearn_ext(v::VariableRef)::Dict
ext = init_miplearn_ext(v.model)
if name(v) keys(ext[:features][:variables])
ext[:features][:variables][name(v)] = Dict{Symbol,Any}()
end
return ext
end
function init_miplearn_ext(c::ConstraintRef)::Dict
ext = init_miplearn_ext(c.model)
if name(c) keys(ext[:features][:constraints])
ext[:features][:constraints][name(c)] = Dict{Symbol,Any}()
end
return ext
end
function set_features!(m::Model, f::Array{Float64})::Nothing
ext = init_miplearn_ext(m)
ext[:features][:instance][:user_features] = f
return
end
function set_features!(v::VariableRef, f::Array{Float64})::Nothing
ext = init_miplearn_ext(v)
ext[:features][:variables][name(v)][:user_features] = f
return
end
function set_category!(v::VariableRef, category::String)::Nothing
ext = init_miplearn_ext(v)
ext[:features][:variables][name(v)][:category] = category
return
end
function set_features!(c::ConstraintRef, f::Array{Float64})::Nothing
ext = init_miplearn_ext(c)
ext[:features][:constraints][name(c)][:user_features] = f
return
end
function set_category!(c::ConstraintRef, category::String)::Nothing
ext = init_miplearn_ext(c)
ext[:features][:constraints][name(c)][:category] = category
return
end
macro feature(obj, features)
quote
set_features!($(esc(obj)), $(esc(features)))
end
end
macro category(obj, category)
quote
set_category!($(esc(obj)), $(esc(category)))
end
end

25
src/problems/knapsack.jl Normal file
View File

@@ -0,0 +1,25 @@
# 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.
using JuMP
function knapsack_model(
weights::Array{Float64, 1},
prices::Array{Float64, 1},
capacity::Float64,
)
model = Model()
n = length(weights)
@variable(model, x[0:(n-1)], Bin)
@objective(model, Max, sum(x[i] * prices[i+1] for i in 0:(n-1)))
@constraint(
model,
eq_capacity,
sum(
x[i] * weights[i+1]
for i in 0:(n-1)
) <= capacity,
)
return model
end

View File

@@ -1,5 +1,5 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020, 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.
import Logging: min_enabled_level, shouldlog, handle_message

24
src/utils/pycall.jl Normal file
View File

@@ -0,0 +1,24 @@
# 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.
macro pycall(expr)
quote
err_msg = nothing
result = nothing
try
result = $(esc(expr))
catch err
args = err.val.args[1]
if (err isa PyCall.PyError) && (args isa String) && startswith(args, "Julia")
err_msg = replace(args, r"Stacktrace.*" => "")
else
rethrow(err)
end
end
if err_msg != nothing
error(err_msg)
end
result
end
end