Initializing help system before first use

Visualize the BB tree


Type: Programming
Rating: 2 (easy-medium)
Description: Shows how to visualize the BB tree of a problem after (partially) solving it.
File(s): bbtree.py


bbtree.py
# This example shows how to visualize the Branch-and-Bound (B&B) tree
# of a problem after (partially) solving it. It assumes that all
# variables to be branched on in the problem are binary variables.
#
# (C) 1983-2025 Fair Isaac Corporation

import networkx as nx
import xpress as xp
import time

from matplotlib import pyplot as plt

def message_addtime (prob, data, msg, info):
    """Message callback example: print a timestamp before the message from
    the optimizer."""
    if msg:
        for submsg in msg.split('\n'):
            print("{0:6.3f}: [{2:+4d}] {1}".format(time.time() - start_time,
                submsg, info))

def postorder_count(node):
    """Recursively count nodes to compute the cardinality of a subtree for
    each node."""
    card = 0

    if node in left.keys():  # see if node has a left key
        postorder_count(left[node])
        card += card_subtree[left[node]]

    if node in right.keys():
        postorder_count(right[node])
        card += card_subtree[right[node]]

    card_subtree[node] = 1 + card

def setpos(T, node, curpos, st_width, depth):
    """Set position depending on cardinality of each subtree."""
    # Special condition: we are at the root.
    if node == 1:
        T.add_node(node, pos=(0.5, 1))

    # Use a convex combination of subtree comparison and
    # depth to assign a width to each subtree.
    alpha = .1

    if node in left.keys():
        # X position in the graph should not just depend on depth,
        # otherwise we'd see a long and thin subtree, and it would just
        # look like a path.

        leftwidth = st_width * (alpha * .5 + (1 - alpha) *
                                card_subtree[left[node]] /
                                card_subtree[node])
        leftpos = curpos - (st_width - leftwidth) / 2

        T.add_node(left[node], pos=(leftpos, - depth))
        T.add_edge(node, left[node])
        setpos(T, left[node], leftpos, leftwidth, depth + 1)

    if node in right.keys():

        rightwidth = st_width * (alpha * .5 + (1 - alpha) *
                                 card_subtree[right[node]] /
                                 card_subtree[node])
        rightpos = curpos + (st_width - rightwidth) / 2

        T.add_node(right[node], pos=(rightpos, - depth))
        T.add_edge(node, right[node])
        setpos(T, right[node], rightpos, rightwidth, depth + 1)

def storeBBnode(prob, Tree, parent, newnode, branch):

    # Tree is the callback data, and it's equal to T.
    if branch == 0:
        left[parent] = newnode
    else:
        right[parent] = newnode

T = nx.Graph()

left = {}
right = {}
card_subtree = {}
pos = {}

start_time = time.time()

p = xp.problem()
p.addMessageCallback(message_addtime)

p.readProb('Data/sampleprob.mps.gz')
p.addNewnodeCallback(storeBBnode, T, 100)
p.controls.maxnode = 40000  # Limit the number of nodes inserted in the graph.
p.optimize()

postorder_count(1)  # Assign card_subtree to each node.

# Determine the position of each node
# depending on subtree cardinalities.
setpos(T, 1, 0.5, 1, 0)

pos = nx.get_node_attributes(T, 'pos')

nx.draw(T, pos)  # Create BB tree representation.
plt.show()       # Display it; you can zoom indefinitely and see all subtrees.

© 2001-2025 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.