Refinement splits cells, so the search terminates with a leaf in hand #
The fuel of the search is n + 1, and canonical falls back on the identity labelling if the
search comes back empty-handed. This file rules that out.
The measure is the number of cells, numCells. Individualising a vertex of a non-singleton cell
creates a new cell start, and refinement never merges cells — that is Refines, proved by
following the cell starts through splitCell, splitCellsFrom, refineStep and refineLoop.
So each level of the tree has at least one more cell than its parent (numCells_child), the tree
has depth at most n, and the fuel is enough (dfsNode_best). The other thing needed is that
the first child of a node is never orbit-pruned, which holds because the orbit mark starts out
empty and is only refreshed when an automorphism has been found — which happens only at a leaf.
Refinement only splits cells #
q refines p: every cell of p is a union of cells of q. Since a cell is determined by
its start position (Part.WF.cst_eq_iff), this is the same as saying every cell start of p is
still a cell start of q.
Instances For
Individualisation creates a cell, refinement keeps it #
The number of cells, and why the search terminates in n levels #
The number of cells of p inside {0, …, n-1}.
Equations
- IsoGraph.Canon.numCells n p = {i ∈ Finset.range n | p.cst[i]! = i}.card
Instances For
Each level of the search splits a cell. This is what bounds the depth of the tree by n
and makes the fuel n + 1 enough to reach a leaf.
The search always finishes with a leaf in hand #
The search always ends holding a leaf. The fuel n + 1 is enough because every level of
the tree splits a cell, so a node at depth d has at least d + 1 cells and the tree has depth at
most n - 1; and the first child of a node is never orbit-pruned, so the descent always reaches a
leaf, which leafUpdate stores.