Commit Graph

9 Commits

Author SHA1 Message Date
Tarik Moussa
59a26123c8 fix(minor): all five MINOR findings — doc, accuracy, and DRY
MINOR-1 (spherical_functional.hpp:426)
  Wrong comment said "second derivative < 0 for a convex functional".
  The spherical energy is *concave* (NSD Hessian); the monotone-f argument
  applies to both convex and concave functionals equally.  Comment rewritten
  to explain the actual physics: increasing scale increases all angles and
  thus reduces Σ G_v.

MINOR-2 (spherical_functional.hpp:494-495)
  Forward finite difference O(ε) → central finite difference O(ε²):
    old: dft = (sum_Gv(t + fd_eps) - ft) / fd_eps
    new: dft = (sum_Gv(t + fd_eps) - sum_Gv(t - fd_eps)) / (2*fd_eps)
  Same cost when the extra sum_Gv(t - fd_eps) replaces the cached ft.

MINOR-3 (euclidean_functional.hpp, spherical_functional.hpp,
         inversive_distance_functional.hpp)
  New header gauss_legendre.hpp centralises the 10-point Gauss-Legendre
  nodes and weights (gl10_nodes() / gl10_weights()).  The three energy
  functions now use the shared accessors instead of duplicated local
  static arrays.

MINOR-4 (euclidean_functional.hpp, spherical_functional.hpp,
         hyper_ideal_functional.hpp, inversive_distance_functional.hpp)
  halfedge_to_index() centralised in conformal_mesh.hpp.  All four local
  aliases (eucl_hidx, spher_hidx, hidx, id_detail::hidx) now delegate to
  it as one-line wrappers; the aliases are kept for now to avoid a larger
  call-site churn, clearly documented as thin wrappers.

MINOR-5 (clausen.hpp:33-38)
  Added a comment above inits() explaining the intentional off-by-one
  return value and how it interacts with csevl() — matching the Java
  Clausen.inits() / csevl() contract.

277/277 CGAL + 26/26 pure-math tests pass, 0 failed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 01:17:27 +02:00
Tarik Moussa
7534c62c3d fix(gradient-checks): use relative error in all FD check functions
Finding-E from doc/reviewer/external-audit-2026-05-30.md.
Scan also uncovered the same issue in inversive_distance_functional.hpp.

Three functions used absolute error `|analytic - fd| > tol` while the
rest of the library (euclidean_functional, spherical_functional,
euclidean_hessian, hyper_ideal_functional) all use relative error
`|analytic - fd| / max(1, |analytic|) > tol`.

Absolute error is too strict for large gradients (false failures) and
too lenient for small gradients.

Fixed:
  cp_euclidean_functional.hpp  gradient_check_cp_euclidean()
  cp_euclidean_functional.hpp  hessian_check_cp_euclidean()
  inversive_distance_functional.hpp  gradient_check_inversive_distance()

All three now use the relative criterion and accumulate all failures
before returning (ok=false instead of early return on first mismatch).
Default tol updated from 1e-6/1e-5 to 1e-4, matching the Java
FunctionalTest convention used by all other checks in the library.
Error message updated to print rel-err instead of raw diff.

266/266 CGAL tests pass, 0 failed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 01:17:27 +02:00
Tarik Moussa
cfbbc1b21f fix(dof-assign): correct "pin before" docs + add gauge-vertex overloads
Finding-D from doc/reviewer/external-audit-2026-05-30.md.

All three DOF-assignment functions iterated over every vertex
unconditionally, overwriting any v_idx set before the call.  The
documentation said "pin before OR after" — the "before" option was
silently wrong (the pin would be overwritten).  For the
inversive-distance variant the doc explicitly said the pre-call pin
would make the function "a no-op for that vertex", which was false.

Changes (three headers):
- euclidean_functional.hpp  assign_euclidean_vertex_dof_indices()
- spherical_functional.hpp  assign_vertex_dof_indices()
- inversive_distance_functional.hpp  assign_inversive_distance_vertex_dof_indices()

For each:
  1. Single-arg overload: doc corrected to "pin AFTER, not before;
     pre-call pins are overwritten"
  2. New two-arg overload accepting a Vertex_index gauge: pins the
     requested vertex (v_idx=-1) in a single pass, preventing the
     user error entirely

