Skip to content

Commit

Permalink
draft implementation of ColGen API for tests
Browse files Browse the repository at this point in the history
  • Loading branch information
guimarqu committed Mar 17, 2023
1 parent 1f36796 commit 31d62b4
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 20 deletions.
13 changes: 13 additions & 0 deletions src/Algorithm/colgen/default.jl
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ function ColGen.setup_reformulation!(reform, ::ColGenPhase3)
return
end

######### Pricing strategy
struct ClassicPricingStrategy <: ColGen.AbstractPricingStrategy
subprobs::Dict{FormId, Formulation{DwSp}}
end

function ColGen.get_pricing_strategy(ctx::ColGen.AbstractColGenContext, _)
ClassicPricingStrategy(Dict(i => sp for (i, sp) in ColGen.get_pricing_subprobs(ctx)))
end

ColGen.iterate_sp(ps::ClassicPricingStrategy) = iterate(ps.subprobs)
ColGen.iterate_sp(ps::ClassicPricingStrategy, state) = iterate(ps.subprobs, state)


######### Column generation

# Placeholder methods:
Expand Down
40 changes: 27 additions & 13 deletions src/ColGen/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,13 @@ abstract type AbstractPricingStrategy end
"""
Returns the first subproblem that must be optimized.
"""
@mustimplement "ColGenPricingStrategy" first_sp(::AbstractPricingStrategy)
@mustimplement "ColGenPricingStrategy" iterate_sp(::AbstractPricingStrategy)

"""
Returns the next subproblem to optimize, or `nothing` if all subproblems have been solved or
if we should stop solving pricing subproblems.
"""
@mustimplement "ColGenPricingStrategy" next_sp(::AbstractPricingStrategy)
@mustimplement "ColGenPricingStrategy" iterate_sp(::AbstractPricingStrategy, state)


@mustimplement "ColGenIteration" check_primal_ip_feasibility()
Expand All @@ -185,11 +185,22 @@ if we should stop solving pricing subproblems.

@mustimplement "ColGenIteration" get_orig_costs(context)
@mustimplement "ColGenIteration" get_coef_matrix(context)
@mustimplement "ColGenIteration" get_pricing_subprobs(context)

@mustimplement "ColGenIteration" get_pricing_strategy(context, phase)

@mustimplement "ColGenIteration" get_primal_sols(res)

@mustimplement "ColGenIteration" get_dual_bound(res)

function check_master_termination_status(mast_result)
# TODO
end

function check_pricing_termination_status(pricing_result)
# TODO
end

"""
run_colgen_iteration!(context, phase, reform)
"""
Expand Down Expand Up @@ -231,8 +242,9 @@ function run_colgen_iteration!(context, phase, env)
# TODO: dispatch using a "calculation" type.
#red_costs = compute_sp_vars_red_costs(context, get_reform(context), mast_dual_sol)

@show red_costs

for sp in get_dw_sp(reform)
for (_, sp) in get_pricing_subprobs(context)
update_sp_vars_red_costs!(context, sp, red_costs)
end

Expand All @@ -242,39 +254,41 @@ function run_colgen_iteration!(context, phase, env)
# Depending on the pricing strategy, the user can choose to solve only some subproblems.
# If the some subproblems have not been solved, we use this initial dual bound to
# compute the master dual bound.
sps_db = Dict(id(sp) => compute_sp_init_db(context, sp) for sp in get_dw_sp(reform))
sps_db = Dict(sp_id => compute_sp_init_db(context, sp) for (sp_id, sp) in get_pricing_subprobs(context))

# Solve pricing subproblems
pricing_strategy = get_pricing_strategy(context, phase)
sp_to_solve = first_sp(pricing_strategy, reform)
sp_to_solve_it = iterate_sp(pricing_strategy)

# All generated columns will be stored in the following container. We will insert them
# into the master after the optimization of the pricing subproblems.
generated_columns = pool_of_columns()
generated_columns = pool_of_columns(context)

while !isnothing(sp_to_solve)
while !isnothing(sp_to_solve_it)
(sp_id, sp_to_solve), state = sp_to_solve_it
pricing_result = optimize_pricing_problem!(context, sp_to_solve)
_check_pricing_termination_status(pricing_result)
check_pricing_termination_status(pricing_result)

primal_sols = get_primal_sols(pricing_result)
for primal_sol in primal_sols # multi column generation
# TODO: ensure eltype of generated_columns is the same as eltype of primal_sol
push!(generated_columns, primal_sol)
end

# Updates the initial bound if the pricing subproblem result has a dual bound.
sp_db = get_dual_bound(context, pricing_result)
sp_db = get_dual_bound(pricing_result)
if !isnothing(sp_db)
sps_db[id(sp_to_solve)] = sp_db
sps_db[sp_id] = sp_db
end

sp_to_solve = next_sp(pricing_strategy, sp_to_solve)
sp_to_solve_it = iterate(sp_to_solve_it, state)
end

