MVA · Course notes

Algorithms for Network Optimization and Management

M2 MVA · Semester 2 · Modelling · 5 ECTS

Contents

Introduction and overview

Module Algorithms for Network Optimization and Management
Official title Algorithmes pour l’optimisation et la gestion des réseaux
Instructors N. PERROT (master-mva.com, 2026–2027)
Period Semester 2 · period 2 (04/01/2027 – 02/04/2027)
Credits and volume 5 ECTS · 30 h (lectures 12 h, labs 18 h) per the Université Paris-Saclay programme table
Label ModellingTrack I&I
Language French, with course materials in English (master-mva.com) · Université Paris-Saclay: French, no distance learning
Assessment Université Paris-Saclay: “Evaluation Continue non Intégrale” (non-integral continuous assessment) · master-mva.com displays no assessment modality · weights not displayed
Prerequisites Operations research, Python programming, networks (master-mva.com and Université Paris-Saclay)

Audience: a student with an M1 in mathematics, computer science or physics who knows what a linear program is and wants to see how operators decide where to install capacity, how to route traffic and where to place network functions.

Empirical first: on the six-node backbone used throughout this page, routing each demand on one minimum-hop path loads a 5 Gb/s link to 92 %. A linear program routes the same 40 Gb/s with a maximum load of 82.1 %, and a two-link cut proves that nobody can do better. Splitting evenly over all minimum-hop paths, which sounds safer, overloads that link to 123 %.

The course is about decision support for networks. It turns design, planning and operation questions into mathematical programs, solves them exactly when possible and approximately when time is short, and then studies the new problems of virtualized and autonomous networks. It shares its duality and decomposition tools with Convex Optimization and Distributed and Large-Scale Optimization, and its learning-based methods with Graphs in Machine Learning and Reinforcement Learning.

What you will be able to do

Official objectives and syllabus (2026–2027)

The course description on master-mva.com presents decision support as essential to plan and manage networks. Its objective is to give knowledge of mathematical optimization methods and of their use for network design and management, methods it calls all the more crucial as algorithmic challenges grow with virtualized and autonomous networks.

The first part refreshes operations research (combinatorial optimization, graph theory) and telecom networks, presents the main exact methods, applies them to network design, capacity planning, traffic engineering and placement of network components, and uses labs with off-the-shelf solvers and heuristics. The second part covers the optimization problems of recent network evolutions, with labs based on research papers. The Université Paris-Saclay course detail repeats the objective and the list of topics.

Official topic Course section
“Thèmes abordés”: study of network optimization problems, formulations, methods and algorithms to dimension, plan and orchestrate today’s and future networks The whole page: dimensioning and planning in ch. 4–5, orchestration in ch. 6 and ch. 8, today’s networks in ch. 1–7, future networks in ch. 8–10
“Les intervenants” (second item of the list) Not a topic: the heading that introduces the instructor, Nancy Perrot, listed in the fiche
Part 1: (re)fresher in operations research, combinatorial optimization ch. 0: linear programming, duality, integer programs and NP-hardness; integrality in ch. 2.3
Part 1: graph theory ch. 0.1 (graphs and their matrices) and ch. 2 (paths, flows, cuts, multicommodity flows)
Part 1: telecom networks ch. 1: layers, traffic matrices, routing protocols, virtualization, taxonomy of decisions
Part 1: the most relevant exact methods, in theory and on the classic examples of the literature ch. 5: branch-and-bound, cutting planes, Lagrangian relaxation, column generation, Benders decomposition, each on a network problem
Application: network design ch. 4.1–4.3: fixed-charge multicommodity design, formulation strength, modelling toolbox
Application: capacity planning ch. 4.4–4.5: modular capacities, cut-set and metric inequalities, multi-period upgrades; Benders in ch. 5.5
Application: flow routing (traffic engineering) ch. 3: congestion objectives, minimum maximum utilisation, shortest-path routing and weight setting, explicit and segment routing
Application: placement of network components ch. 6: facility location, controller placement, VNF placement and service chains, virtual network embedding
Labs: programming formulations and solving them with off-the-shelf solvers Python blocks of ch. 2–6 (SciPy’s interface to the HiGHS solver); labs of sessions 1–7
Labs: designing and testing heuristic algorithms ch. 7: greedy, rounding, local search, metaheuristics, matheuristics; lab of session 7
Part 2: optimization problems raised by recent network evolutions (virtualized, autonomous networks) ch. 8: what changes, slicing models, online optimization, autonomous control loops
Part 2: algorithmic challenges of future networks and the latest models ch. 8.1–8.4; ch. 11 (self-study)
Part 2: cybersecurity ch. 9.3: network interdiction, monitor placement, isolation constraints
Part 2: resilience ch. 9.1–9.2: survivable design, protection, robust routing under uncertain traffic; hose model and oblivious routing in ch. 11.3
Part 2: energy consumption ch. 9.4: power models and energy-aware routing
Part 2: faster methods for the combinatorial explosion and the very short computation times of autonomous networks ch. 7.4 (matheuristics), ch. 8.3–8.4 (online and incremental re-optimization), ch. 10
Part 2: hybridization of optimization with AI (learning) ch. 10: three patterns of hybridization, learning inside branch-and-bound, learning to prune; learned routing in ch. 11.1 and decision-focused learning in ch. 11.7
Part 2 labs: reading and presenting scientific papers Practice of sessions 8–10; ch. 11; the paper-study projects
Part 2: building a formulation from a telecom standardization document ch. 8.5 (method and worked example) and Exercise 17
Université Paris-Saclay “Programme / plan / contenus”: the same list of topics Same sections as the first row
“Presentation: here” Not a topic: a link to the presentation slides on master-mva.com

Extensions beyond the syllabus: the Fortz–Thorup congestion cost and the analysis of even splitting (ch. 3), Γ-robust routing (ch. 9.2), decision-focused learning (ch. 11.7) and the research chapter (ch. 11, self-study).

Where this course fits in the programme

Official prerequisites: operations research, Python programming and networks. The course carries the I&I track label and is limited to 20 students (numerus clausus, master-mva.com).

Course outline

Session plan

30 h in class = 10 sessions of 3 h.

The volume is the 30 h of the Université Paris-Saclay programme table, which master-mva.com also announces: 12 h of lectures and 18 h of labs (“TP”). The split below is a proposal: each session combines about 1 h 15 of lecture with a lab, and sessions 8–10 follow the second part of the course, where labs become paper readings and presentations.

Level 1 = foundations, 2 = core models and exact methods, 3 = future networks and research-level material; the level never decreases.

Session Hours Level Content Practice
1 3 h 1 ch. 0 Review: graphs and incidence matrices, LP duality, integer programs and NP-hardness; ch. 1 Telecom networks: layers, traffic matrices, routing, virtualization, the three time scales Ex. 1, 2; lab: first LP with SciPy and HiGHS, shortest-path routing of the backbone
2 3 h 1 ch. 2 Flows, paths and cuts: Dijkstra, maximum flow and minimum cut, total unimodularity, multicommodity flows and the Japanese theorem Ex. 3, 4, 7; lab: flows with NetworkX
3 3 h 2 ch. 3 Traffic engineering: congestion objectives, minimum maximum utilisation and its dual, weight setting and ECMP, explicit and segment routing Ex. 5, 8; lab: LP routing against shortest-path routing
4 3 h 2 ch. 4 Network design and capacity planning: fixed charges, formulation strength, modelling toolbox, modular capacities, cut-set and metric inequalities Ex. 6, 9; lab: weak and strong formulations
5 3 h 2 ch. 5.1–5.3 Branch-and-bound, cutting planes and branch-and-cut, Lagrangian relaxation Ex. 10, 11; lab: a branch-and-bound and a subgradient method
6 3 h 2 ch. 5.4–5.5 Column generation and Benders decomposition Ex. 12; lab: column generation for path routing
7 3 h 2 ch. 6 Placement: facility location, controllers, VNF chains, virtual network embedding; ch. 7 Heuristics and matheuristics Ex. 13, 14; lab: heuristics against the MILP optimum
8 3 h 3 ch. 8 Future networks: what changes, slicing models, online admission, autonomous loops, from a standard to a model Ex. 17; first paper reading
9 3 h 3 ch. 9 Resilience, security and energy: survivable design, robust routing, interdiction, energy-aware routing Ex. 15, 16; second paper reading
10 3 h 3 ch. 10 Hybrid methods: three patterns, learning in branch-and-bound, learning to prune and the guarantees that remain Ex. 18; paper presentations

Self-study, outside the 30 h (about 5 h in total): ch. 11 Research directions and open problems (≈ 3 h, with Ex. 19) · finishing the labs on a public SNDlib instance (≈ 2 h).

How to study this module

0. Review: graphs, linear programming and complexity

Every model of the course is a graph with numbers on its arcs, turned into a linear or integer program. This chapter fixes the notation and recalls the three results used everywhere: the incidence matrix, LP duality and the meaning of NP-hardness.

0.1 Graphs and their matrices

Definition 0.1 — Directed graph, paths and cuts A directed graph G = (V, A) has n nodes and m arcs. An arc a = (u, v) leaves u and enters v. A path is a sequence of arcs in which each arc enters the node the next one leaves.

For a set of nodes S, the cut \delta^+(S) contains the arcs leaving S and \delta^-(S) those entering it.

Each arc may carry a capacity c_a \geq 0 and a cost or length w_a.

A telecom link is full duplex: it is modelled by two opposite arcs with the same capacity, as in the backbone of ch. 1.

Definition 0.2 — Node–arc incidence matrix The incidence matrix N \in \{-1, 0, 1\}^{n \times m} has one row per node and one column per arc. The column of arc (u, v) has +1 in row u, -1 in row v and zeros elsewhere.

Arc flows x \in \mathbb{R}^m satisfy flow conservation with supplies b \in \mathbb{R}^n when N x = b . Row v reads: flow out of v minus flow into v equals b_v. Sources have b_v > 0, sinks b_v < 0, and the supplies sum to zero.

0.2 Linear programming and duality

Definition 0.3 — Primal and dual linear programs For A \in \mathbb{R}^{p \times q}, b \in \mathbb{R}^p and c \in \mathbb{R}^q, the primal and dual programs are (P)\ \max \{ c^\top x : A x \leq b,\ x \geq 0 \}, \qquad (D)\ \min \{ b^\top y : A^\top y \geq c,\ y \geq 0 \}. Each dual variable y_i is attached to one primal constraint.

Lemma 0.4 — Farkas Exactly one of the two systems has a solution: \text{(i)}\ A x = b,\ x \geq 0; \qquad \text{(ii)}\ A^\top y \geq 0,\ b^\top y < 0 . Geometrically, either b lies in the cone generated by the columns of A, or a hyperplane separates b from that cone; the certificate y is what Benders decomposition adds as a cut (ch. 5.5).

With slack variables, the inequality form follows: A x \leq b, x \geq 0 is infeasible if and only if some y \geq 0 has A^\top y \geq 0 and b^\top y < 0.

Theorem 0.5 — Weak and strong duality If x is feasible for (P) and y for (D), then c^\top x \leq b^\top y . If (P) has a finite optimum, so has (D), and \max (P) = \min (D).

Proof. Weak duality is one line: c^\top x \leq (A^\top y)^\top x = y^\top A x \leq y^\top b, using x \geq 0, y \geq 0 and both sets of constraints.

Strong duality follows from the inequality form of Lemma 0.4. Let z^\star = \max (P); it suffices to find a dual feasible y with b^\top y \leq z^\star.

  1. If none exists, the system A^\top y \geq c, y \geq 0, b^\top y \leq z^\star is infeasible, and Lemma 0.4 gives x \geq 0 and \lambda \geq 0 with A x \leq \lambda b, \qquad c^\top x > \lambda z^\star .
  2. If \lambda > 0, the point x / \lambda is feasible for (P) with value above z^\star, a contradiction.
  3. If \lambda = 0, then A x \leq 0 and c^\top x > 0: adding t x to any feasible point improves it without bound as t \to \infty, which contradicts a finite optimum. Bertsimas and Tsitsiklis (1997, ch. 4) give the details. ∎

Proposition 0.6 — Complementary slackness Feasible x and y are both optimal if and only if, for every row i and every column j, y_i \, (b - A x)_i = 0, \qquad x_j \, (A^\top y - c)_j = 0 .

Proof. The gap b^\top y - c^\top x equals y^\top (b - Ax) + x^\top (A^\top y - c), a sum of non-negative products. By Theorem 0.5, x and y are both optimal exactly when the gap is zero, that is, when every product vanishes. ∎

A dual variable is a shadow price: the rate at which the optimal value changes when the right-hand side of its constraint grows. In network models, the dual of a capacity constraint is the value of one more unit of capacity on that link (ch. 3.2, ch. 5.4).

Example 0.7 — Duality on a small LP Maximise 3x_1 + 2x_2 subject to x_1 + x_2 \leq 4, x_1 + 3x_2 \leq 6, x_1 \leq 3.5 and x \geq 0.

  • The first and third constraints are tight at x = (3.5, 0.5), of value 11.5.
  • Complementary slackness forces y_2 = 0, since the second constraint has slack 1.
  • The dual constraints then read y_1 + y_3 = 3 and y_1 = 2, so y = (2, 0, 1).
  • The dual value is 4 \cdot 2 + 3.5 \cdot 1, and both values agree: c^\top x = b^\top y = 11.5 .
import numpy as np
from scipy.optimize import linprog

# Primal: max 3x1 + 2x2  s.t.  x1 + x2 <= 4,  x1 + 3x2 <= 6,  x1 <= 3.5,  x >= 0
c = np.array([3.0, 2.0])
A = np.array([[1.0, 1.0], [1.0, 3.0], [1.0, 0.0]])
b = np.array([4.0, 6.0, 3.5])
res = linprog(-c, A_ub=A, b_ub=b, bounds=[(0, None)] * 2, method="highs")
x = res.x
y = -res.ineqlin.marginals          # dual prices (HiGHS reports d(min)/db)
print("primal x =", x, " value =", c @ x)
print("dual   y =", y, " value =", b @ y)
print("slack b - Ax =", b - A @ x, " y * slack =", y * (b - A @ x))
print("reduced costs A^T y - c =", A.T @ y - c)

The solver returns the primal and dual solutions of Example 0.7, and every complementary product is zero.

0.3 Integer programs and complexity

Definition 0.8 — Mixed-integer program and LP relaxation A mixed-integer linear program (MILP) is z_{IP} = \min \{ c^\top x : A x \geq b,\ x \geq 0,\ x_j \in \mathbb{Z} \text{ for } j \in J \}. Its LP relaxation drops the integrality of the variables in J; its value is z_{LP}.

Proposition 0.9 — The relaxation bounds the integer optimum For a minimisation, z_{LP} \leq z_{IP} . For any feasible integer solution of value z_{UB} > 0, the optimality gap (z_{UB} - z_{LP}) / z_{UB} bounds how far that solution is from the optimum.

Proof. Every feasible point of the MILP is feasible for the relaxation, so the relaxation minimises over a larger set. ∎

The whole of ch. 4–5 is about making z_{LP} close to z_{IP} (strong formulations, cuts) and exploiting the gap (branch-and-bound).

Definition 0.10 — P, NP and NP-hardness A decision problem is in P if an algorithm answers it in time polynomial in the input size. It is in NP if a “yes” answer has a certificate checkable in polynomial time. A problem is NP-hard if every problem of NP reduces to it by a polynomial transformation.

Unless P = NP, an NP-hard problem has no polynomial algorithm. Deciding whether a MILP is feasible is NP-hard, and so are most network problems with integer decisions: the knapsack problem behind capacity upgrades (ch. 5.1), the Steiner tree problem behind network design (ch. 4.1), integer multicommodity flow (ch. 2.4) and the facility location problems of ch. 6 (Garey and Johnson, 1979).

NP-hardness is a worst-case statement. Modern solvers routinely solve network instances with thousands of binary variables, and the exact methods of ch. 5 exploit structure to go further. When the time budget is seconds, heuristics take over (ch. 7–10).

