Skip to content

feat(Geometry/Polygon): simple polygons and boundary map#35069

Open
A-M-Berns wants to merge 15 commits intoleanprover-community:masterfrom
A-M-Berns:simple-clean
Open

feat(Geometry/Polygon): simple polygons and boundary map#35069
A-M-Berns wants to merge 15 commits intoleanprover-community:masterfrom
A-M-Berns:simple-clean

Conversation

@A-M-Berns
Copy link
Copy Markdown
Contributor

@A-M-Berns A-M-Berns commented Feb 10, 2026

This PR introduces Simple polygons with the predicate IsSimple, which captures the idea of a non-self-intersecting boundary, in the file Simple.lean. In the file Boundary.lean, a boundary map from AddCircle n is defined. I prove that the range of this map is the boundary and that this map is injective if and only if the polygon is simple. I kept Boundary.lean and Simple.lean separate because future results will include stuff just about the boundary map independent of simplicity (e.g. that it is continuous in the appropriate setting) and stuff just about simple polygons independent of the boundary map (e.g. that for n = 3, IsSimple iff HasNondegenerateVertices.) I used Claude Code to help generate some of the proof material, but I golfed and edited all AI contribution.


@github-actions github-actions bot added the new-contributor This PR was made by a contributor with at most 5 merged PRs. Welcome to the community! label Feb 10, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 10, 2026

PR summary aac298aee3

Import changes for modified files

No significant changes to the import graph

Import changes for all files
Files Import difference
Mathlib.Geometry.Polygon.Simple (new file) 1398
Mathlib.Geometry.Polygon.Boundary (new file) 1601

Declarations diff

+ IsSimple
+ boundaryMap
+ continuous_boundaryMap
+ finRotate_apply
+ isSimple_iff_boundaryMap_injective
+ range_boundaryMap

You can run this locally as follows
## summary with just the declaration names:
./scripts/declarations_diff.sh <optional_commit>

## more verbose report:
./scripts/declarations_diff.sh long <optional_commit>

The doc-module for scripts/declarations_diff.sh contains some details about this script.


No changes to technical debt.

You can run this locally as

./scripts/technical-debt-metrics.sh pr_summary
  • The relative value is the weighted sum of the differences with weight given by the inverse of the current value of the statistic.
  • The absolute value is the relative value divided by the total sum of the inverses of the current values (i.e. the weighted average of the differences).

variable (R) in
/-- A polygon is simple if edges are nondegenerate, non-adjacent edges are disjoint,
and adjacent edges meet only at their shared vertex. -/
def IsSimple (poly : Polygon P n) : Prop :=
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe consider turning this into a structure so you can name each condition?

@wwylele
Copy link
Copy Markdown
Collaborator

wwylele commented Feb 10, 2026

@A-M-Berns
Copy link
Copy Markdown
Contributor Author

Not necessarily in this PR but should we connect this to https://leanprover-community.github.io/mathlib4_docs/Mathlib/Topology/Path.html#Path in some way?

My next PR will prove the map is continuous, and then I think the connection to Path wouldn't be hard from there although idk how valuable it would be

variable (R) in
/-- A polygon is simple if edges are nondegenerate, non-adjacent edges are disjoint,
and adjacent edges meet only at their shared vertex. -/
structure IsSimple (poly : Polygon P n) : Prop where
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need its own file? Can we put this in the Boundary file, and move later if needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like I mentioned in the PR description, I think there are results about simple polygons that don't have anything to do with the boundary that will come later. For example, the theorem that if n = 3 then the polygon is simple if and only if it HasNondegenerateVertices.

variable (R) in
/-- The boundary parametrization on `R` formed by concatenating edges using `edgePath`. -/
noncomputable def boundaryParam (poly : Polygon P n) (t : R) : P :=
let i : Fin n := ⟨(Int.floor t).toNat % n, Nat.mod_lt _ (Nat.pos_of_neZero n)⟩
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this?

Suggested change
let i : Fin n := ⟨(Int.floor t).toNat % n, Nat.mod_lt _ (Nat.pos_of_neZero n)⟩
let i : Fin n := .ofNat (.floor t)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making this suggestion directly creates a bunch of typeclass errors

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe my dot notation was wrong, I meant Fin.ofNat (Nat.floor t)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That gives me the same errors, first complaining about FloorSemiring R and then about NeZero \lfloor t \rfloor

@mathlib-dependent-issues mathlib-dependent-issues bot added the blocked-by-other-PR This PR depends on another PR (this label is automatically managed by a bot) label Feb 13, 2026
@mathlib-merge-conflicts mathlib-merge-conflicts bot added the merge-conflict The PR has a merge conflict with master, and needs manual merging. (this label is managed by a bot) label Feb 13, 2026
@mathlib-merge-conflicts
Copy link
Copy Markdown

This pull request has conflicts, please merge master and resolve them.

