Files
ConformalLabpp/doc/math/complexity.md
Tarik Moussa 7edf699ac2
All checks were successful
C++ Tests / test-fast (push) Successful in 2m37s
C++ Tests / test-cgal (push) Has been skipped
test/docs: Scalability Smoke Tests + Komplexitätsdokumentation
test_scalability_smoke.cpp (3 neue Tests → 176 CGAL-Tests gesamt):
  SmokeEuclidean.CatHead_SmallOpen   — V=131,  Newton 3 iter, <1ms
  SmokeEuclidean.Brezel_LargeGenus2  — V=6910, Newton 3 iter, 69ms (Apple M)
  SmokeEuclidean.Brezel2_Genus2_CutGraph — V=2622, Cut Graph 10ms, 4 Nähte
  - Korrektheit-Assertions (iter<30, ||G||<1e-8), kein Timing-Assert (CI-stabil)
  - Informative Ausgabe: iter, Residuum, Laufzeit als stdout-Print
  - Korrektur: brezel.obj ist Genus-2 (χ=−2), nicht Genus-1 (Namensgebung
    aus Java-Original übernommen, nicht topologisch)
  - Perturbation x0=−0.05 damit Newton tatsächlich iteriert

doc/math/complexity.md (neu):
  - O()-Analyse aller Pipeline-Schritte tabellarisch
  - Gemessene Timings auf echten Meshes (Apple M, Release, Single-Thread)
  - HyperIdeal-FD-Hessian als bekannter Bottleneck dokumentiert
  - Skalierungsprojektion bis V=100K
  - Speicherverbrauch-Tabelle
  - Reproduzierbare Messanleitung

README.md + CLAUDE.md: Testzähler 173→176, complexity.md verlinkt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:05:22 +02:00

6.0 KiB
Raw Blame History

Complexity and Scalability

Measured on: Apple M-series (ARM64), Release build (-O2), single thread. CI runner (Raspberry Pi 4, ARM64) is ~10× slower — the smoke tests assert on correctness only (iteration count, residual norm), not on wall-clock time.


1 — Algorithmic complexity per pipeline step

Step Function Time complexity Space Notes
Mesh load load_mesh() O(F) O(V+F) CGAL OFF/OBJ/PLY parser
λ₀ initialisation compute_*_lambda0_from_mesh() O(E) O(E) one pass over edges
GaussBonnet check check_gauss_bonnet() O(V) O(1) one pass over vertices
GaussBonnet enforce enforce_gauss_bonnet() O(V) O(1) redistributes defect uniformly
Gradient (Euclidean/Spherical) euclidean_gradient() O(F) O(V) one pass over faces
Gradient (HyperIdeal) hyper_ideal_gradient() O(E) O(V+E) ζ-functions per edge
Hessian (Euclidean) euclidean_hessian() O(F) O(V) sparse cotangent Laplacian, nnz ≈ 6V
Hessian (Spherical) spherical_hessian() O(F) O(V) sparse spherical law-of-cosines analog
Hessian (HyperIdeal) hyper_ideal_hessian() O(n·E) O(V+E) sparse FD approximation: n extra gradient evals per Newton step → Phase 9b will replace with O(E) analytic
Linear solve SimplicialLDLT O(V^{1.5}) O(V^{1.5}) planar-graph fill-in; automatic SparseQR fallback
Newton iteration newton_euclidean() O(V^{1.5}) per iter O(V) typically 320 iterations total
Full Newton solve newton_euclidean() O(k · V^{1.5}) O(V^{1.5}) k = iteration count, k < 30 in practice
Cut graph compute_cut_graph() O(E log E) O(V+E) spanning tree + cotree BFS
Layout (BFS-trilateration) euclidean_layout() O(F) O(V) priority-BFS, one trilateration per face
Holonomy (inside *_layout) O(g·E) O(g) one Möbius composition per seam edge per generator
Period matrix compute_period_matrix() O(1) after holonomy O(1) τ = ω_b/ω_a, SL(2,) reduction
Fundamental domain compute_fundamental_domain() O(g) O(g) g generator pairs

