Minimal Spanning Tree API Reference
Data
Data model for Minimal Spanning Tree use case.
MinimalSpanningTreeData
Bases: UcData
Data for the Minimal Spanning Tree use case.
Finds the minimum-weight spanning tree in a weighted graph.
Attributes:
-
name(Literal['minimal_spanning_tree']) –Identifier for this data type.
-
adjacency_matrix(NumPyArray) –Symmetric weighted adjacency matrix. Zero means no edge.
-
node_names(list[int | str]) –Node identifiers.
plot(*, ax: Axes | None = None) -> Axes
Plot the Minimal Spanning Tree graph instance.
Parameters:
-
ax(Axes | None, default:None) –Matplotlib axes to draw on. Creates a new figure if
None.
Returns:
-
Axes–The axes with the plot.
to_string() -> str
from_adjacency_matrix(adjacency_matrix: np.ndarray, node_names: list[int | str]) -> MinimalSpanningTreeData
staticmethod
Create MinimalSpanningTreeData from an adjacency matrix.
Parameters:
-
adjacency_matrix(ndarray) –Symmetric weighted adjacency matrix. Zero means no edge.
-
node_names(list[int | str]) –List of node identifiers.
Returns:
-
MinimalSpanningTreeData–The Minimal Spanning Tree data instance.
generate_random(n_nodes: int = 5, edge_prob: float = 0.5, max_weight: float = 10.0, seed: int | None = None) -> MinimalSpanningTreeData
staticmethod
Generate a random Minimal Spanning Tree instance.
The generated graph is guaranteed to be connected. A random spanning
tree is first created, and then additional edges are added with
probability edge_prob.
Parameters:
-
n_nodes(int, default:5) –Number of nodes, by default 5.
-
edge_prob(float, default:0.5) –Probability of additional edges beyond the spanning tree, by default 0.5.
-
max_weight(float, default:10.0) –Maximum edge weight, by default 10.0.
-
seed(int | None, default:None) –Random seed for reproducibility, by default None.
Returns:
-
MinimalSpanningTreeData–A randomly generated data instance.
Examples:
Formulation
Formulation for Minimal Spanning Tree use case.
MinimalSpanningTreeFormulation
Bases: UcFormulation[MinimalSpanningTreeData, MinimalSpanningTreeSolution]
Constraint-based flow formulation for Minimal Spanning Tree.
Mathematical Formulation
Indices and Sets:
V = {0, 1, ..., n-1} -- set of nodes (node 0 is the root)
E = {(i,j) : i < j,
adjacency_matrix[i,j] != 0} -- set of undirected edges (upper triangle only)
n = |V| -- number of nodes
Decision Variables:
y_{i,j} in {0, 1}
for each (i,j) in E.
y_{i,j} = 1 if undirected edge {i,j} is selected for the spanning tree,
y_{i,j} = 0 otherwise.
f_{i,j} in {0, 1, ..., n-1}
for each (i,j) in E and for each (j,i) in E (i.e., both directions).
f_{i,j} = amount of flow routed from node i to node j along edge {i,j}.
The flow is directed even though the underlying graph is undirected;
at most one of f_{i,j} and f_{j,i} will be nonzero in any feasible solution.
Objective:
minimize sum_{(i,j) in E} adjacency_matrix[i,j] * y_{i,j}
Minimises the total weight of selected edges.
Constraints:
1. Exactly n-1 edges selected (1 constraint):
sum_{(i,j) in E} y_{i,j} == n - 1
A spanning tree on n nodes has exactly n-1 edges.
2. Flow conservation at each non-root node v in V \\ {0}
(n-1 constraints, one per non-root node):
sum_{(u,v) in E} f_{u,v} - sum_{(v,w) in E} f_{v,w} == 1
Each non-root node must receive exactly 1 unit of net flow.
Together with constraint 4 this guarantees that every node is
reachable from the root, i.e. the selected edges form a connected graph.
3. Flow capacity: flow only on selected edges
(|E| constraints, one per undirected edge):
f_{i,j} + f_{j,i} <= (n-1) * y_{i,j} for all (i,j) in E
If edge {i,j} is not selected (y_{i,j} = 0) no flow can cross it
in either direction. If it is selected (y_{i,j} = 1) the combined
flow in both directions is bounded by n-1 (the maximum possible
flow any single edge could carry in a tree on n nodes).
4. Root sends exactly n-1 units of flow (1 constraint):
sum_{j : (0,j) in E} f_{0,j} - sum_{j : (j,0) in E} f_{j,0} == n - 1
The root (node 0) is the sole source of flow; it emits one unit
for each of the n-1 non-root nodes.
5. Flow variable bounds (4*|E| constraints):
0 <= f_{i,j} <= n-1 for all directed pairs (i,j)
Explicit lower and upper bounds on the integer flow variables.
Connectivity argument:
Constraints 2 and 4 together ensure that the root can reach every other
node via the selected edges: if any node v were unreachable from the root,
the net inflow to v would be 0, violating constraint 2. Combined with
constraint 1 (exactly n-1 edges), connectivity implies the selected edges
form a tree (a connected acyclic graph on n nodes with n-1 edges is
always a tree).
to_string(data: MinimalSpanningTreeData) -> str
staticmethod
Format the formulation as a human-readable string.
Summarises the sizes of the index sets, the decision variables, the objective, and the constraint groups for a given problem instance.
Parameters:
-
data(MinimalSpanningTreeData) –The problem data. Relevant fields: -
data.node_names: list of length n with a name for each node. -data.adjacency_matrix: n×n weight matrix; entry [i,j] is the edge weight for {i,j}, or 0 if the edge does not exist.
Returns:
-
str–Multi-line string describing the formulation dimensions and constraint groups for the given instance.
formulate(data: MinimalSpanningTreeData) -> Model
staticmethod
Formulate the MST problem as a mixed-integer programming (MIP) model.
Uses a flow-based formulation to enforce connectivity without exponential subtour elimination constraints. The key insight is that routing one unit of flow from a designated root to every other node is equivalent to requiring that those nodes are reachable from the root — i.e. the selected edges span the graph.
Variable naming convention
Binary edge variables:
y_{i}_{j} where i < j and (i,j) in E.
The indices i and j are the 0-based positions of the two endpoints
in data.node_names.
Integer flow variables (one per directed arc):
f_{i}_{j} for the arc from node i to node j.
f_{j}_{i} for the arc from node j to node i.
Again, i < j to match the edge set E; both directions are created
for each undirected edge.
Constraint naming convention
n_minus_1_edges -- constraint 1 (exactly n-1 edges).
flow_conservation_{v} -- constraint 2 for node v (v != 0).
flow_capacity_{i}_{j} -- constraint 3 for edge (i,j).
root_flow -- constraint 4 (root net outflow = n-1).
flow_lb_{i}_{j} -- lower bound f_{i,j} >= 0.
flow_ub_{i}_{j} -- upper bound f_{i,j} <= n-1.
(similarly flow_lb_{j}_{i} / flow_ub_{j}_{i} for the reverse arc)
Parameters:
-
data(MinimalSpanningTreeData) –The problem data. Relevant fields: -
data.node_names: list of n node labels (strings or ints). The index of a label in this list is the node index used throughout the model. Node at index 0 is the root. -data.adjacency_matrix: n×n numpy array. Entry [i, j] is the weight of edge {i, j}; 0 means the edge does not exist. The matrix is assumed to be symmetric; only the upper triangle (i < j) is read.
Returns:
-
Model–A LunaModel ready to be solved. The model name encodes a hash of the input data so that repeated calls with identical data reuse cached results where possible.
interpret(solution: Solution, data: MinimalSpanningTreeData) -> MinimalSpanningTreeSolution
staticmethod
Extract a Minimal Spanning Tree solution from the solver result.
Reads the binary edge-selection variables y_{i}_{j} from the best
sample returned by the solver and reconstructs the list of selected
tree edges in terms of the original node names (rather than their
0-based indices).
Variable lookup:
The solver stores each variable under its string name. For a binary
edge variable between nodes at indices i and j (i < j) the name is
"y_{i}_{j}". A value above BINARY_DIVIDER (0.5) is rounded
to 1 (edge selected); at or below it is rounded to 0 (not selected).
Index-to-name mapping:
data.node_names[i] gives the original label for node index i.
Selected edges are therefore reported as
(data.node_names[i], data.node_names[j]).
Parameters:
-
solution(Solution) –The solver solution object.
solution.best()returns a list of samples;sample.sampleis a dict mapping variable name (str) to its value in that sample. -
data(MinimalSpanningTreeData) –The original problem data. Used to: - determine n = len(data.node_names), - reconstruct the edge list E (same upper-triangle enumeration used in
formulate), - look up edge weights viadata.adjacency_matrix[i, j], - map node indices back to node names viadata.node_names.
Returns:
-
MinimalSpanningTreeSolution–Structured solution with: -
tree_edges: list of (name_i, name_j) pairs for selected edges. -total_weight: sum of weights of selected edges (float). -is_valid: True iff the selected edges form a valid spanning tree.
Raises:
-
NoSolutionFoundError–If the solver did not find any feasible solution (
solution.best()returns None).
Solution
Solution model for Minimal Spanning Tree use case.
MinimalSpanningTreeSolution
Bases: UcSolution
Solution for the Minimal Spanning Tree use case.
Attributes:
-
name(Literal['minimal_spanning_tree']) –Identifier.
-
tree_edges(list[tuple[int | str, int | str]]) –Edges in the spanning tree.
-
total_weight(float) –Total weight of the spanning tree.
-
is_valid(bool) –Whether the solution is a valid spanning tree (n-1 edges, connected, all edges exist in the graph).
plot(data: MinimalSpanningTreeData | None = None, *, ax: Axes | None = None) -> Axes
Plot the Minimal Spanning Tree solution on the problem graph.
Tree edges are highlighted in green; other edges are grey.
Parameters:
-
data(MinimalSpanningTreeData | None, default:None) –Problem data used to reconstruct the graph. Required -- a
ValueErroris raised whenNone. -
ax(Axes | None, default:None) –Matplotlib axes to draw on. Creates a new figure if
None.
Returns:
-
Axes–The axes with the plot.
Raises:
-
ValueError–If data is
None.
to_string() -> str
Format the solution as a human-readable string.
Returns:
-
str–String representation of the solution.
Instance
Instance model for Minimal Spanning Tree use case.
MinimalSpanningTreeInstance
Bases: UcInstance[MinimalSpanningTreeData, MinimalSpanningTreeFormulation, MinimalSpanningTreeSolution]
Instance combining data and formulation for Minimal Spanning Tree.
Collection
Collection of Minimal Spanning Tree instances.
MinimalSpanningTreeCollection
Bases: UcInstanceCollection[MinimalSpanningTreeInstance]
Collection of Minimal Spanning Tree instances.
This collection provides methods to generate benchmark instances with various characteristics for testing and evaluation.
from_random(min_nodes: int | None = None, max_nodes: int | None = None, edge_prob: float = 0.5, max_weight: float = 10.0, num_instances: int = 1, *, sizes: Sequence[int] | None = None, seed: int | None = None) -> MinimalSpanningTreeCollection
classmethod
Generate random Minimal Spanning Tree instances.
Parameters:
-
min_nodes(int | None, default:None) –Minimum number of nodes.
-
max_nodes(int | None, default:None) –Maximum number of nodes.
-
edge_prob(float, default:0.5) –Additional edge probability beyond the spanning tree, by default 0.5.
-
max_weight(float, default:10.0) –Maximum edge weight, by default 10.0.
-
num_instances(int, default:1) –Number of instances per size, by default 1.
-
seed(int | None, default:None) –Random seed for reproducibility, by default None.
-
sizes(Sequence[int] | None, default:None) –Explicit sizes to generate, e.g.
[10, 50, 100], instead of a range. Mutually exclusive withmin_nodes/max_nodes, by default None.
Returns:
-
MinimalSpanningTreeCollection–Collection containing generated instances.
Examples:
filter_infeasible(max_runtime: float = 3600, *, quiet: bool = True) -> list[bool]
Drop the instances of this collection that have no feasible solution.
Every instance is formulated and handed to SCIP, which stops as soon as
it finds the first feasible solution. An instance is removed from the
collection when SCIP proves the model infeasible, when no solution turns
up within max_runtime, or when formulating it fails altogether. This
keeps randomly generated instances from breaking a downstream pipeline.
Parameters:
-
max_runtime(float, default:3600) –SCIP time limit per instance in seconds. Must be positive. Defaults to 3600 seconds.
-
quiet(bool, default:True) –Suppress the SCIP solver output.
Returns:
-
list[bool]–Feasibility mask over the instances as they were before filtering, in that order:
Truewhere the instance was kept,Falsewhere it was removed.
Raises:
-
ValueError–If
max_runtimeis not positive.