Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,39 @@
[![License](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](LICENSE)

Fast N-dimensional Delaunay triangulation in Rust with Python bindings (PyO3).
Drop-in replacement for [adaptive](https://github.com/python-adaptive/adaptive)'s `Triangulation` class — **5-99× faster**.
Drop-in replacement for [adaptive](https://github.com/python-adaptive/adaptive)'s `Triangulation` class — **30-300× faster** standalone, **3.7×** end-to-end in `LearnerND` (where adaptive's own Python code dominates).

## Performance

Measured with the scripts in [`examples/`](examples/), best of 3 for the standalone runs.
Absolute times are machine-dependent; the ratios are representative.

### Standalone triangulation (incremental insertion)
| Case | Rust | Python | Speedup |
|---|---:|---:|---:|
| 2D, 1K pts | 38.5 ms | 668 ms | **17×** |
| 2D, 5K pts | 260 ms | 8,547 ms | **33×** |
| 3D, 500 pts | 133 ms | 5,571 ms | **42×** |
| 2D, 1K pts | 18 ms | 731 ms | **40×** |
| 2D, 5K pts | 134 ms | 14,611 ms | **109×** |
| 3D, 500 pts | 32 ms | 3,001 ms | **94×** |
| 3D, 2K pts | 152 ms | 44,262 ms | **291×** |

### LearnerND integration (end-to-end, `ring_of_fire` 2D)
| N pts | Learner2D (scipy) | LearnerND (Python) | LearnerND (Rust) |
|---|---:|---:|---:|
| 1,000 | 0.34 s | 0.91 s | **0.23 s** |
| 2,000 | 1.17 s | 1.80 s | **0.38 s** |
| 5,000 | 6.99 s | 4.57 s | **0.99 s** |
| 1,000 | 0.23 s | 0.59 s | **0.16 s** |
| 2,000 | 0.90 s | 1.16 s | **0.32 s** |
| 5,000 | 5.64 s | 2.95 s | **0.81 s** |

LearnerND + Rust is **5× faster** than LearnerND + Python, and **7× faster** than Learner2D at 5K points.
LearnerND + Rust is **3.7× faster** than LearnerND + Python, and **7× faster** than Learner2D at 5K points.
The end-to-end ratio is smaller than the standalone one because adaptive's own Python-side loss machinery dominates once the triangulation is fast.

## Installation

```bash
pip install adaptive-triangulation
```

Requires a Rust toolchain for building from source. Pre-built wheels are available for common platforms via CI.
Requires a Rust toolchain for building from source.
Pre-built wheels are available for common platforms via CI.

## Quick start

Expand Down Expand Up @@ -67,7 +73,8 @@ lnd_mod.circumsphere = at.circumsphere
lnd_mod.simplex_volume_in_embedding = at.simplex_volume_in_embedding
lnd_mod.point_in_simplex = at.point_in_simplex

# Now use LearnerND as normal — it's 5× faster
# Now use LearnerND as normal — including neighbor-aware losses
# like curvature_loss_function()
learner = LearnerND(my_function, bounds=[(-1, 1), (-1, 1)])
```

Expand All @@ -87,6 +94,8 @@ tri.volumes() # All simplex volumes
tri.point_in_simplex(point, simplex) # Containment test
tri.point_in_circumcircle(pt, simplex) # Circumcircle test
tri.bowyer_watson(pt_index) # Direct Bowyer-Watson
tri.get_opposing_vertices(simplex) # Facet neighbours' opposite vertices
tri.get_simplices_attached_to_points(simplex) # Facet-sharing neighbours
tri.reference_invariant() # Consistency check
```

Expand All @@ -112,6 +121,13 @@ from adaptive_triangulation import (
- [`examples/adaptive_learnernd.py`](examples/adaptive_learnernd.py) — LearnerND integration with timing
- [`examples/benchmark_vs_python.py`](examples/benchmark_vs_python.py) — Standalone benchmarks across dimensions

## Robustness on degenerate input

Point sets that mix widely separated coordinate scales force sliver simplices that no floating-point predicate can handle reliably.
Unlike the Python reference (which can corrupt its state on such input), this implementation validates every insertion before mutating: a cavity that cannot be re-triangulated is first repaired with exact predicates (Shewchuk's, via the [`robust`](https://crates.io/crates/robust) crate), and if even that fails the insertion raises with the triangulation untouched, so callers can skip the point and continue.
Well-conditioned inputs behave identically to the reference.
The full policy is documented in [`src/tolerances.rs`](src/tolerances.rs).

## Development

```bash
Expand Down
3 changes: 2 additions & 1 deletion examples/adaptive_learnernd.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""Using adaptive-triangulation with adaptive's LearnerND.

Drop-in replacement for adaptive's built-in Triangulation class,
providing 5× speedup for LearnerND and 7× vs Learner2D at 5K points.
providing ~3.7× end-to-end speedup for LearnerND (and ~7× vs Learner2D
at 5K points); the triangulation work itself is 30-300× faster.

Requirements:
pip install adaptive adaptive-triangulation
Expand Down
Loading