Recent from talks
Nothing was collected or created yet.
Network simplex algorithm
View on WikipediaIn mathematical optimization, the network simplex algorithm is a graph theoretic specialization of the simplex algorithm. The algorithm is usually formulated in terms of a minimum-cost flow problem. The network simplex method works very well in practice, typically 200 to 300 times faster than the simplex method applied to general linear program of same dimensions.[1]
History
[edit]For a long time, the existence of a provably efficient network simplex algorithm was one of the major open problems in complexity theory, even though efficient-in-practice versions were available. In 1995 Orlin provided the first polynomial algorithm with runtime of where is maximum cost of any edges.[2] Later Tarjan improved this to using dynamic trees in 1997.[3] Strongly polynomial dual network simplex algorithms for the same problem, but with a higher dependence on the numbers of edges and vertices in the graph, have been known for longer.[4]
Overview
[edit]The network simplex method is an adaptation of the bounded variable primal simplex algorithm. The basis is represented as a rooted spanning tree of the underlying network, in which variables are represented by arcs, and the simplex multipliers by node potentials. At each iteration, an entering variable is selected by some pricing strategy, based on the dual multipliers (node potentials), and forms a cycle with the arcs of the tree. The leaving variable is the arc of the cycle with the least augmenting flow. The substitution of entering for leaving arc, and the reconstruction of the tree is called a pivot. When no non-basic arc remains eligible to enter, the optimal solution has been reached.
Applications
[edit]The network simplex algorithm can be used to solve many practical problems including,[5]
- Transshipment problem
- Hitchcock transportation problem
- Assignment problem
- Chains and antichains in partially ordered sets
- System of distinct representatives
- Covers and matching in bipartite graphs
- Caterer problem
References
[edit]- ^ Bazaraa, Mokhtar S.; Jarvis, John J.; Sherali, Hanif D. (2010). Linear Programming and Network Flows (4th ed.). Wiley. p. 453.
- ^ Orlin, James B. (1997-08-01). "A polynomial time primal network simplex algorithm for minimum cost flows". Mathematical Programming. 78 (2): 109–129. doi:10.1007/BF02614365. hdl:1721.1/2584. ISSN 0025-5610. S2CID 3107792.
- ^ Tarjan, Robert E. (1997-08-01). "Dynamic trees as search trees via euler tours, applied to the network simplex algorithm". Mathematical Programming. 78 (2): 169–177. doi:10.1007/BF02614369. ISSN 0025-5610. S2CID 18977577.
- ^ Orlin, James B.; Plotkin, Serge A.; Tardos, Éva (June 1993), "Polynomial dual network simplex algorithms", Mathematical Programming, 60 (1–3): 255–276, CiteSeerX 10.1.1.297.5730, doi:10.1007/bf01580615, S2CID 5838223
- ^ Chvatal, Vasek (1983). "20". Linear Programming. Macmillan. pp. 320–351. ISBN 9780716715870.
External links
[edit]- Solving Network Problems Archived 2015-05-26 at the Wayback Machine Section 14, p B-113 shows an example execution
Network simplex algorithm
View on GrokipediaBackground
Minimum-Cost Flow Problem
The minimum-cost flow problem involves finding the least expensive way to route a specified amount of flow through a network of nodes and arcs, respecting capacity limits and satisfying supply and demand requirements at each node. This problem generalizes classical transportation and assignment problems by allowing intermediate transshipment nodes and is a cornerstone of network optimization.[7] Formally, the problem is posed on a directed graph , where denotes the set of nodes and the set of arcs. Each node is associated with a supply/demand value , satisfying (positive indicates supply, negative indicates demand). Each arc has a nonnegative capacity and a per-unit cost .[7] The objective is to determine a flow on each arc that minimizes the total transportation cost , subject to flow conservation constraints for all , and capacity constraints for all .[7] Node potentials and cycle structures provide essential theoretical foundations for network flows, enabling the analysis of optimality and the design of efficient algorithms. Node potentials are real-valued labels assigned to each node , which define reduced costs for arcs; these reduced costs preserve the total flow cost while simplifying the detection of improving directions. Cycle structures refer to directed cycles in the residual graph, where a flow is optimal if and only if there exists a set of node potentials such that all reduced costs are nonnegative, implying no negative-cost cycles that could reduce the objective further.[7] A representative example is a capacitated transshipment network with five nodes (1 through 5), where nodes 1 and 2 supply 2 and 1 units respectively (, ), node 4 demands 5 units (), node 3 has zero net balance (), and node 5 acts as a slack supply node with to ensure feasibility. The network includes eight arcs with specified capacities and costs as follows:| Arc | From | To | Capacity | Cost |
|---|---|---|---|---|
| 1 | 1 | 2 | 5 | 11 |
| 2 | 1 | 3 | 4 | 16 |
| 3 | 2 | 3 | 3 | 12 |
| 4 | 2 | 4 | 4 | 18 |
| 5 | 3 | 4 | 3 | 13 |
| 6 | 3 | 5 | 5 | -25 |
| 7 | 4 | 5 | 3 | 10 |
| 8 | 5 | 1 | 5 | 11 |
Relation to Simplex Method
The network simplex algorithm represents a specialized adaptation of the general simplex method for linear programming, tailored to exploit the structured nature of network flow problems. In the standard simplex method, the algorithm maintains a basic feasible solution defined by a set of n linearly independent variables forming a basis, with the remaining non-basic variables set to zero or bounds; it iteratively selects an entering variable with a negative reduced cost to improve the objective, performs a pivot to update the basis by exchanging entering and leaving variables, and continues until optimality is reached where all reduced costs are non-negative.[9] For network problems, such as the minimum-cost flow formulation, basic feasible solutions correspond directly to spanning trees of the underlying directed graph, where the basic arcs form a tree connecting all nodes without cycles, ensuring linear independence of the corresponding columns in the node-arc incidence matrix. Non-basic arcs, when added to this spanning tree, create a unique fundamental cycle, allowing the algorithm to identify the leaving arc through flow augmentation along this cycle while preserving feasibility and restoring the tree structure. This graph-theoretic interpretation simplifies pivot operations compared to the general case.[9] In the dual formulation, the simplex multipliers—dual variables associated with the flow balance constraints—are interpreted as node potentials for each node . The reduced cost for an arc is then given by where is the original arc cost; optimality holds when for all non-basic arcs (or satisfies bound conditions for capacitated flows), ensuring no improving direction exists. These potentials can be efficiently computed and updated using the spanning tree, as the differences along tree paths define consistent values.[9] This specialization yields significant advantages over the general simplex method applied to the equivalent linear program, including a sparser structure with far fewer non-basic variables (typically on the order of the number of arcs rather than the full dimension), and explicit cycle detection via tree traversals that avoids costly matrix inversions or full tableau updates, often resulting in pivots that are computationally faster by factors of 200 to 300 in practice for large networks.[9]Core Concepts
Network Representation
The network simplex algorithm models the underlying optimization problem using a directed graph , where denotes the set of nodes and the set of directed arcs. Each node has an associated supply or demand , satisfying , while each arc carries a cost , an upper capacity bound (possibly infinite), and a flow value . This graph representation captures the conservation of flow at nodes and the objective of minimizing total arc costs subject to capacity constraints.[10][11] To handle cases where the original network has unbalanced supplies and demands—preventing an immediate feasible flow—artificial nodes or arcs are incorporated. A common approach introduces an artificial root node not in , connected via return arcs for nodes with positive supply and supply arcs for nodes with negative demand . These artificial elements, often assigned zero or high costs and infinite capacities, ensure the extended graph admits a basic feasible solution for initialization, with initial flows set to on these arcs.[3][10] Central to the algorithm is an acyclic spanning tree , comprising arcs that connect all nodes without forming cycles, thereby defining a basic feasible solution where node supplies uniquely determine arc flows via tree paths. Adding any non-tree arc to the tree induces a unique fundamental cycle consisting of and the path in between its endpoints, which plays a crucial role in identifying adjustment directions for flows during iterations.[11][12] Node potentials for are computed relative to this spanning tree structure to evaluate reduced costs efficiently. The tree is rooted at an arbitrary node, inducing a hierarchical ordering of nodes by levels or depths along tree paths from the root, which allows solving the system for all tree arcs in linear time via backward substitution.[3][10] For computational efficiency, the graph employs adjacency lists to store outgoing and incoming arcs per node, facilitating traversals for residual network updates. The spanning tree is augmented with specialized data structures, such as parent arrays (e.g., predecessor links) for path tracing, depth arrays for level-based ordering, and thread links connecting nodes at the same level to enable cycle identification and potential recomputation without full graph rescans.[10][3]Basis and Tree Structure
In the network simplex algorithm, a feasible basis for the minimum-cost flow problem corresponds to a spanning tree of the underlying directed graph, where the tree arcs represent the basic variables and non-tree arcs the non-basic ones. This structure leverages the totally unimodular property of the node-arc incidence matrix, ensuring that each spanning tree defines a unique basic solution. The tree is typically rooted at an arbitrary node, such as a reference node with zero supply, to facilitate flow computations.[11][13] A basic feasible solution is obtained by assigning flows solely to the tree arcs such that the flow conservation constraints (node balances) are satisfied, with non-tree arcs carrying zero flow. Specifically, for a tree arc directed from node to node , the flow value is equal to the negative of the sum of supplies (or demands) over all nodes in the subtree rooted at , assuming supplies are positive for sources and negative for sinks. This recursive computation begins at the leaves of the tree, where a leaf node's flow on its outgoing arc matches its own supply, and propagates upward, ensuring global balance without cycles. If the resulting flows on tree arcs violate capacity bounds, the solution is infeasible, but a feasible basis requires all flows to lie within their bounds. Reduced costs for non-basic arcs can then be computed relative to dual variables associated with the tree, analogous to the general simplex method.[13][14][11] Degeneracy arises when one or more tree arcs carry zero flow, meaning the basic solution has fewer than positive variables for a graph with nodes, yet still spans the feasible region. In such cases, these zero-flow tree arcs serve as potential candidates for removal during pivots, allowing the algorithm to handle degenerate bases without explicit perturbation techniques common in general linear programming. This property simplifies degeneracy resolution in network contexts compared to dense LPs.[14][13] A key property is the cycle theorem: introducing a non-basic (non-tree) arc into the spanning tree creates exactly one unique fundamental cycle, consisting of the new arc and the unique path in the tree connecting its endpoints. Augmenting flow along this cycle—by increasing flow on forward arcs and decreasing on reverse arcs—preserves feasibility as long as the augmentation amount does not exceed the minimum residual capacity on the cycle, maintaining node balances since the cycle nets zero supply. This ensures that the updated flow remains a valid basic solution associated with the new spanning tree obtained by removing the appropriate tree arc from the cycle.[13][14][11] To illustrate, consider a simple network with four nodes (1,2,3,4) and supplies , , , , rooted at node 1. A spanning tree might include arcs (1,2), (1,3), and (3,4). The flow on (3,4) would be (directed toward the root, but adjusted for direction), on (1,3) the sum for subtree at 3 (), and on (1,2) simply . Adding a non-tree arc (2,3) forms a cycle 1-2-3-1; augmenting along it would adjust flows while preserving the total supplies in subtrees. This tree structure with flows and the induced cycle highlights how bases encode feasible solutions compactly.[13]Algorithm Steps
Initialization Phase
The initialization phase of the network simplex algorithm requires establishing an initial basic feasible solution, represented by a spanning tree that satisfies the node balance constraints of the minimum-cost flow problem. One approach to finding this initial tree involves constructing a shortest-path tree rooted at an arbitrary node, where paths are computed using reduced costs (initially set to original costs) to ensure the tree arcs form a feasible basis without cycles. Alternatively, greedy methods can build the spanning tree by iteratively selecting arcs that connect components while respecting supply and demand balances, often starting from a forest and merging until a single tree is formed. These methods ensure the tree has exactly arcs for nodes, providing a basic solution where flows can be determined to meet the balances.[15] For networks that are unbounded or unbalanced (e.g., total supply not equal to total demand), dummy nodes are introduced to facilitate feasibility. A dummy source node is added with supply equal to the excess demand, connected to demand nodes via zero-cost arcs of sufficient capacity, while a dummy sink node receives excess supply from supply nodes through similar zero-cost arcs. This transformation balances the network, allowing the spanning tree to incorporate these arcs temporarily without affecting the original costs, and ensures a feasible circulation can be computed. If capacities are infinite on some arcs, additional checks during tree construction verify boundedness by detecting infinite residual cycles.[16] In cases where an obvious feasible tree is unavailable, artificial variables are employed in a Phase I procedure to reach feasibility. A root node is selected, and artificial arcs are added from to nodes with positive supply (or vice versa for demands) with high penalty costs (e.g., larger than any original cost) and infinite capacity. Initial flows on these arcs are set to the absolute values of the imbalances , forming a trivial tree. The network simplex is then run on this auxiliary problem with minimized artificial flow as the objective; if the optimal solution has zero artificial flow, the resulting tree and flows provide the initial basic feasible solution for Phase II on the original problem. Otherwise, the problem is infeasible.[13] Once the initial spanning tree is obtained (including any artificial or dummy arcs), the flows on the tree arcs are computed via backward substitution from the leaves to satisfy the balance equations. This process, known as the Compute-Flows algorithm, processes nodes in a post-order traversal, assigning flows based on node imbalances and propagating adjustments upward.Algorithm Compute-Flows(T, b):
Initialize e_i = b_i for all nodes i
S = set of tree arcs T
While S is not empty:
Select a leaf node i ≠ root in the current tree
Let (i,j) or (j,i) be the unique tree arc incident to i
If arc is (i,j): set x_{ij} = e_i
Else: set x_{ji} = -e_i
Update e_j = e_j - x_{ij} (or +x_{ji})
Remove node i and the arc from S
Return flows x on tree arcs
Algorithm Compute-Flows(T, b):
Initialize e_i = b_i for all nodes i
S = set of tree arcs T
While S is not empty:
Select a leaf node i ≠ root in the current tree
Let (i,j) or (j,i) be the unique tree arc incident to i
If arc is (i,j): set x_{ij} = e_i
Else: set x_{ji} = -e_i
Update e_j = e_j - x_{ij} (or +x_{ji})
Remove node i and the arc from S
Return flows x on tree arcs
Iteration and Pivot Selection
In the network simplex algorithm, each iteration begins with the selection of an entering arc, which is a non-basic arc in the residual network exhibiting a negative reduced cost . This reduced cost, defined as where are the node potentials and is the original arc cost, signals an opportunity to decrease the total cost by incorporating the arc into the basis. Heuristically, the arc with the most negative reduced cost is often chosen to accelerate convergence, though various pricing strategies, such as candidate lists, may be employed to identify eligible arcs efficiently.[9] Adding the entering arc to the current spanning tree basis creates a unique directed cycle in the network, oriented in the direction of the entering arc. This cycle is efficiently identified by finding the unique path in the tree between nodes i and j, for example, by tracing paths from i and j to their lowest common ancestor using predecessor indices in a rooted tree representation, then combining the segments. The cycle consists of the entering arc and the tree path arcs, forming the basis for the subsequent pivot operation.[9] To determine the pivot's extent, the augmenting amount is computed as the minimum residual capacity along the forward arcs of the cycle: where denotes the forward arcs in the cycle , is the capacity, and is the current flow. The leaving arc is then selected as the tree arc on the cycle achieving this minimum , which will be driven to its bound during augmentation. In cases of ties or degeneracy (where ), selection rules prioritize arcs by cost impact or apply Bland's rule to prevent cycling and ensure finite termination.[9] Following the pivot, which exchanges the entering and leaving arcs in the basis, the node potentials are updated to maintain dual feasibility. Specifically, for the subtree separated by the leaving arc, all potentials are adjusted by the negative of the entering arc's reduced cost (with appropriate sign based on orientation), ensuring that reduced costs for the new basic arcs revert to zero. This update preserves the optimality conditions for the revised basis without recomputing all reduced costs from scratch.[9]Augmentation and Update
Once the entering arc and leaving arc have been selected based on reduced costs and feasibility, the augmentation phase adjusts the current flow along the unique cycle formed by incorporating the entering arc into the spanning tree. This cycle consists of the entering arc and the path in the tree connecting its endpoints. The augmentation amount θ is determined as the minimum residual capacity along this cycle, ensuring that no arc violates its capacity bounds: specifically, θ is the smallest value such that increasing flow on forward arcs and decreasing it on backward arcs (relative to the cycle direction) keeps all flows feasible.[9] For forward arcs in the cycle, flow increases by θ, while for backward arcs, flow decreases by θ; this adjustment propagates the change around the entire cycle, improving the objective function by the cycle's reduced cost times θ.[9] The tree update follows immediately after augmentation, replacing the leaving arc (the one that reaches its bound first, limiting θ) with the entering arc to maintain a feasible spanning tree basis. This involves removing the leaving arc, which partitions the tree into two subtrees, and then reconnecting them via the entering arc. To efficiently identify and update paths in the new tree, depth-first search (DFS) is typically used to traverse from the endpoints of the entering arc, relinking nodes and updating predecessor pointers; alternatively, union-find structures can accelerate subtree mergers by maintaining path compression and union by rank for O(α(n)) amortized time per operation, where α is the inverse Ackermann function.[17] Flow propagation occurs concurrently, applying the θ adjustment along the updated paths using the new tree structure, ensuring consistency across all affected arcs.[9] Node potentials are updated to preserve the reduced costs for non-basic arcs in the new basis. Specifically, after the pivot, let δ = -c'_{entering} (>0); the potentials π for nodes in the subtree separated by the leaving arc (the one not containing the root, say) are adjusted by adding or subtracting δ depending on whether the tail or head of the entering arc is in that subtree; this can be done efficiently via a DFS traversal or by leveraging the tree's hierarchical structure to propagate the change in O(n time per pivot in basic implementations, or faster with dynamic tree data structures that support subtree updates in O(log n) time.[9][17][2] Degeneracy arises when θ = 0, meaning the entering arc can be added without changing flows, as the leaving arc is already at its bound. In this case, the pivot simply swaps the arcs in the basis without altering flows or potentials, but anti-cycling rules (such as lexicographic ordering or perturbation) must be applied to ensure progress and prevent infinite loops.[9] The following pseudocode outlines the core steps for tree relinking and flow propagation after augmentation, assuming a rooted tree representation with predecessor arrays:Procedure UpdateTreeAndFlow(entering_arc = (k, l), leaving_arc = (p, q), θ, c_prime_entering):
δ = -c_prime_entering // Positive adjustment amount for potentials
// Step 1: Identify the cycle path using proper tree path finding
// Trace path from k to root and l to root
path_k_to_root = []
current = k
while current != root:
path_k_to_root.append((pred[current], current))
current = pred[current]
path_k_to_root.reverse() // Now from root to k
path_l_to_root = []
current = l
while current != root:
path_l_to_root.append((pred[current], current))
current = pred[current]
path_l_to_root.reverse() // From root to l
// Find LCA: last common node in path_k_to_root and path_l_to_root
min_len = min(len(path_k_to_root), len(path_l_to_root))
lca_index = 0
for idx in 0 to min_len-1:
if path_k_to_root[idx] == path_l_to_root[idx]:
lca_index = idx
else:
break
lca = path_k_to_root[lca_index] if lca_index < len(path_k_to_root) else root
// Tree path from k to l: reverse(path from lca to k) + path from lca to l (excluding lca duplicate)
tree_path = reverse(path_k_to_root[lca_index+1:]) + path_l_to_root[lca_index+1:]
cycle = tree_path + [entering_arc] // Close cycle
// Step 2: Propagate flow along cycle
for arc in cycle:
if arc is forward in cycle direction:
flow[arc] += θ
else:
flow[arc] -= θ
// Update residual capacities accordingly
// Step 3: Remove leaving arc and relink subtrees (using DFS for reconnection)
RemoveArc(leaving_arc) // Disconnect p and q, creating two subtrees
AddArc(entering_arc) // Connect k and l
// Relink: Perform DFS from root to update predecessors in affected subtrees
DFSRelink(root, entering_arc) // Traverse and set new pred pointers for subtrees
// Step 4: Update potentials for affected subtree (add/subtract δ based on side)
affected_subtree = Subtree(q) // Assuming q's subtree after removal
sign = +1 if l in affected_subtree else -1 // Adjust sign based on entering head/tail location
for node in affected_subtree:
π[node] += sign * δ
// Handle θ = 0: Skip flow changes, but still update tree and potentials if needed
if θ == 0:
// Proceed with Steps 3 and 4, no flow/propagation
Procedure UpdateTreeAndFlow(entering_arc = (k, l), leaving_arc = (p, q), θ, c_prime_entering):
δ = -c_prime_entering // Positive adjustment amount for potentials
// Step 1: Identify the cycle path using proper tree path finding
// Trace path from k to root and l to root
path_k_to_root = []
current = k
while current != root:
path_k_to_root.append((pred[current], current))
current = pred[current]
path_k_to_root.reverse() // Now from root to k
path_l_to_root = []
current = l
while current != root:
path_l_to_root.append((pred[current], current))
current = pred[current]
path_l_to_root.reverse() // From root to l
// Find LCA: last common node in path_k_to_root and path_l_to_root
min_len = min(len(path_k_to_root), len(path_l_to_root))
lca_index = 0
for idx in 0 to min_len-1:
if path_k_to_root[idx] == path_l_to_root[idx]:
lca_index = idx
else:
break
lca = path_k_to_root[lca_index] if lca_index < len(path_k_to_root) else root
// Tree path from k to l: reverse(path from lca to k) + path from lca to l (excluding lca duplicate)
tree_path = reverse(path_k_to_root[lca_index+1:]) + path_l_to_root[lca_index+1:]
cycle = tree_path + [entering_arc] // Close cycle
// Step 2: Propagate flow along cycle
for arc in cycle:
if arc is forward in cycle direction:
flow[arc] += θ
else:
flow[arc] -= θ
// Update residual capacities accordingly
// Step 3: Remove leaving arc and relink subtrees (using DFS for reconnection)
RemoveArc(leaving_arc) // Disconnect p and q, creating two subtrees
AddArc(entering_arc) // Connect k and l
// Relink: Perform DFS from root to update predecessors in affected subtrees
DFSRelink(root, entering_arc) // Traverse and set new pred pointers for subtrees
// Step 4: Update potentials for affected subtree (add/subtract δ based on side)
affected_subtree = Subtree(q) // Assuming q's subtree after removal
sign = +1 if l in affected_subtree else -1 // Adjust sign based on entering head/tail location
for node in affected_subtree:
π[node] += sign * δ
// Handle θ = 0: Skip flow changes, but still update tree and potentials if needed
if θ == 0:
// Proceed with Steps 3 and 4, no flow/propagation
Optimality and Analysis
Reduced Costs and Optimality
In the network simplex algorithm, reduced costs are computed relative to node potentials associated with each node . For a basic arc in the spanning tree, the reduced cost is zero by construction, ensuring that the potentials satisfy . These potentials can be recovered by fixing for a root node and propagating along the tree paths, yielding for each tree arc.[13][18] For non-basic arcs , the reduced cost is given by , which adjusts the original cost by the potential difference. A feasible solution is optimal if and only if for all non-basic arcs in the residual network, implying the absence of negative-cost cycles that could improve the objective. This condition verifies that no augmentation along non-basic arcs would decrease the total cost.[13][18][19] Node potentials serve as dual variables in the linear programming formulation of the minimum-cost flow problem, maximizing subject to for all arcs, where denotes the supply or demand at node . The optimality condition aligns with complementary slackness: for each arc, implies zero flow (), while holds for arcs with positive flow within capacity bounds. This primal-dual framework ensures the solution satisfies both feasibility and optimality in the network context.[18][13][19]Termination Criteria
The network simplex algorithm terminates upon achieving an optimal feasible solution to the minimum-cost flow problem, which requires verifying both feasibility and optimality conditions after each augmentation step. Feasibility is confirmed by ensuring that all flows satisfy capacity constraints (0 ≤ x_{ij} ≤ u_{ij} for each arc) and node balance requirements (supply/demand conservation), typically through a post-augmentation flow computation procedure that propagates values along the spanning tree. Optimality is established when reduced costs indicate no potential improvement, specifically by scanning all non-tree arcs to confirm that none have a negative reduced cost c'_{ij} < 0, as this absence ensures the current basis is optimal with no beneficial pivots available. To prevent unbounded iterations due to cycling—a phenomenon where the algorithm revisits previous bases without progress—termination is guaranteed through anti-cycling mechanisms such as cost perturbation, where small values ε^i (with ε > 0 and i increasing with iteration count) are added to arc costs to enforce a strict decrease in the objective function, or lexicographic pivot rules that prioritize arcs in a fixed order to avoid degenerate cycles. These techniques ensure the algorithm converges in a finite number of steps for any non-degenerate problem instance, though practical implementations often monitor iteration counts as an additional safeguard.[20] Upon termination, the algorithm outputs the optimal flow values x_{ij} for all arcs, computed via the final spanning tree, along with the minimum total cost ∑ c_{ij} x_{ij}, providing a verifiable solution that satisfies all problem constraints. If no feasible solution exists, the process may detect infeasibility earlier through the absence of an initial basic feasible tree or if artificial variables remain positive in the basis after attempting to find a feasible solution.[20]Complexity and Variants
Time Complexity Bounds
The basic network simplex algorithm exhibits a per-iteration time complexity dominated by O(m) operations for scanning reduced costs across all edges to identify a potential entering arc, followed by O(n) operations for tracing the unique cycle in the spanning tree and updating the tree structure upon pivoting.[21][13] These costs arise from the network's sparsity, where m denotes the number of edges and n the number of vertices, making each iteration significantly more efficient than in the dense tableau representation of the general simplex method. In the worst case, the overall time complexity of the basic algorithm is exponential in the input size, mirroring the behavior of the general simplex method due to the possibility of an exponential number of degenerate pivots before reaching optimality.[22] However, polynomial-time variants exist, such as the primal network simplex algorithm with cost scaling, which achieves a total time bound of O(\min(n^2 m \log n C, n^2 m^2 \log n)), where C is the maximum absolute arc cost.[6] Key factors influencing the complexity include the number of pivots, which can reach up to O(n m U \log C) in scaling-based implementations before termination, where U bounds the arc capacities.[23] Capacity scaling techniques mitigate the impact of large U by processing the problem in phases with geometrically decreasing capacity thresholds, reducing the pivot count logarithmically.[23] For network-structured problems, the network simplex algorithm is empirically 200 to 300 times faster than general linear programming solvers like the revised simplex method applied to the equivalent dense formulation, owing to the avoidance of full tableau manipulations.[13]Improved Implementations
Improved implementations of the network simplex algorithm have focused on enhancing pivot operations through advanced data structures and scaling techniques to achieve better practical performance and guaranteed polynomial-time execution. One key advancement involves the use of dynamic trees to maintain the spanning tree structure efficiently during pivots. By representing the tree via Euler tours and treating it as a search tree, updates such as linking and cutting edges can be performed in O(log n) time, where n is the number of vertices, leading to O(log n) time per pivot overall.[24] This approach, introduced by Tarjan in 1997, leverages parallel graph algorithm techniques to combine vertex and edge values across subtrees rapidly.[24] To ensure polynomial-time complexity, variants incorporating scaling mechanisms have been developed. Orlin's 1995 cost-scaling primal network simplex algorithm uses premultipliers with respect to a rooted tree and adjusts the root per pivot, achieving a time complexity of O(min(n²m log(nC), n²m² log n)), where m is the number of arcs and C is the maximum absolute arc cost (or 0 for non-integer costs).[25] This method performs O(min(nm log(nC), nm² log n)) pivots, with average O(n) time per pivot using appropriate data structures, making it the first such polynomial-time version of the primal simplex for minimum cost flows.[25] Capacity scaling variants, building on Edmonds-Karp ideas, further refine this by processing flows in phases based on capacity scales, ensuring strong polynomial time in certain cases.[26] Practical software implementations incorporate these and other optimizations for real-world use. The LEMON C++ library provides a highly efficient NetworkSimplex class, which outperforms implementations in LEDA and MCF libraries while remaining competitive with state-of-the-art cost-scaling codes like CS2, as demonstrated in extensive benchmarks on sparse networks.[27] Similarly, NetworkX, a Python library for graph analysis, includes a network_simplex function that implements the primal variant with the leaving arc rule to prevent cycling, supporting directed graphs with node demands, edge capacities, and costs; it returns the minimum flow cost and edge flow dictionary.[5] Numerical stability is a critical concern in implementations, particularly to avoid floating-point precision issues that can lead to incorrect optimality detection or cycling. For problems with integer capacities and costs, exact arithmetic using integers ensures solutions remain precise and optimal, as the algorithm naturally yields integer flows under these conditions.[28] NetworkX documentation recommends scaling non-integer inputs (e.g., multiplying by 100) to integers before invocation to mitigate floating-point errors, highlighting that the algorithm is not guaranteed for direct floating-point use.[5] Such practices maintain reliability in software libraries without relying on floating-point approximations.History
Early Development
The network simplex algorithm originated from the adaptation of George Dantzig's general simplex method, introduced in 1947 for solving linear programming problems, to specialized network flow structures in the early 1950s. Dantzig's seminal application of the simplex method to the transportation problem appeared in 1951, where he demonstrated how the network representation of constraints—modeled as bipartite graphs with supply and demand nodes—could streamline pivot operations by maintaining a tree-structured basis, thus forming the foundational framework for the network simplex approach.[29] In the mid-1950s, advancements in network flow theory by L. R. Ford Jr. and D. R. Fulkerson, particularly their 1956 augmenting path algorithm for maximum flow problems, underscored the computational advantages of exploiting graph-theoretic properties in flow networks, paving the way for simplex-based methods tailored to minimum-cost variants. This period marked the initial recognition that network problems, such as the minimum-cost flow problem, benefited from specialized implementations that avoided the full matrix manipulations of general linear programming. By the 1960s, researchers including Harold W. Kuhn further emphasized the special structure of transportation and assignment problems, integrating dual price adjustments and cycle detection into simplex iterations to enhance efficiency for these subclasses of network flows.[30] The first explicit formulations of the network simplex algorithm were detailed in operations research texts, such as those by Salah E. Elmaghraby in 1964, and it had likely been employed earlier in military logistics planning for resource allocation during the post-World War II era. Like its general counterpart, the early network simplex shared a non-polynomial worst-case time complexity, prone to exponential pivots in contrived instances, yet it proved remarkably effective in practice for real-world network applications due to the sparsity and acyclicity of typical bases.Key Contributions and Advances
In the 1980s, significant progress was made toward establishing polynomial-time guarantees for minimum-cost flow problems. Éva Tardos developed the first strongly polynomial algorithm for minimum-cost circulation in 1986, resolving whether the problem could be solved in time independent of the magnitudes of costs and capacities. Building on scaling ideas, Ahuja, Goldberg, Orlin, and Tarjan introduced a double-scaling approach in their 1988 working paper (published 1990), which combines capacity and cost scaling to solve the minimum-cost flow problem in polynomial time. This method iteratively reduces an error bound on costs through scaling phases, using flow augmentations to achieve ε-optimality, with a resulting complexity polynomial in the input size and addressing the exponential worst-case behavior of simplex implementations.[31] Orlin's 1988 algorithm further improved the strongly polynomial bounds for minimum-cost flow. The 1990s saw additional theoretical refinements focused on pivot selection and data structure enhancements to achieve tighter polynomial bounds for simplex-based methods. Orlin developed a primal network simplex algorithm in 1995 that guarantees polynomial time by selecting entering variables based on a steepest-edge criterion adapted for networks, combined with careful basis maintenance. This yields a time complexity of , where is the number of vertices, the number of edges, and the maximum arc cost, marking a key advance in making the simplex method competitive with other polynomial algorithms for minimum-cost flows. Complementing this, Tarjan's 1997 work integrated Euler-tour representations of dynamic trees into the network simplex framework, enabling efficient updates to the spanning tree basis during pivots.[32] The approach achieves a complexity of for minimum-cost circulation problems, leveraging the logarithmic-time operations of the data structure to handle path computations and reduced cost evaluations.[32] These contributions collectively transformed the network simplex from a practical but theoretically unpredictable method into a robust solver with polynomial-time variants, enabling its application to large-scale network optimization in fields such as logistics and telecommunications.Applications
Transportation and Assignment
The transportation problem involves distributing goods from multiple sources to multiple destinations at minimum cost, subject to supply and demand constraints. It is modeled as a bipartite graph where one set of nodes represents sources with given supplies , the other set represents sinks with demands , and directed edges from sources to sinks carry costs per unit flow . The objective is to minimize while satisfying for sources and for sinks, with . This formulation aligns with the minimum-cost flow problem on a network, allowing the network simplex algorithm to solve it efficiently by exploiting the graph structure to identify improving directions via cycles in the residual network.[33] The assignment problem is a special case of the transportation problem where all supplies and demands are equal to one, reducing it to selecting edges in an bipartite graph to form a perfect matching of minimum total cost. The network simplex algorithm applies directly here, using a primal simplex framework adapted for networks, where the basis corresponds to a spanning tree of the bipartite graph plus return arcs for supplies and demands. This approach yields a polynomial-time solution, performing time for sparse instances with edges, though for the dense complete bipartite graph of assignment problems, it runs in time. While conceptually equivalent to the Hungarian algorithm, which also solves the assignment problem in time, the network simplex often performs better in practice for very large instances due to its ability to leverage network data structures and avoid redundant computations in dual price updates.[34][35] Consider a 3x3 transportation example with sources having supplies 45, 60, and 35 units (total 140), and original destinations with demands 50 and 60 units (total 110). To balance, add a dummy sink with demand 30 and zero costs on edges to it. The cost matrix is:| Dest 1 | Dest 2 | Dummy Dest 3 | |
|---|---|---|---|
| Source 1 | 3 | 2 | 0 |
| Source 2 | 1 | 5 | 0 |
| Source 3 | 2 | 4 | 0 |