@github-actions github-actions bot removed the merge-conflict The PR has a merge conflict with master, and needs manual merging. (this label is managed by a bot) label Feb 13, 2026
@github-actions github-actions bot added merge-conflict The PR has a merge conflict with master, and needs manual merging. (this label is managed by a bot) and removed merge-conflict The PR has a merge conflict with master, and needs manual merging. (this label is managed by a bot) labels Feb 13, 2026
@github-actions github-actions bot added merge-conflict The PR has a merge conflict with master, and needs manual merging. (this label is managed by a bot) and removed merge-conflict The PR has a merge conflict with master, and needs manual merging. (this label is managed by a bot) labels Feb 17, 2026
@A-M-Berns
Copy link
Copy Markdown
Contributor Author

A-M-Berns commented Feb 17, 2026

The theorem continuous_boundaryMap ended up being simpler than I thought, so I've added it to this PR.

@mathlib-dependent-issues mathlib-dependent-issues bot removed the blocked-by-other-PR This PR depends on another PR (this label is automatically managed by a bot) label Feb 25, 2026
@mathlib-dependent-issues
Copy link
Copy Markdown

@vihdzp
Copy link
Copy Markdown
Collaborator

vihdzp commented Feb 25, 2026

I suspect we must be missing some kind of API about paths. The boundary of a polygon is intuitively just "all the edge paths concatenated together", not some weird operation involving floors and moduli.

What about adding an unbundled version of Path.concat (one without the continuity assumptions), adding whatever relevant API there is to it, and redefine the boundary map of a polygon by using it?

/-- A map from `AddCircle n` to the boundary points of the polygon. -/
noncomputable def boundaryMap (poly : Polygon P n) : AddCircle (n : R) → P :=
let boundaryParam : R → P := fun t =>
let i : Fin n := ⟨(Int.floor t).toNat % n, Nat.mod_lt _ (Nat.pos_of_neZero n)⟩
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think perhaps this is worth extracting as AddCircle.floor : AddCircle (n : R) -> Fin n?

@eric-wieser
Copy link
Copy Markdown
Member

I suspect we must be missing some kind of API about paths. The boundary of a polygon is intuitively just "all the edge paths concatenated together", not some weird operation involving floors and moduli.

What about adding an unbundled version of Path.concat (one without the continuity assumptions), adding whatever relevant API there is to it, and redefine the boundary map of a polygon by using it?

I think one difference is that Path has distinct endpoints, whereas here we explicitly want wraparound behavior.

@vihdzp
Copy link
Copy Markdown
Collaborator

vihdzp commented Feb 27, 2026

Path doesn't necessarily have distinct endpoints. One can always write Path x x.

What I'm arguing for here is rather some other type Curve x y (we can call it something else), for a function f : unitInterval → X with f 0 = x and f 1 = y (notably, there are no continuity hypotheses!). We can then transfer all the standard constructions on paths; in particular, we can have Curve.concat which would join a Fin-indexed family of curves at the endpoints. This is exactly what we want here. Separately, we can have a theorem which says that the Curve.concat of continuous curves is continuous, which would make it trivial to construct the Path version of the boundary map of a polygon.

@joneugster
Copy link
Copy Markdown
Contributor

t-euclidean-geometry

@github-actions github-actions bot added the t-euclidean-geometry Affine and axiomatic geometry label Feb 27, 2026
@A-M-Berns
Copy link
Copy Markdown
Contributor Author

@vihdzp I think your proposed design is more elegant but it's at least a full separate PR worth of missing infrastructure plus a complete refactor of this PR. My opinion is that the most important results about the boundaryMap are the ones already in the file, so the work that builds on top of this to e.g. define polygon interiors will not care greatly about the internals of boundaryMap. I would prefer for this PR to still be merged with that refactor as a planned change later, but if others agree that the current definition is too inelegant for mathlib I would be happy to take direction.

@vihdzp
Copy link
Copy Markdown
Collaborator

vihdzp commented Mar 2, 2026

I would rather not add tech debt upon ourselves. Mathlib isn't just about proving things, but building good infrastructure.

@A-M-Berns
Copy link
Copy Markdown
Contributor Author

Personally I don't think it's really tech debt when the refactor would be mostly about elegance and this is a functional API surface as is especially for the obvious things that will depend on this boundaryMap, but again I'd like to hear others' thoughts.

@grunweg grunweg added the LLM-generated PRs with substantial input from LLMs - review accordingly label Mar 16, 2026
@mathlib-triage mathlib-triage bot assigned jsm28 and unassigned JovanGerb Mar 28, 2026
@jsm28
Copy link
Copy Markdown
Contributor

jsm28 commented Mar 29, 2026

I agree with the previous comments that some general API for joining paths together at their endpoints would be an appropriate basis for this PR (I haven't checked whether any such API already exists).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

LLM-generated PRs with substantial input from LLMs - review accordingly new-contributor This PR was made by a contributor with at most 5 merged PRs. Welcome to the community! t-euclidean-geometry Affine and axiomatic geometry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants