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

Strategies rework #67

Merged
merged 3 commits into from
May 14, 2019
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
23 changes: 13 additions & 10 deletions src/Coluna.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ import Base.copy
import Base.promote_rule
import Base.convert
import Base.isinteger
import Base.push!

include("types.jl")
include("solvers/solver.jl")
include("strategies/strategy.jl")

include("parameters.jl")
include("counters.jl")

Expand All @@ -59,28 +63,27 @@ include("problem.jl")
include("decomposition.jl")
include("MOIinterface.jl")

###### Solvers & Strategies
include("solvers/solver.jl")
include("strategies/strategy.jl")
# Concrete Solvers & Strategies :

# here include solvers
# Here include solvers
include("solvers/colgen.jl")
include("solvers/masteripheur.jl")
include("solvers/generatechildrennodes.jl")

# here include conquer strategies
include("strategies/conquer/strategy.jl")
# Here include conquer strategies
include("strategies/conquer/simplebnp.jl")

# here include divide strategies
include("strategies/divide/strategy.jl")
# Here include divide strategies
include("strategies/divide/simplebranching.jl")

##### Search tree
# Here include tree search strategies
include("strategies/treesearch/simplestrategies.jl")

# Search tree
include("node.jl")
include("bbtree.jl")

##### Wrapper functions
# Wrapper functions
include("MOIwrapper.jl")

