Skip to content

Commit

Permalink
Draft pricing callback works (#279)
Browse files Browse the repository at this point in the history
* almost working

* manifest

* fixes

* pricing callback works
  • Loading branch information
guimarqu authored Mar 5, 2020
1 parent 7e5d4cb commit 4526e3d
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 83 deletions.
4 changes: 2 additions & 2 deletions Manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ version = "0.5.8"

[[BlockDecomposition]]
deps = ["JuMP", "MathOptInterface"]
git-tree-sha1 = "e33f9ecb65e404270ae3159e70fb0a34c8937768"
repo-rev = "master"
git-tree-sha1 = "df882b2289d0e57deb6d07b77259abc2f787b25e"
repo-rev = "pricing_oracle_bounds_methods"
repo-url = "https://github.com/atoptima/BlockDecomposition.jl.git"
uuid = "6cde8614-403a-11e9-12f1-c10d0f0caca0"
version = "1.1.1"
Expand Down
48 changes: 43 additions & 5 deletions src/MOIcallbacks.jl
Original file line number Diff line number Diff line change
@@ -1,23 +1,61 @@
function MOI.submit(
model::Optimizer,
cb::BD.PricingSolution{MP.OracleData},
cost::Float64,
variables::Vector{MOI.VariableIndex},
values::Vector{Float64}
)
@show variables
@show values
form = cb.oracle_data.form
S = getobjsense(form)
result = OptimizationResult{S}()
pb = PrimalBound(form, cost)

colunavarids = [_get_orig_varid_in_form(model, form, v) for v in variables]

# setup variable
setup_var_id = [id for (id,v) in Iterators.filter(
v -> (iscuractive(form, v.first) && iscurexplicit(form, v.first) && getduty(v.first) <= DwSpSetupVar),
getvars(form)
)][1]
push!(colunavarids, setup_var_id)
push!(values, 1.0)
pb += getcurcost(form, setup_var_id)

result.primal_bound = pb
push!(result.primal_sols, PrimalSolution(form, colunavarids, values, pb))
setfeasibilitystatus!(result, FEASIBLE)
setterminationstatus!(result, OPTIMAL)
cb.oracle_data.result = result
return
end

function MOI.get(model::Optimizer, spid::BD.OracleSubproblemId{MP.OracleData})
oracle_data = spid.oracle_data
uid = getuid(oracle_data.form)
return uid
axis_index_value = model.annotations.ann_per_form[uid].axis_index_value
return axis_index_value
end

function MOI.get(
model::Optimizer, vc::BD.OracleVariableCost{MP.OracleData},
x::MOI.VariableIndex
)
# TODO
return 0.0
form = vc.oracle_data.form
return getcurcost(form, _get_orig_varid(model, x))
end

function MOI.get(
model::Optimizer, vc::BD.OracleVariableLowerBound{MP.OracleData},
x::MOI.VariableIndex
)
form = vc.oracle_data.form
return getcurlb(form, _get_orig_varid(model, x))
end

function MOI.get(
model::Optimizer, vc::BD.OracleVariableUpperBound{MP.OracleData},
x::MOI.VariableIndex
)
form = vc.oracle_data.form
return getcurub(form, _get_orig_varid(model, x))
end
23 changes: 21 additions & 2 deletions src/MOIwrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
moi_index_to_coluna_uid::MOIU.IndexMap
params::Params
annotations::Annotations
varmap::Dict{MOI.VariableIndex,Id{Variable}} # For the user to get VariablePrimal
varmap::Dict{MOI.VariableIndex,VarId} # For the user to get VariablePrimal
result::OptimizationResult
end

Expand All @@ -42,10 +42,29 @@ function Optimizer()
prob = Problem()
return Optimizer(
prob, MOIU.IndexMap(), Params(), Annotations(),
Dict{MOI.VariableIndex,Id{Variable}}(), OptimizationResult{MinSense}(),
Dict{MOI.VariableIndex,VarId}(), OptimizationResult{MinSense}(),
)
end

function _get_orig_varid(optimizer::Optimizer, x::MOI.VariableIndex)
origid = get(optimizer.varmap, x, nothing)
if origid === nothing
msg = """
Cannot find JuMP variable with MOI index $x in original formulation of Coluna.
Are you sure this variable is attached to the JuMP model ?
"""
error(msg)
end
return origid
end

function _get_orig_varid_in_form(
optimizer::Optimizer, form::Formulation, x::MOI.VariableIndex
)
origid = _get_orig_varid(optimizer, x)
return getid(getvar(form, origid))
end

function MOI.optimize!(optimizer::Optimizer)
optimizer.result = optimize!(
optimizer.inner, optimizer.annotations, optimizer.params
Expand Down
8 changes: 5 additions & 3 deletions src/MathProg/optimizerwrappers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ mutable struct UserOptimizer <: AbstractOptimizer
user_oracle::Function
end

struct OracleData
mutable struct OracleData
form::Formulation
result::Union{Nothing, OptimizationResult}
end

function optimize!(form::Formulation, optimizer::UserOptimizer)
@logmsg LogLevel(-2) "Calling user-defined optimization function."
od = OracleData(form)
return optimizer.user_oracle(od)
od = OracleData(form, nothing)
optimizer.user_oracle(od)
return od.result
end

"""
Expand Down
18 changes: 9 additions & 9 deletions src/MathProg/problem.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
mutable struct Annotations
tree::Union{BD.Tree, Nothing}
ann_per_var::Dict{Id{Variable}, BD.Annotation}
ann_per_constr::Dict{Id{Constraint}, BD.Annotation}
vars_per_ann::Dict{BD.Annotation, Dict{Id{Variable},Variable}}
constrs_per_ann::Dict{BD.Annotation, Dict{Id{Constraint},Constraint}}
ann_per_var::Dict{VarId, BD.Annotation}
ann_per_constr::Dict{ConstrId, BD.Annotation}
vars_per_ann::Dict{BD.Annotation, Dict{VarId,Variable}}
constrs_per_ann::Dict{BD.Annotation, Dict{ConstrId,Constraint}}
ann_per_form::Dict{Int, BD.Annotation}
annotation_set::Set{BD.Annotation}
end

Annotations() = Annotations(
nothing,
Dict{Id{Variable}, BD.Annotation}(), Dict{Id{Constraint}, BD.Annotation}(),
Dict{BD.Annotation, Dict{Id{Variable},Variable}}(),
Dict{BD.Annotation, Dict{Id{Constraint},Constraint}}(),
Dict{VarId, BD.Annotation}(), Dict{ConstrId, BD.Annotation}(),
Dict{BD.Annotation, Dict{VarId,Variable}}(),
Dict{BD.Annotation, Dict{ConstrId,Constraint}}(),
Dict{Int, BD.Annotation}(),
Set{BD.Annotation}()
)
Expand All @@ -21,7 +21,7 @@ function store!(annotations::Annotations, ann::BD.Annotation, var::Variable)
push!(annotations.annotation_set, ann)
annotations.ann_per_var[getid(var)] = ann
if !haskey(annotations.vars_per_ann, ann)
annotations.vars_per_ann[ann] = Dict{Id{Variable}, Variable}()
annotations.vars_per_ann[ann] = Dict{VarId, Variable}()
end
annotations.vars_per_ann[ann][getid(var)] = var
return
Expand All @@ -31,7 +31,7 @@ function store!(annotations::Annotations, ann::BD.Annotation, constr::Constraint
push!(annotations.annotation_set, ann)
annotations.ann_per_constr[getid(constr)] = ann
if !haskey(annotations.constrs_per_ann, ann)
annotations.constrs_per_ann[ann] = Dict{Id{Constraint}, Constraint}()
annotations.constrs_per_ann[ann] = Dict{ConstrId, Constraint}()
end
annotations.constrs_per_ann[ann][getid(constr)] = constr
return
Expand Down
96 changes: 34 additions & 62 deletions test/pricing_callback_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,86 +9,58 @@ function pricing_callback_tests()
"params" => CL.Params(solver = ClA.TreeSearchAlgorithm())
)

problem, x, dec = CLD.GeneralizedAssignment.model_without_knp_constraints(data, coluna)
model, x, dec = CLD.GeneralizedAssignment.model_without_knp_constraints(data, coluna)

function my_pricing_oracle(oracledata)
println("Pricing callback called")
machine_id = BD.oracle_spid(oracledata, model)

machine = BD.oracle_spid(oracledata, problem)
costs = [BD.oracle_cost(oracledata, x[machine_id, j]) for j in data.jobs]
lbs = [BD.oracle_lb(oracledata, x[machine_id, j]) for j in data.jobs]
ubs = [BD.oracle_ub(oracledata, x[machine_id, j]) for j in data.jobs]

@show machine
#test_costs = BD.oracle_cost.(oracledata, x[machine_id, :]) # TODO

@show typeof(oracledata)
form = oracledata.form

#subproblem = CL.get_sp_axis_id(cbdata)
# Model to solve the knp subproblem
sp = JuMP.Model(GLPK.Optimizer)
@variable(sp, lbs[j] <= y[j in data.jobs] <= ubs[j], Int)
@objective(sp, Min, sum(costs[j] * y[j] for j in data.jobs))

println("\e[34m ******************** \e[00m")
@show form


vars = [v for (id,v) in Iterators.filter(
v -> (CL.iscuractive(form,v.first) && CL.iscurexplicit(form,v.first) && CL.getduty(v.first) <= CL.DwSpPricingVar),
CL.getvars(form)
)]
vars_job_id = Vector{Int}()
for v in vars
m = match(r"x\[\d+\,(\d)+\]", CL.getname(form, v))
job_id = parse(Int, m.captures[1])
push!(vars_job_id, job_id)
end

m = match(r"x\[(\d)+\,\d+\]", CL.getname(form, vars[1]))
machine_id = parse(Int, m.captures[1])
@show machine_id

setup_var = [v for (id,v) in Iterators.filter(
v -> (CL.iscuractive(form,v.first) && CL.iscurexplicit(form,v.first) && CL.getduty(v.first) <= CL.DwSpSetupVar),
CL.getvars(form)
)][1]

m = JuMP.Model(GLPK.Optimizer)
@variable(m, CL.getcurlb(form, vars[i]) <= xsp[i=1:length(vars)] <= CL.getcurub(form, vars[i]), Int)
@objective(m, Min, sum(CL.getcurcost(form, vars[j]) * xsp[j] for j in 1:length(vars)))
@constraint(m, knp,
sum(data.weight[j,machine_id] * xsp[j]
for j in 1:length(vars)) <= data.capacity[machine_id]
@constraint(sp, knp,
sum(data.weight[j,machine_id] * y[j]
for j in data.jobs) <= data.capacity[machine_id]
)
optimize!(m)

@show m
println("\e[34m ~~~~~******************** \e[00m")

#result = CL.OptimizationResult{CL.MinSense}()
#result.primal_bound = CL.PrimalBound(form, JuMP.objective_value(m))
solvars = Vector{JuMP.VariableRef}()
solvarvals = Vector{Float64}()
for i in 1:length(xsp)
val = JuMP.value(xsp[i])
if val > 0.000001 || val < - 0.000001 # todo use a tolerance
push!(solvars, x[machine_id, vars_job_id[i]])
push!(solvarvals, val)
JuMP.optimize!(sp)

# Retrieve the solution
solcost = JuMP.objective_value(sp)
solvars = JuMP.VariableRef[]
solvarvals = Float64[]
for j in data.jobs
val = JuMP.value(y[j])
if val 1
push!(solvars, x[machine_id, j])
push!(solvarvals, 1.0)
end
end
#push!(solvarids, CL.getid(setup_var))
#push!(solvarvals, 1.0)
#push!(result.primal_sols, CL.PrimalSolution(form, solvarids, solvarvals, result.primal_bound))
#CL.setfeasibilitystatus!(result, CL.FEASIBLE)
#CL.setterminationstatus!(result, CL.OPTIMAL)

MOI.submit(problem, BD.PricingSolution(oracledata), solvars, solvarvals)
return #result
# Submit the solution
MOI.submit(
model, BD.PricingSolution(oracledata), solcost, solvars,
solvarvals
)
return
end

master = BD.getmaster(dec)
subproblems = BD.getsubproblems(dec)

BD.specify!.(subproblems, lower_multiplicity = 0, solver = my_pricing_oracle)

JuMP.optimize!(problem)
@test JuMP.objective_value(problem) 75.0
@test MOI.get(problem.moi_backend.optimizer, MOI.TerminationStatus()) == MOI.OPTIMAL
@test CLD.GeneralizedAssignment.print_and_check_sol(data, problem, x)
JuMP.optimize!(model)
@test JuMP.objective_value(model) 75.0
@test MOI.get(model.moi_backend.optimizer, MOI.TerminationStatus()) == MOI.OPTIMAL
@test CLD.GeneralizedAssignment.print_and_check_sol(data, model, x)
end

end

0 comments on commit 4526e3d

Please sign in to comment.