Skip to content

Commit

Permalink
Merge pull request #286 from atoptima/optimizationoutput
Browse files Browse the repository at this point in the history
New structures for Solutions & Results
  • Loading branch information
guimarqu authored Mar 13, 2020
2 parents 07eb29e + 12968c8 commit ff46bbd
Show file tree
Hide file tree
Showing 40 changed files with 1,399 additions and 1,015 deletions.
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
## Documentation: http://docs.travis-ci.com/user/languages/julia/

language: julia

os:
- linux
# - osx

julia:
- 1.1
- 1.2
- 1.3

notifications:
email: false
Expand All @@ -32,12 +32,12 @@ script: # the default script is equivalent to the following
- julia --project=test/ -e 'using Pkg; Pkg.clone("https://github.com/atoptima/ColunaDemos.jl.git"); Pkg.clone(pwd()); Pkg.build("Coluna"); Pkg.test("Coluna"; coverage=true)';

after_success:
- julia -e 'using Pkg; Pkg.add("Coverage"); cd(Pkg.dir("Coluna")); using Coverage; Codecov.submit(Codecov.process_folder())'
- julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())'

jobs:
include:
- stage: "Documentation"
julia: 1.2
julia: 1.3
os: linux
script:
- julia --project=docs/ -e 'using Pkg; Pkg.clone("https://github.com/atoptima/BlockDecomposition.jl.git"); Pkg.clone("https://github.com/atoptima/ColunaDemos.jl.git"); Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
Expand Down
43 changes: 30 additions & 13 deletions src/Algorithm/Algorithm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,16 @@ import DataStructures
import MathOptInterface
import TimerOutputs

import ..Coluna
using ..Coluna
using ..Containers
using ..MathProg

# To be deleted :
import .MathProg: getsense, optimize! # because of branch

# TO be deleted ???
import .MathProg: FeasibilityStatus, TerminationStatus, AbstractStorage, EmptyStorage, getstorage, setprimalbound!, setdualbound!
import .MathProg: OPTIMAL, TIME_LIMIT, NODE_LIMIT, OTHER_LIMIT, EMPTY_RESULT, NOT_YET_DETERMINED
import ..Coluna: AbstractSense

import .MathProg: get_lp_primal_sol, get_lp_dual_bound, setfeasibilitystatus!, setterminationstatus!, getvalue
import .MathProg: getvalue

using Logging
using Printf
Expand All @@ -27,17 +24,25 @@ const MOI = MathOptInterface

import Base: push!

# Abstract algorithm
include("interface.jl")
# Import to extend methods to OptimizationState
import .MathProg: getfeasibilitystatus, getterminationstatus, setfeasibilitystatus!,
setterminationstatus!, isfeasible, get_ip_primal_bound, get_ip_dual_bound,
get_lp_primal_bound, get_lp_dual_bound, update_ip_primal_bound!, update_ip_dual_bound!,
update_lp_primal_bound!, update_lp_dual_bound!, set_ip_primal_bound!,
set_ip_dual_bound!, set_lp_primal_bound!, set_lp_dual_bound!, ip_gap

# Utilities to build algorithms
include("utilities/optimizationstate.jl")
include("utilities/record.jl")

# Abstract record
include("record.jl")
# Abstract algorithm (interface should be moved in Containers)
include("interface.jl")

# Here include slave algorithms used by conquer algorithms
include("solveipform.jl")
include("solvelpform.jl")
include("colgen.jl")
include("benders.jl")
include("masteripheur.jl")
include("masterlp.jl")
include("preprocessing.jl")

# Here include conquer algorithms
Expand All @@ -55,9 +60,21 @@ include("branching/branchingalgo.jl")

include("treesearch.jl")

# Types
# Algorithm should export only methods usefull to define & parametrize algorithms, and
# data structures from utilities.
# Other Coluna's submodules should be independent to Algorithm

# Utilities
export OptimizationState, getterminationstatus, getfeasibilitystatus, setterminationstatus!,
setfeasibilitystatus!, isfeasible, nb_ip_primal_sols, nb_lp_primal_sols, nb_lp_dual_sols,
get_ip_primal_sols, get_lp_primal_sols, get_lp_dual_sols, get_best_ip_primal_sol,
get_best_lp_primal_sol, get_best_lp_dual_sol, update_ip_primal_sol!,
update_lp_primal_sol!, update_lp_dual_sol!, add_ip_primal_sol!, add_lp_primal_sol!,
add_lp_dual_sol!, set_ip_primal_sol!, set_lp_primal_sol!, set_lp_dual_sol!

