Skip to main content

Fast N-dimensional Delaunay triangulation with incremental point insertion

Project description

adaptive-triangulation

PyPI version Python versions CI License

Fast N-dimensional Delaunay triangulation in Rust with Python bindings (PyO3). Drop-in replacement for adaptive's Triangulation class — 30-300× faster standalone, ~3.3× end-to-end in LearnerND (where adaptive's own Python code dominates). Used automatically by adaptive ≥ 1.5 when installed.

Performance

Measured with the scripts in examples/ against adaptive 1.5.0, best of 3. Absolute times are machine-dependent; the ratios are representative.

Standalone triangulation (incremental insertion)

Case Rust Python Speedup
2D, 1K pts 18 ms 730 ms 41×
2D, 5K pts 138 ms 14,442 ms 105×
3D, 500 pts 32 ms 3,037 ms 94×
3D, 2K pts 150 ms 44,531 ms 297×

LearnerND integration (end-to-end, ring_of_fire 2D)

N pts Learner2D (scipy) LearnerND (Python) LearnerND (Rust)
1,000 0.23 s 0.50 s 0.16 s
2,000 0.90 s 1.01 s 0.32 s
5,000 5.69 s 2.57 s 0.79 s

LearnerND + Rust is 3.3× 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.

Batched LearnerND APIs (not yet wired into adaptive)

simplices_containing and default_loss move two of the remaining LearnerND Python hot loops into Rust. Wired in the way a future adaptive release would use them (examples/learnernd_batched_apis.py), they add 1.17× (2D, 3000 pts) to 1.40× (3D, 1500 pts) on top of the table above, while sampling identical points.

Installation

pip install adaptive-triangulation

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

Quick start

from adaptive_triangulation import Triangulation

# Build a 2D triangulation
tri = Triangulation([(0, 0), (1, 0), (0, 1), (1, 1)])

# Insert points incrementally (Bowyer-Watson)
deleted, added = tri.add_point((0.5, 0.5))

# Query properties
print(len(tri.simplices))     # number of triangles
print(tri.dim)                # 2
print(tri.reference_invariant())  # True

Usage with adaptive's LearnerND

Since adaptive 1.5.0 this package is detected and used automatically — no code changes needed:

pip install "adaptive[rust]"

Per learner, the backend can be selected explicitly with LearnerND(..., triangulation_backend="auto" | "python" | "rust"), or globally with the ADAPTIVE_TRIANGULATION_BACKEND environment variable.

For adaptive < 1.5.0, monkey-patch the module instead:

import adaptive_triangulation as at
from adaptive.learner import learnerND as lnd_mod
from adaptive.learner.learnerND import LearnerND

# Replace both the class and standalone functions
lnd_mod.Triangulation = at.Triangulation
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 — including neighbor-aware losses
# like curvature_loss_function()
learner = LearnerND(my_function, bounds=[(-1, 1), (-1, 1)])

See examples/adaptive_learnernd.py for a full working example with timing comparison.

API

Triangulation class

tri = Triangulation(coords)           # Build from initial points
tri.add_point(point)                   # Incremental insertion → (deleted, added)
tri.locate_point(point)                # Find containing simplex
tri.circumscribed_circle(simplex)      # → (center, radius)
tri.volume(simplex)                    # Simplex volume
tri.volumes()                          # All simplex volumes
tri.point_in_simplex(point, simplex)   # Containment test
tri.simplices_containing(point)        # All simplices containing a point, in one call
                                       # instead of a point_in_simplex loop; pass a known
                                       # containing simplex via simplex=... to skip the
                                       # locate step, or restrict with candidates=...
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

Properties: vertices, simplices, vertex_to_simplices, hull, dim, default_transform

Standalone functions

from adaptive_triangulation import (
    circumsphere,              # General circumsphere
    fast_2d_circumcircle,      # Optimized 2D
    fast_3d_circumsphere,      # Optimized 3D
    point_in_simplex,          # Containment test
    volume,                    # Simplex volume
    simplex_volume_in_embedding,  # Volume in embedding space
    default_loss,              # LearnerND's default loss (embedded simplex volume),
                               # signature-compatible with loss_per_simplex
    orientation,               # Face orientation
)

Examples

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

Development

# Build (requires Rust toolchain)
pip install maturin
maturin develop --release

# Tests
cargo test                    # Rust tests
python -m pytest tests/ -v    # Python tests

# Linting
pre-commit run --all-files    # ruff, mypy, cargo fmt, cargo clippy

License

BSD-3-Clause

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

adaptive_triangulation-0.3.0.tar.gz (106.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

adaptive_triangulation-0.3.0-cp310-abi3-win_amd64.whl (398.2 kB view details)

Uploaded CPython 3.10+Windows x86-64

adaptive_triangulation-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (461.9 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

adaptive_triangulation-0.3.0-cp310-abi3-macosx_11_0_arm64.whl (426.0 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file adaptive_triangulation-0.3.0.tar.gz.

File metadata

  • Download URL: adaptive_triangulation-0.3.0.tar.gz
  • Upload date:
  • Size: 106.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for adaptive_triangulation-0.3.0.tar.gz
Algorithm Hash digest
SHA256 2e75ec6effea2a777ea0c3712907f5710deefca1185573078889552a4ec32dbc
MD5 dd3482b3d8fdba7bc16d4083451d530f
BLAKE2b-256 fe3e8a170cdf8aea368bccaf21a79bab9e7accb42bb5212f602b87c1b66a5b4f

See more details on using hashes here.

Provenance

The following attestation bundles were made for adaptive_triangulation-0.3.0.tar.gz:

Publisher: ci.yml on python-adaptive/adaptive-triangulation

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file adaptive_triangulation-0.3.0-cp310-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for adaptive_triangulation-0.3.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 7d9fc9203f70948f8e1e4ee24af44e34be994103de02dcf4a719e701905709b9
MD5 2331cd87ab69141d7920156a33f6e686
BLAKE2b-256 e924d0fd4befa858281b1f613929706ed2ffdd77eb4668a0f8c76d0d9b73942f

See more details on using hashes here.

Provenance

The following attestation bundles were made for adaptive_triangulation-0.3.0-cp310-abi3-win_amd64.whl:

Publisher: ci.yml on python-adaptive/adaptive-triangulation

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file adaptive_triangulation-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for adaptive_triangulation-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 37b18237f11a94306f12f89647cda674996ebc330b0baaaf12928ed13dba355c
MD5 a1fbde452dcf26a68fb98b5ff3acd7a0
BLAKE2b-256 d70256afef42685c457d6f8525ca82cea0aff80fa065b92e5827015ad6584ed0

See more details on using hashes here.

Provenance

The following attestation bundles were made for adaptive_triangulation-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: ci.yml on python-adaptive/adaptive-triangulation

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file adaptive_triangulation-0.3.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for adaptive_triangulation-0.3.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 dfe51197b4db19e196391245a7b58c1463a2536ad4f44500e9eea8e1ddf7796e
MD5 4a9c1000a8f7d1b4a3e4258cb40b2b87
BLAKE2b-256 4dfca0202b0cd4c66d3d3c778c092695e9993b5bbb67b82761ec1a354064adcb

See more details on using hashes here.

Provenance

The following attestation bundles were made for adaptive_triangulation-0.3.0-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: ci.yml on python-adaptive/adaptive-triangulation

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page