Skip to content

Commit

Permalink
- Updated MasterColumnsUnit, StaticVarConstrUnit. (#1103)
Browse files Browse the repository at this point in the history
- Added PartialSolutionUnit
  • Loading branch information
rrsadykov authored Oct 25, 2023
1 parent 585d5d7 commit 9c44c75
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 219 deletions.
1 change: 1 addition & 0 deletions src/Algorithm/basic/solveipform.jl
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ function get_units_usage(
push!(units_usage, (form, MasterColumnsUnit, READ_ONLY))
push!(units_usage, (form, MasterBranchConstrsUnit, READ_ONLY))
push!(units_usage, (form, MasterCutsUnit, READ_ONLY))
push!(units_usage, (form, PartialSolutionUnit, READ_ONLY))
end
return units_usage
end
Expand Down
1 change: 1 addition & 0 deletions src/Algorithm/basic/solvelpform.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function get_units_usage(
push!(units_usage, (form, StaticVarConstrUnit, READ_ONLY))
if Duty <: MathProg.AbstractMasterDuty
push!(units_usage, (form, MasterColumnsUnit, READ_ONLY))
push!(units_usage, (form, PartialSolutionUnit, READ_ONLY))
push!(units_usage, (form, MasterBranchConstrsUnit, READ_ONLY))
push!(units_usage, (form, MasterCutsUnit, READ_ONLY))
end
Expand Down
1 change: 1 addition & 0 deletions src/Algorithm/benders.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ function get_units_usage(algo::BendersCutGeneration, reform::Reformulation)

# TO DO : everything else should be communicated by the child algorithms
push!(units_usage, (master, StaticVarConstrUnit, READ_ONLY))
push!(units_usage, (master, PartialSolutionUnit, READ_ONLY))

Check warning on line 105 in src/Algorithm/benders.jl

View check run for this annotation

Codecov / codecov/patch

src/Algorithm/benders.jl#L105

Added line #L105 was not covered by tests
push!(units_usage, (master, MasterBranchConstrsUnit, READ_ONLY))
push!(units_usage, (master, MasterColumnsUnit, READ_ONLY))
for (_, spform) in get_benders_sep_sps(reform)
Expand Down
1 change: 1 addition & 0 deletions src/Algorithm/colgen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ function get_units_usage(algo::ColumnGeneration, reform::Reformulation)
master = getmaster(reform)
push!(units_usage, (master, MasterColumnsUnit, READ_AND_WRITE))
push!(units_usage, (master, StaticVarConstrUnit, READ_ONLY))
push!(units_usage, (master, PartialSolutionUnit, READ_ONLY))

# as column generation may call essential cut callbacks
# TO DO: it would be good to verify first whether any callback is really defined
Expand Down
102 changes: 71 additions & 31 deletions src/Algorithm/formstorages.jl
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
"""
VarState
StaticVarState
Used in formulation records
"""
struct VarState
struct StaticVarState
cost::Float64
lb::Float64
ub::Float64
partial_sol_value::Float64
end

function apply_state!(form::Formulation, var::Variable, var_state::VarState)
# To avoid warnings when changing variable bounds.
# Commented, as this line makes var.curdata.is_in_partial_sol and form.manager.partial_solution asynchronous (source of diving with LDS bug)
# var.curdata.is_in_partial_sol = false

function apply_state!(form::Formulation, var::Variable, var_state::StaticVarState)
if getcurlb(form, var) != var_state.lb
setcurlb!(form, var, var_state.lb)
end
Expand All @@ -24,9 +19,6 @@ function apply_state!(form::Formulation, var::Variable, var_state::VarState)
if getcurcost(form, var) != var_state.cost
setcurcost!(form, var, var_state.cost)
end
if MathProg.get_value_in_partial_sol(form, var) != var_state.partial_sol_value
MathProg.set_value_in_partial_solution!(form, var, var_state.partial_sol_value)
end
return
end

Expand Down Expand Up @@ -118,8 +110,11 @@ Can be restored using a MasterColumnsRecord.
struct MasterColumnsUnit <: AbstractRecordUnit end

mutable struct MasterColumnsRecord <: AbstractRecord
cols::Dict{VarId,VarState}
active_cols::Set{VarId}

MasterColumnsRecord() = new(Set{VarId}())
end
push!(record::MasterColumnsRecord, id::VarId) = push!(record.active_cols, id)

struct MasterColumnsKey <: AbstractStorageUnitKey end

Expand All @@ -129,16 +124,10 @@ record_type_from_key(::MasterColumnsKey) = MasterColumnsRecord
ClB.storage_unit(::Type{MasterColumnsUnit}, _) = MasterColumnsUnit()

function ClB.record(::Type{MasterColumnsRecord}, id::Int, form::Formulation, unit::MasterColumnsUnit)
record = MasterColumnsRecord(Dict{VarId,ConstrState}())
record = MasterColumnsRecord()
for (id, var) in getvars(form)
if getduty(id) <= MasterCol && isexplicit(form, var) && iscuractive(form, var)
varstate = VarState(
getcurcost(form, var),
getcurlb(form, var),
getcurub(form, var),
MathProg.get_value_in_partial_sol(form, var)
)
record.cols[id] = varstate
push!(record, id)
end
end
return record
Expand All @@ -152,16 +141,12 @@ function ClB.restore_from_record!(
)
for (id, var) in getvars(form)
if getduty(id) <= MasterCol && isexplicit(form, var)
if haskey(state.cols, id)
if id in state.active_cols
if !iscuractive(form, var)
activate!(form, var)
end
apply_state!(form, var, state.cols[id])
else
if iscuractive(form, var)
if !iszero(MathProg.get_value_in_partial_sol(form, var))
MathProg.set_value_in_partial_solution!(form, var, 0.0)
end
deactivate!(form, var)
end
end
Expand Down Expand Up @@ -245,7 +230,7 @@ StaticVarConstrUnit(::Formulation) = StaticVarConstrUnit()

mutable struct StaticVarConstrRecord <: AbstractRecord
constrs::Dict{ConstrId,ConstrState}
vars::Dict{VarId,VarState}
vars::Dict{VarId,StaticVarState}
end

# TO DO: we need to keep here only the difference with the initial data
Expand All @@ -271,7 +256,7 @@ ClB.storage_unit(::Type{StaticVarConstrUnit}, _) = StaticVarConstrUnit()

function ClB.record(::Type{StaticVarConstrRecord}, id::Int, form::Formulation, unit::StaticVarConstrUnit)
@logmsg LogLevel(-2) string("Storing static vars and consts")
record = StaticVarConstrRecord(Dict{ConstrId,ConstrState}(), Dict{VarId,VarState}())
record = StaticVarConstrRecord(Dict{ConstrId,ConstrState}(), Dict{VarId,StaticVarState}())
for (id, constr) in getconstrs(form)
if isaStaticDuty(getduty(id)) && iscuractive(form, constr) && isexplicit(form, constr)
constrstate = ConstrState(getcurrhs(form, constr))
Expand All @@ -280,11 +265,10 @@ function ClB.record(::Type{StaticVarConstrRecord}, id::Int, form::Formulation, u
end
for (id, var) in getvars(form)
if isaStaticDuty(getduty(id)) && isexplicit(form, var) && iscuractive(form, var)
varstate = VarState(
varstate = StaticVarState(
getcurcost(form, var),
getcurlb(form, var),
getcurub(form, var),
MathProg.get_value_in_partial_sol(form, var)
getcurub(form, var)
)
record.vars[id] = varstate
end
Expand Down Expand Up @@ -321,7 +305,7 @@ function ClB.restore_from_record!(
if isaStaticDuty(getduty(id)) && isexplicit(form, var)
@logmsg LogLevel(-4) "Checking " getname(form, var)
if haskey(record.vars, id)
if !iscuractive(form, var) #&& !isfixed(form, var)
if !iscuractive(form, var)
@logmsg LogLevel(-4) string("Activating variable", getname(form, var))
activate!(form, var)
end
Expand All @@ -336,3 +320,59 @@ function ClB.restore_from_record!(
end
end
end

"""
PartialSolutionUnit
Unit for current the partial solution of a formulation.
Can be restored using a PartialSolutionRecord.
"""

struct PartialSolutionUnit <: AbstractRecordUnit end

PartialSolutionUnit(::Formulation) = PartialSolutionUnit()

Check warning on line 333 in src/Algorithm/formstorages.jl

View check run for this annotation

Codecov / codecov/patch

src/Algorithm/formstorages.jl#L333

Added line #L333 was not covered by tests

mutable struct PartialSolutionRecord <: AbstractRecord
partial_solution::Dict{VarId, Float64}

PartialSolutionRecord(form::Formulation) = new(copy(MathProg.getpartialsol(form)))
end

struct PartialSolutionKey <: AbstractStorageUnitKey end

key_from_storage_unit_type(::Type{PartialSolutionUnit}) = PartialSolutionKey()
record_type_from_key(::PartialSolutionKey) = PartialSolutionRecord

ClB.storage_unit(::Type{PartialSolutionUnit}, _) = PartialSolutionUnit()

function ClB.record(::Type{PartialSolutionRecord}, id::Int, form::Formulation, unit::PartialSolutionUnit)
return PartialSolutionRecord(form)
end

ClB.record_type(::Type{PartialSolutionUnit}) = PartialSolutionRecord
ClB.storage_unit_type(::Type{PartialSolutionRecord}) = PartialSolutionUnit

function ClB.restore_from_record!(
form::Formulation, ::PartialSolutionUnit, record::PartialSolutionRecord
)
@logmsg LogLevel(-2) "Restoring partial solution"
form_part_sol = MathProg.getpartialsol(form)
change_dict = Dict{VarId, Float64}()
for (var_id, cur_value) in form_part_sol
record_value = get(record.partial_solution, var_id, 0.0)
if cur_value != record_value
change_dict[var_id] = record_value
end
end

for (var_id, record_value) in record.partial_solution
if !haskey(form_part_sol, var_id)
change_dict[var_id] = record_value
end
end

for (var_id, value) in change_dict
@logmsg LogLevel(-4) string("Changing value of ", getname(form, var_id), " in partial solution to ", value)
MathProg.set_value_in_partial_solution!(form, var_id, value)
end
end
12 changes: 8 additions & 4 deletions src/Algorithm/presolve/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,6 @@ function update_partial_sol!(form::Formulation{DwMaster}, presolve_form::Presolv
""")
end
if !iszero(val) && (duty <= MasterCol || duty <= MasterPureVar)
println("update partial sol for variable $(getname(form, var)).")
MathProg.add_to_partial_solution!(form, var, val)
partial_sol_counter += 1
setcurlb!(form, var, 0.0)
Expand Down Expand Up @@ -370,6 +369,7 @@ function get_units_usage(algo::PresolveAlgorithm, reform::Reformulation)
units_usage = Tuple{AbstractModel, UnitType, UnitPermission}[]
master = getmaster(reform)
push!(units_usage, (master, StaticVarConstrUnit, READ_AND_WRITE))
push!(units_usage, (master, PartialSolutionUnit, READ_AND_WRITE))

Check warning on line 372 in src/Algorithm/presolve/interface.jl

View check run for this annotation

Codecov / codecov/patch

src/Algorithm/presolve/interface.jl#L372

Added line #L372 was not covered by tests
push!(units_usage, (master, MasterBranchConstrsUnit, READ_AND_WRITE))
push!(units_usage, (master, MasterCutsUnit, READ_AND_WRITE))
push!(units_usage, (master, MasterColumnsUnit, READ_AND_WRITE))
Expand Down Expand Up @@ -550,9 +550,7 @@ function deactivate_non_proper_columns!(master::Formulation{DwMaster}, dw_sps)
if getduty(varid) <= MasterCol
spid = getoriginformuid(varid)
if !_column_is_proper(varid, dw_sps[spid])
if !in_partial_solution(master, varid)
deactivate!(master, varid)
end
deactivate!(master, varid)

Check warning on line 553 in src/Algorithm/presolve/interface.jl

View check run for this annotation

Codecov / codecov/patch

src/Algorithm/presolve/interface.jl#L548-L553

Added lines #L548 - L553 were not covered by tests
end
end
end
Expand Down Expand Up @@ -627,3 +625,9 @@ function _column_is_proper(col_id, sp_form)
end
return true
end

function column_is_proper(col_id, reform)
sp_id = getoriginformuid(col_id)
sp_form = get_dw_pricing_sps(reform)[sp_id]
return _column_is_proper(col_id, sp_form)

Check warning on line 632 in src/Algorithm/presolve/interface.jl

View check run for this annotation

Codecov / codecov/patch

src/Algorithm/presolve/interface.jl#L629-L632

Added lines #L629 - L632 were not covered by tests
end
20 changes: 10 additions & 10 deletions src/MathProg/varconstr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ subsolver.
If the variable had fixed value, it unfixes the variable.
"""
function setcurlb!(form::Formulation, var::Variable, lb)
if in_partial_sol(form, var) && !(getduty(getid(var)) <= MasterCol)
@warn "Changing lower bound of fixed variable."
end
# if in_partial_sol(form, var) && !(getduty(getid(var)) <= MasterCol)
# @warn "Changing lower bound of fixed variable."
# end

var.curdata.lb = lb
if isexplicit(form, var) && iscuractive(form, var)
Expand Down Expand Up @@ -194,9 +194,9 @@ subsolver.
If the variable had fixed value, it unfixes the variable.
"""
function setcurub!(form::Formulation, var::Variable, ub)
if in_partial_sol(form, var)
@warn "Changing upper bound of fixed variable."
end
# if in_partial_sol(form, var)
# @warn "Changing upper bound of fixed variable."
# end

var.curdata.ub = ub
if isexplicit(form, var) && iscuractive(form, var)
Expand Down Expand Up @@ -244,15 +244,15 @@ function add_to_partial_solution!(form::Formulation, varid::VarId, value, propag
end

function add_to_partial_solution!(form::Formulation, var::Variable, value, propagation = false)
if isexplicit(form, var) && iscuractive(form, var)
if isexplicit(form, var)
cumulative_val = _add_partial_value!(form.manager, var, value)
if propagation
_propagate_partial_value_bounds!(form, var, cumulative_val)
end
return true
end
name = getname(form, var)
@warn "Cannot add variable $name to partial solution because it is unactive or non-explicit."
@warn "Cannot add variable $name to partial solution because it is non-explicit."

Check warning on line 255 in src/MathProg/varconstr.jl

View check run for this annotation

Codecov / codecov/patch

src/MathProg/varconstr.jl#L255

Added line #L255 was not covered by tests
return false
end

Expand All @@ -263,12 +263,12 @@ function set_value_in_partial_solution!(form::Formulation, varid::VarId, value)
end

function set_value_in_partial_solution!(form::Formulation, var::Variable, value)
if isexplicit(form, var) && iscuractive(form, var)
if isexplicit(form, var)
_set_partial_value!(form.manager, var, value)
return true
end
name = getname(form, var)
@warn "Cannot set variable $name to partial solution because it is unactive or non-explicit."
@warn "Cannot set variable $name to partial solution because it is non-explicit."

Check warning on line 271 in src/MathProg/varconstr.jl

View check run for this annotation

Codecov / codecov/patch

src/MathProg/varconstr.jl#L271

Added line #L271 was not covered by tests
return false
end

Expand Down
40 changes: 9 additions & 31 deletions test/unit/Algorithm/record_mastercolumns.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
function unit_master_columns_record()
function test_record(state, cost, lb, ub)
@test state.cost == cost
@test state.lb == lb
@test state.ub == ub
end

function test_var(form, var, cost, lb, ub)
@test ClMP.getcurcost(form, var) == cost
@test ClMP.getcurlb(form, var) == lb
@test ClMP.getcurub(form, var) == ub
end

env = CL.Env{ClMP.VarId}(CL.Params())

Expand Down Expand Up @@ -44,33 +33,22 @@ function unit_master_columns_record()
storage = ClB.getstorage(form)
r1 = ClB.create_record(storage, ClA.MasterColumnsUnit)

@test isempty(setdiff(keys(r1.cols), ClMP.getid.(values(vars))))
test_record(r1.cols[ClMP.getid(vars["v1"])], 1, 0, Inf)
test_record(r1.cols[ClMP.getid(vars["v2"])], 2, 0, Inf)
test_record(r1.cols[ClMP.getid(vars["v3"])], 4, 0, Inf)
@test isempty(setdiff(r1.active_cols, ClMP.getid.(values(vars))))

# make changes on the formulation
ClMP.setcurlb!(form, vars["v1"], 5.0)
ClMP.setcurub!(form, vars["v2"], 12.0)
ClMP.setcurcost!(form, vars["v3"], 4.6)
ClMP.deactivate!(form, vars["v2"])

r2 = ClB.create_record(storage, ClA.MasterColumnsUnit)

@test isempty(setdiff(keys(r2.cols), ClMP.getid.(values(vars))))
test_record(r2.cols[ClMP.getid(vars["v1"])], 1, 5, Inf)
test_record(r2.cols[ClMP.getid(vars["v2"])], 2, 0, 12)
test_record(r2.cols[ClMP.getid(vars["v3"])], 4.6, 0, Inf)
v1v3 = Set{ClMP.VarId}([ClMP.getid(vars["v1"]), ClMP.getid(vars["v3"])])
@test isempty(setdiff(r2.active_cols, v1v3))

ClB.restore_from_record!(storage, r1)

test_var(form, vars["v1"], 1, 0, Inf)
test_var(form, vars["v2"], 2, 0, Inf)
test_var(form, vars["v3"], 4, 0, Inf)
active_varids = filter(var_id -> iscuractive(form, var_id), keys(ClMP.getvars(form)))
@test isempty(setdiff(active_varids, ClMP.getid.(values(vars))))

ClB.restore_from_record!(storage, r2)

test_var(form, vars["v1"], 1, 5, Inf)
test_var(form, vars["v2"], 2, 0, 12)
test_var(form, vars["v3"], 4.6, 0, Inf)
active_varids = filter(var_id -> iscuractive(form, var_id), keys(ClMP.getvars(form)))
@test isempty(setdiff(active_varids, v1v3))
end
register!(unit_tests, "master_columns_record", unit_master_columns_record)
register!(unit_tests, "storage_record", unit_master_columns_record, f = true)
Loading

0 comments on commit 9c44c75

Please sign in to comment.