Branch and Bound for Integer Linear Programming
Introduction
In previous lectures, we explored the ideal scenario in Integer Linear Programming (ILP), where the integrality constraint is implicitly satisfied by solving the Linear Programming (LP) relaxation. While this simplifies the problem significantly, it is not a common occurrence in practical applications. Therefore, it is crucial to develop algorithms that can effectively address problems where the LP relaxation does not directly yield integer solutions.
The Branch and Bound method emerges as a powerful, efficient, and conceptually intuitive technique for solving such problems. Although the fundamental principles of Branch and Bound might be known from other areas, we will focus on its application within the realm of optimization, particularly for ILP models. It is worth noting that Branch and Bound is a versatile optimization technique with a wider scope of applicability beyond ILP, extending to various other optimization problems.
The Need for Branch and Bound
Limitations of Linear Programming Relaxation
As previously discussed, solving the Linear Programming (LP) relaxation of an Integer Linear Program (ILP) may occasionally produce an integer solution. In these instances, the integrality constraint, often difficult to satisfy, is fulfilled without extra effort. However, this is an uncommon occurrence. In most ILP problems, the optimal solution to the LP relaxation is not integer. This necessitates the use of methods that can systematically search for and identify optimal integer solutions when simple relaxation fails to do so directly. The Branch and Bound algorithm is specifically designed to address these scenarios.
NP-Hardness of Integer Linear Programming
Integer Linear Programming (ILP) is classified as NP-hard, implying that exact algorithms for solving ILP problems are expected to exhibit exponential worst-case time complexity. A simplistic approach to solving ILP problems might involve complete enumeration, examining every possible integer solution. However, complete enumeration is computationally expensive, being exponential in all cases, not just the worst case. This renders it impractical for problems of any significant size.
Thus, a more refined approach than brute-force enumeration is needed. The objective is to perform a form of "virtual" complete enumeration, implicitly considering all possible integer solutions without explicitly listing or evaluating each one individually. By intelligently grouping and considering sets of solutions, the aim is to circumvent the exponential overhead of explicit enumeration while still ensuring that the optimal integer solution is found.
Core Concepts of Branch and Bound
The Branch and Bound method efficiently explores the solution space of integer linear programming problems through implicit enumeration of candidate solutions. The core idea is to systematically divide the problem into smaller subproblems (branching) and calculate bounds on the optimal solutions within these subproblems (bounding). This allows pruning parts of the search space that cannot contain the optimal solution, significantly reducing the search effort compared to exhaustive enumeration.
Divide and Conquer Strategy
Branch and Bound employs a "divide and conquer" strategy, breaking down a complex problem into simpler, more manageable subproblems. In ILP, this is achieved by partitioning the feasible region into smaller, disjoint regions. Each subproblem is a restriction of the original problem to one of these smaller regions. The idea is that solving these simpler subproblems will eventually lead to the solution of the original, more complex problem.
Implicit Enumeration
The method performs an implicit enumeration of all feasible integer solutions. Instead of explicitly checking every possible integer solution, Branch and Bound intelligently explores subsets of solutions, aiming to eliminate large portions of the solution space without explicitly examining each solution within them. This is crucial for managing the combinatorial explosion inherent in integer programming.
The efficiency of Branch and Bound stems from its ability to discard entire sets of solutions at once. This is achieved by establishing bounds on the optimal objective value within each subproblem. If a bound indicates that the best solution in a subproblem cannot be better than a solution already found, then that subproblem (and all its descendants in the search tree) can be pruned, effectively eliminating a potentially vast number of solutions from consideration.
Branching: Partitioning the Solution Space
Branching is the process of dividing the feasible region of a problem into smaller, disjoint subregions. This is typically performed when the solution to the LP relaxation of a problem is not integer. The goal of branching is to create subproblems that are "closer" to having integer solutions or at least easier to analyze.
Creating Disjoint Subproblems
To partition the solution space, we select a variable that has a fractional value in the optimal solution of the current LP relaxation. Let’s say in the optimal LP solution, a variable \(x_j\) has a fractional value \(x_j^*\). To enforce integrality, we create two disjoint subproblems by adding constraints on \(x_j\):
**Subproblem 1:** We constrain \(x_j\) to be less than or equal to the greatest integer less than or equal to \(x_j^*\), i.e., \(x_j \leq \lfloor x_j^* \rfloor\).
**Subproblem 2:** We constrain \(x_j\) to be greater than or equal to the smallest integer greater than or equal to \(x_j^*\), i.e., \(x_j \geq \lceil x_j^* \rceil\).
These two constraints ensure that the fractional value \(x_j^*\) is excluded, and any integer solution must satisfy either \(x_j \leq \lfloor x_j^* \rfloor\) or \(x_j \geq \lceil x_j^* \rceil\). This process partitions the feasible region into two disjoint parts without losing any integer solutions.
Recursive Application
Branching is applied recursively. After creating subproblems, we solve the LP relaxation of each subproblem. If a subproblem’s LP relaxation yields an integer solution, we have a candidate integer solution. If it yields a fractional solution, we can branch again on another fractional variable, further dividing the problem. This recursive partitioning creates a tree structure, where each node represents a subproblem, and the branches represent the partitioning process.
Bounding: Estimating Solution Quality
Bounding is the process of finding a bound on the optimal objective value for each subproblem. This bound estimates the best possible solution that can be obtained from exploring the current subproblem and its descendants. For maximization problems, we typically calculate an optimistic estimate, which is an upper bound on the optimal solution value.
Optimistic Estimates (Upper Bounds for Maximization)
For a maximization problem, an optimistic estimate is an upper bound. We can obtain such a bound by solving the LP relaxation of the subproblem. Let \(P_i\) be the LP relaxation of a subproblem \(X_i\). The optimal value of the LP relaxation, \(z(P_i)\), is an upper bound on the optimal value of the integer problem \(X_i\), \(z(X_i)\). This is because the feasible region of \(P_i\) contains the feasible region of \(X_i\), and we are maximizing the same objective function.
Mathematically, if we are maximizing \(cx\) subject to \(x \in X_i\), and \(P_i\) is the LP relaxation of \(X_i\), then:
\[\max_{x \in X_i} cx \leq \max_{x \in P_i} cx\]
The value \(\max_{x \in P_i} cx\) serves as an optimistic estimate (upper bound) for the optimal value of the integer problem \(X_i\).
Pruning Subproblems
Bounding enables us to prune subproblems, which is the key to the efficiency of Branch and Bound. Pruning occurs when we can determine that a subproblem cannot lead to a better integer solution than the best one found so far. There are two main scenarios for pruning:
Infeasibility: If the LP relaxation \(P_i\) of a subproblem \(X_i\) is infeasible, then \(X_i\) is also infeasible. In this case, we can prune the subproblem because it contains no feasible solutions.
Bounding Condition: Suppose we have already found a feasible integer solution, say \(x_{incumbent}\), with objective value \(z_{incumbent} = cx_{incumbent}\). For a maximization problem, if the upper bound from the LP relaxation of a subproblem \(X_i\), i.e., \(z(P_i)\), is less than or equal to the current incumbent value \(z_{incumbent}\), then we can prune subproblem \(X_i\). This is because no integer solution in \(X_i\) can have an objective value greater than \(z(P_i)\), and thus, none can be better than the current best solution \(x_{incumbent}\).
Pruning significantly reduces the search space by eliminating subproblems that are guaranteed not to contain a better solution.
Formalization of Branch and Bound for ILP
To formalize the Branch and Bound method for Integer Linear Programming (ILP), we need to define the notation, strategies for branching and bounding, and outline the algorithm.
Notation and Definitions
Let’s establish the notation used in the Branch and Bound algorithm for ILP.
Integer Solution Set (\(X_0\))
Let \(X_0\) denote the set of feasible integer solutions for the original Integer Linear Programming problem. Formally,where \(A\) is an \(m \times n\) matrix, \(b \in \mathbb{R}^m\), and we aim to solve the problem: \[\max \{ cx \mid x \in X_0 \}\]
Linear Relaxation (\(P_0\))
Let \(P_0\) be the linear programming relaxation of the original problem, obtained by relaxing the integrality constraints.The LP relaxation problem is: \[\max \{ cx \mid x \in P_0 \}\]
Subproblems (\(X_i\)) and Relaxations (\(P_i\))
During the Branch and Bound process, we generate a series of subproblems. Let \(X_i\) represent the set of integer solutions for the \(i\)-th subproblem, which is a subset of \(X_0\) due to added constraints from branching. Let \(P_i\) be the LP relaxation of the \(i\)-th subproblem. \(P_i\) is obtained by adding constraints to \(P_0\) corresponding to the branching decisions made to define \(X_i\).
Branching Strategy
The branching strategy is crucial for effectively partitioning the solution space. A common and effective strategy is to branch on a fractional variable.
Selecting a Fractional Variable
When solving the LP relaxation \(P_i\) of a subproblem, if the optimal solution \(x^*_i\) is not integer, we need to select a variable that is fractional to branch on. A common heuristic is to choose a variable \(x_j\) whose value \(x^*_{ij}\) is fractional. A further refinement is to select the variable that is "most fractional," i.e., the fractional part is closest to 0.5. For example, if we have fractional variables, we might choose the one for which the fractional part \(\min \{x^*_{ij} - \lfloor x^*_{ij} \rfloor, \lceil x^*_{ij} \rceil - x^*_{ij} \}\) is maximized.
Creating Subproblems with Added Constraints
Once a fractional variable \(x_j\) is selected, we create two new subproblems. If \(x^*_{ij}\) is the fractional value of \(x_j\) in the optimal solution of \(P_i\), we create two subproblems by adding the following constraints to \(P_i\):
**Subproblem \(i_1\) (e.g., \(P_{i_1}\)):** Add the constraint \(x_j \leq \lfloor x^*_{ij} \rfloor\).
**Subproblem \(i_2\) (e.g., \(P_{i_2}\)):** Add the constraint \(x_j \geq \lceil x^*_{ij} \rceil\).
These constraints are mutually exclusive and collectively cover all integer solutions that were feasible in the parent problem \(P_i\). This process is repeated recursively for the new subproblems until integer solutions are found or subproblems are pruned.
Bounding Strategy
The bounding strategy provides the criteria for pruning subproblems. We use the optimal value of the LP relaxation as an upper bound for maximization problems.
Using Linear Programming Relaxation
For each subproblem \(P_i\), we solve its LP relaxation. Let \(z(P_i) = \max \{ cx \mid x \in P_i \}\) be the optimal value of the LP relaxation. If \(P_i\) is infeasible, we set \(z(P_i) = -\infty\). If the optimal solution \(x^*_i\) of \(P_i\) is integer, then \(x^*_i\) is a feasible integer solution for the subproblem \(X_i\), and \(z(P_i)\) is a lower bound on the optimal value for \(X_i\) and an upper bound for all subproblems branched from \(P_i\) before integrality was enforced.
Comparing Bounds with Incumbent Solution
We maintain a record of the best integer solution found so far, called the incumbent solution, \(x_{incumbent}\), and its objective value, \(z_{incumbent} = cx_{incumbent}\). Initially, \(z_{incumbent} = -\infty\) (for maximization). When we solve the LP relaxation \(P_i\) and obtain an optimal value \(z(P_i)\), we compare it with \(z_{incumbent}\).
Pruning Based on Bounds
We prune a subproblem \(P_i\) under the following conditions:
Infeasibility Pruning: If \(P_i\) is infeasible, we prune \(P_i\).
Bound Pruning: If \(z(P_i) \leq z_{incumbent}\), we prune \(P_i\). This is because no solution in \(X_i\) can be better than the current incumbent solution.
Integer Solution Pruning: If the optimal solution \(x^*_i\) of \(P_i\) is integer, we have found a feasible integer solution. We update the incumbent solution if \(z(P_i) > z_{incumbent}\). In this case, we have found the optimal solution for the subproblem \(X_i\), and we do not need to branch further from \(P_i\). We can prune \(P_i\) after updating the incumbent if necessary.
Branch and Bound Algorithm
The Branch and Bound algorithm systematically explores the solution space using branching and bounding. Here is a pseudocode overview and implementation details.
Pseudocode Overview
The algorithm uses a list of active subproblems to explore. Initially, this list contains only the original problem’s LP relaxation.
Initialization
Initialize Incumbent: Set incumbent solution \(x_{incumbent} \gets \text{null}\) and incumbent value \(z_{incumbent} \gets -\infty\).
Initialize Active Problem List: Set the list of active problems \(L \gets \{P_0\}\), where \(P_0\) is the LP relaxation of the original ILP.
Main Loop
Select and Remove: Select a problem \(P_i\) from \(L\) and remove it from \(L\). Solve LP Relaxation: Solve the LP relaxation \(P_i\). Let \(x^*_i\) be the optimal solution and \(z(P_i)\) be the optimal value. Infeasibility Check: If \(P_i\) is infeasible, prune \(P_i\) and continue to the next iteration. Bounding and Pruning: If \(z(P_i) \leq z_{incumbent}\), prune \(P_i\) and continue to the next iteration. Integer Solution Check and Update: If \(x^*_i\) is integer, then:
If \(z(P_i) > z_{incumbent}\), update \(x_{incumbent} \gets x^*_i\) and \(z_{incumbent} \gets z(P_i)\).
Prune \(P_i\) (no need to branch further).
Branching: If \(x^*_i\) is fractional and \(z(P_i) > z_{incumbent}\), select a fractional variable \(x_j\) in \(x^*_i\). Create two new subproblems \(P_{i_1}\) and \(P_{i_2}\) by adding constraints \(x_j \leq \lfloor x^*_{ij} \rfloor\) and \(x_j \geq \lceil x^*_{ij} \rceil\) to \(P_i\), respectively. Add \(P_{i_1}\) and \(P_{i_2}\) to \(L\).
Termination
Check for Optimality: When \(L\) is empty, the algorithm terminates.
Infeasibility: If \(z_{incumbent} = -\infty\), then the original ILP is infeasible.
Optimal Solution: Otherwise, \(x_{incumbent}\) is the optimal integer solution, and \(z_{incumbent}\) is the optimal objective value.
Implementation Details
Several implementation choices can significantly affect the performance of the Branch and Bound algorithm.
List Management Strategies
The choice of how to manage the list of active problems \(L\) (step 2 in the main loop) can influence the search strategy. Common strategies include:
LIFO (Depth-First Search)
Using a stack for \(L\) implements Depth-First Search (DFS). This strategy explores the search tree deeply. It tends to find feasible solutions quickly and requires less memory since only the problems along the current path in the search tree need to be stored.
FIFO (Breadth-First Search)
Using a queue for \(L\) implements Breadth-First Search (BFS). This strategy explores the search tree level by level. It can provide better bounds earlier in the process but may require more memory to store all problems at the current level.
Priority Queue
Using a priority queue for \(L\) allows selecting the next problem based on a priority criterion. A common priority is the bound \(z(P_i)\). Selecting the problem with the highest bound (for maximization) is often effective, as it prioritizes exploring subproblems that are more likely to yield better solutions. This is sometimes referred to as best-first search.
Variable Selection for Branching
The choice of fractional variable to branch on (step 7 in the main loop) can also impact performance.
Most Fractional Variable
A common heuristic is to select the variable \(x_j\) whose fractional part is closest to 0.5. This aims to create balanced subproblems and potentially lead to faster convergence. The fractional part can be measured as \(\min \{x^*_{ij} - \lfloor x^*_{ij} \rfloor, \lceil x^*_{ij} \rceil - x^*_{ij} \}\). Maximizing this value over all fractional variables selects the "most fractional" one.
Illustrative Example
Let’s illustrate the Branch and Bound algorithm with a copier shop example.
Problem Description: Copier Shop
A copier shop prepares and sells course handouts for two courses. They have limited resources: 240 grams of glue, 1000 sheets of paper, and 3 hours of labor.
Resources and Constraints
Course 1 Handouts: Require 60g of glue, 100 sheets of paper, and 50 minutes of labor per handout.
Course 2 Handouts: Require 40g of glue, 200 sheets of paper, and 20 minutes of labor per handout.
Resource Limits: 240g glue, 1000 sheets of paper, 180 minutes (3 hours) labor.
Profit Maximization
The profit is $1.90 per Course 1 handout and $1.00 per Course 2 handout. The goal is to maximize the total profit.
Mathematical Formulation
Let \(x_1\) be the number of Course 1 handouts and \(x_2\) be the number of Course 2 handouts.
Decision Variables
\(x_1\): Number of Course 1 handouts (integer, \(x_1 \geq 0\))
\(x_2\): Number of Course 2 handouts (integer, \(x_2 \geq 0\))
Objective Function
Maximize the profit: \[\max \quad 1.9x_1 + x_2\]
Constraints
Subject to the resource constraints: \[\begin{aligned} 60x_1 + 40x_2 &\leq 240 \quad &\text{(Glue constraint)} \\ 100x_1 + 200x_2 &\leq 1000 \quad &\text{(Paper constraint)} \\ 50x_1 + 20x_2 &\leq 180 \quad &\text{(Labor constraint)} \\ x_1, x_2 &\geq 0 \quad &\text{(Non-negativity)} \\ x_1, x_2 &\in \Z \quad &\text{(Integrality)} \end{aligned}\]
Graphical Representation
The feasible region and integer solutions can be visualized graphically for this two-variable problem.
Vertices of the Relaxation and Integer Hull
The vertices of the LP relaxation are the extreme points of the feasible region when integrality constraints are relaxed. The integer hull is the convex hull of all feasible integer points, which is the tightest possible LP relaxation for the ILP. For this problem, the vertices of the LP relaxation are approximately: (0,0), (0,5), (1, 4.5), (3, 1.5), (3.6, 0). The vertices of the integer hull are: (0,0), (0,5), (1,4), (2,3), (3,1), (3,0).
Step-by-Step Branch and Bound Execution
Let’s trace the Branch and Bound algorithm using Depth-First Search (LIFO) for this example.
Solving the Initial Relaxation (\(P_0\))
Solving the LP relaxation of the original problem yields the optimal solution \(x^*_0 = (3, 1.5)\) with objective value \(z(P_0) = 1.9 \times 3 + 1.5 = 7.2\). Since \(x_2 = 1.5\) is fractional, we need to branch.
Branching on Fractional Variables
We branch on \(x_2\). We create two subproblems:
\(P_1\): Add constraint \(x_2 \leq \lfloor 1.5 \rfloor = 1\).
\(P_2\): Add constraint \(x_2 \geq \lceil 1.5 \rceil = 2\).
Exploring the Search Tree (Depth-First)
We start with \(P_1\). Solving \(P_1\)’s LP relaxation gives \(x^*_1 = (3.2, 1)\) with \(z(P_1) \approx 7.08\). Again, \(x_1 = 3.2\) is fractional. We branch on \(x_1\):
\(P_3\): Add constraint \(x_1 \leq \lfloor 3.2 \rfloor = 3\) to \(P_1\) (so \(x_1 \leq 3, x_2 \leq 1\)).
\(P_4\): Add constraint \(x_1 \geq \lceil 3.2 \rceil = 4\) to \(P_1\) (so \(x_1 \geq 4, x_2 \leq 1\)).
Bounding and Pruning Subproblems
Solving \(P_3\):
Solving \(P_3\)’s LP relaxation gives \(x^*_3 = (3, 1)\) which is integer, with \(z(P_3) = 1.9 \times 3 + 1 = 6.7\). This is our first integer solution, so we set the incumbent value \(z_{incumbent} = 6.7\) and incumbent solution \(x_{incumbent} = (3, 1)\). We prune \(P_3\) as it’s integer.
Solving \(P_4\):
Solving \(P_4\)’s LP relaxation. It turns out \(P_4\) is infeasible (no feasible solution satisfying \(x_1 \geq 4\) and \(x_2 \leq 1\) within the original constraints). Thus, we prune \(P_4\) due to infeasibility.
Backtrack to \(P_2\).
Solving \(P_2\):
Solving \(P_2\)’s LP relaxation gives \(x^*_2 = (\frac{8}{3}, 2) \approx (2.67, 2)\) with \(z(P_2) \approx 7.06\). \(x_1 = \frac{8}{3}\) is fractional. We branch on \(x_1\):
\(P_5\): Add constraint \(x_1 \leq \lfloor \frac{8}{3} \rfloor = 2\) to \(P_2\) (so \(x_1 \leq 2, x_2 \geq 2\)).
\(P_6\): Add constraint \(x_1 \geq \lceil \frac{8}{3} \rceil = 3\) to \(P_2\) (so \(x_1 \geq 3, x_2 \geq 2\)).
Updating the Incumbent Solution
Solving \(P_5\):
Solving \(P_5\)’s LP relaxation gives \(x^*_5 = (2, 3)\) which is integer, with \(z(P_5) = 1.9 \times 2 + 3 = 6.8\). Since \(z(P_5) = 6.8 > z_{incumbent} = 6.7\), we update the incumbent to \(x_{incumbent} = (2, 3)\) and \(z_{incumbent} = 6.8\). We prune \(P_5\).
Solving \(P_6\):
Solving \(P_6\)’s LP relaxation. It turns out \(P_6\) is infeasible (no feasible solution satisfying \(x_1 \geq 3\) and \(x_2 \geq 2\) within the original constraints). Thus, we prune \(P_6\) due to infeasibility.
Termination and Optimal Solution
All branches have been explored and pruned. The list of active problems is empty. The incumbent solution \(x_{incumbent} = (2, 3)\) with objective value \(z_{incumbent} = 6.8\) is the optimal integer solution.
Branch and Bound Tree
The tree diagram illustrates the branching process. Each node represents a subproblem, and the branches represent the added constraints. The solution at each node is shown below the node.
Conclusion
The Branch and Bound algorithm provides a systematic and effective approach to solving integer linear programming problems. It combines the divide-and-conquer strategy of branching with the efficiency of bounding to prune the search space. By recursively partitioning the problem and using LP relaxations to obtain bounds, Branch and Bound efficiently explores the solution space, implicitly enumerating integer solutions and converging to an optimal solution.
Key Takeaways:
Further Thinking:
How do different branching strategies (e.g., most fractional variable, strong branching) affect the size and shape of the search tree?
Can we improve the bounds (e.g., using cutting planes) to prune more effectively and earlier in the process?
What are the limitations of Branch and Bound, and when might other methods (e.g., heuristics, metaheuristics) be more appropriate?
How does the problem structure (e.g., sparsity, special constraints) affect the performance of Branch and Bound?
How can we exploit parallel computing to accelerate the Branch and Bound algorithm?
These questions can guide further exploration and deeper understanding of integer programming, optimization algorithms, and their practical applications. They highlight the ongoing research and development in the field of optimization, aiming to improve the efficiency and scalability of algorithms like Branch and Bound for solving increasingly complex real-world problems.