include("globals.jl") # Structure that holds values useful in all the procedure
Expand Down
10 changes: 5 additions & 5 deletions src/MOIinterface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,12 @@ function fill_dual_sol(moi_optimizer::MOI.AbstractOptimizer,
end

function call_moi_optimize_with_silence(optimizer::MOI.AbstractOptimizer)
#backup_stdout = stdout
#(rd_out, wr_out) = redirect_stdout()
backup_stdout = stdout
(rd_out, wr_out) = redirect_stdout()
MOI.optimize!(optimizer)
#close(wr_out)
#close(rd_out)
#redirect_stdout(backup_stdout)
close(wr_out)
close(rd_out)
redirect_stdout(backup_stdout)
return
end

Expand Down
39 changes: 15 additions & 24 deletions src/bbtree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,20 @@ end

struct SearchTree
nodes::DS.PriorityQueue{Node, Float64}
priority_function::Function
search_strategy::Type{<:AbstractTreeSearchStrategy}
end

SearchTree(search_strategy::SEARCHSTRATEGY) = SearchTree(
DS.PriorityQueue{Node, Float64}(Base.Order.Forward),
build_priority_function(search_strategy)
SearchTree(search_strategy::Type{<:AbstractTreeSearchStrategy}) = SearchTree(
DS.PriorityQueue{Node, Float64}(Base.Order.Forward), search_strategy
)

getnodes(t::SearchTree) = t.nodes
Base.isempty(t::SearchTree) = isempty(t.nodes)

function pushnode!(t::SearchTree, node::Node)
DS.enqueue!(
t.nodes, node, t.priority_function(node)
)
end

push!(t::SearchTree, node::Node) = DS.enqueue!(t.nodes, node, apply!(t.search_strategy, node))
popnode!(t::SearchTree) = DS.dequeue!(t.nodes)
nb_open_nodes(t::SearchTree) = length(t.nodes)

function build_priority_function(strategy::SEARCHSTRATEGY)
strategy == DepthFirst && return x->(-x.depth)
strategy == BestDualBound && return x->(get_lp_dual_bound(x.incumbents))
end

mutable struct TreeSolver <: AbstractReformulationSolver
primary_tree::SearchTree
secondary_tree::SearchTree
Expand All @@ -42,7 +31,7 @@ mutable struct TreeSolver <: AbstractReformulationSolver
incumbents::Incumbents
end

function TreeSolver(search_strategy::SEARCHSTRATEGY,
function TreeSolver(search_strategy::Type{<:AbstractTreeSearchStrategy},
ObjSense::Type{<:AbstractObjSense})
return TreeSolver(
SearchTree(search_strategy), SearchTree(DepthFirst),
Expand All @@ -54,7 +43,7 @@ get_primary_tree(s::TreeSolver) = s.primary_tree
get_secondary_tree(s::TreeSolver) = s.secondary_tree
cur_tree(s::TreeSolver) = (s.in_primary ? s.primary_tree : s.secondary_tree)
Base.isempty(s::TreeSolver) = isempty(cur_tree(s))
pushnode!(s::TreeSolver, node::Node) = pushnode!(cur_tree(s), node)
push!(s::TreeSolver, node::Node) = push!(cur_tree(s), node)
popnode!(s::TreeSolver) = popnode!(cur_tree(s))
nb_open_nodes(s::TreeSolver) = (nb_open_nodes(s.primary_tree)
+ nb_open_nodes(s.secondary_tree))
Expand Down Expand Up @@ -89,12 +78,14 @@ function setup_node!(n::Node, treat_order::Int, tree_incumbents::Incumbents)
end

function apply(::Type{<:TreeSolver}, reform::Reformulation)
tree_solver = TreeSolver(_params_.search_strategy, reform.master.obj_sense)
pushnode!(tree_solver, RootNode(reform.master.obj_sense))
# Get all strategies
conquer_strategy = reform.strategy.conquer
divide_strategy = reform.strategy.divide
tree_search_strategy = reform.strategy.tree_search

tree_solver = TreeSolver(tree_search_strategy, reform.master.obj_sense)
push!(tree_solver, RootNode(reform.master.obj_sense))

# Node strategy
conquer_strategy = SimpleBnP # Should be kept in reformulation?
divide_strategy = SimpleBranching
strategy_rec = StrategyRecord()

while (!isempty(tree_solver)
Expand Down Expand Up @@ -155,15 +146,15 @@ function update_tree_solver(s::TreeSolver, n::Node)
s.nb_treated_nodes += 1
t = cur_tree(s)
if !to_be_pruned(n)
pushnode!(t, n)
push!(t, n)
end
if ((nb_open_nodes(s) + length(n.children))
>= _params_.open_nodes_limit)
switch_tree(s)
t = cur_tree(s)
end
for idx in length(n.children):-1:1
pushnode!(t, pop!(n.children))
push!(t, pop!(n.children))
end
updatebounds!(s, n)
end
Expand Down
4 changes: 2 additions & 2 deletions src/decomposition.jl
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ function build_dw_pricing_sp!(prob::Problem,
end

function reformulate!(prob::Problem, annotations::Annotations,
method::SolutionMethod)
strategy::GlobalStrategy)
# This function must be cleaned.
# subproblem formulations are modified in the function build_dw_master

Expand All @@ -175,7 +175,7 @@ function reformulate!(prob::Problem, annotations::Annotations,
annotation_set = annotations.annotation_set

# Create reformulation
reformulation = Reformulation(prob, method)
reformulation = Reformulation(prob, strategy)
set_re_formulation!(prob, reformulation)

# Create master formulation
Expand Down
7 changes: 1 addition & 6 deletions src/parameters.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
@enum(SEARCHSTRATEGY, DepthFirst, BestDualBound)
@enum(NODEEVALMODE, SimplexCg, Lp)
@enum(ARTVARSMOE, Global, Local)

Base.@kwdef mutable struct Params
max_num_nodes::Int = 10000
open_nodes_limit::Int = 100000
integrality_tolerance::Float64 = 1e-5
cut_up::Float64 = Inf
cut_lo::Float64 = -Inf
search_strategy::SEARCHSTRATEGY = DepthFirst
force_copy_names::Bool = false
art_vars_mode::ARTVARSMOE = Global
global_strategy::GlobalStrategy = GlobalStrategy()
end

update_field!(f_v::Tuple{Symbol,Any}) = setfield!(_params_, f_v[1], f_v[2])
Expand Down
2 changes: 1 addition & 1 deletion src/problem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function coluna_initialization(prob::Problem, annotations::Annotations,
params::Params)
_welcome_message()
_set_global_params(params)
reformulate!(prob, annotations, DantzigWolfeDecomposition)
reformulate!(prob, annotations, params.global_strategy)
relax_integrality!(prob.re_formulation.master)
initialize_moi_optimizer(prob)
@info "Coluna initialized."
Expand Down
10 changes: 5 additions & 5 deletions src/reformulation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Representation of a formulation which is solved by Coluna using a decomposition approach. All the sub-structures are defined within the struct `Reformulation`.
"""
mutable struct Reformulation <: AbstractFormulation
solution_method::SolutionMethod
strategy::GlobalStrategy
parent::Union{Nothing, AbstractFormulation} # reference to (pointer to) ancestor: Formulation or Reformulation
master::Union{Nothing, Formulation}
dw_pricing_subprs::Vector{AbstractFormulation} # vector of Formulation or Reformulation
Expand All @@ -17,15 +17,15 @@ end

Constructs a `Reformulation`.
"""
Reformulation(prob::AbstractProblem) = Reformulation(prob, DirectMip)
Reformulation(prob::AbstractProblem) = Reformulation(prob, GlobalStrategy())

"""
Reformulation(prob::AbstractProblem, method::SolutionMethod)

Constructs a `Reformulation` that shall be solved using the `SolutionMethod` `method`.
Constructs a `Reformulation` that shall be solved using the `GlobalStrategy` `strategy`.
"""
function Reformulation(prob::AbstractProblem, method::SolutionMethod)
return Reformulation(method,
function Reformulation(prob::AbstractProblem, strategy::GlobalStrategy)
return Reformulation(strategy,
nothing,
nothing,
Vector{AbstractFormulation}(),
Expand Down
8 changes: 0 additions & 8 deletions src/solvers/solver.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
"""
AbstractSolver

A solver is an 'text-book' algorithm applied to a formulation in a node.
"""
abstract type AbstractSolver end

"""
AbstractSolverRecord

Expand All @@ -28,7 +21,6 @@ Runs the solver `SolverType` on the `formulation` in a `node` with `parameters`.
"""
function run! end


# Fallbacks
function prepare!(T::Type{<:AbstractSolver}, formulation, node, strategy_rec, parameters)
error("prepare! method not implemented for solver $T.")
Expand Down
17 changes: 0 additions & 17 deletions src/strategies/conquer/strategy.jl

This file was deleted.

17 changes: 0 additions & 17 deletions src/strategies/divide/strategy.jl

This file was deleted.

78 changes: 76 additions & 2 deletions src/strategies/strategy.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
abstract type AbstractStrategy end

mutable struct StrategyRecord
cur_solver::Type{<:AbstractSolver}
ext::Dict{Symbol, Any}
Expand All @@ -9,3 +7,79 @@ StrategyRecord() = StrategyRecord(StartNode, Dict{Symbol, Any}())

setsolver!(r::StrategyRecord, s::Type{<:AbstractSolver}) = r.cur_solver = s
getsolver(r::StrategyRecord) = r.cur_solver

"""
AbstractConquerStrategy

Conquer strategy is a combination of `Solvers` to treat a node of the
branch-and-bound tree.
"""
abstract type AbstractConquerStrategy <: AbstractStrategy end

"""
AbstractDivideStrategy

Divide strategy is a combination of `Solvers`that generates one or more children
branch-and-bound node.
"""
abstract type AbstractDivideStrategy <: AbstractStrategy end

"""
AbstractTreeSearchStrategy

A TreeSearchStrategy defines how the branch-and-bound tree shall be
searhed. To define a concrete `AbstractTreeSearchStrategy` one must define the function
`apply!(strategy::Type{<:AbstractTreeSearchStrategy}, n::Node)`.
"""
abstract type AbstractTreeSearchStrategy <: AbstractStrategy end

"""
apply!(S::Type{<:AbstractStrategy}, args...)
guimarqu marked this conversation as resolved.
Show resolved Hide resolved

Applies the strategy `S` to whatever context such strategy is defined for.


apply!(::Type{<:AbstractDivideStrategy}, reformulation, node, strategy_record, params)

Applies the divide strategy on a `reformulation` in the `node` with `parameters`.

apply!(::Type{<:AbstractConquerStrategy}, reformulation, node, strategy_record, params)

Applies the conquer strategy on a `reformulation` in the `node` with `parameters`.

apply!(S::Type{<:AbstractTreeSearchStrategy}, n::Node)::Real

computes the `Node` `n` preference to be treated according to
the strategy type `S` and returns the corresponding Real number.
"""
function apply! end

# Fallback
function apply!(S::Type{<:AbstractStrategy}, args...)
error("Method apply! not implemented for conquer strategy $S.")
end

"""
GlobalStrategy

A GlobalStrategy encapsulates all three strategies necessary to define Coluna's behavious
in solving a `Reformulation`. Each `Reformulation` keeps an objecto of type GlobalStrategy.
"""
struct GlobalStrategy <: AbstractStrategy
conquer::Type{<:AbstractConquerStrategy}
divide::Type{<:AbstractDivideStrategy}
tree_search::Type{<:AbstractTreeSearchStrategy}
end

"""
GlobalStrategy()

Constructs a default GlobalStrategy using the strategies
`SimpleBnP` as Conquer Strategy, `SimpleBranching` as DivideStrategy
and `DepthFirst` as TreeSearchStrategy.
"""
GlobalStrategy() = GlobalStrategy(SimpleBnP, SimpleBranching, DepthFirst)

function apply!(S::Type{<:GlobalStrategy}, args...)
error("Method apply! is not supposed to be implemented for the Global Strategies.")
end
7 changes: 7 additions & 0 deletions src/strategies/treesearch/simplestrategies.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Depth-first strategy
struct DepthFirst <: AbstractTreeSearchStrategy end
apply!(::Type{DepthFirst}, n::AbstractNode) = (-n.depth)

# Best dual bound strategy
struct BestDualBound <: AbstractTreeSearchStrategy end
apply!(::Type{BestDualBound}, n::AbstractNode) = get_ip_dual_bound(getincumbents(n))
Loading