# Algorithm's types
export AbstractOptimizationAlgorithm, TreeSearchAlgorithm, ColGenConquer, ColumnGeneration,
BendersConquer, BendersCutGeneration, MasterIpHeuristic, ExactBranchingPhase,
BendersConquer, BendersCutGeneration, SolveIpForm, SolveLpForm, ExactBranchingPhase,
OnlyRestrictedMasterBranchingPhase

end
98 changes: 50 additions & 48 deletions src/Algorithm/benders.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ Base.@kwdef struct BendersCutGeneration <: AbstractOptimizationAlgorithm
end

mutable struct BendersCutGenRuntimeData
incumbents::Incumbents
incumbents::OptimizationState
has_converged::Bool
is_feasible::Bool
spform_phase::Dict{Int, FormulationPhase}
spform_phase_applied::Dict{Int, Bool}
spform_phase::Dict{FormId, FormulationPhase}
spform_phase_applied::Dict{FormId, Bool}
#slack_cost_increase::Float64
#slack_cost_increase_applied::Bool
end
Expand All @@ -23,37 +23,38 @@ function all_sp_in_phase2(algdata::BendersCutGenRuntimeData)
return true
end

function BendersCutGenRuntimeData(S::Type{<:Coluna.AbstractSense}, node_inc::Incumbents)
i = Incumbents(S)
update_ip_primal_sol!(i, get_ip_primal_sol(node_inc))

function BendersCutGenRuntimeData(form::Reformulation, node_inc::OptimizationState)
i = OptimizationState(getmaster(form))
if nb_ip_primal_sols(node_inc) > 0
add_ip_primal_sol!(i, get_best_ip_primal_sol(node_inc))
end
return BendersCutGenRuntimeData(i, false, true, Dict{FormId, FormulationPhase}(), Dict{FormId, Bool}())#0.0, true)
end

function run!(algo::BendersCutGeneration, reform::Reformulation, input::OptimizationInput)::OptimizationOutput
function run!(algo::BendersCutGeneration, reform::Reformulation, input::NewOptimizationInput)::OptimizationOutput

initincumb = getincumbents(input)
data = BendersCutGenRuntimeData(reform.master.obj_sense, initincumb)
init_res = getinputresult(input)
data = BendersCutGenRuntimeData(reform, init_res)
@logmsg LogLevel(-1) "Run BendersCutGeneration."
Base.@time bend_rec = bend_cutting_plane_main_loop!(algo, data, reform)

Sense = getsense(initincumb)
ip_primal_sols = Vector{PrimalSolution{Sense}}()
if length(get_ip_primal_sol(data.incumbents)) > 0
push!(ip_primal_sols, get_ip_primal_sol(data.incumbents))
end

return OptimizationOutput(
OptimizationResult{Sense}(
data.has_converged ? OPTIMAL : OTHER_LIMIT,
data.is_feasible ? FEASIBLE : INFEASIBLE,
get_ip_primal_bound(data.incumbents), get_ip_dual_bound(data.incumbents),
ip_primal_sols, Vector{DualSolution{Sense}}()
),
get_lp_primal_sol(data.incumbents),
get_lp_dual_bound(data.incumbents)
runtime_res = data.incumbents
result = OptimizationState(
getmaster(reform),
feasibility_status = data.is_feasible ? FEASIBLE : INFEASIBLE,
termination_status = data.has_converged ? OPTIMAL : OTHER_LIMIT,
ip_dual_bound = get_ip_dual_bound(runtime_res),
lp_dual_bound = get_lp_dual_bound(runtime_res)
)

if nb_lp_primal_sols(runtime_res) > 0
add_lp_primal_sol!(result, get_best_lp_primal_sol(runtime_res))
end
if nb_ip_primal_sols(runtime_res) > 0
for ip_primal_sol in get_ip_primal_sols(runtime_res)
add_ip_primal_sol!(result, ip_primal_sol)
end
end
return OptimizationOutput(result)
end

function update_benders_sp_slackvar_cost_for_ph1!(spform::Formulation)
Expand Down Expand Up @@ -93,8 +94,8 @@ end

function update_benders_sp_problem!(
algo::BendersCutGeneration, algdata::BendersCutGenRuntimeData, spform::Formulation,
master_primal_sol::PrimalSolution{S}, master_dual_sol::DualSolution{S}
) where {S}
master_primal_sol::PrimalSolution, master_dual_sol::DualSolution
)
masterform = spform.parent_formulation

# Update rhs of technological constraints
Expand Down Expand Up @@ -162,8 +163,8 @@ end

