The inner grid of the anchored square mesh: 2-connectivity and the outer cycle #
Schoenflies/SquareMesh.lean delivers the geometry of prop:anchored-square-mesh — the
diameter bound, the anchors, the inward edges at the fresh points, and connectedness of
|T| ∖ S — and leaves two clauses open, both of them combinatorial:
- clause 5, the skeleton of
Tis 2-connected, is absent; - clause 3 is proved only as a point-set equation, never as a cycle of the graph.
This module supplies both for the object the blueprint's own proof of the proposition is about: the rectangular grid. See "What this does and does not close" below for exactly what the integrator still has to do.
The grid, concretely #
Two coordinate functions xc yc : ℕ → ℝ and two sizes m n : ℕ fix a grid: the vertices are
the (m+1)(n+1) points gridPt xc yc i j = (xc i, yc j), the edges are the m(n+1)
horizontal steps gridHEdge and the (m+1)n vertical steps gridVEdge. Everything is a
def — an existentially quantified grid could not be refined against a second one, which is
what Part II does.
The hypotheses on the coordinates are exactly as strong as each statement needs, and no
stronger: 2-connectivity needs only that consecutive coordinates differ, the outer cycle
needs the coordinates distinct (Set.InjOn xc (Set.Iic m)), and only the drawing — the
clause that makes the grid a plane graph — needs them increasing
(StrictMonoOn xc (Set.Iic m)).
The graph is pieceListGraph (gridEdges xc yc m n), where pieceListGraph is the general
"a list of straight segments, read as a graph" constructor: an edge is a Piece, and it links
exactly its two ends. pieceListGraph is overlayGraph without the subdivision — the vertices
are the ends of the listed segments and nothing is cut — and its point is that
pieceListGraph l₁ ∪ pieceListGraph l₂ = pieceListGraph (l₁ ++ l₂) on the nose
(pieceListGraph_union), so that the blueprint's "add these finitely many cycles one at a
time" is list concatenation and every union is an equation rather than an inclusion.
2-connectivity: two nested chains of lem:union-two-connected #
The blueprint says "the inner grid is 2-connected by the same ear construction used for K".
The ear construction is replaced here by two applications of Graph.IsTwoConnected.union,
which is shorter and needs no ear at all:
- one cell — the boundary of a single rectangle of the grid — is a quadrilateral, and
Graph.isTwoConnected_of_quadproves any quadrilateral 2-connected directly from the definition (delete a vertex; the other three are still a path); - a strip (one column of cells) is the union of its cells, consecutive cells sharing the horizontal edge between them, hence two vertices;
- the grid is the union of its strips, consecutive strips sharing the whole vertical line between them, hence again two vertices.
Both chains need at least two vertices in common at every step, so both need 1 ≤ m and
1 ≤ n: a grid with a single row of points is a path and is not 2-connected. That is the
degenerate case, and it is a hypothesis, not an oversight.
The outer cycle #
The boundary walk goes once round the four sides, gridBoundaryEdge and gridBoundaryPt
naming the 2m + 2n edges and vertices in order by a four-way if on the index. Everything
about the walk is then arithmetic in ℕ, which omega decides.
gridGraph_isCycleThrough_boundary is a Graph.IsCycleThrough of the grid graph — the
distinguished outer cycle — and edgesCover_gridBoundary computes what it occupies: for
increasing coordinates it is exactly the frame of the bounding rectangle, and for the grid on
[-1,1]² that frame is modelCurve, i.e. S (edgesCover_gridBoundary_modelCurve,
gridGraph_outer_cycle). That is the cycle thm:finite-transfer(b) asks for, in the form it
asks for it.
The grid is also shown to be a plane graph (gridGraph_isDrawing), so the outer cycle is a
Jordan curve (gridBoundary_isJordanCurve) and the whole face machinery applies to it.
What this does and does not close #
Closed. Both clauses, for the rectangular grid, from an explicit construction, together with the drawing that makes the grid a plane graph.
Not closed. Neither clause is transported to Schoenflies.squareMesh, and the reason is
structural, not a matter of effort: squareMesh is overlayGraph applied to a list of
segments and an unspecified list of cut points obtained from exists_cut_points by choice.
Its edges are therefore the pieces of a subdivision nobody can enumerate, so neither "the
edges on S form a cycle" nor "the rings are cycles glued along the spokes" is available
without first proving that the subdivision of a segment at a finite point set is a path —
a theorem no module on main has. The honest statement of the missing collar step is
Graph.IsTwoConnected.attach_cycles below: it takes a 2-connected hub K and finitely many
2-connected pieces each meeting K in two distinct vertices, and concludes for the union.
Instantiating it at squareMesh needs the collar rectangles of the mesh as cycles, which is
exactly the enumeration that is missing.
A degenerate case that would have to be excluded. squareMesh δ fresh anchors is not
2-connected when fresh has fewer than two distinct points, so the missing theorem cannot be
stated without a hypothesis. With fresh = [] the mesh is meshCount δ pairwise disjoint
concentric ring frames and is not even connected. With one fresh point z the spoke at z is
the only thing joining the rings, so every vertex of that spoke is a cut vertex: deleting the
one at radius k/N separates the rings inside it from the rings outside. Two fresh points are
what make the rings and the two spokes a chain of quadrilaterals, which is the same picture the
grid presents here. The corresponding hypothesis for the grid is 1 ≤ m and 1 ≤ n, and it is
carried explicitly by every theorem below that needs it.
Blueprint #
prop:anchored-square-mesh, clause 5, for the inner grid —gridGraph_isTwoConnected.prop:anchored-square-mesh, clause 3, as a cycle —gridGraph_isCycleThrough_boundarytogether withedgesCover_gridBoundary,edgesCover_gridBoundary_modelCurveand the packagedgridGraph_outer_cycle.lem:polygonal-overlay, the drawing clause, for the grid —gridGraph_isDrawing.lem:union-two-connected— used throughGraph.IsTwoConnected.union; iterated here asGraph.IsTwoConnected.attach_cycles, which is the blueprint's "adding these finitely many cycles one at a time".pieceListGraph,pieceListGraph_union— the list-of-segments graph, and the fact that its unions are concatenations.
A quadrilateral is 2-connected #
The base case of both chains below. The blueprint reaches it through the ear decomposition
(lem:subdivision-ear-preserve applied to a cycle); here it is read straight off the
definition of Graph.IsTwoConnected, because a four-vertex graph is small enough that
"delete one vertex and the rest is still connected" is four applications of one lemma.
A four-cycle is 2-connected. No hypothesis on G beyond the four edges, the four
distinct vertices, and the fact that there are no other vertices.
Attaching finitely many pieces to a 2-connected hub #
prop:anchored-square-mesh: "The boundary of every collar rectangle is a cycle sharing with
the inner grid at least the two endpoints of its inner boundary arc. Adding these finitely many
cycles one at a time and applying lem:union-two-connected proves that the full mesh is
2-connected."
Graph.chainUnion_isTwoConnected (Schoenflies/OuterChain.lean) iterates the same lemma along
a chain in which consecutive members meet. Here every piece meets the hub, which is
what the collar rectangles do — each meets the inner grid, but two collar rectangles on
opposite sides of the square meet nothing of each other. The two statements are independent;
the integrator may want them beside each other.
The hub K with the pieces Γ 0, …, Γ (m-1) glued on, one at a time.
Equations
- K.attachUnion Γ 0 = K
- K.attachUnion Γ m.succ = (K.attachUnion Γ m).union (Γ m)
Instances For
lem:union-two-connected, iterated at a hub. A 2-connected K and finitely many
2-connected pieces, each meeting K in two distinct vertices, have 2-connected union.
A march along indices is a path #
The boundary walk of the grid is a march: vertices p s, p (s+1), … joined by edges
q s, q (s+1), …. The freshness clause of Graph.IsPath is then exactly the injectivity of
p on the index range, because an edge of the chain is incident with nothing but its own two
ends (Graph.Inc.eq_or_eq_of_isLink).
The graph carried by a list of segments #
overlayGraph subdivides; this does not. When the segments are already in general position —
which for a grid they are, by construction — no subdivision is wanted, and what is gained is
that unions are concatenations.
Schoenflies/ArcComplement.lean has the same construction indexed by a Set Piece, under the
name segGraph, and pieceListGraph l is segGraph {P | P ∈ l}. The two were written in
parallel and collided at merge; they are kept apart for now because this module's proofs run on
the list and the other's on the set. If a third consumer appears, hoist the set version into
Schoenflies/OverlayGraph.lean beside endSet and derive this one from it.
The canonical link of a listed segment: it joins its own two ends.
Two plane points with the same coordinates are equal.
Schoenflies.Plane.coord_ext in Schoenflies/JordanSeparates.lean is the same fact in a more
general form; that module is not on this one's import path, and the integrator should collapse
the two if it ever is.
What a segment graph occupies is what its segments occupy: every vertex is an end of a listed segment, so the vertices add nothing.
Two segment graphs never disagree about a shared edge: an edge is its own pair of ends.
The union of two segment graphs is the segment graph of the concatenation. This is the
whole reason for pieceListGraph: it turns "glue one more cycle on" into an equation.
The rectangular grid #
Two coordinate functions and two sizes. Nothing here asks the coordinates to be sorted: the combinatorics needs only that consecutive coordinates differ, which is exactly what makes each cell a genuine quadrilateral. Sortedness enters only with the geometry of the outer cycle.
The grid point with coordinate indices (i, j).
Equations
- Schoenflies.gridPt xc yc i j = Schoenflies.Plane.mk (xc i) (yc j)
Instances For
The horizontal grid edge from (i, j) to (i+1, j).
Equations
- Schoenflies.gridHEdge xc yc i j = (Schoenflies.gridPt xc yc i j, Schoenflies.gridPt xc yc (i + 1) j)
Instances For
The vertical grid edge from (i, j) to (i, j+1).
Equations
- Schoenflies.gridVEdge xc yc i j = (Schoenflies.gridPt xc yc i j, Schoenflies.gridPt xc yc i (j + 1))
Instances For
The four sides of the cell whose lower-left corner is (i, j), listed bottom, right, top,
left.
Equations
- Schoenflies.cellEdges xc yc i j = [Schoenflies.gridHEdge xc yc i j, Schoenflies.gridVEdge xc yc (i + 1) j, Schoenflies.gridHEdge xc yc i (j + 1), Schoenflies.gridVEdge xc yc i j]
Instances For
One column of n cells, the cells (i, 0), …, (i, n-1).
Equations
- Schoenflies.stripEdges xc yc i n = List.flatMap (Schoenflies.cellEdges xc yc i) (List.range n)
Instances For
The m × n grid: m columns of n cells.
Equations
- Schoenflies.gridEdges xc yc m n = List.flatMap (fun (i : ℕ) => Schoenflies.stripEdges xc yc i n) (List.range m)
Instances For
The grid graph: the m × n rectangular grid on the coordinates xc, yc, as a plane
graph with straight edges.
Equations
- Schoenflies.gridGraph xc yc m n = Schoenflies.pieceListGraph (Schoenflies.gridEdges xc yc m n)
Instances For
The cell #
One cell of the grid is 2-connected. Its boundary is a quadrilateral, so this is
Graph.isTwoConnected_of_quad with the four corners named. Only the two consecutive-coordinate
inequalities are used.
The strip, then the grid #
Two chains of Graph.IsTwoConnected.union, and the two shared vertices are in each case the
two ends of one edge that both parts contain.
A column of cells is 2-connected. Consecutive cells share the horizontal edge between them, hence its two ends.
The grid is 2-connected. Each new column shares with what is already there the whole vertical line between them — in particular the two ends of its lowest edge.
The two size hypotheses are not decoration: a grid one point wide is a path, and a path is not 2-connected.
Which grid edges exist #
Two membership lemmas, both by the same trick: a horizontal edge at height n is the top
of a cell rather than the bottom of one, and a vertical edge on the line i = m is the right
side of a cell rather than the left.
The outer cycle #
The boundary of the grid, walked once anticlockwise from the corner (0,0): m steps right
along the bottom, n up the right side, m back along the top, n down the left side. The
walk is parametrised by an index t < 2m + 2n, and both the vertex and the edge at index t
are given by a four-way if. Everything about the walk then reduces to arithmetic in ℕ,
which omega decides; the geometry enters only in edgesCover_gridBoundary at the end.
The index pair determines the position on the walk. Pure arithmetic: the four sides overlap only at the corners, and there the two descriptions agree.
1 ≤ m and 1 ≤ n are needed and are not slack. With n = 0 the walk goes out along the
bottom and comes back along the same row, so index t and index 2m - t name one point;
this is the degenerate case in which the grid is a path rather than a cycle.
The t-th vertex of the boundary walk.
Equations
- Schoenflies.gridBoundaryPt xc yc m n t = Schoenflies.gridPt xc yc (Schoenflies.bIdx m n t) (Schoenflies.bIdy m n t)
Instances For
The walk closes up: index 2m + 2n is the corner it started from.
The vertices of the boundary walk are pairwise distinct.
Each step of the boundary walk is an edge of the grid, joining the two vertices it should.
The detour: all the boundary edges but the last.
Equations
- Schoenflies.gridBoundaryDetour xc yc m n = List.map (Schoenflies.gridBoundaryEdge xc yc m n) (List.range' 0 (2 * m + 2 * n - 1))
Instances For
The last boundary edge, which closes the cycle.
Equations
- Schoenflies.gridBoundaryLast xc yc m n = Schoenflies.gridBoundaryEdge xc yc m n (2 * m + 2 * n - 1)
Instances For
prop:anchored-square-mesh, clause 3, as a cycle. The boundary of the grid is a cycle
of the grid graph — not merely a point set.
What the outer cycle occupies #
Up to here nothing has been asked of the order of the coordinates. It is asked now: the union
of the boundary edges is the frame of the bounding rectangle only if the coordinates increase,
and the proof is the one-dimensional fact Set.Icc_union_Icc_eq_Icc transported along
Schoenflies.mem_segment_horiz / mem_segment_vert.
The realisation of the cycle is the union of the boundary edges, indexed by position on the walk.
Consecutive intervals #
The outer cycle of a grid on [-1,1]² occupies exactly S. This is the missing half of
prop:anchored-square-mesh clause 3: not "the edges lying on S cover S", but "S is the
realisation of a distinguished cycle of the graph", which is the form
thm:finite-transfer(b) consumes.
The distinguished outer cycle of the grid on [-1,1]², both halves at once: it is a
cycle of the grid graph, and what it occupies is S.
The grid is a plane graph #
Sorted coordinates make the drawing clause a coordinate computation. A point of a horizontal
grid edge has the row's y-coordinate and an x-coordinate in one closed coordinate interval;
strict monotonicity turns "xc a lies in [xc i, xc (i+1)]" into "a = i or a = i+1", and
that single step settles both remaining clauses of Graph.IsDrawing.
A grid edge is nondegenerate: its two ends differ in one coordinate.
A grid point on a grid edge is one of that edge's two ends.
A point on two grid edges is a grid point on both. The heart of the drawing clause: two grid segments cross only where a coordinate of one is pinned by the other.
The grid is a plane graph, drawn with straight edges.
The outer cycle is a Jordan curve. Now that the grid is a plane graph this is
Graph.IsDrawing.cycle_isJordanCurve applied to the boundary cycle.