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

134 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```bash
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.