docs: 5-document meeting prep — tutorials + research note + status + architecture
External-reviewer-visit prep package (Springborn-Bobenko PhD alumnus,
2026-05-26). All five documents target the same audience: a
mathematician who wants to evaluate, extend, or contribute to
conformallab++. Goal: make the project maximally hackable BEFORE the
meeting. Code unchanged in this commit — pure documentation.
Files added
───────────
1. **doc/tutorials/block-fd-hessian.md** (460 lines)
Step-by-step tutorial on the per-face block-FD Hessian pattern
shipped in Phase 9b (96× speed-up). Matches the style of
add-inversive-distance.md. Covers:
* The per-face locality lemma (mathematical justification).
* Cost analysis (full-FD vs block-FD vs analytic).
* Implementation walkthrough through face_angles_from_local_dofs +
hyper_ideal_hessian_block_fd.
* Porting checklist for applying the same pattern to a new
functional.
* The four cross-validation criteria.
* When NOT to use block-FD + upgrade path to Phase 9b-analytic.
2. **doc/tutorials/add-output-uv-map.md** (477 lines)
Tutorial for the `output_uv_map` named-parameter pattern shipped in
PR #14. Covers:
* The UX problem (two-step pipeline → one-call wrapper).
* The CGAL named-parameter mechanism + how the entry functions
wire it (get_parameter + constexpr if).
* Step-by-step recipe for adding a new named parameter (worked
example: hypothetical `output_holonomy_map`).
* The five test patterns for verification.
* Why CP-Euclidean (face-DOF) and Inversive-Distance (Luo-edge-length)
do not yet support output_uv_map — what is needed to add them.
3. **doc/math/hyperideal-hessian-derivation.md** (805 lines)
Research-quality LaTeX-formatted derivation of the analytic
HyperIdeal Hessian via the Schläfli identity (Phase 9b-analytic
preparation). Covers:
* Schläfli identity (1858/60) — gradient and second-order form.
* Derivatives of ζ, ζ₁₃, ζ₁₄, ζ₁₅ (all hyper-ideal-to-fully-ideal cases).
* Chain rule for ∂β_i/∂(b,a) and ∂α_ij/∂(b,a) — case-split on the
four α_ij branches.
* Per-face 6×6 block formulas.
* Acceptance criteria for the future implementation.
* Implementation outline (Conformal_map header sketch).
* Appendix A: sign / argument-order pitfalls reading the code.
* References: Schläfli 1858, Milnor 1982, Vinberg 1993, Cho-Kim 1999,
Rivin, Glickenstein 2011, Springborn 2020, BPS 2015.
4. **doc/roadmap/porting-status.md** (~250 lines)
Operational snapshot of "where is each piece of Java math today"
at v0.9.0. Sections:
* 25 000 lines of Java in one table (ported / worth porting /
intentionally skipped breakdown).
* Five DCE models — full status matrix with Java port status,
Hessian type, Newton support, CGAL entry, UV-output capability.
* Topology + solver infrastructure status.
* CGAL public API map + known limitations (no chaining, Surface_mesh
only, submission-readiness gaps).
* Reverse cross-reference: Java class → C++ port location (or
"skipped: replaced by …" / "in roadmap: phase X").
* Things in C++ that the Java original does NOT have (research
extensions track).
* "How to use the library today" quickstart.
5. **doc/architecture/locked-vs-flexible.md** (~270 lines)
12-item architecture-decision review with tier classification
(🔴 load-bearing / 🟡 semi-fixed / 🟢 opportunistic). Each item
includes: locked-since date, cost to change, when to revisit,
recommended posture for new contributors. Key insight stated up
front: "the load-bearing decisions are all good in 2026". Closes
with five open questions for the external reviewer — items where
a second opinion would genuinely help (Phase 9c algorithm choice,
Phase 10a priorities, analytic-Hessian payoff justification,
CGAL upstream vs independent distribution, geometry-central
cross-validation).
Total: ~2 250 lines across five new docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
281
doc/architecture/locked-vs-flexible.md
Normal file
281
doc/architecture/locked-vs-flexible.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# Locked-in vs flexible architecture decisions
|
||||
|
||||
> **Purpose.** External collaborators (especially mathematicians
|
||||
> evaluating whether to extend the library) need to know which
|
||||
> design choices are **load-bearing** (changing them is expensive
|
||||
> across the whole codebase) and which are **opportunistic** (made
|
||||
> on first principles and easy to revisit).
|
||||
>
|
||||
> **Why this matters now.** A v0.9.0 + Springborn-Bobenko-alumnus
|
||||
> external review (May 2026) is the right moment to surface these
|
||||
> decisions before they ossify further. If any of the *locked*
|
||||
> decisions need revisiting, this is the cheapest moment in the
|
||||
> project's life to do so.
|
||||
|
||||
Three tiers:
|
||||
|
||||
```
|
||||
🔴 LOAD-BEARING changing requires repo-wide refactoring
|
||||
🟡 SEMI-FIXED changing affects multiple subsystems; possible but not casual
|
||||
🟢 OPPORTUNISTIC changing is one-PR work
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Mesh data structure → `CGAL::Surface_mesh<P>`
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🔴 load-bearing |
|
||||
| Locked since | Phase 3 (2025) |
|
||||
| Alternative considered | OpenMesh / pmp-library / custom halfedge / Java `CoHDS` literal port |
|
||||
| Why locked | Every header `code/include/*.hpp` uses `ConformalMesh = CGAL::Surface_mesh<Point3>` and CGAL property maps explicitly. Phase 8a Traits provide a *concept* abstraction but the Default model is `Surface_mesh`-only. |
|
||||
| Cost to change | ~3 weeks: rewrite traits, every functional, every test mesh factory. Touched in ~30 headers + ~25 test files. |
|
||||
| Mitigation | Traits-based design (Phase 8a) means user-supplied mesh types CAN be added as Default-traits specialisations without touching algorithms — Phase 8a.2 plan documents this. But the Default model is Surface_mesh. |
|
||||
| When to revisit | If a major user wants Polyhedron_3 or OpenMesh as default. Currently no concrete request → keep. |
|
||||
|
||||
**Recommended posture for an external contributor:** use the existing
|
||||
Surface_mesh-based API for any new functional. Adding generic-FaceGraph
|
||||
support is a single architectural step that doesn't need to be repeated
|
||||
per functional — wait for one user to need it.
|
||||
|
||||
---
|
||||
|
||||
## 2. Floating-point kernel → `CGAL::Simple_cartesian<double>`
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🟡 semi-fixed |
|
||||
| Locked since | Phase 3 (deliberate decision: conformal geometry doesn't need exact predicates) |
|
||||
| Cost to change | Per-functional template parameterisation. The Phase 8 MVP wrapper already deduces the kernel from the mesh point type, so user code can specify any CGAL kernel — but the **legacy** `code/include/*.hpp` headers are hardcoded. |
|
||||
| When to revisit | If a user reports floating-point catastrophic cancellation in the half-tangent angle formula on extreme meshes. Hasn't happened in 250 tests over 9 phases. |
|
||||
|
||||
**Recommended posture:** stay with `Simple_cartesian<double>`. If a
|
||||
specific algorithm needs `Exact_predicates_inexact_constructions_kernel`
|
||||
for robustness, parameterise that one algorithm — don't refactor the
|
||||
whole codebase.
|
||||
|
||||
---
|
||||
|
||||
## 3. Header-only, no compiled library
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🔴 load-bearing |
|
||||
| Locked since | Phase 1 |
|
||||
| Cost to change | Significant: would need to introduce `.cpp` files, link order, ABI compatibility decisions. But everything in `code/include/*.hpp` is `inline` or template, so a header-only-to-compiled migration is mechanical. |
|
||||
| Why locked | CGAL package convention is also header-only. Forking would break that goal. |
|
||||
| When to revisit | If compilation times become prohibitive (currently < 30 s clean build with all 5 functionals). Or if a future cyclic dependency between functionals forces it. Neither is on the horizon. |
|
||||
|
||||
**Recommended posture:** stay header-only. This is also the de-facto
|
||||
norm for CGAL packages of comparable scope.
|
||||
|
||||
---
|
||||
|
||||
## 4. Five DCE models on the same mesh
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🟡 semi-fixed |
|
||||
| Locked since | Phase 9a (2026-05) — when CP-Euclidean introduced face-DOFs |
|
||||
| Why semi-fixed | Each model has its own `*Maps` bundle with its own property-map name prefix (`ev:`, `sv:`, `v:`, `cf:`/`ce:`, `iv:`/`ie:`) so all five can coexist on the same `CGAL::Surface_mesh`. Adding a sixth model means picking a new prefix + writing a new `*Maps` struct + Default trait. |
|
||||
| Cost to add a sixth model | ~1 week (CP-Euclidean took ~3 days, Inversive-Distance ~3 days, Hessian + Newton + CGAL entry add another ~3 days). |
|
||||
| When to revisit | If a unified base-Maps abstraction would actually win something (currently it would not — the property-map sets differ in *kind*, not just in name). |
|
||||
|
||||
**Recommended posture for adding a new functional:**
|
||||
1. Pick a 2-letter prefix not in `{ev, sv, v, cf, ce, iv, ie}`.
|
||||
2. Define your `*Maps` struct with that prefix.
|
||||
3. Define your `Default_*_traits<Surface_mesh, K>` class in a new
|
||||
header `code/include/CGAL/Discrete_*.h`.
|
||||
4. Wire it into `newton_solver.hpp` (template-copy from
|
||||
`newton_inversive_distance` is the closest pattern for vertex-DOFs;
|
||||
`newton_cp_euclidean` for face-DOFs).
|
||||
5. Add a CGAL entry in the new header.
|
||||
6. Add tests following [`add-inversive-distance.md`](../tutorials/add-inversive-distance.md).
|
||||
|
||||
This recipe has been validated three times now (CP-Euclidean, Inversive
|
||||
Distance, and the four Phase-8b-Lite wrappers).
|
||||
|
||||
---
|
||||
|
||||
## 5. Newton solver with line search + SparseQR fallback
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🟡 semi-fixed |
|
||||
| Locked since | Phase 4 |
|
||||
| Cost to change | Each `newton_*` function is ~50-80 lines; replacing the solver across all five is ~2 days. |
|
||||
| When to revisit | If a future functional needs trust-region or BFGS. None of the five currently does — Newton converges quadratically near the optimum and the line search handles bad initial points. |
|
||||
|
||||
**Recommended posture:** Newton with line search is enough for any
|
||||
strictly-convex variational problem. For non-convex variants, consider
|
||||
adding a `newton_with_trust_region()` helper alongside, not replacing.
|
||||
|
||||
---
|
||||
|
||||
## 6. Eigen as the linear-algebra back-end
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🔴 load-bearing |
|
||||
| Locked since | Phase 4 |
|
||||
| Alternative considered | PETSc/Tao (Java original) / Boost.uBLAS / Blaze |
|
||||
| Why locked | Eigen is header-only (no external dependency at build time), bundled as a CGAL dependency, fast, and offers `SimplicialLDLT + SparseQR` which the gauge-singular-mesh case needs. |
|
||||
| Cost to change | Significant — every Hessian header (`*_hessian.hpp`) and every Newton solver uses `Eigen::SparseMatrix` and `Eigen::VectorXd` directly. ~2 weeks repo-wide. |
|
||||
| When to revisit | If a sparse-solver feature (e.g. parallel Cholesky) is needed that Eigen doesn't offer. |
|
||||
|
||||
**Recommended posture:** stay with Eigen.
|
||||
|
||||
---
|
||||
|
||||
## 7. CGAL public-API surface layout
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🟡 semi-fixed (Phase 8a-MVP design decision, 2026-05-19) |
|
||||
| Locked since | PR #6 (v0.9.0) |
|
||||
| Strategy chosen | "Strategy C" — functional-specific Default traits, one entry function per functional, no fat unified trait. |
|
||||
| Cost to change to unified trait | ~1 week — refactor `Default_*_traits<>` into a single `Default_conformal_map_traits<>` with all property-map fields. Existing 8 tests would need updating. |
|
||||
| When to revisit | When the first cross-functional algorithm (e.g. a hybrid functional that uses both face and vertex DOFs) lands. Speculation today. |
|
||||
|
||||
**Recommended posture:** stay with Strategy C. CGAL's own
|
||||
`Polygon_mesh_processing` package follows the same convention — one
|
||||
default trait per algorithm family.
|
||||
|
||||
---
|
||||
|
||||
## 8. Named-parameter mechanism
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🟢 opportunistic |
|
||||
| Locked since | Phase 8 MVP (2026-05-19) |
|
||||
| Current state | Six tags: `vertex_curvature_map`, `fixed_vertex_map`, `gradient_tolerance`, `max_iterations`, `output_uv_map`, `normalise_layout`. No chaining yet. |
|
||||
| Cost to extend with chaining | ~2 days. Generate CGAL-style member-function chainers via macros (CGAL has a `CGAL_add_named_parameter` macro for this). |
|
||||
| When to revisit | At any time; this is the lowest-risk change in the codebase. Tutorials [`add-output-uv-map.md`](../tutorials/add-output-uv-map.md) §4 explains the mechanism. |
|
||||
|
||||
**Recommended posture:** add chaining when the first user complains. Or
|
||||
when adding the 8th-10th named parameter (still 6 today — manageable).
|
||||
|
||||
---
|
||||
|
||||
## 9. Property-map name conventions
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🟢 opportunistic |
|
||||
| Locked since | Phase 3 + 9a (prefix `ev:`/`sv:`/`v:`/`cf:`/`ce:`/`iv:`/`ie:` set when each functional was introduced) |
|
||||
| Cost to change | One sed-replace + recompile. No user-visible effect because the names are an *internal* convention; the CGAL public API never exposes them. |
|
||||
| When to revisit | If a future functional reuses an existing letter prefix. Already discussed in [`locked-vs-flexible.md`](#4-five-dce-models-on-the-same-mesh). |
|
||||
|
||||
---
|
||||
|
||||
## 10. Tests: GTest, not CGAL's own test format
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🟡 semi-fixed |
|
||||
| Locked since | Phase 1 |
|
||||
| Cost to change | ~1 week — rewrite test harnesses to CGAL's `test/Conformal_map/` convention. This is Phase 8d (planned for CGAL submission). |
|
||||
| Why GTest now | Faster development cycle, IDE-friendly (Xcode / VSCode / CLion all have native GTest support). No CGAL submission is in progress yet. |
|
||||
| When to revisit | When committing to CGAL submission (Phase 8c-d, decided to be a future commitment, see [`release-policy.md`](../release-policy.md)). |
|
||||
|
||||
**Recommended posture:** keep GTest as primary. When/if CGAL submission
|
||||
happens, add a `test/Conformal_map/` shim that calls into the GTest
|
||||
suite — both formats can coexist.
|
||||
|
||||
---
|
||||
|
||||
## 11. License: MIT
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🔴 load-bearing |
|
||||
| Locked since | Project inception |
|
||||
| Cost to change | High organisational cost (requires consent of all contributors); ~no code cost. |
|
||||
| Why locked | MIT was chosen for academic friendliness (citing, modifying, embedding). CGAL upstream requires LGPL for submitted packages. |
|
||||
| Trade-off | Submitting to CGAL upstream is not possible without re-licensing. The codebase architecture is "CGAL-style" but the project would publish independently. |
|
||||
| When to revisit | If/when a concrete CGAL upstream submission is decided. See [`release-policy.md`](../release-policy.md) for the formal policy. |
|
||||
|
||||
**Recommended posture:** stay with MIT. Build the "CGAL-style package
|
||||
for external distribution" as the primary deliverable. Re-license only
|
||||
when the CGAL editorial board commits to accepting the submission.
|
||||
|
||||
---
|
||||
|
||||
## 12. Documentation pattern: Markdown + Doxygen
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Status | 🟢 opportunistic |
|
||||
| Locked since | Phase 7.5 (2026-05) |
|
||||
| Current state | `code/include/*.hpp` carry Doxygen-style `///` comments (87% coverage); `doc/*.md` for prose; `Doxyfile` generates HTML in `doc/doxygen/`. |
|
||||
| Cost to add more | Per-file basis; ~1 hour per header for full Doxygen. |
|
||||
| When to revisit | When chasing CGAL-submission readiness (need `PackageDescription.txt` + `User_manual.md`). |
|
||||
|
||||
**Recommended posture:** keep adding `///` comments incrementally with
|
||||
each new public function.
|
||||
|
||||
---
|
||||
|
||||
## Summary table
|
||||
|
||||
| Decision | Tier | Cost to change |
|
||||
|-----------------------------------------|----------------|----------------------|
|
||||
| 1. CGAL::Surface_mesh as default mesh | 🔴 load-bearing | ~3 weeks |
|
||||
| 2. Simple_cartesian<double> kernel | 🟡 semi-fixed | per-functional |
|
||||
| 3. Header-only architecture | 🔴 load-bearing | medium (mechanical) |
|
||||
| 4. Five DCE models, separate Maps | 🟡 semi-fixed | ~1 week per new model |
|
||||
| 5. Newton + line search + SparseQR | 🟡 semi-fixed | ~2 days |
|
||||
| 6. Eigen back-end | 🔴 load-bearing | ~2 weeks |
|
||||
| 7. Strategy C (per-functional traits) | 🟡 semi-fixed | ~1 week |
|
||||
| 8. Named-parameter mechanism | 🟢 opportunistic | ~2 days (chaining) |
|
||||
| 9. Property-map name conventions | 🟢 opportunistic | ~1 hour |
|
||||
| 10. GTest, not CGAL test format | 🟡 semi-fixed | ~1 week |
|
||||
| 11. MIT license | 🔴 load-bearing | organisational |
|
||||
| 12. Markdown + Doxygen | 🟢 opportunistic | per-file |
|
||||
|
||||
**Key insight:** the **load-bearing decisions are all good in 2026**.
|
||||
Surface_mesh + Eigen + header-only + MIT are the right defaults for a
|
||||
research-quality CGAL-style package. The **semi-fixed decisions are
|
||||
all behind one concrete blocker** (single user request, CGAL submission
|
||||
commitment, etc.). The **opportunistic decisions are cheap to revisit
|
||||
any time**.
|
||||
|
||||
The architecture is in a good place for the v0.9.0 → v0.10.0 transition.
|
||||
No "expensive corner" has been painted into; every locked decision
|
||||
matches the project's three-goal hierarchy in
|
||||
[`research-track.md`](../roadmap/research-track.md).
|
||||
|
||||
---
|
||||
|
||||
## Open questions for the external reviewer
|
||||
|
||||
Items where the project would benefit from a second opinion:
|
||||
|
||||
1. **Phase 9c (4g-polygon) algorithm choice.** Two routes:
|
||||
* Port the Java `FundamentalPolygonUtility` + `CanonicalFormUtility`
|
||||
literally (~2 weeks).
|
||||
* Or: re-derive from Springborn 2020 §5 using the existing
|
||||
`cut_graph.hpp` + holonomy infrastructure (~3 weeks, cleaner
|
||||
architecture).
|
||||
Which is preferred? See [`phases.md`](../roadmap/phases.md) §Phase 9c.
|
||||
|
||||
2. **Phase 10a (forms) priorities.** Three sub-items
|
||||
(`DiscreteHarmonicFormUtility`, `DiscreteHolomorphicFormUtility`,
|
||||
`CanonicalBasisUtility`) interlock. Which to start with?
|
||||
|
||||
3. **Analytic Hessian payoff.** The Schläfli-based analytic HyperIdeal
|
||||
Hessian (Phase 9b-analytic — derivation already written:
|
||||
[`hyperideal-hessian-derivation.md`](../math/hyperideal-hessian-derivation.md))
|
||||
would add another ~6× over block-FD. Is that worth ~2 weeks of
|
||||
implementation effort for a working-mesh size on which?
|
||||
|
||||
4. **CGAL upstream vs independent distribution.** Does the reviewer
|
||||
know a CGAL editor / has personal opinion on the LGPL-vs-MIT
|
||||
trade-off?
|
||||
|
||||
5. **geometry-central cross-validation (GC-1).** Two libraries solve
|
||||
the same DCE problem from different algorithmic directions
|
||||
(Newton-on-mesh vs Ptolemaic-flips-on-intrinsic-triangulation). An
|
||||
independent comparison would be a nice paper. Interested?
|
||||
Reference in New Issue
Block a user