Skip to content

Commit

Permalink
Remove unused presolve code and rearrange the remaining presolve code
Browse files Browse the repository at this point in the history
  • Loading branch information
rrsadykov committed Jan 17, 2024
1 parent 72701ec commit fad7b99
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 450 deletions.
104 changes: 19 additions & 85 deletions src/Algorithm/presolve/helpers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -131,24 +131,25 @@ function _var_ub_from_row(sense::ConstrSense, min_slack::Real, max_slack::Real,
return Inf
end

function rows_to_deactivate(form::PresolveFormRepr)
# Compute slacks of each constraints
rows_to_deactivate = Int[]
min_slacks = Float64[row_min_slack(form, row) for row in 1:form.nb_constrs]
max_slacks = Float64[row_max_slack(form, row) for row in 1:form.nb_constrs]

for row in 1:form.nb_constrs
sense = form.sense[row]
rhs = form.rhs[row]
if _infeasible_row(sense, min_slacks[row], max_slacks[row], 1e-6)
error("Infeasible row $row.")
end
if _unbounded_row(sense, rhs) || _row_bounded_by_var_bounds(sense, min_slacks[row], max_slacks[row], 1e-6)
push!(rows_to_deactivate, row)
end
end
return rows_to_deactivate
end
# Is not used for the moment, but we keep it as it might be needed
# function rows_to_deactivate(form::PresolveFormRepr)
# # Compute slacks of each constraints
# rows_to_deactivate = Int[]
# min_slacks = Float64[row_min_slack(form, row) for row in 1:form.nb_constrs]
# max_slacks = Float64[row_max_slack(form, row) for row in 1:form.nb_constrs]

# for row in 1:form.nb_constrs
# sense = form.sense[row]
# rhs = form.rhs[row]
# if _infeasible_row(sense, min_slacks[row], max_slacks[row], 1e-6)
# error("Infeasible row $row.")
# end
# if _unbounded_row(sense, rhs) || _row_bounded_by_var_bounds(sense, min_slacks[row], max_slacks[row], 1e-6)
# push!(rows_to_deactivate, row)
# end
# end
# return rows_to_deactivate
# end

function bounds_tightening(form::PresolveFormRepr)
#length(ignore_rows) == form.nb_constrs || throw(ArgumentError("Inconsistent sizes of ignore_rows and nb of constraints."))
Expand Down Expand Up @@ -235,73 +236,6 @@ function tighten_bounds_presolve_form_repr(form::PresolveFormRepr, tightened_bou
col_mask
end

# function partial_sol_update(form::PresolveFormRepr, lm, um, store_unpropagated_partial_sol)
# coef_matrix = form.col_major_coef_matrix
# rhs = form.rhs
# sense = form.sense
# lbs = form.lbs
# ubs = form.ubs

# new_partial_sol = zeros(Float64, length(form.partial_solution))
# for (i, (lb, ub)) in enumerate(Iterators.zip(form.lbs, form.ubs))
# @assert !isnan(lb)
# @assert !isnan(ub)
# if lb > ub
# error("Infeasible.")
# end
# if lb > 0.0
# @assert !isinf(lb)
# new_partial_sol[i] += lb
# elseif ub < 0.0 && !isinf(ub)
# @assert !isinf(ub)
# new_partial_sol[i] += ub
# end
# end

# # Update rhs
# new_rhs = rhs - coef_matrix * new_partial_sol

# # Update bounds
# new_lbs = lbs - new_partial_sol
# new_ubs = ubs - new_partial_sol

# # Update partial_sol
# partial_sol = form.partial_solution + new_partial_sol

# row_mask = ones(Bool, form.nb_constrs)
# col_mask = ones(Bool, form.nb_vars)

# return PresolveFormRepr(
# coef_matrix, new_rhs, sense, new_lbs, new_ubs, partial_sol, lm, um;
# unpropagated_partial_solution = store_unpropagated_partial_sol ? new_partial_sol : nothing
# ),
# row_mask,
# col_mask
# end

function shrink_col_presolve_form_repr(form::PresolveFormRepr)
# nb_cols = form.nb_vars
# nb_rows = form.nb_constrs
# coef_matrix = form.col_major_coef_matrix
# rhs = form.rhs
# sense = form.sense

# # Update partial solution
# col_mask = ones(Bool, nb_cols)
# fixed_vars = Tuple{Int,Float64}[]
# # for (i, (lb, ub)) in enumerate(Iterators.zip(form.lbs, form.ubs))
# # @assert !isnan(lb)
# # @assert !isnan(ub)
# # if abs(ub) <= Coluna.TOL && abs(lb) <= Coluna.TOL
# # col_mask[i] = false
# # push!(fixed_vars, (i, partial_sol[i]))
# # end
# # end

# nb_cols -= length(fixed_vars)
return
end

function shrink_row_presolve_form_repr(form::PresolveFormRepr, rows_to_deactivate::Vector{Int}, lm, um)
nb_rows = form.nb_constrs
coef_matrix = form.col_major_coef_matrix
Expand Down
253 changes: 0 additions & 253 deletions src/Algorithm/presolve/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -121,58 +121,6 @@ function create_presolve_form(
)
end

function propagate_in_presolve_form(
form::PresolveFormulation,
rows_to_deactivate::Vector{Int},
tightened_bounds::Dict{Int,Tuple{Float64,Bool,Float64,Bool}}
)
form_repr, row_mask, col_mask = PresolveFormRepr(
form.form,
rows_to_deactivate,
tightened_bounds,
form.form.lower_multiplicity,
form.form.upper_multiplicity
)

col_to_var = form.col_to_var[col_mask]
row_to_constr = form.row_to_constr[row_mask]

deactivated_constrs = form.deactivated_constrs
fixed_vars_dict = form.fixed_variables

var_to_col = Dict(getid(var) => k for (k, var) in enumerate(col_to_var))
constr_to_row = Dict(getid(constr) => k for (k, constr) in enumerate(row_to_constr))

for constr in form.row_to_constr[.!row_mask]
push!(deactivated_constrs, getid(constr))
end

# if !isnothing(fixed_vars)
# for (col, val) in fixed_vars
# varid = getid(form.col_to_var[col])
# if !haskey(fixed_vars_dict, varid)
# fixed_vars_dict[varid] = val
# else
# error("Cannot fix variable twice.")
# end
# end
# end

@assert length(col_to_var) == length(form_repr.lbs)
@assert length(col_to_var) == length(form_repr.ubs)
@assert length(row_to_constr) == length(form_repr.rhs)

return PresolveFormulation(
col_to_var,
row_to_constr,
var_to_col,
constr_to_row,
form_repr,
deactivated_constrs,
fixed_vars_dict
)
end

function create_presolve_reform(reform::Reformulation{DwMaster}; verbose::Bool=false)
master = getmaster(reform)
# Create the presolve formulations
Expand Down Expand Up @@ -368,207 +316,6 @@ struct PresolveOutput
feasible::Bool
end

function get_partial_sol(
presolve_form::PresolveFormulation, partial_sol_to_fix::Dict{VarId,Float64}
)
new_partial_sol = zeros(Float64, length(presolve_form.col_to_var))
for (var_id, value) in partial_sol_to_fix
new_partial_sol[presolve_form.var_to_col[var_id]] += value
end
return new_partial_sol
end

function compute_rhs(presolve_form, restr_partial_sol)
rhs = presolve_form.form.rhs
coef_matrix = presolve_form.form.col_major_coef_matrix
return rhs - coef_matrix * restr_partial_sol
end

function update_subproblem_multiplicities!(dw_sps, nb_fixed_columns_per_sp)
for (spid, presolve_sp) in dw_sps
lm = presolve_sp.form.lower_multiplicity
um = presolve_sp.form.upper_multiplicity

presolve_sp.form.lower_multiplicity = max(
0, lm - nb_fixed_columns_per_sp[spid]
)
presolve_sp.form.upper_multiplicity = max(
0, um - nb_fixed_columns_per_sp[spid]
) # TODO if < 0 -> error
end
return
end

function propagate_partial_sol_to_global_bounds!(
presolve_repr_master, local_repr_partial_sol, master_var_domains
)
new_lbs = zeros(Float64, presolve_repr_master.form.nb_vars)
new_ubs = zeros(Float64, presolve_repr_master.form.nb_vars)

for (col, (val, lb, ub, (min_value, max_value))) in enumerate(
Iterators.zip(
local_repr_partial_sol,
presolve_repr_master.form.lbs,
presolve_repr_master.form.ubs,
master_var_domains
)
)
new_lbs[col] = max(lb - val, min_value)
new_ubs[col] = min(ub - val, max_value)
end

presolve_repr_master.form.lbs = new_lbs
presolve_repr_master.form.ubs = new_ubs
return
end

# For each master variable (master representative or master pure),
# this function calculates the domain, i.e. intevals in which their new (global) bounds should lie
function compute_repr_master_var_domains(
dw_pricing_sps,
presolve_reform::DwPresolveReform,
local_repr_partial_sol
)
sp_domains = Dict{VarId,Tuple{Float64,Float64}}()

for (sp_id, sp_presolve_form) in presolve_reform.dw_sps
lm = sp_presolve_form.form.lower_multiplicity
um = sp_presolve_form.form.upper_multiplicity

# Update domains for master representative variables using multiplicity.
sp_form = dw_pricing_sps[sp_id]
for (varid, var) in getvars(sp_form)
if getduty(varid) <= DwSpPricingVar
lb = getcurlb(sp_form, var)
ub = getcurub(sp_form, var)

(global_lb, global_ub) = get(sp_domains, varid, (0.0, 0.0))
global_lb += (lb > 0 ? lm : um) * lb
global_ub += (ub > 0 ? um : lm) * ub

sp_domains[varid] = (global_lb, global_ub)
end
end
end

presolve_repr_master = presolve_reform.representative_master
domains = Vector{Tuple{Float64, Float64}}()
sizehint!(domains, presolve_repr_master.form.nb_vars)
for col in 1:presolve_repr_master.form.nb_vars
varid = getid(presolve_repr_master.col_to_var[col])
domain = if haskey(sp_domains, varid)
sp_domains[varid]
elseif iszero(local_repr_partial_sol[col])
(-Inf, Inf)
elseif local_repr_partial_sol[col] > 0
(0, Inf)
else # local_repr_partial_sol[col] < 0
(-Inf, 0)
end
push!(domains, domain)
end

return domains
end

"""
Returns the local restricted partial solution.
"""
function propagate_partial_sol_into_master!(
reform::Reformulation,
presolve_reform::DwPresolveReform,
partial_sol_to_fix::Dict{VarId,Float64},
verbose::Bool
)
presolve_representative_master = presolve_reform.representative_master
presolve_restricted_master = presolve_reform.restricted_master

# Create the local partial solution from the restricted master presolve representation.
# This local partial solution must then be "fixed" & propagated.
local_restr_partial_sol = get_partial_sol(presolve_restricted_master, partial_sol_to_fix)

# Compute the rhs of all constraints.
# Non-robust and convexity constraints rhs can only be computed using this representation.
new_rhs = compute_rhs(presolve_restricted_master, local_restr_partial_sol)

# Project local partial solution on the representative master.
local_repr_partial_sol, nb_fixed_columns_per_sp = partial_sol_on_repr(
get_dw_pricing_sps(reform),
presolve_representative_master,
presolve_restricted_master,
local_restr_partial_sol
)

if verbose
print("Partial solution in the representative formulation:")
master = getmaster(reform)
for (var, value) in zip(presolve_representative_master.col_to_var, local_repr_partial_sol)
if !iszero(value)
print(" ", getname(master, var), "=>", value)
end
end
println()
end

# Update the multiplicity of each subproblem.
update_subproblem_multiplicities!(presolve_reform.dw_sps, nb_fixed_columns_per_sp)

if verbose
print("New subproblem multiplicities:")
for (form_id, presolve_sp) in presolve_reform.dw_sps
print(
" sp.",
form_id,
":[",
presolve_sp.form.lower_multiplicity,
",",
presolve_sp.form.upper_multiplicity,
"]"
)
end
println()
end

# Compute master variables domains (in which variable bounds should lie)
master_var_domains = compute_repr_master_var_domains(
get_dw_pricing_sps(reform), presolve_reform, local_repr_partial_sol
)

# Propagate local partial solution from the representative master representation
# into the global bounds.
propagate_partial_sol_to_global_bounds!(
presolve_representative_master,
local_repr_partial_sol,
master_var_domains
)

if verbose
print("Global bounds after fixing partial solution:")
for (col, var) in enumerate(presolve_representative_master.col_to_var)
print(
" ",
getname(master, var),
":[",
presolve_representative_master.form.lbs[col],
",",
presolve_representative_master.form.ubs[col],
"]"
)
end
println()
end

# Update the rhs of the representative master.
@assert length(new_rhs) == length(presolve_restricted_master.form.rhs) ==
length(presolve_representative_master.form.rhs)
for (row, rhs) in enumerate(new_rhs)
presolve_representative_master.form.rhs[row] = rhs
end
return local_restr_partial_sol
end

function presolve_formulation!(presolve_form::PresolveFormulation)
tightened_bounds = bounds_tightening(presolve_form.form)
presolve_form = propagate_in_presolve_form(presolve_form, Int[], tightened_bounds)
Expand Down
Loading

0 comments on commit fad7b99

Please sign in to comment.