Key takeaways — chapter 0

  • Flow conservation is N x = b, with the node–arc incidence matrix N.
  • LP duality: weak duality gives bounds, strong duality equality, complementary slackness certificates of optimality.
  • Dual variables are prices; Farkas’ lemma gives certificates of infeasibility.
  • The LP relaxation bounds a MILP; the optimality gap measures the quality of any feasible solution.
  • Most network problems with integer decisions are NP-hard, yet often solvable in practice.

1. Telecommunication networks and their optimization problems

An optimization model of a network needs four ingredients: a graph with capacities, a traffic to carry, a set of routing rules and a time scale for the decision. This chapter introduces them, with only the networking vocabulary the models use.

A backbone network is built in layers. The optical layer carries wavelengths on fibres (wavelength-division multiplexing, WDM). The IP/MPLS layer sees each wavelength, or each bundle of them, as one link between two routers.

Capacity is installed in modules: interfaces of 10, 100 or 400 Gb/s, wavelengths, fibre pairs. Its cost is therefore a step function of the volume, which is why capacity planning is an integer problem (ch. 4.4).

1.2 Traffic matrices

Definition 1.1 — Demand and traffic matrix A demand (or commodity) k is a triple (s_k, t_k, d_k): an origin, a destination and a volume in Gb/s. The traffic matrix T collects the volumes T_{st} of all origin–destination pairs, usually at the busy hour.

Traffic matrices are rarely measured directly; they are estimated from link counts and sampled flows. The simplest model, often used as a prior, is the gravity model.

Definition 1.2 — Gravity model Given node weights P_v > 0 (population, number of customers) and a total volume \tau, the gravity traffic matrix is T_{st} = \tau \, \frac{P_s P_t}{\sum_{u \neq v} P_u P_v}, \qquad s \neq t .

Example 1.3 — The six-node backbone The backbone of this page has nodes A to F and eight full-duplex links, all of 10 Gb/s except B–C and C–E, of 5 Gb/s:

  • links A–B, A–C, B–C, B–D, C–E, D–E, D–F and E–F;
  • weights P = (4, 2, 2, 2, 2, 4) for A to F and a total of \tau = 40 Gb/s;
  • the normalising sum is 16^2 - 48 = 208, so for instance T_{AF} = 40 \cdot \tfrac{16}{208} = 3.08 \text{ Gb/s}, \qquad T_{BC} = 40 \cdot \tfrac{4}{208} = 0.77 \text{ Gb/s}.

1.3 Routing

How traffic follows the links depends on the technology.

The quality of a routing is first measured by the utilisation of each link, load divided by capacity. Queueing delay grows quickly as utilisation approaches 1, so operators keep headroom below full load.

import numpy as np, networkx as nx
links = [("A", "B", 10), ("A", "C", 10), ("B", "C", 5), ("B", "D", 10),
         ("C", "E", 5), ("D", "E", 10), ("D", "F", 10), ("E", "F", 10)]      # Gb/s
G = nx.DiGraph([(u, v, {"cap": c}) for u, v, c in links] + [(v, u, {"cap": c}) for u, v, c in links])
nodes, P = sorted(G), {"A": 4, "B": 2, "C": 2, "D": 2, "E": 2, "F": 4}
Z = sum(P[s] * P[t] for s in nodes for t in nodes if s != t)
T = {(s, t): 40 * P[s] * P[t] / Z for s in nodes for t in nodes if s != t}  # gravity, 40 Gb/s

# Route every demand on one minimum-hop path and accumulate the load of each arc.
load = dict.fromkeys(G.edges, 0.0)
for (s, t), d in T.items():
    path = nx.shortest_path(G, s, t)                    # fewest hops, first one found
    for a in zip(path, path[1:]):
        load[a] += d
util = {a: load[a] / G.edges[a]["cap"] for a in G.edges}
worst = max(util, key=util.get)
print("T[A,F] = %.2f Gb/s, T[B,C] = %.2f Gb/s, total %.0f Gb/s" % (T["A", "F"], T["B", "C"], sum(T.values())))
print("most loaded arc", worst, "utilisation %.2f" % util[worst])

The first lines of this block define the backbone; later blocks repeat them so that each one runs on its own. With one minimum-hop path per demand, the arc C→E carries 4.6 Gb/s on 5 Gb/s:

\text{maximum utilisation} = 0.92 .

Chapter 3 shows that a better routing of the same traffic exists, and how to prove that it is optimal.

1.4 Virtualization and autonomy

Three evolutions change the optimization problems.

1.5 Three time scales of decisions

Time scale Decisions Typical model Chapter
Years: design and dimensioning which links and nodes to build, how many modules MILP with fixed costs ch. 4
Months: capacity planning upgrades for forecast traffic, protection capacity MILP, decomposition ch. 4–5, 9
Minutes to milliseconds: operation and orchestration routing, placement of functions, admission of slices LP, fast heuristics, learned policies ch. 3, 6–8, 10

The objectives also vary: installation cost, maximum utilisation, latency, energy, robustness to failures. A good model states which one matters and turns the others into constraints.

Key takeaways — chapter 1

  • A network model has a graph with capacities, a traffic matrix, routing rules and a time scale.
  • Capacities come in modules, so dimensioning and planning are integer problems.
  • Shortest-path protocols route through weights; explicit routing and SDN choose paths directly.
  • Virtualization adds compute resources and service chains; slicing and autonomy add guarantees and time limits.
  • On the backbone, one minimum-hop path per demand loads a link to 92 %.

2. Flows, paths and cuts

Routing is a flow problem. This chapter solves the single-commodity problems in polynomial time, explains why their solutions are integral, and shows what breaks when several commodities share the capacities.

2.1 Shortest paths

Definition 2.1 — Shortest path problem Given arc lengths w_a \geq 0 and two nodes s, t, find a path from s to t of minimum total length. The distance \mathrm{dist}(s, v) is that minimum for target v.

Proposition 2.2 — Dijkstra’s algorithm Keep a tentative label \ell(v), with \ell(s) = 0 and \ell(v) = \infty otherwise. Repeatedly make permanent the non-permanent node u of smallest label, and relax its outgoing arcs: \ell(v) \leftarrow \min \big( \ell(v),\ \ell(u) + w_{uv} \big). With non-negative lengths, every permanent label equals the distance. A binary heap gives a running time of order (n + m) \log n.

Proof. By induction on the permanent nodes, whose labels are assumed exact. Let u be the next node made permanent and take a shortest path P to u.

  1. Let (q, r) be the first arc of P that leaves the permanent set. Since q is permanent, its arcs were relaxed, so \ell(r) \leq \mathrm{dist}(s, r).
  2. Lengths are non-negative, so \mathrm{dist}(s, r) \leq \mathrm{dist}(s, u), the length of P.
  3. Node u was chosen with the smallest label, so \ell(u) \leq \ell(r). Chaining, \ell(u) \leq \mathrm{dist}(s, u), and a label is always the length of some path, hence equality. ∎

With negative lengths but no negative cycle, the Bellman–Ford algorithm relaxes every arc n - 1 times; a further improvement reveals a negative cycle.

The shortest path problem is also an LP: send one unit from s to t at minimum cost, \min \{ w^\top x : N x = e_s - e_t,\ x \geq 0 \}. Its dual has one potential \pi_v per node: \max \ \pi_s - \pi_t \quad \text{s.t.} \quad \pi_u - \pi_v \leq w_{uv} \ \text{ for every arc } (u, v). Distances to t are optimal potentials. Pricing in column generation (ch. 5.4) is exactly this problem.

2.2 Maximum flow and minimum cut

Definition 2.3 — s–t flow An s–t flow is a vector 0 \leq x \leq c that satisfies conservation at every node other than s and t. Its value is the net flow out of s: |x| = x(\delta^+(\{s\})) - x(\delta^-(\{s\})). An s–t cut is a set S with s \in S and t \notin S; its capacity is c(\delta^+(S)).

Theorem 2.4 — Max-flow min-cut (Ford and Fulkerson, 1956) The maximum value of an s–t flow equals the minimum capacity of an s–t cut: \max_x |x| = \min_{S} c(\delta^+(S)).

Proof.

  1. Weak inequality. For any flow and cut, summing conservation over S gives |x| = x(\delta^+(S)) - x(\delta^-(S)) \leq c(\delta^+(S)).
  2. Residual graph. An arc (u, v) has residual capacity c_{uv} - x_{uv} forwards and x_{uv} backwards. Pushing the smallest residual capacity along an s–t path of the residual graph increases |x|.
  3. Optimality. Let x be maximum and S the nodes reachable from s in the residual graph; then t \notin S. Every arc leaving S is saturated and every arc entering S is empty, otherwise more nodes would be reachable. Step 1 then holds with equality. ∎

Augmenting along shortest residual paths (Edmonds and Karp, 1972) terminates after at most nm augmentations, whatever the capacities.

2.3 Total unimodularity and minimum-cost flows

Definition 2.5 — Totally unimodular matrix A matrix is totally unimodular (TU) if every square submatrix has determinant -1, 0 or 1.

Theorem 2.6 — Incidence matrices are TU The node–arc incidence matrix N of a directed graph is totally unimodular.

Proof. By induction on the size k of a square submatrix B. Entries are in \{-1, 0, 1\}, so the case k = 1 holds.

For k > 1, each column of B has at most one +1 and one -1.

  1. If some column of B is zero, \det B = 0.
  2. If some column has a single non-zero entry, expand along it: \det B = \pm \det B' with B' of size k - 1, in \{-1, 0, 1\} by induction.
  3. Otherwise every column has exactly one +1 and one -1, so the rows of B sum to zero and \det B = 0. ∎

Corollary 2.7 — Integral flows If A is TU and b, c are integral, every vertex of \{ x : A x = b,\ 0 \leq x \leq c \} is integral. In particular, the minimum-cost flow problem \min \{ w^\top x : N x = b,\ 0 \leq x \leq c \} with integral supplies and capacities has an integral optimal solution, found by any simplex method.

Proof. Once the variables at a bound are fixed, a vertex is the unique solution of a square non-singular system B x_B = b', with B a square submatrix of A and b' integral.

Total unimodularity gives \det B = \pm 1, and Cramer’s rule writes each coordinate as an integer determinant divided by \det B. ∎

So single-commodity problems with integer data are solved by linear programming, with integral answers for free. Maximum flow, shortest paths, transportation and assignment are all special cases.

import numpy as np, networkx as nx
links = [("A", "B", 10), ("A", "C", 10), ("B", "C", 5), ("B", "D", 10),
         ("C", "E", 5), ("D", "E", 10), ("D", "F", 10), ("E", "F", 10)]      # Gb/s
G = nx.DiGraph([(u, v, {"cap": c}) for u, v, c in links] + [(v, u, {"cap": c}) for u, v, c in links])
ms = {("A", "B"): 2, ("A", "C"): 3, ("B", "C"): 1, ("B", "D"): 4,
      ("C", "E"): 2, ("D", "E"): 1, ("D", "F"): 2, ("E", "F"): 3}          # latency (ms)
for (u, v), w in ms.items():
    G.edges[u, v]["w"] = G.edges[v, u]["w"] = w

print("Dijkstra A->F:", nx.dijkstra_path(G, "A", "F", weight="w"),
      nx.dijkstra_path_length(G, "A", "F", weight="w"), "ms")
value, flow = nx.maximum_flow(G, "A", "F", capacity="cap")
cut_value, (S, Sbar) = nx.minimum_cut(G, "A", "F", capacity="cap")
print("max flow A->F =", value, "| min cut", sorted((u, v) for u in S for v in G[u] if v in Sbar), cut_value)

# Minimum-cost flow: 12 Gb/s from A to F, cost = latency x volume.
G.nodes["A"]["demand"], G.nodes["F"]["demand"] = -12, 12
f = nx.min_cost_flow(G, capacity="cap", weight="w")
print("min cost =", nx.cost_of_flow(G, f, weight="w"),
      "| integral:", all(float(x).is_integer() for u in f for x in f[u].values()))

Example 2.8 — Flows on the backbone With the latencies of the block (in ms), several paths from A to F tie at 8 ms. The maximum flow from A to F is 15 Gb/s, and the cut S = \{A, B, C\} certifies it: c_{BD} + c_{CE} = 10 + 5 = 15 \text{ Gb/s}. Sending 12 Gb/s from A to F at minimum latency-weighted cost gives a cost of 96, with integral arc flows, as Corollary 2.7 predicts.

2.4 Multicommodity flows

A network carries many demands at once, and they share the capacities. Let K be the set of demands of Definition 1.1.

Definition 2.9 — Arc-node formulation One flow vector x^k \in \mathbb{R}^m_{\geq 0} per demand, with its own conservation constraints and a joint capacity constraint: N x^k = d_k (e_{s_k} - e_{t_k}) \quad (k \in K), \qquad \sum_{k \in K} x^k_a \leq c_a \quad (a \in A). It has |K| \cdot m variables and a polynomial number of constraints.

Definition 2.10 — Arc-path formulation Let \mathcal{P}_k be the set of paths from s_k to t_k, and x_p \geq 0 the flow on path p: \sum_{p \in \mathcal{P}_k} x_p = d_k \quad (k \in K), \qquad \sum_{k} \sum_{p \in \mathcal{P}_k : a \in p} x_p \leq c_a \quad (a \in A). It has few constraints but exponentially many variables, handled by column generation (ch. 5.4).

Proposition 2.11 — Flow decomposition Every flow x \geq 0 satisfying N x = d (e_s - e_t) is a sum of at most m flows, each along an s–t path or a cycle. Hence the two formulations have the same LP optimum for any cost w \geq 0.

Proof. While x \neq 0, follow arcs of positive flow from s, or from any node with positive flow if |x| = 0. Conservation guarantees an exit at every intermediate node, so the walk reaches t or closes a cycle. Subtract the bottleneck value along it: at least one arc becomes zero, hence at most m steps. With w \geq 0, dropping the cycles does not increase the cost. ∎

Integrality is lost with several commodities: the constraint matrix is no longer TU, and the LP optimum can split a demand over paths with fractional volumes. Finding an integral multicommodity flow is NP-hard already with two commodities (Even, Itai and Shamir, 1976), and so is routing each demand on a single path (the unsplittable flow problem).

Feasibility is also subtler. The cut condition, that every cut has at least the capacity of the demands crossing it, is necessary but not sufficient. On the complete bipartite graph between \{a_1, a_2\} and \{b_1, b_2, b_3\}, with unit undirected capacities and unit demands a_1 a_2, b_1 b_2, b_2 b_3, b_1 b_3, every cut satisfies it. Yet each demand needs a path of two edges, 8 units in total for 6 available: only 75 % of each demand can be routed.

Theorem 2.12 — Japanese theorem (Iri, 1971; Onaga and Kakusho, 1971) The multicommodity flow problem of Definition 2.9 is feasible if and only if, for every length vector \mu \in \mathbb{R}^m_{\geq 0}, \sum_{a \in A} \mu_a c_a \ \geq \ \sum_{k \in K} d_k \, \mathrm{dist}_\mu(s_k, t_k). These are the metric inequalities; cut inequalities are the special case where \mu is the indicator of a cut.

Proof. Necessity: route the flow on paths (Proposition 2.11); each unit of demand k uses arcs whose lengths sum to at least \mathrm{dist}_\mu(s_k, t_k), and the total length-weighted load is at most \sum_a \mu_a c_a.

Sufficiency: apply Lemma 0.4 to the arc-path system with slack variables on the capacities. Infeasibility yields multipliers \mu_a \geq 0 for the capacities and u_k for the demands such that u_k \leq \sum_{a \in p} \mu_a \ \ (p \in \mathcal{P}_k), \qquad \sum_k d_k u_k > \sum_a \mu_a c_a . The first condition says u_k \leq \mathrm{dist}_\mu(s_k, t_k), so the metric inequality for \mu is violated. ∎

The theorem is the bridge between routing and dimensioning: it describes the capacity vectors that can carry a traffic matrix without mentioning flows, which ch. 4.5 and Benders decomposition (ch. 5.5) exploit.

