Branch-and-Bound tree functionality

The functionality of the Branch-and-Bound implementation extended from Bonobo.jl and some extended features like strong branching and the callbacks.

Problem

The main problem structure as stored in the Branch-and-Bound tree.

Customized Bonobo structures and functions

Our adaptations to the functions in Bonobo.jl.

Bonobo.optimize!Method
optimize!(tree::BnBTree; callback=(args...; kwargs...)->())

Optimize the problem using a branch and bound approach. The steps, repeated until terminated is true, are the following:

# 1. get the next open node depending on the traverse strategy
node = get_next_node(tree, tree.options.traverse_strategy)
# 2. evaluate the current node and return the lower and upper bound
# if the problem is infeasible both values should be set to NaN
lb, ub = evaluate_node!(tree, node)
# 3. update the upper and lower bound of the node struct
set_node_bound!(tree.sense, node, lb, ub)
# 4. update the best solution
updated = update_best_solution!(tree, node)
updated && bound!(tree, node.id)
# 5. remove the current node
close_node!(tree, node)
# 6. compute the node children and adds them to the tree
# internally calls get_branching_variable and branch_on_variable!
branch!(tree, node)

A callback function can be provided which will be called whenever a node is closed. It always has the arguments tree and node and is called after the node is closed. Additionally the callback function must accept additional keyword arguments (kwargs) which are set in the following ways:

  1. If the node is infeasible the kwarg node_infeasible is set to true.
  2. If the node has a higher lower bound than the incumbent the kwarg worse_than_incumbent is set to true.
source

Node Evaluation

Evaluation of the nodes and handling of branching.

Boscia.FrankWolfeNodeType
FrankWolfeNode <: AbstractFrankWolfeNode

A node in the branch-and-bound tree storing information for a Frank-Wolfe subproblem.

std stores the id, lower and upper bound of the node. active_set store the active set structure. local_bounds instead of storing the complete LMO, it just stores the bounds specific to THIS node. All other integer bounds are stored in the root. 'level' stores the level in the tree 'fwdualgaplimit' set the tolerance for the dual gap in the FW algorithms 'precomputed_set' stores specifically the extreme points computed in DICG for warm-start.

source
Boscia.NodeInfoType
NodeInfo

Holds the necessary information of every node. This needs to be added by every AbstractNode as std::NodeInfo

This variant is more flexibel than Bonobo.BnBNodeInfo.

source

Callbacks

There are two callbacks. One for the Branch-and-Bound tree that records progress data, checks the time limit and prints the logs. The other is a callback for the Frank-Wolfe runs that runs some checks in each iteration. Additionally, the computed vertices are added to the solution pool. Lastly, the Frank-Wolfe solve can be interrupted if either the dual bound has reached the current incumbent or there are enough more promising nodes open.

Boscia.build_FW_callbackMethod

Frank-Wolfe Callback.

Is called in every Frank-Wolfe iteration. Node evaluation can be dynamically stopped here. Time limit is checked. If the vertex is providing a better incumbent, it is added as solution.

source
Boscia.build_bnb_callbackMethod

Branch-and-Bound Callback. Collects statistics and prints logs if verbose is turned on.

Output of Boscia: iter : current iteration of Boscia node id : current node id lower bound : treelb(tree) incumbent : tree.incumbent gap : tree.incumbent-treelb(tree) rel. gap : dualgap/tree.incumbent time : total time of Boscia time/nodes : average time per node FW time : time spent in FW LMO time : time used by LMO LMO calls : number of computeextreme_point calls in FW FW iterations : number of iterations in FW

source

Tightenings

Tightenings are performed on node level and can be used either just for the node in question or globally. If the obejctive is strongly convex and/or sharp, this can also be used to tighten the lower bound at the current node.

Boscia.prune_childrenMethod

Use strong convexity and/or sharpness to potentially remove one of the children nodes. If both sharpness and strong convexity parameters are provided, strong convexity is preferred.

source

Strong and Hybrid Branching

We provide a strong branching strategy consisting of running Frank-Wolfe for only a few iterations to get an estimate of the bound increase. Due to the cost of strong branching, it is usually not advisable to run strong branching through the whole tree. Hence, we provide a hybrid branching which performs strong branching until a user specified depth and then switches to most-infeasible branching.

Boscia.HybridStrongBranchingType

Hybrid between partial strong branching and another strategy. perform_strong_branch(tree, node) -> Bool decides whether to perform strong branching or not.

source
Bonobo.get_branching_variableMethod

Get branching variable using strong branching. Create all possible subproblems, solve them and pick the one with the most progress.

source
Boscia.strong_up_to_depthFunction

strongupto_depth performs strong branching on nodes up to a predetermined depth, and the falls back to another rule

source