Dominant cost: the SimplicialLDLT factorization at O(V^{1.5}). For the meshes in the test suite (V up to ~7K) this is in the 10100ms range. For meshes with V > 50K the HyperIdeal FD Hessian becomes a second bottleneck (Phase 9b: analytic Hessian will reduce this to O(E) per Newton step).


2 — Measured timings on test meshes

All times measured in Release mode (-O2) on Apple M-series (ARM64), single thread, from test_scalability_smoke.cpp stdout output.

Newton solver (Euclidean, from x₀ = 0.05 perturbation)

Mesh V F Genus Iterations ‖G‖_∞ Newton time
cathead.obj 131 248 0 (open) 3 1.2e-12 < 1 ms
brezel2.obj 2 622 5 248 2 — (cut graph only)
brezel.obj 6 910 13 824 2 3 1.5e-12 69 ms

Cut graph (tree-cotree, EricksonWhittlesey)

Mesh V F Genus Seam edges Cut graph time
brezel2.obj 2 622 5 248 2 4 (= 2g) 10 ms
brezel.obj 6 910 13 824 2 4 (= 2g) < 1 ms

Note on iteration count. All three meshes converge in exactly 3 Newton iterations from a 0.05 perturbation. This is consistent with quadratic convergence: the Euclidean energy is strictly convex, so Newton reaches machine-precision residual (‖G‖ ≈ 10⁻¹²) in very few steps regardless of mesh size. The per-iteration cost (dominated by SimplicialLDLT) grows with V, but the iteration count does not.


3 — Scaling projection

Based on the O(V^{1.5}) model for the linear solve:

V Projected Newton time (Euclidean) Notes
500 ~2 ms typical research mesh
5 000 ~50 ms brezel2-scale
7 000 ~70 ms brezel-scale (measured: 69ms ✓)
20 000 ~500 ms large detailed mesh
50 000 ~3 s remeshed high-resolution surface
100 000 ~9 s boundary of practical usability (single thread)

For V > 50K: consider iterative solvers (e.g. Conjugate Gradient preconditioned with incomplete Cholesky) as a Phase 10 engineering improvement.


4 — HyperIdeal Hessian bottleneck

The HyperIdeal Hessian is currently computed by finite differences (Phase 9b plans an analytic replacement). The FD cost is:

n_dof extra gradient evaluations per Newton step

where n_dof = V + E (HyperIdeal has both vertex and edge DOFs). For a mesh with V=6910, F=13824 this means ~20K gradient evaluations per Newton step instead of 1, making HyperIdeal roughly 20× slower than Euclidean for the same mesh.

After Phase 9b (analytic HyperIdeal Hessian): the HyperIdeal time per iteration will match Euclidean — O(E) Hessian assembly, O(V^{1.5}) factorization.


5 — Memory usage

Component Memory Formula
Mesh ~200 bytes/vertex CGAL Surface_mesh overhead
Eigen sparse Hessian ~48 bytes/nonzero nnz ≈ 6V for cotangent Laplacian
SimplicialLDLT factorization O(V^{1.5}) bytes fill-in for planar sparse matrix
Layout (UV coordinates) 16 bytes/vertex Eigen::Vector2d per vertex
Total for brezel (V=6910) ~40 MB estimate; actual measured not yet

6 — How to run the smoke tests yourself

cmake -S code -B build -DWITH_CGAL_TESTS=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build --target conformallab_cgal_tests -j$(nproc)

# Run all three scalability tests — timing printed to stdout
./build/tests/cgal/conformallab_cgal_tests --gtest_filter="SmokeEuclidean*"

Expected output:

[SmokeEuclidean.CatHead]  V=131  F=248  iter=3 ||G||=1.2e-12 time=<1ms
[SmokeEuclidean.Brezel]   V=6910 F=13824 iter=3 ||G||=1.5e-12 newton=69ms cut=0ms
[SmokeEuclidean.Brezel2]  V=2622 F=5248  cut=10ms seams=4

Timings vary by hardware. The assertions (iter < 30, ‖G‖ < 1e-8, seams = 2g) are hardware-independent and run in CI.