# Insert columns into the master.
insert_columns!(context, phase, reform, generated_columns)
insert_columns!(context, phase, get_reform(context), generated_columns)


db = compute_dual_bound!(context, phase, reform, sps_db)
db = compute_dual_bound!(context, phase, get_reform(context), sps_db)
# check gap

return
Expand Down
1 change: 0 additions & 1 deletion test/.222-revise-exit-code

This file was deleted.

1 change: 0 additions & 1 deletion test/parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,6 @@ function add_master_constraints!(master::ClMP.Formulation, mastervars::Dict{Stri
end

function reformfromcache(cache::ReadCache)
@show cache.master
if isempty(cache.master.objective.vars)
throw(UndefObjectiveParserError())
end
Expand Down
56 changes: 51 additions & 5 deletions test/unit/ColGen/colgen_iteration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,19 @@ struct ColGenIterationTestContext <: ColGen.AbstractColGenContext
reform::ClMP.Reformulation
end

struct ColGenIterationTestResult
struct ColGenIterationTestMasterResult
term_status::ClB.TerminationStatus
primal_sol::Union{Nothing, Vector{Float64}}
dual_sol::Union{Nothing, Vector{Float64}}
end

struct ColGenIterationTestPricingResult
term_status::ClB.TerminationStatus
primal_sols::Vector{Vector{Float64}}
primal_bound::Float64
dual_bound::Float64
end

struct ColGenIterationTestPhase <: ColGen.AbstractColGenPhase end

function ColGen.optimize_master_lp_problem!(master, ctx::ColGenIterationTestContext, env)
Expand All @@ -75,7 +82,7 @@ function ColGen.optimize_master_lp_problem!(master, ctx::ColGenIterationTestCont
nothing
end

return ColGenIterationTestResult(ctx.master_term_status, primal_sol, dual_sol)
return ColGenIterationTestMasterResult(ctx.master_term_status, primal_sol, dual_sol)
end

# Reduced costs
Expand All @@ -84,17 +91,56 @@ end
ColGen.get_master(ctx::ColGenIterationTestContext) = ClMP.getmaster(ctx.reform)
ColGen.get_reform(ctx::ColGenIterationTestContext) = ctx.reform

ColGen.get_primal_sol(res::ColGenIterationTestResult) = res.primal_sol
ColGen.get_dual_sol(res::ColGenIterationTestResult) = res.dual_sol
ColGen.get_primal_sol(res::ColGenIterationTestMasterResult) = res.primal_sol
ColGen.get_dual_sol(res::ColGenIterationTestMasterResult) = res.dual_sol

ColGen.check_primal_ip_feasibility(::ColGenIterationTestPhase, sol, reform) = nothing

ColGen.update_master_constrs_dual_vals!(::ColGenIterationTestContext, ::ColGenIterationTestPhase, reform, dual_mast_sol) = nothing

ColGen.get_orig_costs(::ColGenIterationTestContext) = [7, 2, 1, 5, 3, 6, 8, 4, 2, 9]
ColGen.get_coef_matrix(::ColGenIterationTestContext) = [
1 1 1 1 0 0 0 0 0 0;
1 0 0 0 1 1 1 0 0 0;
0 1 0 0 1 0 0 1 1 0;
0 0 1 0 0 1 0 1 0 1;
0 0 0 1 0 0 1 0 1 1
]

function ColGen.update_sp_vars_red_costs!(::ColGenIterationTestContext, subprob, red_costs)
for (var_id, var) in ClMP.getvars(subprob)
setcurcost!(subprob, var, red_costs[var_id])
end
return
end

ColGen.get_pricing_subprobs(context) = ClMP.get_dw_pricing_sps(context.reform)

ColGen.compute_sp_init_db(::ColGenIterationTestContext, sp) = -Inf

ColGen.pool_of_columns(::ColGenIterationTestContext) = Vector{Float64}[]

function ColGen.optimize_pricing_problem!(::ColGenIterationTestContext, form)
return ColGenIterationTestPricingResult(ClB.OPTIMAL, [[1, 0, 0, 0, 0, 0, 0]], 0, 0)
end

function ColGen.get_primal_sols(res::ColGenIterationTestPricingResult)
return res.primal_sols
end

ColGen.get_dual_bound(res::ColGenIterationTestPricingResult) = res.dual_bound

function ColGen.insert_columns!(::ColGenIterationTestContext, phase, reform, generated_columns)
return
end

function ColGen.compute_dual_bound!(::ColGenIterationTestContext, phase, reform, sps_db)
return 0
end

function run_colgen_iteration_test()
reform, master, vars_by_name = get_reform_master_and_vars_colgen_iteration()

ColGen.run_colgen_iteration!(ColGenIterationTestContext(ClB.OPTIMAL, reform), ColGenIterationTestPhase(), nothing)

end
register!(unit_tests, "colgen_iteration", run_colgen_iteration_test)

0 comments on commit 31d62b4

Please sign in to comment.