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
4,221 changes: 322 additions & 3,899 deletions CHANGELOG.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ cff-version: 1.2.0
message: "If you use this software, please cite it as below."
type: software
title: "delaunay: A d-dimensional Delaunay triangulation library"
version: 0.7.8
version: 0.8.0
doi: 10.5281/zenodo.16931097
date-released: 2026-05-21
date-released: 2026-07-27
url: "https://github.com/acgetchell/delaunay"
repository-code: "https://github.com/acgetchell/delaunay"
authors:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "delaunay"
version = "0.7.8"
version = "0.8.0"
edition = "2024"
rust-version = "1.97.1"
autobins = false
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Choose the path that matches your use case:
Add the crate to your project:

```bash
cargo add delaunay@0.7.8
cargo add delaunay@0.8.0
```

Use `cargo add delaunay` instead if you want Cargo to select the newest published release.
Expand Down
136 changes: 53 additions & 83 deletions benches/PERFORMANCE_RESULTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,49 @@
This file contains performance benchmarks and analysis for the delaunay library.
The results are automatically generated and updated by the benchmark infrastructure.

- **Last Updated**: 2026-05-21 21:04:39 UTC
- **Last Updated**: 2026-07-27 17:07:32 UTC
- **Generated By**: benchmark_utils.py
- **Git Commit**: 3456ca96abeeee5efb2ef413c3eb91d57f3921b4
- **Git Commit**: 1f15b9352456aca9eade44d116cd8d15d3d1ac20
- **Hardware**: Apple M4 Max (16 cores)
- **Memory**: 64.0 GB
- **OS**: macOS
- **Rust**: rustc 1.95.0 (59807616e 2026-04-14)
- **Rust**: rustc 1.97.1 (8bab26f4f 2026-07-14)

## Triangulation Data Structure Performance

### Current Criterion Run Information

- **Date: 2026-05-21 21:01:27 UTC**
- **Git commit: 3456ca96abeeee5efb2ef413c3eb91d57f3921b4**
- **Date: unavailable**
- **Git commit: 1f15b9352456aca9eade44d116cd8d15d3d1ac20**
- **Source: current `target/criterion` construction results**

### 2D Triangulation Performance

| Benchmark ID | Vertices | Time (mean) | Throughput (mean) | Simplices Generated |
|--------------|--------|-------------|-------------------|---------------------|
| `tds_new_2d/tds_new/4000` | 4000 | 1154.959 ms | 3.463 Kelem/s | 7,974 |
| `tds_new_2d/tds_new_adversarial/4000` | 4000 | 1318.191 ms | 3.034 Kelem/s | 7,971 |
| `tds_new_2d/tds_new/4000` | 4000 | 1268.172 ms | 3.154 Kelem/s | N/A |
| `tds_new_2d/tds_new_adversarial/4000` | 4000 | 1352.241 ms | 2.958 Kelem/s | N/A |

### 3D Triangulation Performance

| Benchmark ID | Vertices | Time (mean) | Throughput (mean) | Simplices Generated |
|--------------|--------|-------------|-------------------|---------------------|
| `tds_new_3d/tds_new/750` | 750 | 884.208 ms | 0.848 Kelem/s | 4,663 |
| `tds_new_3d/tds_new_adversarial/750` | 750 | 1123.063 ms | 0.668 Kelem/s | 4,726 |
| `tds_new_3d/tds_new/750` | 750 | 772.417 ms | 0.971 Kelem/s | N/A |
| `tds_new_3d/tds_new_adversarial/750` | 750 | 911.729 ms | 0.823 Kelem/s | N/A |

### 4D Triangulation Performance

| Benchmark ID | Vertices | Time (mean) | Throughput (mean) | Simplices Generated |
|--------------|--------|-------------|-------------------|---------------------|
| `tds_new_4d/tds_new/75` | 75 | 901.804 ms | 0.083 Kelem/s | 1,105 |
| `tds_new_4d/tds_new_adversarial/75` | 75 | 953.070 ms | 0.079 Kelem/s | 1,129 |
| `tds_new_4d/tds_new/75` | 75 | 629.035 ms | 0.119 Kelem/s | N/A |
| `tds_new_4d/tds_new_adversarial/75` | 75 | 664.791 ms | 0.113 Kelem/s | N/A |

