Skip to content

Commit

Permalink
Presolve new var fixing using partial solution (#1065)
Browse files Browse the repository at this point in the history
* change how variable fixing is handled

* ok
  • Loading branch information
guimarqu authored Sep 19, 2023
1 parent 41f24dc commit bd68509
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 143 deletions.
2 changes: 1 addition & 1 deletion src/Algorithm/branching/single_var_branching.jl
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,4 @@ function Branching.apply_branching_rule(::SingleVarBranchingRule, env::Env, refo
return collection
end
return candidates
end
end
68 changes: 42 additions & 26 deletions src/Algorithm/presolve/helpers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ struct PresolveFormRepr
sense::Vector{ConstrSense} # on constraints
lbs::Vector{Float64} # on variables
ubs::Vector{Float64} # on variables
partial_solution::Vector{Float64} # on variables
lower_multiplicity::Float64
upper_multiplicity::Float64
end

function PresolveFormRepr(coef_matrix, rhs, sense, lbs, ubs, lm, um)
function PresolveFormRepr(coef_matrix, rhs, sense, lbs, ubs, partial_solution, lm, um)
length(lbs) == length(ubs) || throw(ArgumentError("Inconsistent sizes of bounds and coef_matrix."))
length(rhs) == length(sense) || throw(ArgumentError("Inconsistent sizes of rhs and coef_matrix."))
nb_vars = length(lbs)
nb_constrs = length(rhs)
return PresolveFormRepr(
nb_vars, nb_constrs, coef_matrix, transpose(coef_matrix), rhs, sense, lbs, ubs, lm, um
nb_vars, nb_constrs, coef_matrix, transpose(coef_matrix), rhs, sense, lbs, ubs, partial_solution, lm, um
)
end

Expand Down Expand Up @@ -192,10 +193,9 @@ end
function PresolveFormRepr(
form::PresolveFormRepr,
rows_to_deactivate::Vector{Int},
vars_to_fix::Dict{Int, Float64},
tightened_bounds::Dict{Int, Tuple{Float64, Bool, Float64, Bool}},
lm::Float64,
um::Float64
lm,
um
)
nb_cols = form.nb_vars
nb_rows = form.nb_constrs
Expand All @@ -205,19 +205,7 @@ function PresolveFormRepr(
lbs = form.lbs
ubs = form.ubs

col_mask = ones(Bool, nb_cols)
col_mask[collect(keys(vars_to_fix))] .= false
fixed_col_mask = .!col_mask
row_mask = ones(Bool, nb_rows)
row_mask[rows_to_deactivate] .= false

# Deactivate rows
new_coef_matrix = coef_matrix[row_mask, col_mask]

new_rhs = rhs[row_mask]
new_sense = sense[row_mask]

# Tighten Bounds
# Tighten bounds
for (col, (lb, tighter_lb, ub, tighter_ub)) in tightened_bounds
if tighter_lb
lbs[col] = lb
Expand All @@ -227,14 +215,42 @@ function PresolveFormRepr(
end
end

# Fix variables
# Make sure we can fix the variable.
_check_if_vars_can_be_fixed(vars_to_fix, lbs, ubs)

# Update partial solution
fixed_col_mask = zeros(Bool, nb_cols)
nb_fixed_vars = 0
new_partial_sol = zeros(Float64, length(form.partial_solution))
for (i, (lb, ub)) in enumerate(Iterators.zip(form.lbs, form.ubs))
if lb > ub
error("Infeasible.")
end
if lb > 0.0
new_partial_sol[i] += lb
elseif ub < 0.0
new_partial_sol[i] += ub
end
if abs(ub - lb) <= Coluna.TOL
fixed_col_mask[i] = true
nb_fixed_vars += 1
end
end

col_mask = .!fixed_col_mask
nb_cols -= nb_fixed_vars
row_mask = ones(Bool, nb_rows)
row_mask[rows_to_deactivate] .= false

new_sense = sense[row_mask]
new_coef_matrix = coef_matrix[row_mask, col_mask]

# Update rhs
new_rhs = new_rhs - coef_matrix[row_mask, fixed_col_mask] * lbs[fixed_col_mask]
new_lbs = lbs[col_mask]
new_ubs = ubs[col_mask]
new_rhs = rhs[row_mask] - coef_matrix[row_mask, :] * new_partial_sol

# Update bounds
new_lbs = lbs[col_mask] - new_partial_sol[col_mask]
new_ubs = ubs[col_mask] - new_partial_sol[col_mask]

# Update partial_sol
partial_sol = form.partial_solution[col_mask] + new_partial_sol[col_mask]

return PresolveFormRepr(new_coef_matrix, new_rhs, new_sense, new_lbs, new_ubs, lm, um)
return PresolveFormRepr(new_coef_matrix, new_rhs, new_sense, new_lbs, new_ubs, partial_sol, lm, um)
end
38 changes: 21 additions & 17 deletions src/Algorithm/presolve/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ struct PresolveFormulation
constr_to_row::Dict{ConstrId,Int64}
form::PresolveFormRepr
deactivated_constrs::Vector{ConstrId}
fixed_vars::Dict{VarId,Int}
fixed_variables::Dict{VarId, Float64}
end

struct DwPresolveReform
Expand Down Expand Up @@ -49,9 +49,11 @@ function create_presolve_form(

lbs_vals = Float64[]
ubs_vals = Float64[]
partial_sol = Float64[]
for var in col_to_var
push!(lbs_vals, getcurlb(form, var))
push!(ubs_vals, getcurub(form, var))
push!(partial_sol, MathProg.get_value_in_partial_sol(form, var))
end

rhs_vals = Float64[]
Expand All @@ -67,12 +69,12 @@ function create_presolve_form(
sense_vals,
lbs_vals,
ubs_vals,
partial_sol,
lower_multiplicity,
upper_multiplicity
)

deactivated_constrs = ConstrId[]
fixed_vars = Dict{VarId,Float64}()

return PresolveFormulation(
col_to_var,
Expand All @@ -81,18 +83,19 @@ function create_presolve_form(
constr_to_row,
form,
deactivated_constrs,
fixed_vars,
Dict{VarId, Float64}()
)
end

function propagate_in_presolve_form(
form::PresolveFormulation,
rows_to_deactivate::Vector{Int},
vars_to_fix::Dict{Int, Float64},
tightened_bounds::Dict{Int, Tuple{Float64, Bool, Float64, Bool}}
)
fixed_vars = vars_to_fix(form.form, tightened_bounds)

col_mask = ones(Bool, form.form.nb_vars)
col_mask[collect(keys(vars_to_fix))] .= false
col_mask[collect(keys(fixed_vars))] .= false
row_mask = ones(Bool, form.form.nb_constrs)
row_mask[rows_to_deactivate] .= false

Expand All @@ -107,19 +110,14 @@ function propagate_in_presolve_form(
push!(deactivated_constrs, getid(constr))
end

fixed_vars = form.fixed_vars
for (col, val) in vars_to_fix
var_id = getid(form.col_to_var[col])
@assert !haskey(fixed_vars, var_id)
fixed_vars[var_id] = val
end
form_repr = PresolveFormRepr(form.form, rows_to_deactivate, tightened_bounds, form.form.lower_multiplicity, form.form.upper_multiplicity)

return PresolveFormulation(
col_to_var,
row_to_constr,
var_to_col,
constr_to_row,
PresolveFormRepr(form.form, rows_to_deactivate, vars_to_fix, tightened_bounds, form.form.lower_multiplicity, form.form.upper_multiplicity),
form_repr,
deactivated_constrs,
fixed_vars
)
Expand Down Expand Up @@ -209,11 +207,12 @@ function update_form_from_presolve!(form::Formulation, presolve_form::PresolveFo
deactivate!(form, getconstr(form, constr_id))
end

# Fix variables
for (var_id, val) in presolve_form.fixed_vars
# Do not propagate variable fixing! The new rhs of constraints is updated in the
# next step.
# TODO: fix!(form, getvar(form, var_id), val, false)
# Fixed variables
for (var_id, val) in presolve_form.fixed_variables
setcurlb!(form, var_id, 0.0)
setcurub!(form, var_id, 0.0)
MathProg.add_to_partial_solution!(form, var_id, val)
deactivate!(form, var_id)
end

# Update rhs
Expand All @@ -229,6 +228,11 @@ function update_form_from_presolve!(form::Formulation, presolve_form::PresolveFo
setcurlb!(form, presolve_form.col_to_var[col], lb)
setcurub!(form, presolve_form.col_to_var[col], ub)
end

# Update partial solution
for (col, val) in enumerate(presolve_form.form.partial_solution)
MathProg.add_to_partial_solution!(form, presolve_form.col_to_var[col], val)
end
return
end

Expand Down
8 changes: 4 additions & 4 deletions src/Algorithm/presolve/propagation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ function propagate_var_bounds_from!(dest::PresolveFormulation, src::PresolveForm
end

# Look at fixed variable
common_var_ids = intersect(keys(src.fixed_vars), keys(dest.var_to_col))
common_var_ids = intersect(keys(src.fixed_variables), keys(dest.var_to_col))

for var_id in common_var_ids
src_var_val = src.fixed_vars[var_id]
src_var_val = src.fixed_variables[var_id]
dest_col = dest.var_to_col[var_id]
dest.form.lbs[dest_col] = src_var_val
dest.form.ubs[dest_col] = src_var_val
dest.form.lbs[dest_col] = src_var_val - dest.form.partial_solution[dest_col]
dest.form.ubs[dest_col] = src_var_val - dest.form.partial_solution[dest_col]
end
return
end
Loading

0 comments on commit bd68509

Please sign in to comment.