Hackability meeting-prep: 5 docs + pipe-chaining + tests (250 -> 257) #15
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/hackability-meeting-prep"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Maximises the Hackability axis (Goal B from the three-goal hierarchy) ahead of the 2026-05-26 external review by a Springborn-Bobenko PhD alumnus.
This PR bundles seven commits (3 from PR #13, 2 from PR #14, 2 new) — cleanest review path is to land this single PR and close #13/#14 retrospectively, or merge #13 + #14 first and rebase this on top.
Deliverables
5 new documents (~2 250 lines total)
add-inversive-distance.md.output_uv_mapnamed parameter, including the new pipe-chaining syntax.Pipe-operator chaining for named parameters
operator|lives innamespace CGALso ADL finds it forNamed_function_parametersoperands. Achieves the same composition semantics as CGAL's canonical.a().b().c()without modifying the vendored CGAL upstream. 2 new tests verify both 2-param and 3-param chains.Bundled from earlier branches (already on PR #13 / #14)
doc/api/tests.md= single source of truth, qualitative phrasing elsewhere)scripts/check-test-counts.shCI gatedoc/release-policy.md— SemVer rules + release process + recovery from failure modesoutput_uv_mapnamed parameter for Euclidean / Spherical / HyperIdealTest count progression
Verification
cmake --build build-cgal --target conformallab_cgal_tests— clean.ctest— 257/257 PASSED, 0 SKIPPED.bash scripts/check-test-counts.sh— OK (non-CGAL: 23, CGAL: 234, total: 257).operator|(pure addition).What is intentionally NOT in this PR
p2_utility, internalperiod_matrixhelpers) where block-comment-above suffices. Postponed to Phase 9c PR..member()chaining via macro patching of vendored CGAL. The|operator is the maintainable equivalent;.member()would be ~2 days IF we ever fork CGAL upstream (we don't).output_circle_map+ Inversive-Distanceinversive_distance_layout. Documented as wishlist in the output_uv_map tutorial §7.Why one large PR
All items target the same audience (the external reviewer) and the same goal (maximise hackability before the meeting). Splitting into 7 PRs would force the reviewer to read 7 cover letters describing the same context. Bisectability is preserved via the 7 underlying commits.
Two complementary improvements aimed at reducing recurring maintenance overhead: 1. **Test-count centralisation** — `doc/api/tests.md` is now the single source of truth for the test counts. All other docs (README, CLAUDE.md, doc/contributing.md, doc/getting-started.md, doc/math/validation.md, doc/math/validation-protocol.md, scripts/try_it.sh) use qualitative phrasing + a link instead of hardcoded numbers. The previous regime had eight places with "227 CGAL tests, 23 non-CGAL tests" that drifted apart across releases (the v0.9.0 release-prep needed to touch nine files). 2. **Versioning policy** — `doc/release-policy.md` (new, ~250 lines) formalises: * SemVer rules for the pre-1.0 and post-1.0 phases. * Phase-milestone → MINOR-bump mapping (v0.10.0 → Phase 9c, …). * Single-source-of-truth table for moving numbers (test counts, version, date). * Step-by-step release process (the recipe that worked for v0.9.0 after the false-start with PR #11/#12). * Hotfix policy + post-1.0 deprecation policy. * Known failure modes and how to recover from them. Plus a small CI gate: 3. **scripts/check-test-counts.sh** — verifies the totals in doc/api/tests.md match `ctest` output. Re-uses existing build-cgal/ if present. Exit 0 on match, 1 on divergence with recovery hints. Cheap enough (~30 s) to run on every PR. Other cleanups ────────────── * code/tests/cgal/CMakeLists.txt — stale "Test 7 (genus-2 homology) as GTEST_SKIP stub until Phase 8" comment removed; that test landed as HomologyGenerators.Genus2_FourCutEdges in Phase 7. * CLAUDE.md — "test-fast also runs stubs" Known Quirks entry updated to reflect the v0.9.0 stub cleanup (no GTEST_SKIPs remain). * CLAUDE.md doc map — new entry for doc/release-policy.md. Stubs audit ─────────── Zero GTEST_SKIP() calls remain in the codebase as of this commit. The only references to stubs are in historical documentation (CHANGELOG.md v0.7.0 entry, doc/roadmap/* "deferred to research-track" notes) — those are intended. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>Follow-up to the doc-audit: fills the 30 high-priority docstring gaps identified across the public-API headers. Code unchanged — comments only. Headers updated ─────────────── * code/include/cp_euclidean_functional.hpp (5 docstrings added) - setup_cp_euclidean_maps — defaults + naming convention - assign_cp_euclidean_face_dof_indices — gauge-pin semantics - (overload) — first-face convenience - cp_euclidean_dimension — DOF counting (gradient, energy, Hessian, and FD-check were already documented via the header-block comments.) * code/include/inversive_distance_functional.hpp (4 docstrings added) - setup_inversive_distance_maps — defaults + Bowers-Stephenson init note - assign_inversive_distance_vertex_dof_indices — gauge-pin caveat - inversive_distance_dimension — DOF counting - compute_inversive_distance_init_from_mesh — two-phase init + Bowers-Stephenson formula * code/include/euclidean_functional.hpp (4 docstrings added) - setup_euclidean_maps — defaults + naming convention - assign_euclidean_vertex_dof_indices — gauge-pin caveat - assign_euclidean_all_dof_indices — cyclic-functional usage - euclidean_dimension — DOF counting * code/include/spherical_functional.hpp (5 docstrings added) - setup_spherical_maps — defaults + naming convention - assign_vertex_dof_indices — gauge-pin - assign_all_spherical_dof_indices — cyclic-functional usage - spherical_dimension — DOF counting - compute_lambda0_from_mesh — unit-sphere precondition * code/include/hyper_ideal_functional.hpp (3 docstrings added) - setup_hyper_ideal_maps — defaults + cross-functional naming explanation - hyper_ideal_dimension — DOF counting - assign_all_dof_indices — strictly-convex no-gauge usage * code/include/mesh_utils.hpp (3 docstrings added) - cgal_to_eigen — libigl-style (V, F) conversion + side-effect note - simple_visualize_mesh — requires WITH_VIEWER, lifetime - get_vertex_map — zero-copy + lifetime warning File header upgraded to a proper Doxygen file-level comment block. Total: 24 new Doxygen-style docstrings added. Coverage statistics (per the doc-audit) ─────────────────────────────────────── Before: 110 / 154 public symbols documented (71.4%) After: 134 / 154 public symbols documented (87.0%) Remaining gaps (20 entries) cluster in lower-priority utilities (p2_utility.hpp, period_matrix.hpp internal helpers, mesh_builder already has block-comments above each factory). These can be filled in a future PR when the public-API surface for Phase 9c lands. Verification ──────────── * Build: clean (no new compiler warnings). * Tests: 250/250 PASSED, 0 SKIPPED. * scripts/check-test-counts.sh: OK. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>Closes the UX gap identified in the Phase-8b-Lite design discussion: the four classical-DCE CGAL entries now optionally run their `*_layout()` step internally if the caller supplies `CGAL::parameters::output_uv_map(pmap)`. Before this PR, the workflow was: auto res = CGAL::discrete_conformal_map_euclidean(mesh); // ... user has to re-set up maps, re-pin vertex, then ... auto layout = euclidean_layout(mesh, res.x, maps); // ... and copy coordinates into a property map manually. After this PR: auto uv = mesh.add_property_map<Vertex_index, K::Point_2>("uv", ...).first; CGAL::discrete_conformal_map_euclidean( mesh, CGAL::parameters::output_uv_map(uv)); // ... uv now populated for every vertex. Coverage ──────── * `discrete_conformal_map_euclidean` — `Point_2` per vertex. * `discrete_conformal_map_spherical` — `Point_3` per vertex (on S²). * `discrete_conformal_map_hyper_ideal` — `Point_2` per vertex (Poincaré disk). CP-Euclidean and Inversive-Distance entries do not yet support `output_uv_map` — face-based packing has no per-vertex UV concept, and inversive-distance needs a dedicated layout routine that uses Luo's edge-length formula (planned follow-up). New named parameters (`code/include/CGAL/Conformal_map/internal/parameters.h`) ───────────────────────────────────────────────────────────────────────────── * `output_uv_map(pmap)` — write coordinates into pmap after layout. * `normalise_layout(bool)` — apply post-layout canonical normalisation (PCA centroid for Euclidean, north-pole alignment for Spherical, Möbius centring for Hyper-ideal). Both follow the existing Phase-8a-MVP named-parameter convention. Chained syntax (`.output_uv_map(...).normalise_layout(true)`) is not yet supported — pass them one at a time. Tests (5 new in test_cgal_phase8b_lite.cpp) ─────────────────────────────────────────── * `OutputUvMap_Euclidean_PopulatesPmap` — UVs are finite + non-trivial. * `OutputUvMap_Spherical_PopulatesXyz` — every output on unit S². * `OutputUvMap_HyperIdeal_PointsInPoincareDisk` — |p|² ≤ 1 if converged. * `OutputUvMap_Absent_DoesNotRunLayout` — no parameter ⇒ no layout. * `OutputUvMap_NormaliseLayout_TakesEffect` — both raw + norm calls return finite UVs (named-parameter chaining limitation documented). All five pass. Full CGAL suite: 232/232, 0 skipped (was 227). doc/api/tests.md ──────────────── Updated the per-suite table to list the 7 CGAL test suites that landed in v0.9.0 (8a MVP + 9a + 9b + 8b-Lite) but had not yet been added to the canonical table. Total: 227 → 232. This brings the doc back in sync with `ctest` output and unblocks future `scripts/check-test-counts.sh` runs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>Audit found that 2 of the 4 Java-port candidates from the conformal- mapping discussion were missing from the documentation: * StereographicUnwrapper (266 Java LoC) — projects spherical layout S² → ℂ via stereographic projection + Möbius centring. Closes the visualisation gap from discrete_conformal_map_spherical() which currently returns Point_3 on S²; downstream uses typically want a 2-D atlas. Suggested phase: 10b' (alternative methods, parallel to Hyperbolic / Quasi-isothermic). Effort: small (~3 days). * CircleDomainUnwrapper (570 Java LoC) — conformal map of a multiply-connected planar region onto a disk-with-holes (Koebe's general uniformization theorem 1909). A use-case class conformallab++ does not currently cover (annulus, slit torus, fluid flow around obstacles, electrostatics with multiple conductors). Suggested phase: 11c. Effort: large (~2 weeks). Added to all three roadmap documents: * doc/roadmap/java-parity.md — worth-porting table extended * doc/roadmap/research-track.md — Java-backlog summary extended * doc/roadmap/phases.md — Phase 10b' bullet + new Phase 11c block with full math context (Koebe 1909 reference, classical complex-analysis use cases). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>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>Adds `operator|` in `namespace CGAL` so package-local named parameters can be combined left-to-right without modifying CGAL upstream: auto p = CGAL::parameters::gradient_tolerance(1e-12) | CGAL::parameters::max_iterations(500) | CGAL::parameters::output_uv_map(uv); CGAL::discrete_conformal_map_euclidean(mesh, p); Why not the canonical `.a().b().c()` syntax ─────────────────────────────────────────── CGAL's standard chaining mechanism requires registering each named parameter as a member function on `Named_function_parameters` via the `CGAL_add_named_parameter` macro in `CGAL/STL_Extension/internal/parameters_interface.h` — a vendored upstream file that conformallab++ deliberately treats as read-only. Adding member-function chainers for our package-local tags would require either forking CGAL or modifying the vendored copy. Neither is acceptable for a library that wants to remain portable across future CGAL releases. The pipe-operator achieves the same compositional semantics via a free function in `namespace CGAL` (so ADL finds it for `Named_function_parameters` operands). Implementation: rebuild the right-hand-side `Named_function_parameters` with the left-hand-side as its `Base`, producing an indistinguishable chain that every entry function accepts unchanged. Implementation: `code/include/CGAL/Conformal_map/internal/parameters.h` lines 158-187. The operator is constrained to right-hand-sides with `No_property` base (i.e. fresh single-parameter packs from the helper functions), so it never collides with any future CGAL operator on the same type. Tests (2 new, total Phase-8b-Lite suite 15 → 17) ──────────────────────────────────────────────── * CGALPhase8bLite.NamedParamPipe_MultipleParamsTakeEffect Chain three parameters; verify all three take effect (tight tolerance respected + UV pmap populated + iteration cap honoured). * CGALPhase8bLite.NamedParamPipe_TwoParams Chain two parameters; verify max_iterations(0) blocks the loop even when combined with another param. Full CGAL suite: 234/234 PASSED, 0 SKIPPED (was 232). Total: 257/257 PASSED, 0 SKIPPED (was 255). scripts/check-test-counts.sh: OK. Documentation updates ───────────────────── * doc/tutorials/add-output-uv-map.md §3.4: "Current limitation: no chaining" → "Chaining: use the pipe operator `|`". Explains why CGAL's `.member()` syntax isn't available and shows the `|` workaround with a working code example. * doc/architecture/locked-vs-flexible.md §8: chaining now flagged as shipped via pipe; recommended posture says `.member()` chaining only if a user pushes for the CGAL-canonical syntax. * doc/roadmap/porting-status.md §5: API limitations table updated. * doc/api/tests.md: CGALPhase8bLite row 15 → 17, total 232 → 234. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>