function record_solutions!(
algo::BendersCutGeneration, algdata::BendersCutGenRuntimeData, spform::Formulation,
spresult::OptimizationResult{S}
)::Vector{ConstrId} where {S}
spresult::MoiResult
)::Vector{ConstrId}

recorded_dual_solution_ids = Vector{ConstrId}()

Expand Down Expand Up @@ -213,8 +214,8 @@ function insert_cuts_in_master!(
end

function compute_benders_sp_lagrangian_bound_contrib(
algdata::BendersCutGenRuntimeData, spform::Formulation, spsol::OptimizationResult{S}
) where {S}
algdata::BendersCutGenRuntimeData, spform::Formulation, spsol::MoiResult
)
dualsol = getbestdualsol(spsol)
contrib = getvalue(dualsol)
return contrib
Expand All @@ -223,10 +224,10 @@ end
function solve_sp_to_gencut!(
algo::BendersCutGeneration, algdata::BendersCutGenRuntimeData,
masterform::Formulation, spform::Formulation,
master_primal_sol::PrimalSolution{S}, master_dual_sol::DualSolution{S},
master_primal_sol::PrimalSolution, master_dual_sol::DualSolution,
up_to_phase::FormulationPhase
)::Tuple{Bool, Bool, Vector{ConstrId}, Float64, Float64} where {S}

)::Tuple{Bool, Bool, Vector{ConstrId}, Float64, Float64}
S = getobjsense(masterform)
recorded_dual_solution_ids = Vector{ConstrId}()
sp_is_feasible = true

Expand Down Expand Up @@ -278,7 +279,7 @@ function solve_sp_to_gencut!(
benders_sp_lagrangian_bound_contrib = compute_benders_sp_lagrangian_bound_contrib(algdata, spform, optresult)

primalsol = getbestprimalsol(optresult)
spsol_relaxed = contains(spform, primalsol, BendSpSlackFirstStageVar)
spsol_relaxed = contains(primalsol, varid -> getduty(varid) == BendSpSlackFirstStageVar)

benders_sp_primal_bound_contrib = 0.0
# compute benders_sp_primal_bound_contrib which stands for the sum of nu var,
Expand Down Expand Up @@ -347,9 +348,9 @@ end

function solve_sps_to_gencuts!(
algo::BendersCutGeneration, algdata::BendersCutGenRuntimeData,
reform::Reformulation, master_primalsol::PrimalSolution{S},
master_dualsol::DualSolution{S}, up_to_phase::FormulationPhase
) where {S}
reform::Reformulation, master_primalsol::PrimalSolution,
master_dualsol::DualSolution, up_to_phase::FormulationPhase
)

nb_new_cuts = 0
spsols_relaxed = false
Expand Down Expand Up @@ -399,16 +400,16 @@ end


function compute_master_pb_contrib(algdata::BendersCutGenRuntimeData, master::Formulation,
restricted_master_sol_value::DualBound{S}) where {S}
restricted_master_sol_value)
# TODO: will change with stabilization
return PrimalBound(master, getvalue(restricted_master_sol_value))
return PrimalBound(master, restricted_master_sol_value)
end

function update_lagrangian_pb!(algdata::BendersCutGenRuntimeData, reform::Reformulation,
restricted_master_sol_dual_sol::DualSolution{S},
benders_sp_sp_primal_bound_contrib) where {S}
restricted_master_sol_dual_sol::DualSolution,
benders_sp_sp_primal_bound_contrib)
master = getmaster(reform)
restricted_master_sol_value = getbound(restricted_master_sol_dual_sol)
restricted_master_sol_value = getvalue(restricted_master_sol_dual_sol)
lagran_bnd = PrimalBound(master, 0.0)
lagran_bnd += compute_master_pb_contrib(algdata, master, restricted_master_sol_value)
lagran_bnd += benders_sp_sp_primal_bound_contrib
Expand All @@ -425,9 +426,10 @@ end

function generatecuts!(
algo::BendersCutGeneration, algdata::BendersCutGenRuntimeData, reform::Reformulation,
master_primal_sol::PrimalSolution{S}, master_dual_sol::DualSolution{S}, phase::FormulationPhase
)::Tuple{Int, Bool, PrimalBound{S}} where {S}
master_primal_sol::PrimalSolution, master_dual_sol::DualSolution, phase::FormulationPhase
)::Tuple{Int, Bool, PrimalBound}
masterform = getmaster(reform)
S = getobjsense(masterform)
filtered_dual_sol = filter(elem -> getduty(elem[1]) == MasterPureConstr, master_dual_sol)