Key takeaways — chapter 2

  • Dijkstra computes distances with non-negative lengths; distances are the optimal dual potentials of the shortest-path LP.
  • Maximum flow equals minimum cut; the residual graph gives both.
  • Incidence matrices are totally unimodular, so single-commodity flows with integer data are integral.
  • Multicommodity flows have arc-node and arc-path formulations with the same LP value, but lose integrality.
  • Feasibility is characterised by metric inequalities, not by cuts alone.

3. Traffic engineering: routing the flows

Traffic engineering (TE) chooses how the traffic matrix is routed on a given network, so that no link is congested. This chapter states the usual objectives, solves the splittable problem by linear programming with a dual certificate, and then studies the routings that protocols can actually implement.

3.1 Congestion objectives

Let \ell_a be the load of arc a, the total flow of all demands on it, and u_a = \ell_a / c_a its utilisation.

Proposition 3.1 — Convex piecewise-linear costs are linear programs If \phi(u) = \max_i (\alpha_i u + \beta_i) is convex and piecewise linear, minimising \sum_a c_a \phi(\ell_a / c_a) over flows is the LP obtained with one variable \Phi_a per arc and \Phi_a \geq \alpha_i \ell_a + \beta_i c_a \quad \text{for all } i, \qquad \min \sum_a \Phi_a .

Proof. For fixed loads, the smallest feasible \Phi_a is the maximum of the affine pieces, which is c_a \phi(\ell_a / c_a). Minimising over \Phi and the flows jointly is therefore the same as minimising the original cost. ∎

3.2 Minimum maximum utilisation and its dual

Definition 3.2 — MLU linear program With the arc-node variables of Definition 2.9 and one more variable U, \min U \quad \text{s.t.} \quad N x^k = d_k (e_{s_k} - e_{t_k}) \ (k \in K), \qquad \sum_{k} x^k_a \leq U c_a \ (a \in A), \qquad x \geq 0 .

For a splittable objective, demands with the same origin can be merged into one commodity with several sinks. This reduces the number of flow vectors from |K| to n, which is what the code below does.

Theorem 3.3 — The dual of MLU is a worst-case length function The optimal value of the MLU program is U^\star = \max_{\mu \geq 0,\ \sum_a \mu_a c_a = 1} \ \sum_{k \in K} d_k \, \mathrm{dist}_\mu(s_k, t_k). In particular, for every set of nodes S, with d(S) the total demand from S to its complement, U^\star \geq \frac{d(S)}{c(\delta^+(S))} .

Proof. A value U is feasible when the capacities U c can carry the traffic. By Theorem 2.12 this holds if and only if, for every \mu \geq 0, U \sum_a \mu_a c_a \geq \sum_k d_k \, \mathrm{dist}_\mu(s_k, t_k). Normalising \sum_a \mu_a c_a = 1 gives the formula.

For the cut bound, take \mu_a = 1 / c(\delta^+(S)) on the arcs of \delta^+(S) and zero elsewhere. Every path from S to its complement uses at least one of them, so demands leaving S have length at least 1 / c(\delta^+(S)). ∎

The optimal dual lengths \mu are the shadow prices of the capacities: they say which links limit the network. As long as the same dual stays optimal, one more Gb/s on arc a lowers U^\star at the rate U^\star \mu_a.

import numpy as np, networkx as nx
from scipy.optimize import linprog
links = [("A", "B", 10), ("A", "C", 10), ("B", "C", 5), ("B", "D", 10),
         ("C", "E", 5), ("D", "E", 10), ("D", "F", 10), ("E", "F", 10)]      # Gb/s
G = nx.DiGraph([(u, v, {"cap": c}) for u, v, c in links] + [(v, u, {"cap": c}) for u, v, c in links])
nodes, P = sorted(G), {"A": 4, "B": 2, "C": 2, "D": 2, "E": 2, "F": 4}
Z = sum(P[s] * P[t] for s in nodes for t in nodes if s != t)
T = {(s, t): 40 * P[s] * P[t] / Z for s in nodes for t in nodes if s != t}  # gravity, 40 Gb/s
arcs = list(G.edges); m, n = len(arcs), len(nodes)

# (a) Minimum maximum utilisation, splittable: flows aggregated by source, f[s, a] >= 0, plus U.
nv = n * m + 1
c = np.zeros(nv); c[-1] = 1.0
A_eq = np.zeros((n * n, nv)); b_eq = np.zeros(n * n)
for i, s in enumerate(nodes):
    for j, v in enumerate(nodes):
        for q, (a, b) in enumerate(arcs):
            A_eq[i * n + j, i * m + q] = (a == v) - (b == v)       # out minus in
        b_eq[i * n + j] = sum(T[s, t] for t in nodes if t != s) if v == s else -T.get((s, v), 0)
A_ub = np.zeros((m, nv))
for q, a in enumerate(arcs):
    A_ub[q, q:n * m:m] = 1.0; A_ub[q, -1] = -G.edges[a]["cap"]     # load_a <= U * c_a
r = linprog(c, A_ub=A_ub, b_ub=np.zeros(m), A_eq=A_eq, b_eq=b_eq, method="highs")
mu = -r.ineqlin.marginals                                          # dual "lengths" of the arcs
print("LP optimum U* = %.3f | arcs with a positive dual:" % r.fun,
      [(arcs[q], round(float(mu[q]), 3)) for q in range(m) if mu[q] > 1e-9])

# (b) Shortest-path routing with unit weights and even splitting (ECMP).
def ecmp_load(w):
    load = dict.fromkeys(arcs, 0.0)
    for t in nodes:                                                # one destination at a time
        dist = nx.shortest_path_length(G.reverse(), t, weight=lambda u, v, d: w[v, u])
        inflow = {v: T.get((v, t), 0.0) for v in nodes}
        for v in sorted(nodes, key=lambda v: -dist[v]):           # farthest nodes first
            nxt = [(v, u) for u in G[v] if v != t and abs(dist[v] - w[v, u] - dist[u]) < 1e-9]
            for a in nxt:
                load[a] += inflow[v] / len(nxt); inflow[a[1]] += inflow[v] / len(nxt)
    return load
L = ecmp_load(dict.fromkeys(arcs, 1))
worst = max(arcs, key=lambda a: L[a] / G.edges[a]["cap"])
print("ECMP, unit weights: arc", worst, "utilisation %.3f" % (L[worst] / G.edges[worst]["cap"]))

Example 3.4 — The backbone is limited by one cut Take S = \{A, B, C\}, whose weights sum to 8, like those of its complement.

  • Traffic from S to the complement: 40 \cdot 8 \cdot 8 / 208 = 12.31 Gb/s.
  • Capacity leaving S: arcs B→D and C→E, 10 + 5 = 15 Gb/s.
  • Theorem 3.3 gives U^\star \geq 12.31 / 15, and the LP reaches it: U^\star = 0.821 .
  • The solver’s dual puts the length 1/15 \approx 0.067 on B→D and C→E and zero elsewhere: this is the cut of the theorem, an optimal dual solution.

3.3 Shortest-path routing and the weight-setting problem

With OSPF or IS-IS, the operator does not choose paths: it chooses weights, and the protocol routes every packet on shortest paths for these weights, splitting evenly among equal-cost next hops (ECMP).

Definition 3.5 — Weight-setting problem Find integer weights w_a \in \{1, \dots, w_{\max}\} such that the ECMP routing they induce minimises a congestion cost, such as the MLU or the Fortz–Thorup cost of ch. 3.1.

Fortz and Thorup proved that finding optimal weights is NP-hard, and showed that local search on the weights comes close to the LP optimum on the backbones they studied (Fortz and Thorup, 2000; 2004). Not every routing is reachable this way.

Proposition 3.6 — Structure of shortest-path routings For any weights, the routing is destination-based (two packets for the same destination at the same node are forwarded alike), and every subpath of a used path is a shortest path between its endpoints. With ECMP, the split at a node is even among its shortest next hops.

Proof. The set of shortest next hops of v towards t depends only on v, t and the weights. If a subpath from u to v were not shortest, replacing it by a shorter one would shorten the whole path, a contradiction. ∎

On the backbone, unit weights send through the 5 Gb/s arc C→E all of the demands A→E, C→E and C→F, and half of A→F, B→E and C→D, that is 6.15 Gb/s. The block above confirms it: \text{ECMP with unit weights:}\quad u_{CE} = 1.231 . Even splitting is worse here than a single path (0.92, ch. 1). Ch. 7.3 tunes the weights by local search.

Complementary slackness explains why weights are powerful. In the min-cost multicommodity LP with capacity duals \pi_a \geq 0, every path carrying flow in an optimal solution is a shortest path for the lengths w_a + \pi_a. Wang, Wang and Zhang (2001) used this to show that optimal routings are shortest-path routings for suitable weights, provided the splitting ratios can be set freely rather than evenly.

3.4 Explicit routing and segment routing

MPLS traffic engineering sets up label-switched paths chosen by the operator. If each demand must follow a single path chosen among a candidate set \mathcal{P}_k (for instance its q shortest paths), the problem becomes a MILP with binary variables z_p: \sum_{p \in \mathcal{P}_k} z_p = 1 \ (k \in K), \qquad \sum_k \sum_{p \in \mathcal{P}_k : a \in p} d_k z_p \leq U c_a \ (a \in A). This is the unsplittable flow problem of ch. 2.4, NP-hard but solved well in practice for moderate sizes, or by rounding (ch. 7.2).

Segment routing (Filsfils et al., 2015) encodes a path as a short list of waypoints; between consecutive waypoints, packets follow the ECMP shortest paths of the protocol. With at most one intermediate waypoint per demand, TE stays an LP.

Definition 3.7 — Two-segment routing LP (Bhatia et al., 2015) Let g_{uv}(a) be the fraction of the traffic from u to v that ECMP sends on arc a.

Variable y_{k w} \geq 0 is the share of demand k sent through waypoint w, where w = t_k means “direct”: \sum_{w \in V} y_{k w} = 1, \qquad \sum_{k} d_k \sum_{w} y_{k w} \big( g_{s_k w}(a) + g_{w t_k}(a) \big) \leq U c_a .

Bhatia and coauthors (2015) give such LPs for known and for uncertain traffic, and report maximum utilisations close to that of the fully splittable LP in their experiments.

Key takeaways — chapter 3

  • TE minimises the maximum utilisation or a convex congestion cost; convex piecewise-linear costs stay LPs.
  • The MLU optimum equals the worst normalised metric bound; any cut gives a lower bound and a certificate.
  • On the backbone, the cut \{A, B, C\} proves that U^\star = 0.821 is optimal.
  • Shortest-path protocols route through weights; finding the best weights is NP-hard and even splitting can be harmful.
  • Explicit routing with candidate paths is a MILP; two-segment routing is an LP.

4. Network design and capacity planning

Design decides which links to build; capacity planning decides how many modules to install on them. Both add integer variables to the flow models of ch. 2, and the quality of the formulation, not only its correctness, decides whether a solver can handle it.

4.1 Fixed-charge network design

Let E be a set of candidate links, each with an opening cost f_e and a capacity c_e per direction; arc a belongs to link e(a).

Definition 4.1 — Fixed-charge multicommodity network design With binary y_e (link e is built) and arc-node flows x^k, \min \ \sum_{e \in E} f_e y_e + \sum_{k \in K} \sum_{a \in A} w_a x^k_a subject to the conservation constraints of Definition 2.9 and the weak linking constraints \sum_{k \in K} x^k_a \leq c_{e(a)} \, y_{e(a)} \qquad (a \in A).

The problem is NP-hard: with zero routing costs, large capacities and demands from one root to a set of terminals, an optimal design is a minimum Steiner tree connecting them.

4.2 Strong and weak formulations

Two formulations with the same integer solutions can have very different LP relaxations. A demand that uses a fraction of a link only needs a fraction of its opening in the weak formulation.

Definition 4.2 — Strong linking constraints Add, for every arc and every demand, x^k_a \leq d_k \, y_{e(a)} \qquad (a \in A,\ k \in K). They are valid: a closed link carries no flow, and an open one carries at most d_k of demand k.

Proposition 4.3 — The strong relaxation is tighter Let Q_w and Q_s be the LP relaxations with weak and with weak plus strong linking constraints. Then Q_s \subseteq Q_w, so z_{LP}(Q_w) \leq z_{LP}(Q_s) \leq z_{IP}, and the first inequality can be strict.

Proof. Q_s has the constraints of Q_w and more, so it is smaller and its minimum is larger.

For strictness, take one link with c_e = 10 and f_e = 1, one demand of 1 Gb/s and no routing cost. The weak relaxation sets y_e = 0.1, of value 0.1; the strong one forces y_e = 1, the integer optimum. ∎

import itertools, time
import numpy as np
from scipy.optimize import milp, LinearConstraint, Bounds
from scipy.sparse import lil_matrix

# Fixed-charge network design on 8 sites: which of the 28 candidate links to open?
rng = np.random.default_rng(1)
n = 8; xy = rng.uniform(0, 100, size=(n, 2))
E = list(itertools.combinations(range(n), 2))                       # candidate links
A = E + [(j, i) for i, j in E]                                      # both directions
length = {(i, j): np.linalg.norm(xy[i] - xy[j]) for i, j in A}
K = [(0, 7, 6), (1, 6, 4), (2, 5, 5), (3, 4, 3), (0, 5, 4), (6, 3, 2), (1, 2, 5), (4, 7, 3)]
cap, fixed = 10.0, 10.0                 # module capacity (Gb/s) and cost per km of an opened link

def model(strong):
    nx_, ny = len(A) * len(K), len(E)
    nv = nx_ + ny
    c = np.zeros(nv)
    for q, a in enumerate(A):
        for k in range(len(K)): c[k * len(A) + q] = 0.1 * length[a]   # routing cost per Gb/s
    c[nx_:] = [fixed * length[e] for e in E]
    rows_eq, beq = lil_matrix((len(K) * n, nv)), []
    for k, (s, t, d) in enumerate(K):
        for v in range(n):
            for q, (i, j) in enumerate(A):
                if i == v: rows_eq[k * n + v, k * len(A) + q] = 1
                if j == v: rows_eq[k * n + v, k * len(A) + q] = -1
            beq.append(d * ((v == s) - (v == t)))
    ub = []
    for q, (i, j) in enumerate(A):                                  # sum_k x_a^k <= cap * y_e
        e = E.index((min(i, j), max(i, j)))
        r = np.zeros(nv); r[[k * len(A) + q for k in range(len(K))]] = 1; r[nx_ + e] = -cap; ub.append(r)
        if strong:                                                  # x_a^k <= d_k * y_e
            for k, (_, _, d) in enumerate(K):
                r = np.zeros(nv); r[k * len(A) + q] = 1; r[nx_ + e] = -d; ub.append(r)
    cons = [LinearConstraint(rows_eq.tocsr(), beq, beq), LinearConstraint(np.array(ub), -np.inf, 0)]
    integ = np.r_[np.zeros(nx_), np.ones(ny)]
    lb, ubd = np.zeros(nv), np.r_[np.full(nx_, np.inf), np.ones(ny)]
    lp = milp(c, constraints=cons, integrality=np.zeros(nv), bounds=Bounds(lb, ubd))
    t0 = time.perf_counter()
    ip = milp(c, constraints=cons, integrality=integ, bounds=Bounds(lb, ubd))
    return lp.fun, ip.fun, ip.mip_node_count, time.perf_counter() - t0, int(round(ip.x[nx_:].sum()))

for strong in (False, True):
    lp, ip, nodes, sec, opened = model(strong)
    print("%-6s LP bound %7.1f | optimum %7.1f | gap %4.1f%% | B&B nodes %4d | %.2f s | links opened %d"
          % ("strong" if strong else "weak", lp, ip, 100 * (ip - lp) / ip, nodes, sec, opened))

Example 4.4 — Eight sites, 28 candidate links The block designs a network of 10 Gb/s links for eight demands. Both formulations reach the same optimum, 2363.8, with seven links opened. The difference is in the bound:

  • weak formulation: LP bound 1565.7, a root gap of 33.8 %, and 71 branch-and-bound nodes;
  • strong formulation: LP bound 2036.7, a gap of 13.8 %, and 7 nodes.

