mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Make Julia's solution format consistent with Python's
This commit is contained in:
@@ -15,36 +15,49 @@ Instance = miplearn.Instance
|
|||||||
LearningSolver = miplearn.LearningSolver
|
LearningSolver = miplearn.LearningSolver
|
||||||
InternalSolver = miplearn.solvers.internal.InternalSolver
|
InternalSolver = miplearn.solvers.internal.InternalSolver
|
||||||
|
|
||||||
|
function varname_split(varname::String)
|
||||||
|
m = match(r"([^[]*)\[(.*)\]", varname)
|
||||||
|
return m.captures[1], m.captures[2]
|
||||||
|
end
|
||||||
|
|
||||||
@pydef mutable struct JuMPSolver <: InternalSolver
|
@pydef mutable struct JuMPSolver <: InternalSolver
|
||||||
function __init__(self; optimizer=CPLEX.Optimizer)
|
function __init__(self; optimizer=CPLEX.Optimizer)
|
||||||
self.optimizer = optimizer
|
self.optimizer = optimizer
|
||||||
end
|
end
|
||||||
|
|
||||||
function add_constraint(self, constraint)
|
function add_constraint(self, constraint)
|
||||||
@error "Not implemented"
|
@error "JuMPSolver: add_constraint not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_warm_start(self, solution)
|
function set_warm_start(self, solution)
|
||||||
for (varname, value) in solution
|
for (basename, subsolution) in solution
|
||||||
var = JuMP.variable_by_name(self.model, varname)
|
for (idx, value) in subsolution
|
||||||
JuMP.set_start_value(var, value)
|
var = self.basename_idx_to_var[basename, idx]
|
||||||
|
JuMP.set_start_value(var, value)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function clear_warm_start(self)
|
function clear_warm_start(self)
|
||||||
@error "Not implemented"
|
@error "JuMPSolver: clear_warm_start not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
function fix(self, solution)
|
function fix(self, solution)
|
||||||
for (varname, value) in solution
|
for (basename, subsolution) in solution
|
||||||
var = JuMP.variable_by_name(self.model, varname)
|
for (idx, value) in subsolution
|
||||||
JuMP.fix(var, value, force=true)
|
var = self.basename_idx_to_var[basename, idx]
|
||||||
|
JuMP.fix(var, value, force=true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_instance(self, instance, model)
|
function set_instance(self, instance, model)
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self.model = model
|
self.model = model
|
||||||
|
self.var_to_basename_idx = Dict(var => varname_split(JuMP.name(var))
|
||||||
|
for var in JuMP.all_variables(self.model))
|
||||||
|
self.basename_idx_to_var = Dict(varname_split(JuMP.name(var)) => var
|
||||||
|
for var in JuMP.all_variables(self.model))
|
||||||
self.bin_vars = [var
|
self.bin_vars = [var
|
||||||
for var in JuMP.all_variables(self.model)
|
for var in JuMP.all_variables(self.model)
|
||||||
if JuMP.is_binary(var)]
|
if JuMP.is_binary(var)]
|
||||||
@@ -102,20 +115,27 @@ InternalSolver = miplearn.solvers.internal.InternalSolver
|
|||||||
end
|
end
|
||||||
|
|
||||||
function _update_solution(self)
|
function _update_solution(self)
|
||||||
self.solution = Dict(JuMP.name(var) => JuMP.value(var)
|
solution = Dict()
|
||||||
for var in JuMP.all_variables(self.model))
|
for var in JuMP.all_variables(self.model)
|
||||||
|
basename, idx = self.var_to_basename_idx[var]
|
||||||
|
if !haskey(solution, basename)
|
||||||
|
solution[basename] = Dict()
|
||||||
|
end
|
||||||
|
solution[basename][idx] = JuMP.value(var)
|
||||||
|
end
|
||||||
|
self.solution = solution
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_gap_tolerance(self, gap_tolerance)
|
function set_gap_tolerance(self, gap_tolerance)
|
||||||
@error "Not implemented"
|
@error "JuMPSolver: set_gap_tolerance not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_node_limit(self)
|
function set_node_limit(self)
|
||||||
@error "Not implemented"
|
@error "JuMPSolver: set_node_limit not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_threads(self, threads)
|
function set_threads(self, threads)
|
||||||
@error "Not implemented"
|
@error "JuMPSolver: set_threads not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_time_limit(self, time_limit)
|
function set_time_limit(self, time_limit)
|
||||||
@@ -148,6 +168,6 @@ end
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
export JuMPSolver, KnapsackInstance
|
export LearningSolver, JuMPSolver, KnapsackInstance
|
||||||
|
|
||||||
end # module
|
end # module
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ using MIPLearn
|
|||||||
using CPLEX
|
using CPLEX
|
||||||
using Gurobi
|
using Gurobi
|
||||||
|
|
||||||
|
@testset "varname_split" begin
|
||||||
|
@test MIPLearn.varname_split("x[1]") == ("x", "1")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
@testset "JuMPSolver" begin
|
@testset "JuMPSolver" begin
|
||||||
for optimizer in [CPLEX.Optimizer, Gurobi.Optimizer]
|
for optimizer in [CPLEX.Optimizer, Gurobi.Optimizer]
|
||||||
instance = KnapsackInstance([23., 26., 20., 18.],
|
instance = KnapsackInstance([23., 26., 20., 18.],
|
||||||
@@ -17,12 +22,12 @@ using Gurobi
|
|||||||
solver = JuMPSolver(optimizer=optimizer)
|
solver = JuMPSolver(optimizer=optimizer)
|
||||||
solver.set_instance(instance, model)
|
solver.set_instance(instance, model)
|
||||||
solver.set_time_limit(30)
|
solver.set_time_limit(30)
|
||||||
solver.set_warm_start(Dict(
|
solver.set_warm_start(Dict("x" => Dict(
|
||||||
"x[1]" => 1.0,
|
"1" => 1.0,
|
||||||
"x[2]" => 0.0,
|
"2" => 0.0,
|
||||||
"x[3]" => 0.0,
|
"3" => 0.0,
|
||||||
"x[4]" => 1.0,
|
"4" => 1.0,
|
||||||
))
|
)))
|
||||||
stats = solver.solve()
|
stats = solver.solve()
|
||||||
|
|
||||||
@test stats["Lower bound"] == 1183.0
|
@test stats["Lower bound"] == 1183.0
|
||||||
@@ -31,26 +36,26 @@ using Gurobi
|
|||||||
@test stats["Wallclock time"] > 0
|
@test stats["Wallclock time"] > 0
|
||||||
|
|
||||||
solution = solver.get_solution()
|
solution = solver.get_solution()
|
||||||
@test solution["x[1]"] == 1.0
|
@test solution["x"]["1"] == 1.0
|
||||||
@test solution["x[2]"] == 0.0
|
@test solution["x"]["2"] == 0.0
|
||||||
@test solution["x[3]"] == 1.0
|
@test solution["x"]["3"] == 1.0
|
||||||
@test solution["x[4]"] == 1.0
|
@test solution["x"]["4"] == 1.0
|
||||||
|
|
||||||
stats = solver.solve_lp()
|
stats = solver.solve_lp()
|
||||||
@test round(stats["Optimal value"], digits=3) == 1287.923
|
@test round(stats["Optimal value"], digits=3) == 1287.923
|
||||||
|
|
||||||
solution = solver.get_solution()
|
solution = solver.get_solution()
|
||||||
@test round(solution["x[1]"], digits=3) == 1.000
|
@test round(solution["x"]["1"], digits=3) == 1.000
|
||||||
@test round(solution["x[2]"], digits=3) == 0.923
|
@test round(solution["x"]["2"], digits=3) == 0.923
|
||||||
@test round(solution["x[3]"], digits=3) == 1.000
|
@test round(solution["x"]["3"], digits=3) == 1.000
|
||||||
@test round(solution["x[4]"], digits=3) == 0.000
|
@test round(solution["x"]["4"], digits=3) == 0.000
|
||||||
|
|
||||||
solver.fix(Dict(
|
solver.fix(Dict("x" => Dict(
|
||||||
"x[1]" => 1.0,
|
"1" => 1.0,
|
||||||
"x[2]" => 0.0,
|
"2" => 0.0,
|
||||||
"x[3]" => 0.0,
|
"3" => 0.0,
|
||||||
"x[4]" => 1.0,
|
"4" => 1.0,
|
||||||
))
|
)))
|
||||||
stats = solver.solve()
|
stats = solver.solve()
|
||||||
@test stats["Lower bound"] == 725.0
|
@test stats["Lower bound"] == 725.0
|
||||||
@test stats["Upper bound"] == 725.0
|
@test stats["Upper bound"] == 725.0
|
||||||
|
|||||||
50
src/julia/test/learning_solver.jl
Normal file
50
src/julia/test/learning_solver.jl
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# 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 Test
|
||||||
|
using MIPLearn
|
||||||
|
using CPLEX
|
||||||
|
using Gurobi
|
||||||
|
|
||||||
|
@testset "LearningSolver" begin
|
||||||
|
for optimizer in [CPLEX.Optimizer, Gurobi.Optimizer]
|
||||||
|
instance = KnapsackInstance([23., 26., 20., 18.],
|
||||||
|
[505., 352., 458., 220.],
|
||||||
|
67.0)
|
||||||
|
model = instance.to_model()
|
||||||
|
|
||||||
|
solver = LearningSolver(solver=JuMPSolver(optimizer=optimizer))
|
||||||
|
stats = solver.solve(instance, model)
|
||||||
|
|
||||||
|
@test stats["Lower bound"] == 1183.0
|
||||||
|
@test stats["Upper bound"] == 1183.0
|
||||||
|
@test stats["Sense"] == "max"
|
||||||
|
@test stats["Wallclock time"] > 0
|
||||||
|
|
||||||
|
# solution = solver.get_solution()
|
||||||
|
# @test solution["x[1]"] == 1.0
|
||||||
|
# @test solution["x[2]"] == 0.0
|
||||||
|
# @test solution["x[3]"] == 1.0
|
||||||
|
# @test solution["x[4]"] == 1.0
|
||||||
|
#
|
||||||
|
# stats = solver.solve_lp()
|
||||||
|
# @test round(stats["Optimal value"], digits=3) == 1287.923
|
||||||
|
#
|
||||||
|
# solution = solver.get_solution()
|
||||||
|
# @test round(solution["x[1]"], digits=3) == 1.000
|
||||||
|
# @test round(solution["x[2]"], digits=3) == 0.923
|
||||||
|
# @test round(solution["x[3]"], digits=3) == 1.000
|
||||||
|
# @test round(solution["x[4]"], digits=3) == 0.000
|
||||||
|
#
|
||||||
|
# solver.fix(Dict(
|
||||||
|
# "x[1]" => 1.0,
|
||||||
|
# "x[2]" => 0.0,
|
||||||
|
# "x[3]" => 0.0,
|
||||||
|
# "x[4]" => 1.0,
|
||||||
|
# ))
|
||||||
|
# stats = solver.solve()
|
||||||
|
# @test stats["Lower bound"] == 725.0
|
||||||
|
# @test stats["Upper bound"] == 725.0
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -3,10 +3,8 @@
|
|||||||
# 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 Test
|
using Test
|
||||||
using MIPLearn
|
|
||||||
using CPLEX
|
|
||||||
using Gurobi
|
|
||||||
|
|
||||||
@testset "MIPLearn" begin
|
@testset "MIPLearn" begin
|
||||||
include("jump_solver.jl")
|
include("jump_solver.jl")
|
||||||
|
#include("learning_solver.jl")
|
||||||
end
|
end
|
||||||
@@ -79,9 +79,8 @@ class LearningSolver:
|
|||||||
solver = GurobiSolver()
|
solver = GurobiSolver()
|
||||||
elif callable(self.internal_solver_factory):
|
elif callable(self.internal_solver_factory):
|
||||||
solver = self.internal_solver_factory()
|
solver = self.internal_solver_factory()
|
||||||
assert isinstance(solver, InternalSolver)
|
|
||||||
else:
|
else:
|
||||||
raise Exception("solver %s not supported" % self.internal_solver_factory)
|
solver = self.internal_solver_factory
|
||||||
solver.set_threads(self.threads)
|
solver.set_threads(self.threads)
|
||||||
if self.time_limit is not None:
|
if self.time_limit is not None:
|
||||||
solver.set_time_limit(self.time_limit)
|
solver.set_time_limit(self.time_limit)
|
||||||
|
|||||||
Reference in New Issue
Block a user