### 5D Triangulation Performance

| Benchmark ID | Vertices | Time (mean) | Throughput (mean) | Simplices Generated |
|--------------|--------|-------------|-------------------|---------------------|
| `tds_new_5d/tds_new/25` | 25 | 997.657 ms | 0.025 Kelem/s | 421 |
| `tds_new_5d/tds_new_adversarial/25` | 25 | 871.978 ms | 0.029 Kelem/s | 410 |
| `tds_new_5d/tds_new/25` | 25 | 732.645 ms | 0.034 Kelem/s | N/A |
| `tds_new_5d/tds_new_adversarial/25` | 25 | 628.691 ms | 0.040 Kelem/s | N/A |

## Performance Results Summary

Expand All @@ -57,125 +57,95 @@ boundary traversal, and explicit bistellar flip roundtrips.

#### Construction

Public API: `DelaunayTriangulation::builder(...).construction_options(...).build()`
Public API: `DelaunayTriangulation::new_with_options`

| Benchmark ID | Dimension | Input | Variant | Mean | 95% CI |
|--------------|-----------|-------|---------|------|--------|
| `tds_new_2d/tds_new/4000` | 2D | 4000 | well-conditioned | 1.155 s | 1.154 s - 1.156 s |
| `tds_new_2d/tds_new_adversarial/4000` | 2D | 4000 | adversarial | 1.318 s | 1.314 s - 1.325 s |
| `tds_new_3d/tds_new/750` | 3D | 750 | well-conditioned | 884.208 ms | 882.600 ms - 886.255 ms |
| `tds_new_3d/tds_new_adversarial/750` | 3D | 750 | adversarial | 1.123 s | 1.121 s - 1.125 s |
| `tds_new_4d/tds_new/75` | 4D | 75 | well-conditioned | 901.804 ms | 889.885 ms - 916.534 ms |
| `tds_new_4d/tds_new_adversarial/75` | 4D | 75 | adversarial | 953.070 ms | 948.482 ms - 957.962 ms |
| `tds_new_5d/tds_new/25` | 5D | 25 | well-conditioned | 997.657 ms | 982.027 ms - 1.025 s |
| `tds_new_5d/tds_new_adversarial/25` | 5D | 25 | adversarial | 871.978 ms | 868.052 ms - 877.029 ms |
| `tds_new_2d/tds_new/4000` | 2D | 4000 | well-conditioned | 1.268 s | 1.263 s - 1.275 s |
| `tds_new_2d/tds_new_adversarial/4000` | 2D | 4000 | adversarial | 1.352 s | 1.348 s - 1.357 s |
| `tds_new_3d/tds_new/750` | 3D | 750 | well-conditioned | 772.417 ms | 771.435 ms - 773.388 ms |
| `tds_new_3d/tds_new_adversarial/750` | 3D | 750 | adversarial | 911.729 ms | 910.397 ms - 912.878 ms |
| `tds_new_4d/tds_new/75` | 4D | 75 | well-conditioned | 629.035 ms | 626.526 ms - 631.668 ms |
| `tds_new_4d/tds_new_adversarial/75` | 4D | 75 | adversarial | 664.791 ms | 662.769 ms - 666.761 ms |
| `tds_new_5d/tds_new/25` | 5D | 25 | well-conditioned | 732.645 ms | 724.367 ms - 742.284 ms |
| `tds_new_5d/tds_new_adversarial/25` | 5D | 25 | adversarial | 628.691 ms | 623.966 ms - 633.918 ms |

#### Boundary facets

Public API: `DelaunayTriangulation::boundary_facets`

