Walks and paths in a multigraph #
Mathlib's Graph α β carries incidence (G.IsLink e x y) and nothing that moves along it.
This file is the bottom of the combinatorial layer: walks, paths, reachability and
connectedness, all of which the plane development consumes through one theorem —
Graph.IsWalk.contains_path, the blueprint's "shorten an arbitrary walk at repeated
vertices".
A walk is a list of edges #
G.IsWalk u W v says the edge list W walks from u to v, each edge taken from the vertex
the previous one arrived at. It names its edges, not its vertices. That is forced by
parallel edges: two edges with the same ends are distinguishable only by name, and a walk
along one of them must be able to say which it took — a two-vertex cycle, which the plane
layer builds out of a Jordan curve cut at two points, is exactly such a graph. Nothing is
lost by naming edges, because an edge determines where taking it arrives
(Graph.IsLink.right_unique upstream, Graph.IsWalk.target_unique here).
It is an inductive family, not a recursion over the list. Every consumer argues by
induction along the walk, and the inductive gives that directly, naming the waypoint of each
step; a recursion fun u (e :: W) v => ∃ w, G.IsLink e u w ∧ IsWalk G w W v would force
every proof to generalize the source vertex by hand and to destructure an existential at each
step. The relation is built at the source end, since that is the end a list is extended
at; concatenation (Graph.IsWalk.append) and reversal (Graph.IsWalk.reverse) are proved
here once, and after that no argument has to care.
The empty walk demands x ∈ V(G). This buys the whole file: every vertex a walk visits is a
vertex of the graph with no side hypothesis, because Mathlib's Graph already forces the
ends of an edge into V(G).
A path carries its own freshness #
G.IsPath u W v is a walk with one clause added at each step: the vertex the step departs
from is not among those the rest of the path visits. The condition travels with the
induction, instead of being imposed afterwards on a separately computed vertex list — which
would have to be computed, and would need the graph to be loopless before it was even
correct.
Loops #
Mathlib's Graph allows an edge with both ends equal; the blueprint's graphs have none.
Walks tolerate loops and paths exclude them automatically — no hypothesis on G anywhere
in this file. A step of a path from u along e to w requires u ∉ G.walkVertices w W,
and w is always in that set, so u ≠ w: the freshness clause already says a path's step is
not a loop (Graph.IsPath.not_isLoopAt). Imposing looplessness on the graph instead would
put a hypothesis on every statement in the layer to rule out something the definitions
already rule out.
Namespace #
Declarations live in the root Graph namespace, extending Mathlib's, rather than in
Schoenflies: G.IsWalk, hW.append, hP.isWalk are the spellings every consumer wants,
and dot notation on a Mathlib type resolves through the type's own namespace.
Blueprint #
The blueprint uses finite graphs as an imported foundation (§"Imported facts", Finite graphs: "in a finite connected graph, any two vertices are joined by a simple path, obtained by shortening an arbitrary walk at repeated vertices"). This file supplies that import.
Graph.IsWalk.contains_path— shortening a walk to a path; used atlem:polygonal-connected("take a simple path in the resulting finite graph"),lem:finite-polygonal-union("a simple graph path has the required realization") andlem:skeleton-crosscuts("the resulting finite graph is connected, so it contains a simple graph pathPfromatob").Graph.Reaches,Graph.Connected— the connectedness those three lemmas invoke.Graph.IsPath.nodup— a simple path repeats no edge; underneath the finiteness arguments that bound a path's length.Graph.IsPath.reverse,Graph.IsPath.split— a path backwards, and a path cut in two at a vertex it visits; whatlem:subdivision-ear-preserveandlem:relative-earrun on.
The vertices an edge list touches #
The vertices a walk from u along W visits: its source, plus the ends of every edge it
takes. Defined for any edge list, not just for one that walks — the freshness clause of a
path reads it about the rest of the path before that rest is known to be a walk at all.
Equations
- G.walkVertices u W = insert u (G.coveredVertices W)
Instances For
Every vertex on an edge of the graph is a vertex of the graph.
Moving the visited set across a first step, forwards: a vertex the walk visits is either
the vertex it departs from or a vertex the rest of it visits. This is where the step lemma is
used — the edge e has only the two ends u and w, so a vertex on e is one of them.
Moving the visited set across a first step, backwards.
Walks #
G.IsWalk u W v : the edges of W, in order, walk from u to v in G. Each edge is
taken from the vertex the previous one arrived at; the list is read at its head, so the
relation is built at the source end.
- nil
{α : Type u_1}
{β : Type u_2}
{G : Graph α β}
{x : α}
(hx : x ∈ G.vertexSet)
: G.IsWalk x [] x
Standing still at a vertex of the graph is a walk.
- cons
{α : Type u_1}
{β : Type u_2}
{G : Graph α β}
{u w v : α}
{e : β}
{W : List β}
(hl : G.IsLink e u w)
(hW : G.IsWalk w W v)
: G.IsWalk u (e :: W) v
Taking an edge from the source of a walk prepends it.
Instances For
Every vertex a walk visits is a vertex of the graph — with no hypothesis, since the empty walk already stands at one.
The step lemma, run along a whole walk: naming the edges determines the vertices, so a
walk from a given source along a given edge list arrives nowhere else. Its engine is that an
edge has only two ends, Graph.IsLink.right_unique, which holds in Mathlib's Graph with no
distinctness hypothesis (a loop arrives back where it started, deterministically).
A walk of a graph whose edges all survive into a subgraph is a walk of that subgraph. This is the direction a deletion argument needs: an edge is removed, and a walk that never used it is still there.
Paths #
G.IsPath u W v : a walk from u to v that never returns to a vertex it has left.
The condition is carried step by step — the vertex a step departs from is not among those the
rest of the path visits — rather than imposed afterwards on a computed vertex list.
- nil
{α : Type u_1}
{β : Type u_2}
{G : Graph α β}
{x : α}
(hx : x ∈ G.vertexSet)
: G.IsPath x [] x
Standing still at a vertex of the graph is a path.
- cons
{α : Type u_1}
{β : Type u_2}
{G : Graph α β}
{u w v : α}
{e : β}
{W : List β}
(hl : G.IsLink e u w)
(hW : G.IsPath w W v)
(hfresh : u ∉ G.walkVertices w W)
: G.IsPath u (e :: W) v
A step onto a path is allowed when the vertex it departs from is fresh.
Instances For
A path's step never departs from where it arrives: the freshness clause already says so, because a walk always visits its own source.
A path never takes a loop. This is why nothing in this file has to assume the graph is loopless: a step along a loop would depart from the vertex it arrives at, which the freshness clause forbids.
A path in a subgraph is a path in the graph: the freshness clause reads only which vertices the remaining edges touch, and a subgraph's edges have the same ends there.
A path grows at its target end. The relation is built at the source end, so this is the direction that has to be worked for; it is what reversal runs on.
Cutting a path at a vertex it passes through. A path through x has a piece of itself
running from x to its target, and that piece uses only the path's own edges. This is the
engine of Graph.IsWalk.contains_path: when the next vertex is already on the path built so
far, the path is cut there instead of extended, so nothing ever gets longer.
A path splits at any vertex it visits, and the far half never returns to the source.
Graph.IsPath.from_visited is the suffix alone; the prefix is the awkward half, since the
relation is built at the source end and the prefix is what the induction has to carry
forward.
Every walk contains a path between the same two vertices, using only the walk's own
edges. This is the blueprint's "shorten an arbitrary walk at repeated vertices"; the
induction never lengthens anything, because the only alternative to prepending an edge is to
cut the path already built (Graph.IsPath.from_visited).
Reachability #
G.Reaches u v : some walk of G runs from u to v. Stated with a walk rather than a
path because walks are what arguments build — they concatenate and reverse with no side
condition — and a path is recovered on demand (Graph.Reaches.exists_isPath).
Instances For
Connectedness #
A graph is connected when it has a vertex and every vertex reaches every other. The nonemptiness clause is not bureaucracy: it is what makes the counting statements about trees true as stated.
Equations
Instances For
In a connected graph any two vertices are joined by a path — the blueprint's imported fact about finite graphs, with the finiteness it does not actually need dropped.
Transferring walks and paths along an inclusion or a deletion #
IsWalk.anti above pulls a walk down an inclusion. These are its companions, factored here
because both the cycle module and the two-connectivity module reached for them independently.
A path of a graph whose edges all survive into a subgraph is a path of that subgraph —
the companion of Graph.IsWalk.anti, which it is otherwise identical to. Freshness comes
down the inclusion because the subgraph's edges touch no vertices the graph's do not.
A walk that avoids the deleted names survives the deletion.