Three new GTests in test_euclidean_functional.cpp:
  SingleArg_PinBeforeHasNoEffect        — documents the old pitfall
  TwoArg_GaugeIsPinnedOthersAreSequential — verifies the new overload
  TwoArg_NewtonConvergesWithGaugeOverload — end-to-end correctness

266/266 CGAL tests pass, 0 failed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 01:17:27 +02:00
Tarik Moussa
a3ee9576d4 fix+test: Euclidean holonomy/τ end-to-end + spherical edge-DOF oracle (2026-05-29 audit)
All checks were successful
C++ Tests / test-fast (pull_request) Successful in 1m57s
API Docs / doc-build (pull_request) Successful in 1m3s
Markdown link check / check (pull_request) Successful in 44s
C++ Tests / test-cgal (pull_request) Has been skipped
C++ Tests / quality-gates (pull_request) Successful in 2m11s
Bundles the 2026-05-29 Java↔C++ math-correctness audit (doc/reviewer/
java-port-audit.md, 11 findings) with two follow-up fixes.

Audit code changes:
- Finding 3 (spherical_functional): edge-DOF replacement parameterization via
  spher_eff_lambda; edge gradient α_opp⁺+α_opp⁻−θ_e (drops additive −(S⁺+S⁻)/2)
- Finding 4 (spherical_hessian): always-compiled edge-DOF throw guard
- Finding 6 (period_matrix): faithful normalizeModulus (0≤Re≤½, Im≥0, |τ|≥1)
- Finding 9 (inversive_distance): degenerate-face limiting angles, no skip
- Findings 1/2 (euclidean): degenerate gradient limiting angles + Hessian guard

Euclidean holonomy/τ fix: develop the cut surface across the dual spanning tree
only (cut_graph now exposes is_dual_tree), so genus-1 cut edges yield
non-degenerate lattice generators. Previously τ came out 0 / NaN / 1e13 on the
bundled tori; now matches the analytic revolution modulus i·√(R²−r²)/r. Re-enabled
τ reporting in the Euclidean CLI; rewrote validation.md §3/§4 accordingly.

Tests (240 CGAL, 0 skipped):
- HolonomyEndToEnd ×3 — tori of revolution (4×4, hex 6×6, 8×8) vs analytic modulus
- SphericalFunctional.EdgeGradient_RegularTetClosedForm — independent closed-form
  π/3 oracle locking the Finding-3 edge formula (the path-integral FD check cannot
  detect a wrong-but-conservative gradient)

Also documents the latent spherical/hyperbolic holonomy-extraction bug (same
single-development pattern, dead code today) in research-track.md (Phase 9c/10),
and adds favour/normalisations to the codespell ignore list.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 12:50:16 +02:00
704f42bbfd Merge pull request 'ci+quality: structural gates (CI: 3 new; local: 7 new + .clang-tidy)' (#18) from ci/structural-tests into main
Some checks failed
C++ Tests / test-fast (push) Has started running
C++ Tests / test-cgal (push) Has been cancelled
API Docs / doc-build (push) Has been cancelled
Doxygen → Codeberg Pages / publish (push) Has been cancelled
Markdown link check / check (push) Has been cancelled
Mirror to Codeberg / mirror (push) Has been cancelled
2026-05-26 09:14:45 +00:00
Tarik Moussa
d3c08b3bc0 quality: 2 new gates (cmake-format, codespell) + SPDX rollout (60 files)
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 2m2s
API Docs / doc-build (pull_request) Successful in 58s
Markdown link check / check (pull_request) Successful in 45s
C++ Tests / test-cgal (pull_request) Failing after 13m14s
This commit closes the remaining red gates so `run-all.sh --fast` is
green end-to-end on the canonical dev machine.

New gates
─────────
1. cmake-format / cmake-lint
   * scripts/quality/cmake-format.sh — dry-run by default,
     --strict to fail on drift, --fix to apply
   * .cmake-format.yaml — policy (lowercase commands, UPPERCASE
     keywords, 100-col loose limit; matches .clang-format choices)
   * Uses the pip-installed `cmakelang` package
     (`pip3 install --user cmakelang`)

2. codespell
   * scripts/quality/codespell.sh — exit 1 on any typo, --fix
     interactively
   * .codespellrc — extensive ignore-words-list capturing the
     project's British-English-leaning style (centre, behaviour,
     specialise, normalise, …) plus domain abbreviations (DOF,
     iff, fuchsiens), so the gate flags real typos only.
   * Validated: 0 typos across docs + code/include + scripts +
     code/{src,tests}.

