|
|
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
|
|
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
|
|
# Released under the modified BSD license. See COPYING.md for more details.
|
|
|
|
|
|
"""
|
|
|
ReliabilityBranching
|
|
|
|
|
|
Branching strategy that uses pseudocosts if there are enough observations
|
|
|
to make an accurate prediction of strong branching scores, or runs the
|
|
|
actual strong branching routine if not enough observations are available.
|
|
|
"""
|
|
|
Base.@kwdef mutable struct ReliabilityBranching <: VariableBranchingRule
|
|
|
min_samples::Int = 8
|
|
|
max_sb_calls::Int = 100
|
|
|
look_ahead::Int = 10
|
|
|
n_sb_calls::Int = 0
|
|
|
side_effect::Bool = true
|
|
|
max_iterations::Int = 1_000_000
|
|
|
end
|
|
|
|
|
|
function find_branching_var(
|
|
|
rule::ReliabilityBranching,
|
|
|
node::Node,
|
|
|
pool::NodePool,
|
|
|
)::Variable
|
|
|
nfrac = length(node.fractional_variables)
|
|
|
pseudocost_scores = [
|
|
|
_pseudocost_score(
|
|
|
node,
|
|
|
pool,
|
|
|
node.fractional_variables[j],
|
|
|
node.fractional_values[j],
|
|
|
) for j = 1:nfrac
|
|
|
]
|
|
|
σ = sortperm(pseudocost_scores, rev = true)
|
|
|
sorted_vars = node.fractional_variables[σ]
|
|
|
_set_node_bounds(node)
|
|
|
no_improv_count, n_sb_calls = 0, 0
|
|
|
max_score, max_var = pseudocost_scores[σ[1]], sorted_vars[1]
|
|
|
for (i, var) in enumerate(sorted_vars)
|
|
|
use_strong_branch = true
|
|
|
if n_sb_calls >= rule.max_sb_calls
|
|
|
use_strong_branch = false
|
|
|
else
|
|
|
if var in keys(pool.var_history)
|
|
|
varhist = pool.var_history[var]
|
|
|
hlength = min(length(varhist.obj_ratio_up), length(varhist.obj_ratio_down))
|
|
|
if hlength >= rule.min_samples
|
|
|
use_strong_branch = false
|
|
|
end
|
|
|
end
|
|
|
end
|
|
|
if use_strong_branch
|
|
|
n_sb_calls += 1
|
|
|
rule.n_sb_calls += 1
|
|
|
score = _strong_branch_score(
|
|
|
node = node,
|
|
|
pool = pool,
|
|
|
var = var,
|
|
|
x = node.fractional_values[σ[i]],
|
|
|
side_effect = rule.side_effect,
|
|
|
max_iterations = rule.max_iterations
|
|
|
)
|
|
|
else
|
|
|
score = pseudocost_scores[σ[i]]
|
|
|
end
|
|
|
if score > max_score
|
|
|
max_score, max_var = score, var
|
|
|
no_improv_count = 0
|
|
|
else
|
|
|
no_improv_count += 1
|
|
|
end
|
|
|
no_improv_count <= rule.look_ahead || break
|
|
|
end
|
|
|
_unset_node_bounds(node)
|
|
|
return max_var
|
|
|
end
|