| Benchmark ID | Dimension | Input | Variant | Mean | 95% CI |
|--------------|-----------|-------|---------|------|--------|
| `boundary_facets/boundary_facets_2d/4000` | 2D | 4000 | well-conditioned | 1.694 ms | 1.690 ms - 1.698 ms |
| `boundary_facets/boundary_facets_2d_adversarial/4000` | 2D | 4000 | adversarial | 1.694 ms | 1.690 ms - 1.698 ms |
| `boundary_facets/boundary_facets_3d/750` | 3D | 750 | well-conditioned | 1.492 ms | 1.489 ms - 1.496 ms |
| `boundary_facets/boundary_facets_3d_adversarial/750` | 3D | 750 | adversarial | 1.511 ms | 1.505 ms - 1.518 ms |
| `boundary_facets/boundary_facets_4d/75` | 4D | 75 | well-conditioned | 498.7 µs | 493.5 µs - 504.7 µs |
| `boundary_facets/boundary_facets_4d_adversarial/75` | 4D | 75 | adversarial | 503.2 µs | 499.1 µs - 507.4 µs |
| `boundary_facets/boundary_facets_5d/25` | 5D | 25 | well-conditioned | 241.6 µs | 239.1 µs - 244.0 µs |
| `boundary_facets/boundary_facets_5d_adversarial/25` | 5D | 25 | adversarial | 234.2 µs | 230.7 µs - 238.1 µs |
| `boundary_facets/boundary_facets_2d/4000` | 2D | 4000 | well-conditioned | 1.764 ms | 1.756 ms - 1.774 ms |
| `boundary_facets/boundary_facets_2d_adversarial/4000` | 2D | 4000 | adversarial | 1.780 ms | 1.767 ms - 1.792 ms |
| `boundary_facets/boundary_facets_3d/750` | 3D | 750 | well-conditioned | 1.591 ms | 1.583 ms - 1.600 ms |
| `boundary_facets/boundary_facets_3d_adversarial/750` | 3D | 750 | adversarial | 1.614 ms | 1.608 ms - 1.621 ms |
| `boundary_facets/boundary_facets_4d/75` | 4D | 75 | well-conditioned | 564.7 µs | 560.9 µs - 568.7 µs |
| `boundary_facets/boundary_facets_4d_adversarial/75` | 4D | 75 | adversarial | 573.9 µs | 570.1 µs - 578.3 µs |
| `boundary_facets/boundary_facets_5d/25` | 5D | 25 | well-conditioned | 315.2 µs | 312.0 µs - 318.5 µs |
| `boundary_facets/boundary_facets_5d_adversarial/25` | 5D | 25 | adversarial | 297.8 µs | 295.5 µs - 300.1 µs |

#### Convex hull

Public API: `ConvexHull::try_from_triangulation`
Public API: `ConvexHull::from_triangulation`

| Benchmark ID | Dimension | Input | Variant | Mean | 95% CI |
|--------------|-----------|-------|---------|------|--------|
| `convex_hull/from_triangulation_2d/4000` | 2D | 4000 | well-conditioned | 1.700 ms | 1.698 ms - 1.703 ms |
| `convex_hull/from_triangulation_2d_adversarial/4000` | 2D | 4000 | adversarial | 1.694 ms | 1.690 ms - 1.698 ms |
| `convex_hull/from_triangulation_3d/750` | 3D | 750 | well-conditioned | 1.489 ms | 1.485 ms - 1.494 ms |
| `convex_hull/from_triangulation_3d_adversarial/750` | 3D | 750 | adversarial | 1.512 ms | 1.508 ms - 1.517 ms |
| `convex_hull/from_triangulation_4d/75` | 4D | 75 | well-conditioned | 505.6 µs | 501.9 µs - 509.6 µs |
| `convex_hull/from_triangulation_4d_adversarial/75` | 4D | 75 | adversarial | 518.8 µs | 514.0 µs - 523.4 µs |
| `convex_hull/from_triangulation_5d/25` | 5D | 25 | well-conditioned | 242.6 µs | 239.1 µs - 246.7 µs |
| `convex_hull/from_triangulation_5d_adversarial/25` | 5D | 25 | adversarial | 233.8 µs | 231.8 µs - 236.0 µs |
| `convex_hull/from_triangulation_2d/4000` | 2D | 4000 | well-conditioned | 1.764 ms | 1.754 ms - 1.775 ms |
| `convex_hull/from_triangulation_2d_adversarial/4000` | 2D | 4000 | adversarial | 1.822 ms | 1.811 ms - 1.832 ms |
| `convex_hull/from_triangulation_3d/750` | 3D | 750 | well-conditioned | 1.597 ms | 1.588 ms - 1.607 ms |
| `convex_hull/from_triangulation_3d_adversarial/750` | 3D | 750 | adversarial | 1.629 ms | 1.614 ms - 1.649 ms |
| `convex_hull/from_triangulation_4d/75` | 4D | 75 | well-conditioned | 560.9 µs | 559.5 µs - 562.3 µs |
| `convex_hull/from_triangulation_4d_adversarial/75` | 4D | 75 | adversarial | 582.6 µs | 577.8 µs - 589.0 µs |
| `convex_hull/from_triangulation_5d/25` | 5D | 25 | well-conditioned | 320.2 µs | 317.4 µs - 323.1 µs |
| `convex_hull/from_triangulation_5d_adversarial/25` | 5D | 25 | adversarial | 303.0 µs | 300.2 µs - 305.6 µs |