The strong model has more constraints but needs ten times fewer nodes (counts from SciPy 1.18; they vary with the solver version).

4.3 A modelling toolbox

Real requirements are logical as much as linear. The standard linearisations, with binary y, y', continuous 0 \leq x \leq M and a constant M bounding x:

Requirement Linear constraints
x > 0 only if y = 1 (big-M) x \leq M y
at most q of a set J of options \sum_{j \in J} y_j \leq q
y = 1 implies y' = 1 y \leq y'
z = y \, y' (both open) z \leq y, z \leq y', z \geq y + y' - 1
z = x \, y z \leq M y, z \leq x, z \geq x - M (1 - y), z \geq 0
U = \max_a u_a minimised U \geq u_a for all a

Big-M constraints are correct for any valid M but weak when M is large: the relaxation can set y = x / M, close to zero. Use the smallest valid bound, here the demand or the capacity, as in Definition 4.2.

4.4 Modular capacities and cut-set inequalities

In capacity planning the network exists; the question is how many modules to install. With an integer number y_e \geq 0 of modules of capacity C on each link (shared by both directions here) and cost \kappa_e per module, the network loading problem is \min \sum_{e} \kappa_e y_e \quad \text{s.t.} \quad \text{flows carry } T, \qquad \sum_{k} (x^k_{uv} + x^k_{vu}) \leq C y_e \ \ (e = uv),\ y_e \in \mathbb{Z}_{\geq 0}.

Its LP relaxation installs fractional modules exactly where needed and is weak. The classic remedy is to add cuts.

Proposition 4.5 — Cut-set inequalities For a set of nodes S, let \delta(S) be the links with one end in S and D(S) the total demand between S and its complement, in both directions. Every feasible solution satisfies \sum_{e \in \delta(S)} y_e \geq \left\lceil \frac{D(S)}{C} \right\rceil .

Proof. All demand between S and its complement crosses links of \delta(S), so C \sum_{e \in \delta(S)} y_e \geq D(S). Divide by C. The left-hand side is an integer, so it is at least the rounded-up right-hand side: this is a Chvátal–Gomory rounding (ch. 5.2). ∎

On the backbone with C = 10 Gb/s and S = \{A, B, C\}, the demand across is 2 \times 12.31 = 24.6 Gb/s. The LP relaxation can install 2.46 modules on the cut; the inequality demands 3. Barahona (1996) and Bienstock and coauthors (1998) used cut-set inequalities and their extensions to strengthen the relaxation of network loading problems substantially.

4.5 Metric inequalities and multi-period planning

Theorem 2.12 removes the flows altogether. With capacities shared by both directions, it holds with one length \mu_e per link and the same proof. A capacity vector C y carries the traffic if and only if \sum_{e} \mu_e \, C y_e \geq \sum_{k} d_k \, \mathrm{dist}_\mu(s_k, t_k) \qquad \text{for all } \mu \geq 0 . The capacity formulation minimises the module cost subject to these metric inequalities and integrality. There are infinitely many of them, but given y a violated one is found by solving one multicommodity LP: this is Benders decomposition (ch. 5.5).

Planning is also dynamic. With forecast traffic matrices T^1, \dots, T^H for successive periods, modules installed earlier remain, so the cumulative capacities satisfy y^h_e \geq y^{h-1}_e, and the cost of period h is discounted. Delaying an installation saves money but risks congestion if the forecast is wrong, a trade-off revisited with robust models in ch. 9.2.

Key takeaways — chapter 4

  • Network design adds binary opening variables to multicommodity flows; it generalises the Steiner tree problem.
  • Strong linking constraints x^k_a \leq d_k y_e tighten the relaxation; on the example the gap falls from 33.8 % to 13.8 %.
  • Logical requirements linearise with binaries; big-M constants must be as small as possible.
  • Modular capacities make planning an integer program; cut-set inequalities round the capacity needed across each cut.
  • Metric inequalities give a formulation in capacities only, the basis of Benders decomposition.

5. Exact methods: branch-and-bound, cutting planes and decomposition

A solver call hides several algorithms. This chapter opens the box: it enumerates cleverly (branch-and-bound), tightens the relaxation (cutting planes), and splits large models into easy pieces (Lagrangian relaxation, column generation, Benders decomposition), each on a network problem.

5.1 Branch-and-bound

Algorithm 5.1 — Branch-and-bound for a minimisation MILP Keep a list of open subproblems, initially the MILP itself, and an incumbent value z_{UB} = +\infty.

  1. Select an open subproblem and solve its LP relaxation, of value z.
  2. If it is infeasible or z \geq z_{UB}, discard it (pruning by bound).
  3. If its solution is integral, update the incumbent if z < z_{UB}, and discard it.
  4. Otherwise pick a variable x_j with fractional value v and create two subproblems with x_j \leq \lfloor v \rfloor and x_j \geq \lceil v \rceil (branching).

Proposition 5.2 — Correctness If the integer variables are bounded, Algorithm 5.1 terminates. The final incumbent is optimal, and if none was found, the MILP is infeasible.

Proof. Branching splits the feasible integer set of a node into two disjoint parts and loses no integer point. A node is discarded only if its LP, hence its integer set (Proposition 0.9), contains nothing better than the incumbent. Bounded integer variables can be branched on finitely often. ∎

The search order matters. Depth-first search finds feasible solutions quickly and uses little memory; best-bound search processes the node of smallest LP value and minimises the number of nodes. Solvers mix both, and choose the branching variable by estimating the bound improvement (strong branching, pseudo-costs), a step that ch. 10.2 learns.

import numpy as np
# Depth-first branch-and-bound for a 0-1 knapsack: choose link upgrades (value v, price w)
# within a budget W. The bound at a node is the LP relaxation, solved greedily (Dantzig).
rng = np.random.default_rng(4)
n = 30; v = rng.integers(10, 60, n).astype(float); w = rng.integers(5, 40, n).astype(float)
W = 0.4 * w.sum()
order = np.argsort(-v / w); v, w = v[order], w[order]        # sort by value per unit of budget

def lp_bound(i, cap, val):
    """Value of the LP relaxation over items i.. with remaining budget cap."""
    while i < n and w[i] <= cap:
        cap -= w[i]; val += v[i]; i += 1
    return val + (v[i] * cap / w[i] if i < n else 0.0)

best, nodes = 0.0, 0
stack = [(0, W, 0.0)]                                        # (next item, budget left, value)
while stack:
    i, cap, val = stack.pop(); nodes += 1
    best = max(best, val)
    if i == n or lp_bound(i, cap, val) <= best:              # prune by bound
        continue
    stack.append((i + 1, cap, val))                          # branch x_i = 0
    if w[i] <= cap:
        stack.append((i + 1, cap - w[i], val + v[i]))        # branch x_i = 1 (explored first)
print("optimum %.0f | root LP bound %.1f | nodes explored %d of 2^%d = %d"
      % (best, lp_bound(0, W, 0.0), nodes, n, 2 ** n))

The knapsack models a budget of link upgrades, each with a value and a price. Thirty items have about 10^9 subsets; with a root bound only 0.7 % above the optimum, branch-and-bound explores 118 nodes and proves the optimum, 761.

5.2 Cutting planes and branch-and-cut

Definition 5.3 — Valid inequality and separation An inequality \alpha^\top x \leq \beta is valid for a MILP if every integer feasible point satisfies it. Given a fractional point \bar x, the separation problem finds a valid inequality violated by \bar x, or proves that none exists in a given family.

Proposition 5.4 — Chvátal–Gomory rounding Let \sum_j a_j x_j \leq b be valid for a set of non-negative integer vectors x. Then \sum_j \lfloor a_j \rfloor x_j \leq \lfloor b \rfloor is valid too. Every such inequality, derived from non-negative combinations of the constraints, is a Chvátal–Gomory cut.

Proof. Since x \geq 0, \sum_j \lfloor a_j \rfloor x_j \leq \sum_j a_j x_j \leq b. The left-hand side is an integer, hence at most \lfloor b \rfloor. ∎

Gomory’s fractional cut applies this rounding to a row of the optimal simplex tableau, and is violated by the current vertex. For a capacity row with binary variables, \sum_j a_j y_j \leq b, a cover is a set J with \sum_{j \in J} a_j > b; the inequality \sum_{j \in J} y_j \leq |J| - 1 is valid, since not all of J fits. The cut-set inequalities of Proposition 4.5 are problem-specific cuts.

Branch-and-cut separates cuts at the nodes of the tree. Together with presolve and primal heuristics, it is what modern solvers do: HiGHS (used here), SCIP, CPLEX and Gurobi.

5.3 Lagrangian relaxation

Some constraints link otherwise independent subproblems. In multicommodity flows, only the capacity constraints couple the demands.

Definition 5.5 — Lagrangian relaxation For z = \min \{ c^\top x : A x \leq b,\ x \in X \}, where optimising over X is easy, and multipliers \lambda \geq 0, L(\lambda) = \min_{x \in X} \ c^\top x + \lambda^\top (A x - b). The Lagrangian dual is z_{LD} = \max_{\lambda \geq 0} L(\lambda).

Theorem 5.6 — Lagrangian bounds For every \lambda \geq 0, L(\lambda) \leq z.

If X is the set of integer points of a rational polyhedron, z_{LD} is the minimum of c^\top x over Ax \leq b and the convex hull of X. Hence, if the subproblem has integral LP solutions, z_{LD} = z_{LP} .

Proof. For a feasible x, \lambda^\top (A x - b) \leq 0, so L(\lambda) \leq c^\top x. The characterisation (Geoffrion, 1974) follows from LP duality applied to the convex hull of X, which a linear objective cannot distinguish from X. If the LP over X already has integral vertices, this hull is the LP polyhedron itself. ∎

L is concave and piecewise linear. If x_\lambda minimises the Lagrangian at \lambda, the violation g = A x_\lambda - b is a subgradient: L(\lambda') \leq L(\lambda) + g^\top (\lambda' - \lambda) \quad \text{for all } \lambda' \geq 0 . The projected subgradient method updates \lambda \leftarrow \max(0, \lambda + t_k g) with steps t_k \to 0 and \sum_k t_k = \infty. When the subgradients are bounded, the best value found converges to z_{LD}.

Relaxing the capacities of the min-cost multicommodity flow with multipliers \pi_a leaves one shortest-path problem per demand, for the lengths w_a + \pi_a. The multipliers act as congestion tolls.

import numpy as np, networkx as nx
links = [("A", "B", 10), ("A", "C", 10), ("B", "C", 5), ("B", "D", 10),
         ("C", "E", 5), ("D", "E", 10), ("D", "F", 10), ("E", "F", 10)]      # Gb/s
G = nx.DiGraph([(u, v, {"cap": c}) for u, v, c in links] + [(v, u, {"cap": c}) for u, v, c in links])
nodes, P = sorted(G), {"A": 4, "B": 2, "C": 2, "D": 2, "E": 2, "F": 4}
Z = sum(P[s] * P[t] for s in nodes for t in nodes if s != t)
T = {(s, t): 40 * P[s] * P[t] / Z for s in nodes for t in nodes if s != t}  # gravity, 40 Gb/s
ms = {("A", "B"): 2, ("A", "C"): 3, ("B", "C"): 1, ("B", "D"): 4,
      ("C", "E"): 2, ("D", "E"): 1, ("D", "F"): 2, ("E", "F"): 3}          # latency (ms)
for (u, v), w in ms.items():
    G.edges[u, v]["w"] = G.edges[v, u]["w"] = w
arcs = list(G.edges); cap = np.array([G.edges[a]["cap"] for a in arcs])

# Relax the capacity constraints with multipliers pi >= 0: the relaxed problem splits
# into one shortest-path problem per demand, for the arc lengths w + pi.
pi, best = np.zeros(len(arcs)), -np.inf
for k in range(300):
    for a, p in zip(arcs, pi):
        G.edges[a]["red"] = G.edges[a]["w"] + p
    load, value = np.zeros(len(arcs)), -pi @ cap
    for (s, t), d in T.items():
        path = nx.shortest_path(G, s, t, weight="red")
        value += d * nx.path_weight(G, path, "red")
        for a in zip(path, path[1:]):
            load[arcs.index(a)] += d
    best = max(best, value)                      # every L(pi) is a lower bound
    pi = np.maximum(0.0, pi + (load - cap) / (k + 1))   # projected subgradient step
print("best Lagrangian bound after 300 steps: %.3f" % best)

The subproblem is a set of shortest paths, which have integral LP solutions, so Theorem 5.6 predicts that the best bound is the LP optimum of the min-cost multicommodity flow; the next block computes it, 170.769, and the subgradient method reaches it within 300 steps.

5.4 Column generation

The arc-path formulation (Definition 2.10) has one variable per path. Column generation works with a small subset of paths, the restricted master problem, and adds the paths that can improve it.

Proposition 5.7 — Pricing is a shortest-path problem Let \sigma_k be the dual of the demand constraint of k and -\pi_a \leq 0 the dual of the capacity of arc a in the restricted master. The reduced cost of a path p \in \mathcal{P}_k is \bar c_p = \sum_{a \in p} (w_a + \pi_a) - \sigma_k . The master solution is optimal for all paths if and only if, for every k, the shortest path for the lengths w + \pi has length at least \sigma_k.

Proof. The reduced cost is the cost minus the dual value of the column: \sum_{a \in p} w_a - \sigma_k - \sum_{a \in p} (-\pi_a). An LP solution is optimal when no column has a negative reduced cost; minimising \bar c_p over \mathcal{P}_k is a shortest-path problem with non-negative lengths. ∎

import numpy as np, networkx as nx
from scipy.optimize import linprog
links = [("A", "B", 10), ("A", "C", 10), ("B", "C", 5), ("B", "D", 10),
         ("C", "E", 5), ("D", "E", 10), ("D", "F", 10), ("E", "F", 10)]      # Gb/s
G = nx.DiGraph([(u, v, {"cap": c}) for u, v, c in links] + [(v, u, {"cap": c}) for u, v, c in links])
nodes, P = sorted(G), {"A": 4, "B": 2, "C": 2, "D": 2, "E": 2, "F": 4}
Z = sum(P[s] * P[t] for s in nodes for t in nodes if s != t)
T = {(s, t): 40 * P[s] * P[t] / Z for s in nodes for t in nodes if s != t}  # gravity, 40 Gb/s
ms = {("A", "B"): 2, ("A", "C"): 3, ("B", "C"): 1, ("B", "D"): 4,
      ("C", "E"): 2, ("D", "E"): 1, ("D", "F"): 2, ("E", "F"): 3}          # latency (ms)
for (u, v), w in ms.items():
    G.edges[u, v]["w"] = G.edges[v, u]["w"] = w
arcs, K = list(G.edges), list(T)
cap = [G.edges[a]["cap"] for a in arcs]

# Restricted master: path variables x_p, one artificial variable per demand (cost M).
cols = [(k, tuple(nx.shortest_path(G, *k, weight="w"))) for k in K]
for it in range(1, 50):
    n = len(cols)
    cost = [nx.path_weight(G, p, "w") for _, p in cols] + [1e3] * len(K)
    A_eq = np.hstack([np.array([[k == kk for kk, _ in cols] for k in K], float), np.eye(len(K))])
    A_ub = np.hstack([np.array([[a in set(zip(p, p[1:])) for _, p in cols] for a in arcs], float),
                      np.zeros((len(arcs), len(K)))])
    r = linprog(cost, A_ub=A_ub, b_ub=cap, A_eq=A_eq, b_eq=[T[k] for k in K], method="highs")
    u, pi = r.eqlin.marginals, -r.ineqlin.marginals          # duals: demands, capacities
    for a, p in zip(arcs, pi):
        G.edges[a]["red"] = G.edges[a]["w"] + p
    new = [(k, tuple(nx.shortest_path(G, *k, weight="red"))) for k in K]     # pricing
    new = [(k, p) for i, (k, p) in enumerate(new) if nx.path_weight(G, p, "red") < u[i] - 1e-9]
    if not new:
        break
    cols += new
print("column generation: %d master solves, %d paths, cost %.3f" % (it, len(cols), r.fun))

