From 8200c003af5a5e0e370fb28128f1c15ce0eac9d3 Mon Sep 17 00:00:00 2001 From: Alinson S Xavier Date: Sun, 12 Apr 2020 11:07:09 -0500 Subject: [PATCH] Add work-in-progress JuMPSolver --- src/julia/src/MIPLearn.jl | 7 +- src/julia/test/runtests.jl | 137 +++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 3 deletions(-) diff --git a/src/julia/src/MIPLearn.jl b/src/julia/src/MIPLearn.jl index a6b9698..8603ed4 100644 --- a/src/julia/src/MIPLearn.jl +++ b/src/julia/src/MIPLearn.jl @@ -1,5 +1,6 @@ -module MIPLearn - -greet() = print("Hello World!") +# 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. +module MIPLearn end # module diff --git a/src/julia/test/runtests.jl b/src/julia/test/runtests.jl index e69de29..cac36ff 100644 --- a/src/julia/test/runtests.jl +++ b/src/julia/test/runtests.jl @@ -0,0 +1,137 @@ +# 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 MIPLearn +using Test +using JuMP +using Gurobi +using PyCall +using MathOptInterface +const MOI = MathOptInterface + +miplearn = pyimport("miplearn") + +Instance = miplearn.Instance +LearningSolver = miplearn.LearningSolver +InternalSolver = miplearn.solvers.internal.InternalSolver + +@pydef mutable struct JuMPSolver <: InternalSolver + function add_constraint(self, constraint) + end + + function clear_warm_start(self) + end + + function fix(self, solution) + end + + function set_gap_tolerance(self, gap_tolerance) + end + + function set_instance(self, instance, model) + self.instance = instance + self.model = model + end + + function set_node_limit(self) + end + + function set_threads(self, threads) + end + + function set_time_limit(self, time_limit) + end + + function set_warm_start(self, solution) + end + + function solve(self; tee=false) + JuMP.set_optimizer(self.model, Gurobi.Optimizer) + JuMP.optimize!(self.model) + + primal_bound = JuMP.objective_value(self.model) + dual_bound = JuMP.objective_bound(self.model) + + if JuMP.objective_sense(self.model) == MOI.MIN_SENSE + sense = "min" + lower_bound = dual_bound + upper_bound = primal_bound + else + sense = "max" + lower_bound = primal_bound + upper_bound = dual_bound + end + + @show primal_bound, dual_bound + + return Dict("Lower bound" => lower_bound, + "Upper bound" => upper_bound, + "Sense" => sense) + end + + function solve_lp(self; tee=false) + end + + function get_solution(self) + return Dict(JuMP.name(var) => JuMP.value(var) + for var in JuMP.all_variables(self.model)) + end +end + +@pydef mutable struct KnapsackInstance <: Instance + function __init__(self, weights, prices, capacity) + self.weights = weights + self.prices = prices + self.capacity = capacity + end + + function to_model(self) + model = Model() + n = length(self.weights) + @variable(model, x[1:n], Bin) + @objective(model, Max, sum(x[i] * self.prices[i] for i in 1:n)) + @constraint(model, sum(x[i] * self.weights[i] for i in 1:n) <= instance.capacity) + return model + end + + function get_instance_features(self) + return [0.] + end + + function get_variable_features(self, var, index) + @show var + return [0.] + end +end + +instance = KnapsackInstance([23., 26., 20., 18.], + [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 +