#### Validation

Public API: `DelaunayTriangulation::validate`

| Benchmark ID | Dimension | Input | Variant | Mean | 95% CI |
|--------------|-----------|-------|---------|------|--------|
| `validation/validate_3d/750` | 3D | 750 | well-conditioned | 34.983 ms | 34.853 ms - 35.141 ms |
| `validation/validate_3d_adversarial/750` | 3D | 750 | adversarial | 45.030 ms | 44.832 ms - 45.262 ms |
| `validation/validate_4d/75` | 4D | 75 | well-conditioned | 70.964 ms | 70.685 ms - 71.272 ms |
| `validation/validate_4d_adversarial/75` | 4D | 75 | adversarial | 68.111 ms | 67.652 ms - 68.595 ms |
| `validation/validate_5d/25` | 5D | 25 | well-conditioned | 62.154 ms | 62.044 ms - 62.266 ms |
| `validation/validate_5d_adversarial/25` | 5D | 25 | adversarial | 58.031 ms | 57.786 ms - 58.297 ms |

#### Incremental insert

Public API: `DelaunayTriangulation::insert_vertex`

| Benchmark ID | Dimension | Input | Variant | Mean | 95% CI |
|--------------|-----------|-------|---------|------|--------|
| `incremental_insert/insert_2d/10` | 2D | 10 | well-conditioned | 4.551 ms | 4.537 ms - 4.569 ms |
| `incremental_insert/insert_2d_adversarial/10` | 2D | 10 | adversarial | 7.049 ms | 6.915 ms - 7.241 ms |
| `incremental_insert/insert_3d/10` | 3D | 10 | well-conditioned | 6.189 ms | 6.151 ms - 6.233 ms |
| `incremental_insert/insert_3d_adversarial/10` | 3D | 10 | adversarial | 45.273 ms | 45.073 ms - 45.459 ms |
| `incremental_insert/insert_4d/6` | 4D | 6 | well-conditioned | 47.436 ms | 46.955 ms - 47.968 ms |
| `incremental_insert/insert_4d_adversarial/6` | 4D | 6 | adversarial | 145.440 ms | 144.901 ms - 146.033 ms |
| `incremental_insert/insert_5d/4` | 5D | 4 | well-conditioned | 619.248 ms | 615.715 ms - 622.850 ms |
| `incremental_insert/insert_5d_adversarial/4` | 5D | 4 | adversarial | 635.775 ms | 634.427 ms - 637.273 ms |

#### Bistellar flips

Public API: `BistellarFlips`

| Benchmark ID | Dimension | Input | Variant | Mean | 95% CI |
|--------------|-----------|-------|---------|------|--------|
| `bistellar_flips_4d/k1_roundtrip` | 4D | roundtrip | well-conditioned | 189.6 µs | 187.8 µs - 191.1 µs |
| `bistellar_flips_4d/k2_roundtrip` | 4D | roundtrip | well-conditioned | 186.0 µs | 184.4 µs - 187.9 µs |
| `bistellar_flips_4d/k3_roundtrip` | 4D | roundtrip | well-conditioned | 186.5 µs | 185.0 µs - 187.8 µs |
| `validation/validate_3d/750` | 3D | 750 | well-conditioned | 8.723 s | 8.706 s - 8.741 s |

### Circumsphere Predicate Performance