Starting from one shortest path per demand, the artificial variables absorb what the capacities refuse; the duals then price the congested arcs, pricing adds 10 detours, and the second master is optimal: 40 paths and the cost 170.769 of the 480-variable arc-node LP. When paths must be integral (unsplittable routing), column generation is embedded in branch-and-bound: branch-and-price (Barnhart et al., 1998).

5.5 Benders decomposition

In capacity planning, fixing the capacities y leaves a pure routing LP. Benders (1962) keeps y in a master problem and lets the routing subproblem return cuts.

Proposition 5.8 — Benders feasibility cuts are metric inequalities Fix capacities \bar y. If the routing LP is infeasible, Lemma 0.4 returns arc lengths \bar \mu \geq 0 with \sum_e \bar \mu_e \, C \bar y_e < \sum_k d_k \, \mathrm{dist}_{\bar \mu}(s_k, t_k), and the metric inequality for \bar \mu, linear in y, is valid for every feasible capacity vector and cuts off \bar y.

Proof. This is the sufficiency part of Theorem 2.12 applied to the capacities C \bar y. Validity is its necessity part. ∎

The algorithm alternates: solve the master (minimise \sum_e \kappa_e y_e over integer y subject to the cuts found so far), test the routing, add a cut if infeasible. When routing costs matter, optimality cuts built from the dual of the routing LP are added as well. Each cut corresponds to an extreme ray or point of a fixed dual polyhedron, so finitely many cuts suffice. Costa (2005) surveys Benders decomposition for fixed-charge network design.

Key takeaways — chapter 5

  • Branch-and-bound prunes with LP bounds; a tight root bound turns 2^{30} subsets into 118 nodes.
  • Chvátal–Gomory rounding, covers and cut-sets give valid inequalities; branch-and-cut separates them in the tree.
  • Lagrangian relaxation of capacities leaves shortest paths; its bound equals the LP bound when the subproblem is integral.
  • Column generation prices paths with lengths w + \pi; branch-and-price makes it exact for integer routing.
  • Benders decomposition keeps capacities in a master problem and receives metric inequalities as cuts.

6. Placement of network components

Where should controllers, caches, gateways or virtual functions run? Placement problems are facility location problems on a graph, sometimes coupled with routing. This chapter formulates them and compares exact and greedy solutions.

6.1 Facility location

Let I be the clients (switches, users, traffic sources), J the candidate sites and d_{ij} the cost of serving i from j, typically a latency.

Definition 6.1 — p-median, p-center and facility location With binary y_j (site j is open) and z_{ij} (i is served by j): \min \sum_{i, j} d_{ij} z_{ij} \quad \text{s.t.} \quad \sum_j z_{ij} = 1,\quad z_{ij} \leq y_j,\quad \sum_j y_j = p . This is the p-median problem. The p-center problem minimises the worst cost \max_i \sum_j d_{ij} z_{ij} instead. The uncapacitated facility location problem replaces the cardinality constraint by an opening cost \sum_j f_j y_j in the objective.

All three are NP-hard on general graphs. The constraint z_{ij} \leq y_j is the strong form of linking, as in Definition 4.2; it makes the LP relaxation of the p-median often integral or nearly so on practical instances.

6.2 Controller placement in SDN

In a software-defined network, each switch talks to a controller. Heller, Sherwood and McKeown (2012) posed the controller placement problem: average latency leads to the p-median, worst-case latency to the p-center.

The simplest heuristic adds sites one at a time, each time the one that reduces the cost most. Its quality is understood through submodularity.

A set function g is submodular when an element adds less to a larger set: for S \subseteq S' and j \notin S', g(S \cup \{j\}) - g(S) \geq g(S' \cup \{j\}) - g(S') .

Proposition 6.2 — Greedy placement and submodularity For a set S of open sites and a reference cost d_{i0} \geq \max_j d_{ij}, the savings function g(S) = \sum_{i \in I} \Big( d_{i0} - \min_{j \in S \cup \{0\}} d_{ij} \Big) is non-decreasing and submodular. Hence the greedy algorithm achieves at least (1 - 1/e) of the maximum savings with p sites (Nemhauser, Wolsey and Fisher, 1978).

Proof. Monotonicity: adding a site can only lower each minimum.

Submodularity: for a client i, let m_i(S) be its cost under the set S. Adding site j gains \max(0, m_i(S) - d_{ij}) for this client.

If S \subseteq S', then m_i(S') \leq m_i(S), so the gain is smaller at S'. Summing over clients gives diminishing returns, and the cited theorem gives the ratio. ∎

The guarantee is on savings, not on the cost itself; in practice greedy is often within a few per cent of the optimum. A swap-based local search (ch. 7.3) may improve it, but not always: Exercise 13 shows a greedy solution that no single swap improves.

import numpy as np, networkx as nx
from scipy.optimize import milp, LinearConstraint, Bounds
from scipy.sparse import coo_matrix

# Controller placement as a p-median problem on a random geometric topology of 30 switches.
G = nx.random_geometric_graph(30, 0.3, seed=5)
for u, v in G.edges:
    G.edges[u, v]["ms"] = 10 * np.hypot(*np.subtract(G.nodes[u]["pos"], G.nodes[v]["pos"]))
D = nx.floyd_warshall_numpy(G, weight="ms")          # switch-to-switch latency (ms)
n, p = len(D), 3

def p_median(D, p, cand):
    """min sum_ij D_ij z_ij  s.t.  sum_j z_ij = 1,  sum_j y_j = p,  z_ij <= y_j  (sites in cand)."""
    n, m = len(D), len(cand); nv = n * m + m
    z = np.arange(n * m).reshape(n, m); y = n * m + np.arange(m)
    assign = coo_matrix((np.ones(n * m), (np.repeat(np.arange(n), m), z.ravel())), (n, nv))
    count = coo_matrix((np.ones(m), (np.zeros(m, int), y)), (1, nv))
    link = coo_matrix((np.r_[np.ones(n * m), -np.ones(n * m)],
                       (np.r_[z.ravel(), z.ravel()], np.r_[z.ravel(), np.tile(y, n)])), (n * m, nv))
    cons = [LinearConstraint(assign, 1, 1), LinearConstraint(count, p, p), LinearConstraint(link, -np.inf, 0)]
    r = milp(np.r_[D[:, cand].ravel(), np.zeros(m)], constraints=cons,
             integrality=np.r_[np.zeros(n * m), np.ones(m)], bounds=Bounds(0, 1))
    return r.fun, sorted(cand[j] for j in range(m) if r.x[y[j]] > 0.5)

opt, sites = p_median(D, p, list(range(n)))
chosen = []                                           # greedy: add the site that helps most
for _ in range(p):
    chosen.append(min((j for j in range(n) if j not in chosen),
                      key=lambda j: D[:, chosen + [j]].min(1).sum()))
greedy = D[:, chosen].min(1).sum()
print("optimum: sites %s, mean latency %.2f ms, worst %.2f ms" % (sites, opt / n, D[:, sites].min(1).max()))
print("greedy:  sites %s, mean latency %.2f ms (ratio %.3f)" % (sorted(chosen), greedy / n, greedy / opt))

Example 6.3 — Three controllers for thirty switches On a random geometric topology with link latencies proportional to distance:

  • the p-median optimum opens sites 12, 17 and 18, with a mean latency of 2.83 ms and a worst one of 6.15 ms;
  • greedy opens 11, 18 and 27, with a mean latency of 2.90 ms;
  • the greedy cost is therefore 2.4 % above the optimum, which the MILP certifies.

6.3 VNF placement and service function chains

A service function chain is an ordered list of functions (f_1, \dots, f_L) that the traffic of a request must traverse, for instance firewall, then load balancer, then cache. Each node v offers a compute capacity R_v (cores), each function f consumes r_f per instance, and links have capacities.

Definition 6.4 — VNF placement and chaining Binary y_{fv} opens an instance of function f on node v at cost \gamma_{fv}.

Binary z^k_{\ell v} assigns the \ell-th function of request k to node v, and flow variables route each segment of the chain. The model minimises opening plus routing costs subject to: \sum_v z^k_{\ell v} = 1, \qquad z^k_{\ell v} \leq y_{f_\ell v}, \qquad \sum_f r_f \, y_{fv} \leq R_v, flow conservation from the node of function \ell to the node of function \ell + 1 for each segment, and link capacities shared by all segments.

Proposition 6.5 — Chain routing is a shortest path in a layered graph If the instances are already placed and capacities are not binding, the cheapest route of a chain is a shortest path in the graph made of L + 1 copies of the network, in which copy \ell is joined to copy \ell + 1 by a zero-cost arc at every node hosting f_{\ell + 1}.

Proof. A path from the source in copy 0 to the destination in copy L must cross the L layer arcs in order, each at a node hosting the next function. Projecting it onto the network gives a walk that visits the functions in order, with the same cost, and conversely. ∎

The joint problem, placement plus routing with capacities, is NP-hard; MILPs solve instances of tens of nodes, and heuristics or decompositions handle larger ones (Herrera and Botero, 2016, survey the field).

6.4 Virtual network embedding

A slice or a tenant may ask for a whole virtual network: virtual nodes with compute demands, virtual links with bandwidth demands. Virtual network embedding maps virtual nodes to distinct physical nodes and virtual links to physical paths, within capacities, at minimum cost. It contains the unsplittable flow problem and is NP-hard (Fischer et al., 2013, survey it).

Key takeaways — chapter 6

  • Placement is facility location: p-median for mean latency, p-center for the worst case.
  • The savings of a placement are submodular, so greedy has a (1 - 1/e) guarantee on savings.
  • On the example, greedy is 2.4 % above the optimum certified by the MILP.
  • VNF placement couples location, compute capacities and chain routing; with fixed placement, chains are shortest paths in a layered graph.
  • Virtual network embedding maps whole virtual networks; it is NP-hard and solved by staged, rounded or exact methods.

7. Heuristics and matheuristics

When the instance is too large or the time budget too short for an exact method, a heuristic returns a good solution without proof of optimality. This chapter builds heuristics for the problems of ch. 3–6 and insists on measuring their quality against a bound.

7.1 Measuring a heuristic

A heuristic value z_H means little alone. Three measures make it informative:

A test set should contain instances of several sizes and structures, for example from SNDlib (Orlowski et al., 2010) or the Internet Topology Zoo (Knight et al., 2011), never only the instances used to tune parameters.

7.2 Constructive heuristics and rounding

Greedy algorithms build a solution step by step, choosing the locally best move: open the site with the largest saving (ch. 6.2), route demands one by one on the shortest path with enough residual capacity, largest demands first.

LP rounding solves the relaxation and rounds it. For unsplittable routing, Raghavan and Thompson (1987) choose for each demand one path at random, with the probabilities given by the path flows of the LP solution.

Proposition 7.1 — Randomised rounding of path flows Let an LP solution route a fraction x_p / d_k of each demand on path p, with load at most c_a on every arc, and let every demand satisfy d_k \leq c_{\min}. Picking one path per demand independently with these probabilities gives an expected load at most c_a on every arc. With high probability, every load is at most c_a \cdot O\Big( \frac{\log m}{\log \log m} \Big).

Proof. The load of arc a is a sum of independent variables, one per demand, each between 0 and d_k \leq c_{\min}, and its expectation equals the LP load (linearity). A Chernoff bound for such sums bounds the probability that one arc exceeds its expectation by the stated factor by 1/m^2; a union bound over the m arcs concludes. ∎

In practice, rounding is followed by a repair step and a local search, and repeated with several seeds.

7.3 Local search and metaheuristics

Definition 7.2 — Neighbourhood and local optimum A neighbourhood maps a solution to a set of nearby solutions: change one weight, swap one open site for a closed one, reroute one demand. A solution is a local optimum if no neighbour is better. Local search moves to a better neighbour until none exists.

Metaheuristics escape local optima in different ways.

For the weight-setting problem of Definition 3.5, Fortz and Thorup (2000) used a local search whose basic move changes one weight, evaluated with the cost \phi. A minimal version follows.

import numpy as np, networkx as nx
links = [("A", "B", 10), ("A", "C", 10), ("B", "C", 5), ("B", "D", 10),
         ("C", "E", 5), ("D", "E", 10), ("D", "F", 10), ("E", "F", 10)]      # Gb/s
G = nx.DiGraph([(u, v, {"cap": c}) for u, v, c in links] + [(v, u, {"cap": c}) for u, v, c in links])
nodes, P = sorted(G), {"A": 4, "B": 2, "C": 2, "D": 2, "E": 2, "F": 4}
Z = sum(P[s] * P[t] for s in nodes for t in nodes if s != t)
T = {(s, t): 40 * P[s] * P[t] / Z for s in nodes for t in nodes if s != t}  # gravity, 40 Gb/s
arcs, rng = list(G.edges), np.random.default_rng(0)

def ecmp_load(w):                                   # as in ch. 3
    load = dict.fromkeys(arcs, 0.0)
    for t in nodes:
        dist = nx.shortest_path_length(G.reverse(), t, weight=lambda u, v, d: w[v, u])
        inflow = {v: T.get((v, t), 0.0) for v in nodes}
        for v in sorted(nodes, key=lambda v: -dist[v]):
            nxt = [(v, u) for u in G[v] if v != t and abs(dist[v] - w[v, u] - dist[u]) < 1e-9]
            for a in nxt:
                load[a] += inflow[v] / len(nxt); inflow[a[1]] += inflow[v] / len(nxt)
    return load

def phi(x):                                         # Fortz-Thorup cost per unit of capacity
    bp, slope = [0, 1/3, 2/3, 0.9, 1, 1.1, np.inf], [1, 3, 10, 70, 500, 5000]
    return sum(s * max(0.0, min(x, b) - a) for a, b, s in zip(bp, bp[1:], slope))

def evaluate(w):
    L = ecmp_load(w)
    u = {a: L[a] / G.edges[a]["cap"] for a in arcs}
    return sum(G.edges[a]["cap"] * phi(u[a]) for a in arcs), max(u.values())

w = dict.fromkeys(arcs, 1)
best = evaluate(w)
print("unit weights: cost %7.2f, max utilisation %.3f" % best)
for it in range(400):                               # first-improvement random local search
    a = arcs[rng.integers(len(arcs))]
    old, w[a] = w[a], int(rng.integers(1, 21))
    cand = evaluate(w)
    if cand[0] < best[0]:
        best = cand
    else:
        w[a] = old
print("local search: cost %7.2f, max utilisation %.3f (LP optimum 0.821)" % best)

Example 7.3 — Tuning OSPF weights on the backbone Starting from unit weights, whose ECMP routing overloads C→E (ch. 3.3):

  • 400 random single-weight moves are proposed and the improving ones kept, which lowers the Fortz–Thorup cost from 7232 to 178;
  • the maximum utilisation falls from 1.231 to 0.846;
  • the LP bound of Theorem 3.3 is 0.821, so the weights are within \frac{0.846 - 0.821}{0.846} = 3.0 \,\% of the best routing that any protocol could achieve.

7.4 Matheuristics

Matheuristics use a MILP solver as a component, which is often the fastest way to good solutions for network problems with a strong formulation.

These methods answer the “very short computation times” of autonomous networks (ch. 8.4) without giving up the model: the same formulation provides the bound that certifies the answer.

Key takeaways — chapter 7

  • A heuristic is evaluated by its gap to a bound, its time and its variability, on held-out instances.
  • Greedy and LP rounding construct solutions; randomised rounding has a logarithmic congestion guarantee.
  • Local search, simulated annealing, tabu search and GRASP improve them.
  • Local search on OSPF weights brings the backbone within 3 % of the LP bound.
  • Matheuristics use the MILP solver on restricted neighbourhoods and keep a certified gap.

8. Future networks: virtualization, slicing and autonomy

The second part of the course turns to the problems created by virtualized, sliced and autonomous networks. The models are variants of those of ch. 3–6, but decisions arrive continuously, must be taken in seconds, and must respect guarantees written in standards and contracts.

8.1 What changes

8.2 Network slicing as admission and embedding

