# 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 |
| Gauss–Bonnet check | `check_gauss_bonnet()` | O(V) | O(1) | one pass over vertices |
| Gauss–Bonnet 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 3–20 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 10–100ms 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, Erickson–Whittlesey)

| 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.