This focused predicate suite tracks `la-stack`-backed circumsphere and
insphere query performance independently from full triangulation workflows.

#### Version 0.7.8 Results (2026-05-21)
#### Version 0.8.0 Results (2026-07-27)

#### Single Query Performance (2D)

| Test Case | insphere | insphere_distance | insphere_lifted | Winner |
|-----------|----------|------------------|-----------------|---------|
| Basic 2D | 18 ns | 23 ns | 8 ns | **insphere_lifted** |
| Boundary vertex | 1 ns | 24 ns | 209 ns | **insphere** |
| Far vertex | 17 ns | 24 ns | 8 ns | **insphere_lifted** |
| Basic 2D | 20 ns | 21 ns | 12 ns | **insphere_lifted** |
| Boundary vertex | 1 ns | 20 ns | 93 ns | **insphere** |
| Far vertex | 20 ns | 20 ns | 12 ns | **insphere_lifted** |

#### Single Query Performance (3D)

| Test Case | insphere | insphere_distance | insphere_lifted | Winner |
|-----------|----------|------------------|-----------------|---------|
| Basic 3D | 2.1 µs | 26 ns | 18 ns | **insphere_lifted** |
| Boundary vertex | 1 ns | 26 ns | 436 ns | **insphere** |
| Far vertex | 2.1 µs | 26 ns | 18 ns | **insphere_lifted** |
| Basic 3D | 1.5 µs | 60 ns | 20 ns | **insphere_lifted** |
| Boundary vertex | 1 ns | 43 ns | 167 ns | **insphere** |
| Far vertex | 1.1 µs | 43 ns | 19 ns | **insphere_lifted** |

#### Single Query Performance (4D)

| Test Case | insphere | insphere_distance | insphere_lifted | Winner |
|-----------|----------|------------------|-----------------|---------|
| Basic 4D | 5.3 µs | 56 ns | 2.9 µs | **insphere_distance** |
| Boundary vertex | 2 ns | 54 ns | 1.4 µs | **insphere** |
| Far vertex | 3.3 µs | 54 ns | 1.8 µs | **insphere_distance** |
| Basic 4D | 4.4 µs | 74 ns | 2.4 µs | **insphere_distance** |
| Boundary vertex | 1 ns | 69 ns | 859 ns | **insphere** |
| Far vertex | 1.7 µs | 69 ns | 1.0 µs | **insphere_distance** |

#### Single Query Performance (5D)

| Test Case | insphere | insphere_distance | insphere_lifted | Winner |
|-----------|----------|------------------|-----------------|---------|
| Basic 5D | 8.4 µs | 83 ns | 4.9 µs | **insphere_distance** |
| Boundary vertex | 2 ns | 81 ns | 2.3 µs | **insphere** |
| Far vertex | 5.2 µs | 81 ns | 2.9 µs | **insphere_distance** |
| Basic 5D | 7.2 µs | 115 ns | 4.3 µs | **insphere_distance** |
| Boundary vertex | 1 ns | 115 ns | 1.4 µs | **insphere** |
| Far vertex | 2.8 µs | 114 ns | 1.7 µs | **insphere_distance** |

## Implementation Notes

Expand Down
9 changes: 4 additions & 5 deletions benches/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,10 @@ just bench-compare last

`just bench-latest` runs Delaunay's curated release-signal set:
`ci_performance_suite`, `circumsphere_containment`, `cold_path_predicates`,
`topology_guarantee_construction`, and `locate`. This set covers public
construction and validation workflows, circumsphere/insphere query families,
hot and cold predicate paths, topology guarantee construction, and point
location without pulling in broad profiling or allocation-only runs. Use
`just bench-compare <baseline>` to render
and `locate`. This set covers public construction and validation workflows,
circumsphere/insphere query families, hot and cold predicate paths, and point
location without pulling in broad profiling, allocation-only runs, or the
manual topology policy suite. Use `just bench-compare <baseline>` to render
`target/bench-reports/performance.md` from existing Criterion `new` output and a
saved baseline such as `last` or `v0.7.8`. If the baseline and current
checkouts are easy to confuse, prefer `just perf-local`. Use
Expand Down
Loading
Loading