Definition 8.1 — Slice request and admission problem A slice request i asks for a bandwidth b_i between nodes s_i and t_i (or for a whole virtual network, ch. 6.4) and pays r_i if admitted.

With candidate paths \mathcal{P}_i and binary x_{ip}, the offline admission problem is \max \sum_i r_i \sum_{p \in \mathcal{P}_i} x_{ip} \quad \text{s.t.} \quad \sum_{p} x_{ip} \leq 1, \qquad \sum_i \sum_{p \ni a} b_i \, x_{ip} \leq c_a .

It generalises the multi-dimensional knapsack problem and is NP-hard. When all requests are known in advance, a MILP solves moderate instances; in reality they arrive one by one.

import numpy as np, networkx as nx
from itertools import islice
from scipy.optimize import milp, LinearConstraint
links = [("A", "B", 10), ("A", "C", 10), ("B", "C", 5), ("B", "D", 10),
         ("C", "E", 5), ("D", "E", 10), ("D", "F", 10), ("E", "F", 10)]      # Gb/s
G = nx.DiGraph([(u, v, {"cap": c}) for u, v, c in links] + [(v, u, {"cap": c}) for u, v, c in links])
nodes, arcs, rng = sorted(G), list(G.edges), np.random.default_rng(7)

# 40 slice requests (s, t, bandwidth, revenue), each needing one path among its 3 shortest.
R = []
for _ in range(40):
    s, t = rng.choice(nodes, 2, replace=False)
    b = float(rng.integers(1, 6)); R.append((s, t, b, b * rng.uniform(1, 3)))
paths = [list(islice(nx.shortest_simple_paths(G, s, t), 3)) for s, t, _, _ in R]

# Online: accept a request if one of its candidate paths has enough residual capacity.
res, online = {a: G.edges[a]["cap"] for a in arcs}, 0.0
for (s, t, b, r), Ps in zip(R, paths):
    for p in Ps:
        if all(res[a] >= b for a in zip(p, p[1:])):
            for a in zip(p, p[1:]):
                res[a] -= b
            online += r
            break

# Offline optimum, all requests known: binary x_{i,p}, at most one path per request.
cols = [(i, set(zip(p, p[1:]))) for i, Ps in enumerate(paths) for p in Ps]
one = np.array([[i == j for i, _ in cols] for j in range(len(R))], float)
capa = np.array([[R[i][2] * (a in A) for i, A in cols] for a in arcs])
sol = milp(-np.array([R[i][3] for i, _ in cols]), integrality=np.ones(len(cols)), bounds=(0, 1),
           constraints=[LinearConstraint(one, 0, 1), LinearConstraint(capa, 0, [G.edges[a]["cap"] for a in arcs])])
print("revenue online %.1f | offline optimum %.1f | ratio %.2f" % (online, -sol.fun, online / -sol.fun))

On the backbone, 40 requests of 1 to 5 Gb/s exceed the capacity. Accepting greedily in order of arrival earns 156.4, against 177.1 for the offline optimum: 88 % of what full knowledge of the future would give.

8.3 Online optimization and competitive ratio

Definition 8.2 — Competitive ratio An online algorithm decides each request on arrival, irrevocably. For a maximisation problem it is \rho-competitive if, on every sequence \sigma of requests, \mathrm{ALG}(\sigma) \geq \frac{1}{\rho} \, \mathrm{OPT}(\sigma), where OPT knows the whole sequence in advance.

Proposition 8.3 — Greedy admission is not competitive For admission on a single link with arbitrary revenues, the algorithm that accepts every request that fits has no bounded competitive ratio.

Proof. Let the link have capacity 1. The adversary sends a request of bandwidth 1 and revenue \varepsilon, then a request of bandwidth 1 and revenue 1. Greedy accepts the first and must reject the second, earning \varepsilon; OPT earns 1. The ratio 1 / \varepsilon is unbounded. ∎

Good online algorithms therefore reserve capacity. Awerbuch, Azar and Plotkin (1993) price each link exponentially in its utilisation and accept a request only if its revenue exceeds the price of its cheapest path; when each request is small compared with link capacities, this achieves a competitive ratio logarithmic in the number of nodes (and in the range of request durations). The same primal–dual idea, prices from dual variables, underlies the column generation of ch. 5.4. The Stopping Times and Online Algorithms course develops competitive analysis in depth.

8.4 Autonomous networks and fast re-optimization

An autonomous network runs a closed loop: monitor the state, analyse it, plan a decision, execute it, often abbreviated MAPE. The planning step is an optimization problem re-solved every few seconds or minutes, on a state that changed little.

Definition 8.4 — Re-optimization with reconfiguration cost Given the current configuration \bar x (placement, routing), the new configuration solves \min_{x \in X(\text{new state})} \ f(x) + \eta \, \| x - \bar x \|_1 , where \eta \geq 0 prices each change. For binary x, the penalty is linear, as in local branching (ch. 7.4).

Three techniques make the loop fast enough.

8.5 From a standard to a model

Requirements of future networks are written in specifications by standardisation bodies such as 3GPP, ETSI and the IETF, not as equations. Turning such a text into a model is a skill in itself; a reliable method has five steps.

  1. Glossary to sets: list the entities (sites, slices, functions, links) and give each a set and an index.
  2. Quantities to parameters: list every number given or implied (capacities, budgets, latencies) with its unit.
  3. Decisions to variables: ask what the operator chooses; distinguish binary choices from quantities.
  4. “Shall” to constraints, “should” to objectives: mandatory requirements become constraints; preferences become objective terms or soft constraints with penalties.
  5. Check with a small instance: solve it by hand or with a solver, and verify that every sentence of the text is reflected.

Example 8.5 — An illustrative slicing requirement Consider the following excerpt, written for this page in the style of a specification, not quoted from a standard:

Each slice shall be assigned a slice type: low-latency or high-throughput. A low-latency slice shall be served end to end within a delay budget of 5 ms. The traffic of a low-latency slice shall not share a link with traffic of another operator’s slice. The network should minimise the number of active links.

  • Sets: slices i \in I, partitioned into I_{LL} and I_{HT}; operators o(i); links e \in E with delay \Delta_e.
  • Variables: path choice x_{ip} (one per slice), link activation y_e.
  • Delay: only candidate paths with \sum_{e \in p} \Delta_e \leq 5 ms are allowed for i \in I_{LL}; capacities as usual.
  • Isolation: x_{ip} + x_{jq} \leq 1 whenever i \in I_{LL}, o(i) \neq o(j), and paths p and q share a link.
  • Objective: \min \sum_e y_e, with x_{ip} \leq y_e for every link e of p.

The delay budget becomes a filter on candidate paths, isolation a set of conflict constraints, and the “should” sentence the objective. Exercise 17 asks for the complete model and its solution on the backbone.

Key takeaways — chapter 8

  • Future networks couple bandwidth and compute, change continuously and must be optimised within seconds.
  • Slice admission is a multi-dimensional knapsack with routing; online greedy reached 88 % of the offline optimum on the example.
  • Competitive analysis measures online algorithms; greedy admission is not competitive, price-based admission is.
  • Autonomous loops re-optimise with reconfiguration costs, warm starts and time-scale decomposition.
  • A specification becomes a model by mapping glossary to sets, quantities to parameters, choices to variables, “shall” to constraints and “should” to objectives.

9. Resilience, security and energy

Three requirements now shape network optimization beyond cost and congestion: surviving failures and traffic surprises, resisting attacks, and consuming less energy. Each adds constraints or scenarios to the models of ch. 3–6, and they pull in different directions.

9.1 Survivable network design

A failure of one link or node must not interrupt service. Protection reserves backup resources in advance; restoration reroutes after the failure using spare capacity.

Proposition 9.1 — Disjoint paths by minimum-cost flow Two arc-disjoint paths from s to t with minimum total length, used for dedicated 1+1 protection, are obtained by sending a flow of value 2 at minimum cost with unit capacities on every arc.

Proof. With integer data the min-cost flow has an integral optimum (Corollary 2.7), so each arc carries 0 or 1 unit. By Proposition 2.11 the flow decomposes into two s–t paths and cycles; unit capacities make the paths arc-disjoint, and non-negative lengths let us drop the cycles. Conversely, two disjoint paths form a feasible flow of value 2 with the same cost. ∎

Suurballe (1974) gives a combinatorial algorithm for the same problem with two runs of Dijkstra.

At the topology level, surviving any single link failure requires a 2-edge-connected design. In the formulation of Definition 4.1 this is the family of cut constraints \sum_{e \in \delta(S)} y_e \geq 2 \qquad (\emptyset \neq S \subsetneq V), exponentially many, but separated by a minimum-cut computation (Theorem 2.4) on the current solution (Kerivin and Mahjoub, 2005, survey survivable design).

Definition 9.2 — Capacity planning for single-link failures Let F be the set of failure scenarios, with f = \emptyset for the nominal state. For each scenario, a routing x^{k, f} avoids the failed link, and the installed modules must carry it: \sum_{k} \big( x^{k, f}_{uv} + x^{k, f}_{vu} \big) \leq C \, y_e \qquad (e = uv \neq f,\ f \in F). The spare capacity is shared between scenarios, which is cheaper than dedicated protection.

The model has one copy of the routing per scenario; with hundreds of links, it is solved by Benders decomposition or column generation, one subproblem per scenario (ch. 5.4–5.5).

9.2 Robust routing under uncertain traffic

Traffic matrices are forecasts. A robust model requires feasibility for every matrix of an uncertainty set, and fixes the routing as fractions f^k_a \in [0, 1] of each demand, so that the load of arc a is \sum_k d_k f^k_a.

Definition 9.3 — Γ-robust demands (Bertsimas and Sim, 2004) Each demand lies in [\bar d_k, \bar d_k + \hat d_k], and at most \Gamma of them deviate from their nominal value at the same time. The parameter \Gamma \in [0, |K|] sets the level of protection.

Theorem 9.4 — The robust counterpart is linear For fixed fractions f^k_a \geq 0, the constraint “\sum_k d_k f^k_a \leq c_a for all Γ-robust demands” holds if and only if there exist \theta \geq 0 and \rho_k \geq 0 with \sum_k \bar d_k f^k_a + \Gamma \theta + \sum_k \rho_k \leq c_a, \qquad \theta + \rho_k \geq \hat d_k f^k_a \quad (k \in K). With f, \theta and \rho as variables (one \theta and one \rho vector per arc), robust routing remains an LP.

Proof. In the model of Bertsimas and Sim, the worst case adds to the nominal load the value of the LP \beta_a = \max \Big\{ \sum_k \hat d_k f^k_a \delta_k : 0 \leq \delta_k \leq 1,\ \sum_k \delta_k \leq \Gamma \Big\}, whose vertices deviate \lfloor \Gamma \rfloor demands fully and one partially. Its dual is \min \Big\{ \Gamma \theta + \sum_k \rho_k : \theta + \rho_k \geq \hat d_k f^k_a,\ \theta \geq 0,\ \rho \geq 0 \Big\}. By strong duality (Theorem 0.5) the worst case is at most c_a minus the nominal load if and only if some dual feasible (\theta, \rho) achieves it. ∎

The hose model and oblivious routing, two other uncertainty models used in telecommunications, are described in ch. 11.3.

9.3 Security: interdiction and monitoring

Security problems put an adversary in the model.

Definition 9.5 — Maximum-flow interdiction An attacker removes a set R of arcs, each with a removal cost h_a, within a budget B, to minimise the maximum flow from s to t that remains. The defender wants to know the worst case, or to reinforce the arcs that matter.

Proposition 9.6 — Interdiction as a cut problem (Wood, 1993) The optimal interdiction value is \min_{S \ni s,\ t \notin S} \ \min_{R \subseteq \delta^+(S),\ h(R) \leq B} \ c\big(\delta^+(S) \setminus R\big), which a MILP with binary node-side variables and binary removal variables expresses.

Proof. After removing R, the maximum flow equals the minimum cut of the remaining graph (Theorem 2.4), \min_S c(\delta^+(S) \setminus R). Exchanging the two minimisations, and noting that removing arcs outside \delta^+(S) does not lower that cut, gives the formula. ∎

Wood (1993) proved the problem NP-hard, even when every removal costs 1. Yet, with unit costs, both halves are easy separately: the best attack on a fixed cut removes its B largest arcs, and the cut for a fixed attack is a minimum cut. The difficulty lies in choosing them together.

Other security requirements are covering or separation constraints.

9.4 Energy-aware networking

Network equipment consumes much of its power even when idle, so the consumption of a link is well approximated by P_0 y_e + P_1 \ell_e, with a large fixed part P_0 when the link is on. Turning off links at low traffic saves energy (Gupta and Singh, 2003; Chiaraviglio, Mellia and Neri, 2012).

Definition 9.7 — Energy-aware routing With binary y_e (link powered) and a maximum utilisation \bar u, minimise \sum_e (P_0 y_e + P_1 \ell_e) subject to the routing constraints and \ell_a \leq \bar u \, c_a \, y_{e(a)} for every arc.

import numpy as np, networkx as nx
from scipy.optimize import milp, LinearConstraint
links = [("A", "B", 10), ("A", "C", 10), ("B", "C", 5), ("B", "D", 10),
         ("C", "E", 5), ("D", "E", 10), ("D", "F", 10), ("E", "F", 10)]      # Gb/s
G = nx.DiGraph([(u, v, {"cap": c}) for u, v, c in links] + [(v, u, {"cap": c}) for u, v, c in links])
nodes, P = sorted(G), {"A": 4, "B": 2, "C": 2, "D": 2, "E": 2, "F": 4}
Z = sum(P[s] * P[t] for s in nodes for t in nodes if s != t)
T = {(s, t): 40 * P[s] * P[t] / Z for s in nodes for t in nodes if s != t}  # gravity, 40 Gb/s
arcs, n, m, E = list(G.edges), len(nodes), len(G.edges), len(links)
link_of = {a: e for e, (u, v, _) in enumerate(links) for a in [(u, v), (v, u)]}  # arc -> link

def green(scale, mu=0.9):
    """Fewest powered links that carry scale * T with utilisation <= mu (flows per source)."""
    nv = n * m + E
    A_eq, b_eq = np.zeros((n * n, nv)), np.zeros(n * n)
    for i, s in enumerate(nodes):
        for j, v in enumerate(nodes):
            for q, (a, b) in enumerate(arcs):
                A_eq[i * n + j, i * m + q] = (a == v) - (b == v)
            b_eq[i * n + j] = scale * (sum(T[s, t] for t in nodes if t != s) if v == s else -T.get((s, v), 0))
    A_ub = np.zeros((m, nv))
    for q, a in enumerate(arcs):
        A_ub[q, q:n * m:m] = 1                         # load of arc a ...
        A_ub[q, n * m + link_of[a]] = -mu * G.edges[a]["cap"]   # ... <= mu * c_a * y_link
    x = milp(np.r_[np.zeros(n * m), np.ones(E)], integrality=np.r_[np.zeros(n * m), np.ones(E)],
             bounds=(0, np.r_[np.full(n * m, np.inf), np.ones(E)]),
             constraints=[LinearConstraint(A_eq, b_eq, b_eq), LinearConstraint(A_ub, -np.inf, 0)]).x
    return [links[e][:2] for e in range(E) if x[n * m + e] > 0.5]

for scale in (1.0, 0.6, 0.3):
    on = green(scale)
    print("traffic %3.0f%%: %d of %d links powered, connected after any single failure: %s"
          % (100 * scale, len(on), E, nx.is_connected(nx.Graph(on)) and not nx.has_bridges(nx.Graph(on))))

Example 9.8 — Energy against resilience on the backbone The block minimises the number of powered links (P_1 = 0) with \bar u = 0.9:

  • at full traffic, 6 of the 8 links stay on (B–C and D–E are off): the ring A–B–D–F–E–C–A, which survives any single link failure;
  • at 60 % and at 30 % of the traffic, 5 links suffice: a spanning tree, which any single link failure disconnects.

Adding the 2-edge-connectivity cuts of ch. 9.1 restores survivability at the price of one more powered link (Exercise 16). Energy and resilience must be optimised together.