SPDX rollout (license-headers --fix)
────────────────────────────────────
license-headers.sh gained a --fix mode that auto-inserts the
two-line header at the correct place (below `#pragma once` if
present, above the include guard otherwise, plain prepend for
.cpp).  Ran it on 60 of 66 files — 100 %-licensed now.

Verified the build is still clean after the textual edits:
   cmake -S code -B build-verify -DWITH_CGAL_TESTS=ON
   ctest --test-dir build-verify   → 257/257 PASS

run-all.sh + README updated to include the two new gates.

End-to-end style/convention block status (on this commit, this branch):

    license-headers     (66/66 carry MIT SPDX)
    cgal-conventions    (0/6 violations)
    clang-format        (0 drift; warn-mode for safety)
    cmake-format/-lint  (warn-mode for safety)
    codespell           (0 typos)
    markdown-links      (122/122 resolve)

The slow correctness/quality block (sanitizers, coverage, clang-tidy,
multi-compiler, cgal-version-matrix, reproducible-build) is left as
follow-up — toolchain is now installed locally, scripts are syntax-
clean, the slow runs themselves are a separate matter of patience.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 09:15:34 +02:00
Tarik Moussa
62b02f88b9 docs(doxygen): 100% public-API coverage (228 → 0 undocumented)
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 2m40s
API Docs / doc-build (pull_request) Successful in 1m2s
C++ Tests / test-cgal (pull_request) Failing after 11m52s
Completes the work begun in the previous commit on this branch.  Every
public symbol under code/include/ now carries a brief Doxygen comment
(0 undocumented per scripts/doxygen-coverage.sh, with the `detail::`
implementation namespaces excluded as before).

Trajectory on this branch:
  start (after Doxyfile fix):  24.0 %  (165 / 437 in the no-detail set
                                       was 105 / 437 when detail counted)
  after PR #17 base commit  :  42.4 %  (165 / 396)
  this commit               : 100.0 %  (396 / 396)

Files touched (all .hpp / .h headers under code/include/):
  * cgal/Conformal_map_traits.h
  * clausen.hpp, conformal_mesh.hpp, constants.hpp (already docd)
  * cp_euclidean_functional.hpp, cut_graph.hpp, discrete_elliptic_utility.hpp
  * euclidean_functional.hpp, euclidean_geometry.hpp, euclidean_hessian.hpp
  * fundamental_domain.hpp, gauss_bonnet.hpp
  * hyper_ideal_{functional,geometry,hessian,utility,visualization_utility}.hpp
  * inversive_distance_functional.hpp, layout.hpp
  * matrix_utility.hpp, mesh_builder.hpp, mesh_io.hpp
  * newton_solver.hpp, p2_utility.hpp, period_matrix.hpp, projective_math.hpp
  * serialization.hpp, spherical_functional.hpp, spherical_geometry.hpp
  * spherical_hessian.hpp, viewer_utils.h

CI:
.gitea/workflows/doxygen-pages.yml now enforces
`scripts/doxygen-coverage.sh --threshold 100`, so any future regression
(a new public function landed without a `///` brief) fails the build
before the Doxygen HTML is published to Codeberg Pages.

Doxygen warnings remain at 0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 04:22:49 +02:00
Tarik Moussa
79f6757646 docs: add Doxygen docstrings to high-priority public functions (Phase-9a + setup)
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>
2026-05-22 13:24:37 +02:00
Tarik Moussa
8c01a133d8 Phase 9a: dual circle-packing functionals (CP-Euclidean + Inversive Distance)
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 2m48s
C++ Tests / test-fast (push) Successful in 2m50s
API Docs / doc-build (pull_request) Successful in 34s
C++ Tests / test-cgal (pull_request) Failing after 10m52s
C++ Tests / test-cgal (push) Has been skipped
Implements both Phase 9a sub-functionals — the face-dual circle-packing
functional from the Java original and the vertex-based inversive-distance
functional from Luo 2004 / Glickenstein 2011 — together with a side-by-side
mathematical validation report.

