mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Julia: Implement more missing methods from JuMPSolver; test CPLEX
This commit is contained in:
@@ -26,6 +26,12 @@ git-tree-sha1 = "62847acab40e6855a9b5905ccb99c2b5cf6b3ebb"
|
|||||||
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
|
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
||||||
|
[[CPLEX]]
|
||||||
|
deps = ["Libdl", "LinearAlgebra", "MathOptInterface", "MathProgBase", "SparseArrays"]
|
||||||
|
git-tree-sha1 = "f8dac98fbff2f7d7fe58fa1fcdbefaaaf29a1f59"
|
||||||
|
uuid = "a076750e-1247-5638-91d2-ce28b192dca0"
|
||||||
|
version = "0.6.5"
|
||||||
|
|
||||||
[[CPLEXW]]
|
[[CPLEXW]]
|
||||||
deps = ["CEnum", "Libdl"]
|
deps = ["CEnum", "Libdl"]
|
||||||
git-tree-sha1 = "ebad297748ee2a12cc13b5fb07f9bbfa8a900494"
|
git-tree-sha1 = "ebad297748ee2a12cc13b5fb07f9bbfa8a900494"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ authors = ["Alinson S Xavier <git@axavier.org>"]
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
CPLEX = "a076750e-1247-5638-91d2-ce28b192dca0"
|
||||||
CPLEXW = "cfecb002-79c2-11e9-35be-cb59aa640f85"
|
CPLEXW = "cfecb002-79c2-11e9-35be-cb59aa640f85"
|
||||||
Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b"
|
Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b"
|
||||||
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
|
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ __precompile__(false)
|
|||||||
module MIPLearn
|
module MIPLearn
|
||||||
|
|
||||||
using JuMP
|
using JuMP
|
||||||
using Gurobi
|
|
||||||
using PyCall
|
using PyCall
|
||||||
using MathOptInterface
|
using MathOptInterface
|
||||||
const MOI = MathOptInterface
|
const MOI = MathOptInterface
|
||||||
@@ -17,26 +16,44 @@ LearningSolver = miplearn.LearningSolver
|
|||||||
InternalSolver = miplearn.solvers.internal.InternalSolver
|
InternalSolver = miplearn.solvers.internal.InternalSolver
|
||||||
|
|
||||||
@pydef mutable struct JuMPSolver <: InternalSolver
|
@pydef mutable struct JuMPSolver <: InternalSolver
|
||||||
|
function __init__(self; optimizer=CPLEX.Optimizer)
|
||||||
|
self.optimizer = optimizer
|
||||||
|
end
|
||||||
|
|
||||||
function add_constraint(self, constraint)
|
function add_constraint(self, constraint)
|
||||||
|
@error "Not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_warm_start(self, solution)
|
function set_warm_start(self, solution)
|
||||||
|
for (varname, value) in solution
|
||||||
|
var = JuMP.variable_by_name(self.model, varname)
|
||||||
|
JuMP.set_start_value(var, value)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function clear_warm_start(self)
|
function clear_warm_start(self)
|
||||||
|
@error "Not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
function fix(self, solution)
|
function fix(self, solution)
|
||||||
|
for (varname, value) in solution
|
||||||
|
var = JuMP.variable_by_name(self.model, varname)
|
||||||
|
JuMP.fix(var, value, force=true)
|
||||||
|
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.bin_vars = [var
|
||||||
|
for var in JuMP.all_variables(self.model)
|
||||||
|
if JuMP.is_binary(var)]
|
||||||
|
JuMP.set_optimizer(self.model, self.optimizer)
|
||||||
end
|
end
|
||||||
|
|
||||||
function solve(self; tee=false)
|
function solve(self; tee=false)
|
||||||
JuMP.set_optimizer(self.model, Gurobi.Optimizer)
|
|
||||||
JuMP.optimize!(self.model)
|
JuMP.optimize!(self.model)
|
||||||
|
self._update_solution()
|
||||||
|
|
||||||
primal_bound = JuMP.objective_value(self.model)
|
primal_bound = JuMP.objective_value(self.model)
|
||||||
dual_bound = JuMP.objective_bound(self.model)
|
dual_bound = JuMP.objective_bound(self.model)
|
||||||
@@ -55,27 +72,54 @@ InternalSolver = miplearn.solvers.internal.InternalSolver
|
|||||||
|
|
||||||
return Dict("Lower bound" => lower_bound,
|
return Dict("Lower bound" => lower_bound,
|
||||||
"Upper bound" => upper_bound,
|
"Upper bound" => upper_bound,
|
||||||
"Sense" => sense)
|
"Sense" => sense,
|
||||||
|
"Wallclock time" => JuMP.solve_time(self.model),
|
||||||
|
"Nodes" => 1,
|
||||||
|
"Log" => nothing,
|
||||||
|
"Warm start value" => nothing)
|
||||||
end
|
end
|
||||||
|
|
||||||
function solve_lp(self; tee=false)
|
function solve_lp(self; tee=false)
|
||||||
|
for var in self.bin_vars
|
||||||
|
JuMP.unset_binary(var)
|
||||||
|
JuMP.set_upper_bound(var, 1.0)
|
||||||
|
JuMP.set_lower_bound(var, 0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
JuMP.optimize!(self.model)
|
||||||
|
obj_value = JuMP.objective_value(self.model)
|
||||||
|
self._update_solution()
|
||||||
|
|
||||||
|
for var in self.bin_vars
|
||||||
|
JuMP.set_binary(var)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Dict("Optimal value" => obj_value)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_solution(self)
|
function get_solution(self)
|
||||||
return Dict(JuMP.name(var) => JuMP.value(var)
|
return self.solution
|
||||||
for var in JuMP.all_variables(self.model))
|
end
|
||||||
|
|
||||||
|
function _update_solution(self)
|
||||||
|
self.solution = Dict(JuMP.name(var) => JuMP.value(var)
|
||||||
|
for var in JuMP.all_variables(self.model))
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_gap_tolerance(self, gap_tolerance)
|
function set_gap_tolerance(self, gap_tolerance)
|
||||||
|
@error "Not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_node_limit(self)
|
function set_node_limit(self)
|
||||||
|
@error "Not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_threads(self, threads)
|
function set_threads(self, threads)
|
||||||
|
@error "Not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_time_limit(self, time_limit)
|
function set_time_limit(self, time_limit)
|
||||||
|
JuMP.set_time_limit_sec(self.model, time_limit)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
58
src/julia/test/jump_solver.jl
Normal file
58
src/julia/test/jump_solver.jl
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# 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 "JuMPSolver" 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 = JuMPSolver(optimizer=optimizer)
|
||||||
|
solver.set_instance(instance, model)
|
||||||
|
solver.set_time_limit(30)
|
||||||
|
solver.set_warm_start(Dict(
|
||||||
|
"x[1]" => 1.0,
|
||||||
|
"x[2]" => 0.0,
|
||||||
|
"x[3]" => 0.0,
|
||||||
|
"x[4]" => 1.0,
|
||||||
|
))
|
||||||
|
stats = solver.solve()
|
||||||
|
|
||||||
|
@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
|
||||||
@@ -4,36 +4,9 @@
|
|||||||
|
|
||||||
using Test
|
using Test
|
||||||
using MIPLearn
|
using MIPLearn
|
||||||
|
using CPLEX
|
||||||
|
using Gurobi
|
||||||
|
|
||||||
@testset "MIPLearn" begin
|
@testset "MIPLearn" begin
|
||||||
instance = KnapsackInstance([23., 26., 20., 18.],
|
include("jump_solver.jl")
|
||||||
[505., 352., 458., 220.],
|
|
||||||
67.0)
|
|
||||||
model = instance.to_model()
|
|
||||||
|
|
||||||
solver = JuMPSolver()
|
|
||||||
solver.set_instance(instance, model)
|
|
||||||
stats = solver.solve()
|
|
||||||
|
|
||||||
# assert len(stats["Log"]) > 100
|
|
||||||
@test stats["Lower bound"] == 1183.0
|
|
||||||
@test stats["Upper bound"] == 1183.0
|
|
||||||
@test stats["Sense"] == "max"
|
|
||||||
# @test isinstance(stats["Wallclock time"], float)
|
|
||||||
# @test isinstance(stats["Nodes"], int)
|
|
||||||
|
|
||||||
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"], 3) == 1287.923
|
|
||||||
#
|
|
||||||
# solution = solver.get_solution()
|
|
||||||
# @test round(solution["x"][0], 3) == 1.000
|
|
||||||
# @test round(solution["x"][1], 3) == 0.923
|
|
||||||
# @test round(solution["x"][2], 3) == 1.000
|
|
||||||
# @test round(solution["x"][3], 3) == 0.000
|
|
||||||
end
|
end
|
||||||
Reference in New Issue
Block a user