Key takeaways — chapter 9

  • Dedicated protection uses disjoint paths, a min-cost flow of value 2; shared restoration plans capacity for every failure scenario.
  • Survivable topologies satisfy cut constraints, separated by minimum cuts.
  • Γ-robust capacity constraints dualise into linear constraints, so robust routing stays an LP.
  • Interdiction puts an attacker in the model; monitoring and isolation are covering and conflict constraints.
  • Switching links off saves the idle power but removes the redundancy that resilience needs.

10. Hybrid methods: optimization and learning

Operators solve the same kinds of instances again and again: the same network, slightly different traffic. Machine learning can exploit this regularity to speed up optimization or to replace parts of it. This chapter classifies the hybrid methods, implements one, and says which guarantees survive; learned routing is discussed in ch. 11.1.

10.1 Three ways to combine learning and optimization

Bengio, Lodi and Prouvost (2021) distinguish learning end to end, learning to configure an algorithm, and learning alongside an optimization algorithm. For network problems, this gives three practical patterns.

The further one goes down this list, the faster the answer and the weaker the guarantee.

10.2 Learning inside branch-and-bound

Strong branching evaluates each candidate variable by solving the two child LPs and picks the one that improves the bound most. It gives small trees but costs many LP solves per node. Khalil and coauthors (2016) learned to rank candidates like strong branching from cheap features; Gasse and coauthors (2019) imitated it with a graph neural network.

Definition 10.1 — Bipartite representation of a MILP A MILP \min \{ c^\top x : A x \leq b \} is represented by a bipartite graph with one node per variable and one node per constraint, and an edge (i, j) with feature A_{ij} whenever A_{ij} \neq 0. Variable nodes carry features such as c_j, bounds and the current LP value; constraint nodes carry b_i and slacks.

A graph neural network on this graph is invariant to the order of variables and constraints, and applies to instances of any size. Proposition 5.2 holds whatever fractional variable is branched on, so the learned solver still returns a proven optimum. A bad model makes it slower, never wrong.

10.3 Learning to prune

A second idea predicts which binaries are likely to be one in the optimum, and solves a reduced problem. For the controller placement of ch. 6.2, a classifier scores every site from cheap features, and the p-median MILP is solved over the best-scored sites only.

import numpy as np, networkx as nx
from scipy.optimize import milp, LinearConstraint, Bounds
from scipy.sparse import coo_matrix
from sklearn.linear_model import LogisticRegression

def p_median(D, p, cand):
    """Exact p-median restricted to the candidate sites cand (MILP solved by HiGHS)."""
    n, m = len(D), len(cand); nv = n * m + m
    z = np.arange(n * m).reshape(n, m); y = n * m + np.arange(m)
    assign = coo_matrix((np.ones(n * m), (np.repeat(np.arange(n), m), z.ravel())), (n, nv))
    count = coo_matrix((np.ones(m), (np.zeros(m, int), y)), (1, nv))
    link = coo_matrix((np.r_[np.ones(n * m), -np.ones(n * m)],
                       (np.r_[z.ravel(), z.ravel()], np.r_[z.ravel(), np.tile(y, n)])), (n * m, nv))
    cons = [LinearConstraint(assign, 1, 1), LinearConstraint(count, p, p), LinearConstraint(link, -np.inf, 0)]
    r = milp(np.r_[D[:, cand].ravel(), np.zeros(m)], constraints=cons,
             integrality=np.r_[np.zeros(n * m), np.ones(m)], bounds=Bounds(0, 1))
    return r.fun, {cand[j] for j in range(m) if r.x[y[j]] > 0.5}

def greedy_order(D, k):
    chosen = []
    for _ in range(k):
        rest = [j for j in range(len(D)) if j not in chosen]
        chosen.append(min(rest, key=lambda j: D[:, chosen + [j]].min(1).sum()))
    return chosen

def instance(seed, n=30, p=3):
    rng = np.random.default_rng(seed)
    while True:
        G = nx.random_geometric_graph(n, 0.35, seed=int(rng.integers(1 << 30)))
        if nx.is_connected(G): break
    for u, v in G.edges:
        G.edges[u, v]["ms"] = 10 * np.hypot(*np.subtract(G.nodes[u]["pos"], G.nodes[v]["pos"]))
    D = np.array(nx.floyd_warshall_numpy(G, weight="ms"))
    g = greedy_order(D, 2 * p)
    rank = np.full(n, 2 * p); rank[g] = np.arange(2 * p)        # position in the greedy order
    cover = (D <= np.median(D)).sum(1)                           # nodes within the median latency
    f = np.c_[D.mean(1), D.max(1), cover, rank, rank < p]
    return D, (f - f.mean(0)) / (f.std(0) + 1e-9)

p, n = 3, 30
train = [instance(s) for s in range(40)]
X = np.vstack([f for _, f in train])
labels = [p_median(D, p, list(range(n)))[1] for D, _ in train]          # optimal sites
y = np.concatenate([[j in S for j in range(n)] for S in labels])
clf = LogisticRegression().fit(X, y)

gaps = {(name, k): [] for name in ("learned", "central") for k in (6, 10, 15)}
for s in range(100, 120):                                        # 20 unseen instances
    D, f = instance(s)
    opt, _ = p_median(D, p, list(range(n)))
    for name, score in [("learned", clf.predict_proba(f)[:, 1]), ("central", -D.mean(1))]:
        for k in (6, 10, 15):                                    # size of the pruned candidate set
            val, _ = p_median(D, p, list(np.argsort(-score)[:k]))
            gaps[name, k].append(100 * (val - opt) / opt)
for (name, k), g in gaps.items():
    print("%-7s keep %2d of %d sites: optimal on %2d/20, mean gap %5.2f%%"
          % (name, k, n, sum(x < 1e-6 for x in g), np.mean(g)))

Example 10.2 — Learned pruning against a centrality rule A logistic regression is trained on the optimal sites of 40 random instances, then tested on 20 unseen ones. Keeping the 10 best-scored of 30 sites:

  • learned scores: optimal on 2 of 20 instances, mean gap 2.18 %;
  • closeness centrality alone: optimal on 1 of 20, mean gap 13.74 %.

With 15 sites, the learned rule finds the optimum on 9 instances (mean gap 1.24 %), centrality on 7 (4.36 %). Learning helps because the optimal sites are spread out, which centrality ignores and greedy features capture.

Proposition 10.3 — What pruning keeps and loses Solving over a pruned candidate set returns a feasible solution, hence an upper bound, but no optimality guarantee. Using the pruned solution as a warm start of the full MILP keeps exactness and can only help the search.

Proof. Every solution of the reduced problem is feasible for the full one, with the same cost. The full solver with an incumbent of value z_H prunes every node of bound at least z_H, which removes no node that could contain a better solution (Proposition 5.2). ∎

Key takeaways — chapter 10

  • Learning can assist an exact solver, predict parts of solutions, or replace the solver by a learned heuristic, with decreasing guarantees.
  • Learned branching on the bipartite variable–constraint graph keeps the solver exact.
  • Learned pruning gave a mean gap of about 2 % with a third of the sites; as a warm start it keeps exactness.

11. Research directions and open problems

This chapter is for self-study. It points to active lines of research that extend ch. 3–10, with papers to start from; each section ends with questions that are still open. It also serves as a reading list for the paper presentations of the second part of the course.

11.1 Traffic engineering at the scale of global networks

Wide-area networks of cloud providers are routed by centralised controllers: B4 (Jain et al., 2013) and SWAN (Hong et al., 2013) solve TE linear programs every few minutes. As networks and the number of demands grow, the LP of ch. 3.2 becomes too slow for the control loop.

Two cautions apply to all learned controllers. They must be compared with the LP of ch. 3.2 on the same traffic, since the LP is the best any routing can do. And their behaviour on traffic unlike the training data, failures included, must be tested, because no bound comes with them.

How far from optimal can these methods be? MetaOpt (Namyar et al., 2024) searches for the traffic matrices on which a heuristic performs worst, by writing the heuristic and the optimum in one multi-level optimization problem.

11.2 Learning for combinatorial optimization

The methods of ch. 10.1–10.3 are surveyed by Bengio, Lodi and Prouvost (2021) and, for graph neural networks, by Cappart and coauthors (2023). Learned branching (Gasse et al., 2019) and learned primal heuristics, such as the neural diving of Nair and coauthors (2020), report large speed-ups on distributions of similar instances.

11.3 Robustness from data

Robust models (ch. 9.2) need uncertainty sets. Besides the Γ-budget, two models are common in telecommunications. The hose model (Duffield et al., 1999) bounds only the total traffic sent and received by each node, which customers of virtual private networks can state. Oblivious routing goes further: one routing for all traffic matrices, with a guaranteed ratio to the best routing of each matrix, computable by LP (Applegate and Cohen, 2003).

The parameter \Gamma or the hose bounds are usually chosen by hand. Bertsimas, Gupta and Kallus (2018) build uncertainty sets from data with statistical guarantees, and distributionally robust optimization protects against a set of distributions rather than of scenarios.

11.4 Joint orchestration of computing and networking

Virtualization (ch. 6.3, 8) makes compute and network one resource pool, from data centres to edge servers next to base stations. Surveys of network functions virtualization (Mijumbi et al., 2016) and of NFV resource allocation (Herrera and Botero, 2016) list the models: VNF placement, chaining, scaling and migration, usually as MILPs solved by heuristics.

11.5 Energy and carbon

Energy-aware routing (ch. 9.4) and green networking (Bianzino et al., 2012) originally targeted the electricity bill. The goal is shifting towards carbon: the carbon intensity of electricity varies by place and hour, so where and when computation and traffic happen matters, not only how much.

11.6 From specifications to models with language models

Ch. 8.5 builds a model from a specification by hand. Large language models are now used to draft optimization models from text: the NL4Opt competition (Ramamonjison et al., 2023) set up a benchmark of linear programs described in natural language, and OptiMUS (AhmadiTeshnizi, Gao and Udell, 2024) combines language models with solvers to formulate and solve MILPs.

11.7 Decision-focused learning

Optimization models need parameters (future demands, latencies) that are predicted. The usual pipeline, predict then optimize, fits a predictor by least squares and plugs its output into the solver. But prediction errors do not matter equally: an error that does not change the decision costs nothing.

Definition 11.1 — Decision regret Let x^\star(\hat c) be an optimal decision for predicted costs \hat c. Its regret under the true costs c is \mathrm{regret}(\hat c, c) = c^\top x^\star(\hat c) - c^\top x^\star(c) \geq 0 .

Decision-focused learning trains the predictor to minimise regret rather than prediction error. Regret is piecewise constant in \hat c, so it is replaced by a surrogate: the SPO+ loss of Elmachtoub and Grigas (2022) is convex and computed with one call to the optimization oracle per sample.

Key takeaways — chapter 11

  • Global-scale TE needs decomposition or learning, and tools such as MetaOpt to measure the worst-case loss; learned controllers must be compared with the LP optimum.
  • Learning inside solvers gives speed-ups on similar instances; generalisation and certificates remain open.
  • Hose models and oblivious routing complement Γ-robustness; uncertainty sets can be learned from data.
  • Joint compute–network orchestration and carbon-aware networking extend the models of ch. 6, 8 and 9.
  • Language models start to draft optimization models from text; verifying them against specifications is open.
  • Decision-focused learning trains predictors for the decisions they induce, measured by regret.

Exercises

