Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support insertion of identical columns at same colgen iteration #633

Merged
merged 2 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 33 additions & 21 deletions src/Algorithm/colgen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
)

Column generation algorithm that can be applied to formulation reformulated using
Dantzog-Wolfe decomposition.
Dantzig-Wolfe decomposition.

This algorithm first solves the linear relaxation of the master (master LP) using `restr_master_solve_alg`.
Then, it solves the subproblems by calling `pricing_prob_solve_alg` to get the columns that
Expand Down Expand Up @@ -335,32 +335,44 @@ function insert_columns!(
spinfo.bestsol = bestsol
spinfo.isfeasible = true

# First we activate columns that are already in the pool.
primal_sols_to_insert = PrimalSolution{Formulation{DwSp}}[]
for sol in get_ip_primal_sols(sp_optstate)
red_cost = compute_red_cost(algo, masterform, spinfo, sol, dualsol)
if improving_red_cost(red_cost, algo, getobjsense(masterform))
insertion_status, col_id = insert_column!(masterform, sol, "MC")

if !insertion_status && haskey(masterform, col_id) && !iscuractive(masterform, col_id)
# Column already generated but inactive (only possible case when
# insertion status is false meaning that the column already exists).
activate!(masterform, col_id)
elseif !insertion_status
msg = """
Unexpected variable state during column insertion.
======
The column is in the master ? $(haskey(masterform, col_id)).
The column is active ? $(iscuractive(masterform, col_id)).
======
Please open an issue at https://github.com/atoptima/Coluna.jl/issues with an example that reproduces the bug.
"""
error(string(msg))
col_id = get_column_from_pool(sol)
if !isnothing(col_id)
if haskey(masterform, col_id) && !iscuractive(masterform, col_id)
activate!(masterform, col_id)
if phase == 1
setcurcost!(masterform, col_id, 0.0)
end
nb_cols_generated += 1
else
msg = """
Unexpected variable state during column insertion.
======
The column is in the master ? $(haskey(masterform, col_id)).
The column is active ? $(iscuractive(masterform, col_id)).
======
Please open an issue at https://github.com/atoptima/Coluna.jl/issues with an example that reproduces the bug.
======
"""
error(msg)
end
else
push!(primal_sols_to_insert, sol)
end
end
end

if phase == 1
setcurcost!(masterform, col_id, 0.0)
end
nb_cols_generated += 1
# Then, we add the new columns (i.e. not in the pool).
for sol in primal_sols_to_insert
col_id = insert_column!(masterform, sol, "MC")
if phase == 1
setcurcost!(masterform, col_id, 0.0)
end
nb_cols_generated += 1
end
end

Expand Down
2 changes: 1 addition & 1 deletion src/MathProg/MathProg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export AbstractFormulation, Formulation, create_formulation!, getreformulation,
setcol_from_sp_primalsol!, setcut_from_sp_dualsol!, # TODO : merge with setvar! & setconstr
set_objective_sense!, clonevar!, cloneconstr!, clonecoeffs!, initialize_optimizer!,
push_optimizer!, getobjconst, setobjconst!, addcustomvars!, addcustomconstrs!,
insert_column!
insert_column!, get_column_from_pool

# Duties of formulations
export Original, DwMaster, BendersMaster, DwSp, BendersSp
Expand Down
46 changes: 28 additions & 18 deletions src/MathProg/formulation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,6 @@ end
############################################################################################
# Insertion of a column in the master
############################################################################################

# Compute all the coefficients of the column in the coefficient matrix of the
# master formulation.
function _col_members(col, master_coef_matrix)
Expand All @@ -305,16 +304,34 @@ function _col_members(col, master_coef_matrix)
return members
end

"""
get_column_from_pool(primal_sol)

Returns the `var_id` of the master column that represents the primal solution `primal_sol`
to a Dantzig-Wolfe subproblem if the primal solution exists in the pool of solutions to the
subproblem; `nothing` otherwise.
"""
function get_column_from_pool(primal_sol::PrimalSolution{Formulation{DwSp}})
spform = primal_sol.solution.model
new_col_peren_cost = mapreduce(
((var_id, var_val),) -> getperencost(spform, var_id) * var_val,
+,
primal_sol
)
pool = getprimalsolpool(spform)
costs_pool = spform.duty_data.costs_primalsols_pool
return _get_same_sol_in_pool(pool, costs_pool, primal_sol.solution.sol, new_col_peren_cost)
end

"""
insert_column!(master_form, primal_sol, name)

Inserts the primal solution `primal_sol` to a Dantzig-Wolfe subproblem into the
master as a column.

Returns a tuple `(insertion_status, var_id)` where :
- `insertion_status` is `true` if the primal solution was already in the pool
and in the master formulation
- `var_id` is the id of the column variable in the master formulation.
Returns `var_id` the id of the column variable in the master formulation.

**Warning**: this methods does not check if the column already exists in the pool.
"""
function insert_column!(
master_form::Formulation{DwMaster}, primal_sol::PrimalSolution, name::String;
Expand All @@ -328,23 +345,16 @@ function insert_column!(
spform = primal_sol.solution.model

# Compute perennial cost of the column.
new_col_peren_cost = 0.0
for (var_id, var_val) in primal_sol
new_col_peren_cost += getperencost(spform, var_id) * var_val
end
new_col_peren_cost = mapreduce(
((var_id, var_val),) -> getperencost(spform, var_id) * var_val,
+,
primal_sol
)

pool = getprimalsolpool(spform)
costs_pool = spform.duty_data.costs_primalsols_pool
custom_pool = spform.duty_data.custom_primalsols_pool

# Check if the column is already in the pool.
col_id = _get_same_sol_in_pool(pool, costs_pool, primal_sol.solution.sol, new_col_peren_cost)

# If the column is already in the pool, it means that it is already in the
# master formulation, so we return the id of the column and don't insert it
# a second time.
col_id !== nothing && return false, col_id

# Compute coefficient members of the column in the matrix.
members = _col_members(primal_sol, getcoefmatrix(master_form))

Expand Down Expand Up @@ -376,7 +386,7 @@ function insert_column!(
custom_pool[col_id] = primal_sol.custom_data
end
end
return true, getid(col)
return getid(col)
end

############################################################################################
Expand Down
4 changes: 4 additions & 0 deletions test/unit/Algorithm/colgen.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Tests to implement

# Two identical columns at same colgen iteration -> ok
# Two identical columns at two different colgen iteration -> error