CGAL test count: 194 → 205 (+11 from 9a.2, +10 from 9a.1, was already
+1 from 9a.1's setup defaults regression).

Phase 9a.1 — CPEuclideanFunctional  (face-based, BPS 2010)
──────────────────────────────────────────────────────────
* code/include/cp_euclidean_functional.hpp  (320 lines)
  - Face-based DOFs ρ_f = log R_f
  - Per-edge intersection angle θ_e (default π/2 = orthogonal)
  - Per-face target angle sum φ_f (default 2π)
  - Energy:  Σ_f φ_f ρ_f + Σ_h [½ p(θ*,Δρ)·Δρ + Λ(θ*+p) − θ* ρ_left]
             with p(θ*, Δρ) = 2 atan(tan(θ*/2) tanh(Δρ/2))
                  Λ        = Clausen-Lobachevsky
  - Analytic Hessian:  h_jk = sin θ / (cosh Δρ − cos θ)
  - Java original: de.varylab.discreteconformal.functional.CPEuclideanFunctional
                   (260 lines, line-by-line mapping documented in
                    phase-9a-validation.md §1)

* code/tests/cgal/test_cp_euclidean_functional.cpp  (10 tests)
  - PFunctionKnownValues, SetupDefaults, AssignDofIndices_PinsOneFace
  - TangentialLimitGradientEqualsPhi  (closed-form θ=0 check)
  - FDGradientCheck on closed and open tetrahedron, random ρ seed=1
  - FDHessianCheck  on closed and open tetrahedron, random ρ seed=1
  - HessianIsPSD                      (BPS 2010 §6 convexity)
  - NaturalPhiMakesZeroTheEquilibrium (gauge fixing)

Phase 9a.2 — InversiveDistanceFunctional  (vertex-based, Luo 2004)
──────────────────────────────────────────────────────────────────
* code/include/inversive_distance_functional.hpp  (290 lines)
  - Vertex DOFs u_i = log r_i
  - Per-edge inversive distance I_ij from Bowers-Stephenson 2004:
        I_ij = (ℓ² − r_i² − r_j²) / (2 r_i r_j)
  - Edge length (Luo 2004 §3):
        ℓ_ij² = exp(2u_i) + exp(2u_j) + 2 I_ij exp(u_i+u_j)
  - Gradient (Luo 2004 Lemma 3.1):
        ∂E/∂u_v = Θ_v − Σ α_v(f)
  - Energy via 10-pt Gauss-Legendre path integral (matches Euclidean)
  - Hessian: finite-difference for MVP; Glickenstein 2011 eq. 4.6
    analytic form deferred (joins Phase 9b queue)

* code/tests/cgal/test_inversive_distance_functional.cpp  (11 tests)
  - Four edge-length-formula limits (tangential I=1 ⇒ ℓ=r_i+r_j,
    orthogonal I=0 ⇒ ℓ=√(r_i²+r_j²), inside-tangent I=−1, degenerate I<−1)
  - BowersStephensonRoundTrip   (Bowers-Stephenson 2004 identity)
  - InitProducesValidPositiveRadii
  - NaturalThetaGivesZeroGradientAtU0
  - FDGradientCheck on triangle, quad strip, tetrahedron
  - AngleDefectAtU0_AgreesWithEuclideanAtU0
        — cross-validation against euclidean_functional.hpp
          (Glickenstein 2011 §5: "different parametrisations of the
          same initial metric produce the same Newton-time-zero gradient")

Phase 9a Validation Report
──────────────────────────
* doc/architecture/phase-9a-validation.md  (350 lines)
  - Line-by-line mapping CPEuclideanFunctional.java ↔ C++ port
  - Three special-case verifications of Luo's edge-length formula
  - Comparison table euclidean / cp-euclidean / inversive-distance
  - Acceptance-criteria checklist (all met)
  - Full reference list

Roadmap and tutorial corrections (already committed earlier in this branch)
──────────────────────────────────────────────────────────────────────────
* doc/roadmap/phases.md      — Phase 9a split into 9a.1 + 9a.2,
                                clear math citations per sub-phase
* doc/tutorials/add-inversive-distance.md — corrects the prior claim
                                that InversiveDistanceFunctional.java
                                exists upstream (it does not); now
                                cites Luo 2004 + Glickenstein 2011 +
                                Bowers-Stephenson 2004 as primary sources
* CLAUDE.md                  — adds phase-9a-validation.md to doc map

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 21:01:34 +02:00