## TODO stabilization : move the following code inside a loop
Expand All @@ -442,7 +444,8 @@ function generatecuts!(
end
# end TODO
#primal_bound = PrimalBound(masterform, getvalue(master_primal_sol) + getvalue(pb_correction))
primal_bound = getbound(master_primal_sol) + pb_correction
val = getvalue(master_primal_sol) + pb_correction
primal_bound = PrimalBound(masterform, float(val))
#setvalue!(master_primal_sol, getvalue(master_primal_sol) + pb_correction)
return nb_new_cuts, spsols_relaxed, primal_bound
end
Expand Down Expand Up @@ -573,7 +576,6 @@ function bend_cutting_plane_main_loop!(
end
if sol_integer
update_ip_primal_sol!(algdata.incumbents, master_primal_sol)
update_ip_primal_bound!(algdata.incumbents, primal_bound)
end
end
return
Expand Down
39 changes: 21 additions & 18 deletions src/Algorithm/branching/branchingalgo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ end
function run!(algo::NoBranching, reform::Reformulation, input::DivideInput)::DivideOutput
parent = getparent(input)
parent_incumb = getincumbents(parent)
Sense = getsense(parent_incumb)
result = OptimizationResult{Sense}()
result = OptimizationState(getmaster(reform))
return DivideOutput([], result)
end

Expand Down Expand Up @@ -91,7 +90,7 @@ end

function perform_strong_branching_with_phases!(
algo::StrongBranching, reform::Reformulation, parent::Node,
groups::Vector{BranchingGroup}, result::OptimizationResult
groups::Vector{BranchingGroup}, result::OptimizationState
)
for (phase_index, current_phase) in enumerate(algo.phases)
nb_candidates_for_next_phase::Int64 = 1
Expand Down Expand Up @@ -186,13 +185,12 @@ end

function run!(algo::StrongBranching, reform::Reformulation, input::DivideInput)::DivideOutput
parent = getparent(input)
parent_incumb = getincumbents(parent)
sense = getsense(parent_incumb)
result = OptimizationResult{sense}()
setprimalbound!(result, input.ip_primal_bound)
setdualbound!(result, get_ip_dual_bound(parent_incumb))
parent_incumb_res = getincumbentresult(parent)
result = OptimizationState(getmaster(reform))
set_ip_primal_bound!(result, input.ip_primal_bound)
set_ip_dual_bound!(result, get_ip_dual_bound(parent_incumb_res))
if isempty(algo.rules)
@logmsg LogLevel(0) "No branching rule is defined. No children will be generated."
@logmsg LogLevel(1) "No branching rule is defined. No children will be generated."
return DivideOutput([], result)
end

Expand All @@ -204,13 +202,18 @@ function run!(algo::StrongBranching, reform::Reformulation, input::DivideInput):

# we obtain the original and extended solutions
master = getmaster(reform)
original_solution = PrimalSolution{sense}()
extended_solution = PrimalSolution{sense}()
if projection_is_possible(master)
extended_solution = get_lp_primal_sol(parent.incumbents)
original_solution = proj_cols_on_rep(extended_solution, master)
original_solution = PrimalSolution(getmaster(reform))
extended_solution = PrimalSolution(getmaster(reform))
if nb_lp_primal_sols(parent_incumb_res) > 0
if projection_is_possible(master)
extended_solution = get_best_lp_primal_sol(parent_incumb_res)
original_solution = proj_cols_on_rep(extended_solution, master)
else
original_solution = get_best_lp_primal_sol(parent_incumb_res)
end
else
original_solution = get_lp_primal_sol(parent.incumbents)
@logmsg LogLevel(1) "No branching candidates found. No children will be generated."
return DivideOutput(Vector{Node}(), result)
end

# phase 0 of branching : we ask branching rules to generate branching candidates
Expand Down Expand Up @@ -244,7 +247,7 @@ function run!(algo::StrongBranching, reform::Reformulation, input::DivideInput):
nb_candidates_found += length(output.groups)
append!(kept_branch_groups, output.groups)
local_id = output.local_id

if projection_is_possible(master)
output = run!(rule, reform, BranchingRuleInput(
extended_solution, false, nb_candidates_needed, algo.selection_criterion, local_id
Expand All @@ -264,9 +267,9 @@ function run!(algo::StrongBranching, reform::Reformulation, input::DivideInput):
resize!(kept_branch_groups, nb_candidates_needed)
end
end

if isempty(kept_branch_groups)
@logmsg LogLevel(0) "No branching candidates found. No children will be generated."
@logmsg LogLevel(1) "No branching candidates found. No children will be generated."
return DivideOutput(Vector{Node}(), result)
end

Expand Down
Loading

0 comments on commit ff46bbd

Please sign in to comment.