Make Julia's solution format consistent with Python's

pull/3/head
Alinson S. Xavier 6 years ago
parent ad7a1839d4
commit 808e684c11

@ -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
var = self.basename_idx_to_var[basename, idx]
JuMP.set_start_value(var, value) 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
var = self.basename_idx_to_var[basename, idx]
JuMP.fix(var, value, force=true) 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

@ -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)

Loading…
Cancel
Save