Exercises are sorted by level: ★ applies the course, ★★ combines results or asks for a short proof, ★★★ is open-ended. The session plan says which exercises follow which session. Numerical answers refer to the six-node backbone of ch. 1 unless stated otherwise.

  1. ★ Duality by hand. Maximise 5x_1 + 4x_2 subject to 6x_1 + 4x_2 \leq 24, x_1 + 2x_2 \leq 6 and x \geq 0, graphically. Write the dual, find its solution from complementary slackness, and check that the two values agree.

    Solution: the optimum is at the intersection of the two constraints, x = (3, 1.5), of value 21. Both constraints are tight, so both duals may be positive; the dual constraints 6y_1 + y_2 = 5 and 4y_1 + 2y_2 = 4 give y = (0.75, 0.5). The dual value is 24 \cdot 0.75 + 6 \cdot 0.5 = 21 (Theorem 0.5, Proposition 0.6).

  2. ★ A gravity matrix. With the weights of Example 1.3 and 40 Gb/s in total, compute T_{AB}, the total traffic leaving A, and the traffic from \{A, B, C\} to \{D, E, F\}. Repeat the last computation when all weights are equal.

    Solution: T_{AB} = 40 \cdot 8 / 208 = 1.54 Gb/s; A sends 40 \cdot 4 \cdot 12 / 208 = 9.23 Gb/s. Across the cut, 40 \cdot 64 / 208 = 12.31 Gb/s. With equal weights every ordered pair carries 40 / 30 Gb/s and nine pairs cross, so 12 Gb/s: the heavy end nodes A and F add little here.

  3. ★ Dijkstra and a cut. Run Dijkstra from A with the latencies of the ch. 2 block and give the order in which nodes become permanent. Then compute the maximum flow from A to F when the capacity of C–E is raised to 10 Gb/s, and give a minimum cut.

    Solution: A (0), B (2), C (3), E (5), D (6), F (8), with ties C via B and D via E. With C–E at 10 Gb/s, the cut \{A, B, C\} has capacity 20, as do the cuts around A and around F; the maximum flow is 20 Gb/s, each of these cuts certifying it (Theorem 2.4).

  4. ★ Integral or not. (a) Show that the matrix with rows (1, 1, 0), (0, 1, 1), (1, 0, 1) is not totally unimodular. (b) Why is the LP relaxation of the assignment problem integral? (c) Why can the arc-node LP of a two-commodity flow not always have integral optimal solutions?

    Solution: (a) its determinant is 2; it is the incidence matrix of a triangle, an odd cycle. (b) The node–edge incidence matrix of a bipartite graph is TU: negating the rows of one side, which preserves total unimodularity, gives the node–arc incidence matrix of the graph with every edge oriented towards that side (Theorem 2.6). (c) Integral two-commodity flow is NP-hard (Even, Itai and Shamir, 1976), so an LP cannot solve it in general unless P = NP; the capacity rows coupling the commodities break total unimodularity.

  5. ★ Cut bounds for the maximum utilisation. Compute the bound of Theorem 3.3 for S = \{A\}, S = \{C\} and S = \{A, B, C\}. Which link upgrade lowers U^\star, and to what value if C–E becomes 10 Gb/s?

    Solution: 9.23/20 = 0.46, 5.38/20 = 0.27 and 12.31/15 = 0.821. Only the links of the binding cut matter: raising C–E to 10 Gb/s gives a cut of 20 Gb/s and, as the LP confirms, U^\star = 12.31 / 20 = 0.615 .

  6. ★ Modelling with binaries. With y_e (link built) and z_v (node equipped), write linear constraints for: (a) a link may be built only if both end nodes are equipped; (b) at most two of the three links at node B are built; (c) a built link carries at least 1 Gb/s; (d) the number of 10 Gb/s modules is enough for the load \ell_e.

    Solution: (a) y_{uv} \leq z_u and y_{uv} \leq z_v; (b) y_{AB} + y_{BC} + y_{BD} \leq 2.

    1. \ell_e \geq y_e together with \ell_e \leq c_e y_e; (d) an integer q_e \geq 0 with \ell_e \leq 10 q_e, counted in the objective (ch. 4.3–4.4).
  7. ★★ The cut condition is not enough. (a) Prove that the cut condition is necessary for a multicommodity flow, and sufficient for one commodity. (b) On the complete bipartite example of ch. 2.4, use Theorem 2.12 with \mu \equiv 1 to prove that at most 75 % of every demand can be routed simultaneously. (c) Give a routing that achieves 75 %.

    Solution: (a) all demand crossing a cut uses its links; for one commodity, Theorem 2.4. (b) Routing a fraction \lambda of each demand requires 6 \geq \lambda \cdot 4 \cdot 2, so \lambda \leq 0.75.

    1. Split each b_i b_j demand equally through a_1 and a_2 (load 0.75 on each edge) and the a_1 a_2 demand equally through b_1, b_2, b_3 (0.25 more): every edge carries exactly 1.
  8. ★★ The best possible Fortz–Thorup cost. Using Proposition 3.1, write the LP that minimises the Fortz–Thorup cost over splittable routings, and solve it in Python for the backbone. After session 7, compare with the local search of Example 7.3.

    Hints and expected result: the affine pieces of c \, \phi(\ell / c) are \ell,\quad 3\ell - \tfrac{2}{3}c,\quad 10\ell - \tfrac{16}{3}c,\quad 70\ell - \tfrac{178}{3}c,\quad 500\ell - \tfrac{1468}{3}c,\quad 5000\ell - \tfrac{16318}{3}c . The LP value is 149.74, so the weights found by local search (178.21) are within 16 % of the best splittable routing for this cost.

  9. ★★ Cut-set inequalities on the backbone. Consider the network loading problem of ch. 4.4 on the eight backbone links, with modules of 10 Gb/s shared by both directions and unit cost per module. (a) Show that the LP relaxation can install 2.46 modules across \{A, B, C\}. (b) In Python, compare the LP bound, the bound with all cut-set inequalities (2^5 - 1 cuts) and the integer optimum.

    Hints and expected result: (a) the demand across is 2 \times 12.31 Gb/s. (b) The LP gives 6.77 modules, the LP with cut-set inequalities 8.0, the integer optimum 9: the root gap falls from 24.8 % to 11.1 % (Proposition 4.5).

  10. ★★ Search strategies in branch-and-bound. Modify the knapsack block of ch. 5.1 to process nodes in best-bound order (a priority queue on the LP bound). Count the nodes and explain the difference with depth-first search.

    Hints and expected result: counting every node taken from the queue, as the block does, best-bound explores about 90 nodes instead of 118 (89 or 90, depending on how ties are broken) and reaches the same optimum, 761. Discarding a node without counting it, when its bound is no better than the incumbent at the moment it is taken, brings the count to about 60.

    Best-bound never expands a node whose bound is below the optimum, which minimises the tree; depth-first finds good incumbents early and needs less memory.

  11. ★★ Lagrangian and LP bounds. (a) Prove that relaxing the capacity constraints of the min-cost multicommodity flow gives a Lagrangian dual equal to its LP optimum. (b) For the fixed-charge design of Definition 4.1, relax the weak linking constraints instead: what is the subproblem, and is its bound better than the weak LP bound?

    Solution: (a) the subproblem is a set of shortest-path problems, whose LPs have integral optimal solutions (Theorem 2.6), so Theorem 5.6 gives z_{LD} = z_{LP}. (b) The subproblem splits into shortest paths for the flows and a trivial problem in y (y_e = 1 if its reduced cost is negative); both have the integrality property, so the bound equals the weak LP bound, no better.

  12. ★★ Column generation for the maximum utilisation. Write the arc-path version of the MLU program of Definition 3.2 and its dual. Show that pricing is a shortest-path problem, and that the dual is exactly the formula of Theorem 3.3.

    Solution: the primal, with duals in parentheses, is \min U \quad \text{s.t.} \quad \sum_{p \in \mathcal{P}_k} x_p = d_k \ (\sigma_k), \qquad \sum_{p \ni a} x_p \leq U c_a \ (\mu_a \geq 0). The column of U gives the dual constraint \sum_a \mu_a c_a \leq 1, and a path column gives \sigma_k \leq \sum_{a \in p} \mu_a.

    Pricing therefore seeks a path of \mu-length below \sigma_k: a shortest-path problem. At the optimum \sigma_k = \mathrm{dist}_\mu(s_k, t_k), and the dual maximises \sum_k d_k \sigma_k under the normalisation: this is Theorem 3.3.

  13. ★★ Swap local search for placement. Starting from the greedy solution of Example 6.3, implement a local search that replaces one open site by a closed one while the mean latency decreases. Does it reach the optimum? What neighbourhood would?

    Hints and expected result: no single swap improves the greedy sites 11, 18 and 27, so local search stops at 2.90 ms, 2.4 % above the optimum (sites 12, 17 and 18). The optimum differs in two sites, so a 2-swap neighbourhood, simulated annealing or a restart strategy (GRASP) is needed (ch. 7.3).

  14. ★★ Randomised rounding in numbers. An LP solution sends half of each of 20 demands of 1 Gb/s through a link of 10 Gb/s, and half on another path. After independent randomised rounding (Proposition 7.1), compute the probability that the link carries at least 14 Gb/s. Repeat with 200 demands of 0.1 Gb/s.

    Solution: the load is 1 Gb/s times a binomial variable with 20 trials of probability 0.5, so the probability is \sum_{j \geq 14} \binom{20}{j} 2^{-20} = 0.058. With 200 demands of 0.1 Gb/s it is 7.5 \cdot 10^{-9}: rounding is safe when each demand is small compared with the capacity.

  15. ★★ A Γ-robust capacity constraint. Three demands are routed entirely on one arc, with nominal volumes 2, 3 and 4 Gb/s and deviations 2, 1 and 3 Gb/s. Compute the worst-case load for \Gamma = 1, 1.5 and 2, and give dual values (\theta, \rho) of Theorem 9.4 that certify the first and last results.

    Solution: the nominal load is 9 Gb/s. The worst cases add 3, then 3 + 0.5 · 2 = 4, then 3 + 2 = 5, hence 12, 13 and 14 Gb/s.

    For \Gamma = 1, take \theta = 2 and \rho = (0, 0, 1): the dual value is 2 + 1 = 3.

    For \Gamma = 2, take \theta = 1 and \rho = (1, 0, 2): the dual value is 2 + 3 = 5.

  16. ★★ Green and survivable. Add to the block of ch. 9.4 the cut constraints of ch. 9.1, at least two powered links across every cut (31 cuts suffice on six nodes), and solve again for 100 %, 60 % and 30 % of the traffic.

    Hints and expected result: enumerate the sets S containing A. Six links are powered at every traffic level, the ring A–B–D–F–E–C–A, which survives every single link failure. Survivability costs one powered link at low traffic (Example 9.8).

  17. ★★★ From a specification to a model. Write the complete MILP of Example 8.5 for the backbone, with the latencies of ch. 2 as link delays (ms), ten slices of 1 to 3 Gb/s (four low-latency, two operators) and their 3 shortest candidate paths. Solve it, then relax the 5 ms budget to 8 ms and discuss.

    Hints and expected result: filter candidate paths by delay for low-latency slices; generate the isolation constraints only for pairs of paths that share an arc; link activation is x_{ip} \leq y_e. Check every sentence of the excerpt against a constraint, and test on an instance small enough to verify by hand. With 5 ms, slices between distant nodes such as A and F (8 ms at best) cannot be low-latency, so the delay budget drives the choice of paths.

  18. ★★★ Learning to prune, continued. Extend the block of ch. 10.3: (a) use the pruned solution as a warm start of the full MILP instead of a final answer (Proposition 10.3), and measure the solve time; (b) replace the hand-made features by a small graph neural network in PyTorch, trained on the same labels; (c) test on instances with 50 nodes.

    Hints and expected result: SciPy’s milp has no warm-start option, so (a) is best done with a solver that has one (HiGHS through highspy, SCIP or a commercial solver). Report gaps and times as distributions over test instances; expect the learned features to degrade on larger instances, which ch. 11.2 discusses.

  19. ★★★ Paper study. Choose one paper cited in ch. 11, read it, and reproduce one quantitative result or figure on public data, at reduced scale if needed. Present it in ten minutes: problem, model, method, your result, limits, and one open question.

    Hints and expected result: a good presentation states the model in the notation of this page (sets, variables, constraints), the bound or baseline the method is compared with, and where your number differs from the published one, with an explanation.

Projects and assessment

The Université Paris-Saclay course detail describes the assessment as “Evaluation Continue non Intégrale” (non-integral continuous assessment); master-mva.com gives no modality, and no weights are displayed. The course description announces labs with solvers and heuristics in the first part, and the reading and presentation of research papers in the second. The subjects below fit that pattern: each is a lab-scale implementation or a paper study, sized for continuous assessment. The written exam is a proposal for self-assessment: the official pages do not say whether the assessment includes one.

Solo projects

  1. [Solo · ≈ 20 h] OSPF weight optimisation (paper study: Fortz and Thorup, 2000). How close does local search on weights come to the best splittable routing as the traffic grows?
    • Implement: ECMP load evaluation, the Fortz–Thorup cost, a local search on weights with incremental evaluation, and the LP lower bound of Proposition 3.1 (ch. 3, 7).
    • Data: one SNDlib instance with its traffic matrix (Orlowski et al., 2010), scaled from low to saturating load.
    • Deliverables: code, a 4–6 page report, a 10-minute talk.
    • Success criterion: the curve of cost against total traffic for unit weights, optimised weights and the LP bound, with the gap of optimised weights below 25 % up to the load where the LP reaches utilisation 1.
  2. [Solo · ≈ 20 h] Branch-and-price for unsplittable routing (paper study: Barnhart, Hane and Vance, 2000). When does a path formulation beat the compact arc-node MILP?
    • Implement: column generation for the path LP (ch. 5.4), branching on the arcs used by a demand, and the compact MILP of ch. 3.4 solved by HiGHS for comparison.
    • Data: synthetic instances on the backbone and on a public SNDlib topology, with increasing numbers of demands.
    • Deliverables: code, report and talk as above.
    • Success criterion: identical optima on all instances solved by both methods, and a plot of solve times showing where each method wins.
  3. [Solo · ≈ 20 h] The price of robustness in capacity planning (paper study: Bertsimas and Sim, 2004). How much capacity does protection against Γ simultaneous traffic deviations cost?
    • Implement: the network loading problem of ch. 4.4 with the robust counterpart of Theorem 9.4, for \Gamma from 0 to the number of demands.
    • Data: an SNDlib instance; deviations of 30 % of each demand.
    • Deliverables: code, report and talk as above.
    • Success criterion: a curve of cost against \Gamma, and the empirical probability that sampled traffic overloads a link, decreasing with \Gamma, from at least 1000 samples.

Group projects

  1. [Group of 3 · ≈ 30 h each] Learning to branch on network design (paper study: Gasse et al., 2019). Can a graph neural network imitate strong branching on fixed-charge network design and reduce the size of the search tree?
    • Data: synthetic instances of Definition 4.1 with the strong formulation, 10 to 20 sites.
    • Split: member A writes a Python branch-and-bound with LPs solved by HiGHS and the instance generator (ch. 4, 5.1); member B implements strong branching and collects the imitation data; member C trains the bipartite GNN of Definition 10.1 in PyTorch and runs the evaluation.
    • Deliverables: shared code, one report of 8–10 pages, a 20-minute talk.
    • Success criterion: on held-out instances, including larger ones, fewer nodes than most-fractional branching and a time per node far below strong branching.
  2. [Group of 2–3 · ≈ 30 h each] Energy-aware and survivable routing (paper study: Chiaraviglio, Mellia and Neri, 2012). How much energy can switching links off save over a day, with and without survivability?
    • Data: an SNDlib topology with a day of traffic matrices, or a Topology Zoo network (Knight et al., 2011) with gravity traffic modulated over 24 hours.
    • Split: member A solves the MILP of Definition 9.7 per hour; member B implements a greedy switch-off heuristic and compares it with the MILP; member C adds the 2-edge-connectivity cuts and measures the cost of survivability (ch. 9.1, 9.4).
    • Deliverables: shared code, one report of 8–10 pages, a 20-minute talk.
    • Success criterion: a curve of powered links over the day for the three variants, the heuristic within 10 % of the MILP, and every survivable solution verified against all single link failures.
  3. [Group of 3 · ≈ 30 h each] Online slicing with service chains (paper study: Awerbuch, Azar and Plotkin, 1993; Addis et al., 2015). How close can online admission of chained slices come to the offline optimum?
    • Data: the backbone and one public topology, with random sequences of requests (chain of 2–3 functions, bandwidth, compute, revenue) and adversarial sequences.
    • Split: member A writes the offline MILP of Definition 6.4 with admission (ch. 6.3, 8.2); member B implements greedy and exponential-pricing online admission (ch. 8.3); member C writes a one-page specification of the service in the style of Example 8.5, derives the model from it, and runs the evaluation.
    • Deliverables: shared code, one report of 8–10 pages, a 20-minute talk.
    • Success criterion: the distribution of the online/offline revenue ratio over at least 100 sequences, with price-based admission beating greedy on the adversarial ones.

Written exam (proposal)

Format: 2 hours, one handwritten A4 sheet allowed, no electronic device, four exercises.

  1. Question (4 points). Explain why a minimum-cost flow with one commodity and integer data has an integral optimal solution, whereas the multicommodity flow LP may not. State the definitions and results you use, and give the consequence for routing each demand on a single path.

    Expected: total unimodularity, Theorem 2.6 with the column argument, Corollary 2.7 with Cramer’s rule; the capacity rows coupling commodities break the structure, and integral two-commodity flow is NP-hard (Even, Itai and Shamir); single-path routing is therefore a MILP, solved by branch-and-bound, column generation or rounding.

  2. Computation (6 points). A four-node ring A–B–C–D–A has links of 10 Gb/s and a chord A–C of 5 Gb/s. Demands: 12 Gb/s from A to C and 6 Gb/s from B to D. (a) Give a cut bound on the maximum utilisation. (b) Find a routing that attains it. (c) Compute the maximum utilisation of ECMP with unit weights. (d) Find a weight for the arc A→C that brings it to 0.8.

    Expected: (a) S = \{A, B\}: 18 Gb/s across arcs A→D, A→C, B→C of total capacity 25, so U^\star \geq 0.72. (b) 3.6 Gb/s on the chord, 4.2 through B and 4.2 through D for A→C; 3 and 3 for B→D: all cut arcs at 0.72. (c) A→C takes the chord alone: 12 / 5 = 2.4. (d) Weight 2 creates three equal paths: 4 Gb/s each, chord at 0.8, every other arc at 0.7 or less.

  3. Short proof (5 points). (a) Prove the cut-set inequality of Proposition 4.5. (b) Prove that the Lagrangian function gives a lower bound for every \lambda \geq 0, and that the constraint violation at a minimiser is a subgradient.

    Expected: (a) the capacity-crossing argument, then Chvátal–Gomory rounding of an integer left-hand side. (b) Theorem 5.6 for the bound; for the subgradient, L(\lambda') \leq c^\top x_\lambda + \lambda'^\top (A x_\lambda - b) = L(\lambda) + (\lambda' - \lambda)^\top (A x_\lambda - b).

  4. Modelling (5 points). Every request of a service needs a firewall then a NAT function. Servers have 16 cores; a firewall instance uses 4, a NAT instance 2, and each instance serves at most 5 Gb/s. Traffic must stay within 10 ms from source to destination. Write the sets, variables, constraints and objective of a minimum-cost placement and routing model, and explain how you would solve it every second for a large network.

    Expected: Definition 6.4 with instance counts per server, core and throughput capacities, the chain order through flow segments or the layered graph of Proposition 6.5, a latency filter on candidate paths; offline MILP for planning, and for the per-second loop a warm-started re-optimisation with reconfiguration costs or a heuristic (ch. 7.4, 8.4).

Grading rubric (proposal)

Criterion Weight What earns the marks
Modelling 30 % Sets, parameters, variables and constraints stated precisely; the formulation is correct and as strong as reasonably possible.
Algorithms and implementation 25 % Methods implemented correctly, code that runs from a README with fixed seeds, solver settings documented.
Experimental evaluation 20 % Bounds or optimal values as baselines, gaps and times reported over several instances, held-out instances for anything learned.
Report 15 % Clear structure, figures with units, limits of the model discussed, precise references.
Oral presentation 10 % On time, one key result, precise answers about the model and the paper.

References

Official references

Textbooks

Flows, paths and complexity (ch. 0–2)

Networks and traffic engineering (ch. 1, 3)

Network design and exact methods (ch. 4–5)

Placement and heuristics (ch. 6–7)

Future networks, resilience, security and energy (ch. 8–9)

Optimization and learning (ch. 10–11)