Skip to content
11 changes: 11 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ binary(=proptest_delaunay_triangulation)
'''
slow-timeout = { period = "60s", terminate-after = 1 }

[[profile.ci.overrides]]
# This regression deliberately compiles and runs a standalone downstream crate
# in a separate target directory so Cargo feature unification cannot mask the
# serde_json configuration under test. A clean dependency build can cross the
# normal per-test watchdog even though the fixture itself is small.
filter = '''
binary(=checkpoint_downstream)
& test(=downstream_json_without_float_roundtrip_preserves_checkpoint_bits)
'''
slow-timeout = { period = "120s", terminate-after = 1 }

[[profile.ci.overrides]]
# Complete 5D exact SoS expansion can cross the default budget on hosted
# runners. Keep the exception limited to the deterministic co-spherical
Expand Down
56 changes: 56 additions & 0 deletions Cargo.lock

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

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ rustdoc-args = [ "--cfg", "docsrs" ]

[dependencies]
allocation-counter = { version = "0.8.1", optional = true } # for memory profiling
ciborium = { version = "0.2.2", default-features = false }
clap = { version = "4.6.6", features = [ "derive" ], optional = true }
la-stack = { version = "0.4.5", default-features = false, features = [ "exact" ] }
tracing = { version = "0.1.44", default-features = false, features = [ "std" ] }
Expand All @@ -67,6 +68,7 @@ ordered-float = { version = "5.5.0", features = [ "serde" ] }
rand = "0.10.2"
serde = { version = "1.0.229", features = [ "derive" ] }
serde_json = { version = "1.0.151", optional = true }
sha2 = { version = "0.11.0", default-features = false }
same-file = { version = "1.0.6", optional = true }
slotmap = { version = "1.1.1", features = [ "serde" ] }
thiserror = "2.0.20"
Expand Down Expand Up @@ -181,6 +183,11 @@ name = "topology_guarantee_construction"
path = "benches/topology_guarantee_construction.rs"
harness = false

[[bench]]
name = "checkpoint_serialization"
path = "benches/checkpoint_serialization.rs"
harness = false

[lints.rust]
# Per RFC 3389: lint groups must use a lower (more negative) priority so specific
# lints at default priority 0 deterministically override the group level.
Expand Down
9 changes: 9 additions & 0 deletions benches/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ allocation checks, or targeted diagnostics.
| Benchmark | Purpose | Scale | Typical Runtime | Used By |
|-----------|---------|-------|-----------------|---------|
| `allocation_hot_paths.rs` | Bootstrap/insert/query/barycenter allocations | Calibrated 2D-5D canaries | ~1-2 min | Manual allocation checks |
| `checkpoint_serialization.rs` | Manifest and JSON checkpoint write/load | 64-vertex 2D owner | <1 min | Checkpoint tuning |
| `ci_performance_suite.rs` | Public workflow regression contract | Calibrated 2D-5D canaries | ~5-10 min | CI, baselines, `just perf-no-regressions` |
| `circumsphere_containment.rs` | Circumsphere predicates and solves | 2D-5D predicates, 3D LU/exact solves | ~5 min | Predicate/circumcenter tuning |
| `cold_path_predicates.rs` | Track predicate paths | Hot, centered, and certified exact cases in 2D-5D | ~2-5 min | Predicate tuning |
Expand All @@ -186,6 +187,7 @@ allocation checks, or targeted diagnostics.
| Use Case | Command |
|----------|---------|
| Final local invariant validation gate | `just ci` |
| Measure checkpoint manifest and JSON write/load paths | `cargo bench --bench checkpoint_serialization --features bench -- --noplot` |
| Quick local large-scale wall-clock guard | `just perf-large-scale-smoke` |
| Fast local PR performance guard with cached same-machine main baseline | `just perf-no-regressions` |
| Compare current branch against a local release/ref baseline | `just perf-vs-ref v0.7.8` |
Expand Down Expand Up @@ -223,6 +225,13 @@ allocation checks, or targeted diagnostics.
| Isolated 32k-vertex 2D Level 4 acceptance run | `DELAUNAY_LARGE_DEBUG_VALIDATION=realization just debug-large-scale-2d 32000` |
| Deep profiling | `cargo bench --profile perf --bench profiling_suite --features count-allocations` |

The checkpoint benchmark uses one deterministic 2D owner because its purpose is
to isolate digest construction and codec overhead, whose dimension-generic
implementation is shared across dimensions. Correctness and exact replay remain
covered separately by the 2D–5D checkpoint tests; expanding the timed matrix
would primarily repeat topology construction cost rather than sharpen this
serialization signal.

## Profiles And Local Guards

Benchmarks that publish or compare performance data use Cargo's `perf` profile:
Expand Down
72 changes: 72 additions & 0 deletions benches/checkpoint_serialization.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//! Throughput evidence for scientific checkpoint manifest and wire serialization.

#![forbid(unsafe_code)]

use criterion::{Criterion, Throughput, criterion_group, criterion_main};
use delaunay::prelude::checkpoint::DelaunayCheckpoint;
use delaunay::prelude::construction::{
DelaunayTriangulation, DelaunayTriangulationBuilder, Vertex,
};
use delaunay::prelude::geometry::AdaptiveKernel;
use serde_json::{from_slice, to_vec};
use std::hint::black_box;

#[path = "common/bench_utils.rs"]
mod bench_utils;
use bench_utils::{OrAbort, OrAbortWithContext};

fn representative_triangulation() -> DelaunayTriangulation<AdaptiveKernel<f64>, (), (), 2> {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
let mut vertices = Vec::with_capacity(64);
for row in 0_u32..8 {
for column in 0_u32..8 {
let jitter = f64::from((row * 17 + column * 31) % 11) * 1.0e-4;
vertices.push(
Vertex::try_new([f64::from(column) + jitter, f64::from(row) - jitter]).or_abort(),
);
}
}
DelaunayTriangulationBuilder::new(&vertices)
.build()
.or_abort()
}

fn checkpoint_benches(criterion: &mut Criterion) {
let triangulation = representative_triangulation();
let checkpoint_json = to_vec(&triangulation).or_abort();
let expected_manifest = triangulation.checkpoint_manifest().or_abort();
let checkpoint: DelaunayCheckpoint<(), (), 2> = from_slice(&checkpoint_json).or_abort();
let restored = checkpoint
.try_into_delaunay_with_kernel(AdaptiveKernel::new())
.or_abort();
assert_eq!(restored.checkpoint_manifest().or_abort(), expected_manifest);
restored.validate().or_abort();
restored
.simplices()
.next()
.or_abort("restored checkpoint benchmark must contain a simplex");

let mut group = criterion.benchmark_group("checkpoint_serialization/64_vertices");
group.throughput(Throughput::Elements(64));

group.bench_function("manifest", |bencher| {
bencher.iter(|| black_box(triangulation.checkpoint_manifest()).or_abort());
});
group.bench_function("json", |bencher| {
bencher.iter(|| black_box(to_vec(black_box(&triangulation))).or_abort());
});
group.bench_function("json_load", |bencher| {
bencher.iter(|| {
let checkpoint: DelaunayCheckpoint<(), (), 2> =
from_slice(black_box(&checkpoint_json)).or_abort();
black_box(
checkpoint
.try_into_delaunay_with_kernel(AdaptiveKernel::new())
.or_abort(),
);
});
});
group.finish();
}

criterion_group!(benches, checkpoint_benches);
criterion_main!(benches);
16 changes: 11 additions & 5 deletions docs/architecture/module_map.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ algorithm machinery that operates on it:
completion, orientation repair, incidence updates, and neighbor maintenance.
Higher proof owners delegate storage edits here.
- `tds/snapshot.rs` - persistence boundary from raw codec records into
validated UUID snapshots before hydration allocates fresh slotmap keys.
validated UUID snapshots before hydration allocates fresh slotmap keys, plus
the owner-borrowed Levels 1–2 evidence required for unchecked snapshot
encoding.
- `tds/validation.rs` - Level 2 Combinatorial Consistency validation and adjacency checks.
- `tds/rollback.rs` - canonical TDS snapshot ownership plus the shared
transaction window used by nested proof refinements without duplicate
Expand Down Expand Up @@ -172,8 +174,12 @@ coordinate model/API rather than loosening ordinary `f64` APIs.
an orthogonal core transformation before Levels 1–4 restoration.
- `repair.rs` - Delaunay repair policies, rebuild config, and repair outcomes.
- `serialization.rs` - versioned owner-level persistence that stores the
canonical `Tds` plus topology guarantee, global topology, and validation
policy, then re-proves Levels 3–5 during restoration.
canonical `Tds` as an embedded CBOR byte image plus topology
guarantee, global topology, validation policy, and a versioned scientific
integrity manifest. It owns bounded envelope parsing, typed load/migration
APIs, canonical UUID-ordered streaming hashes with map-only buffering,
owner-bound reuse of Level-3 f-vector/Euler evidence, and independently
replayed D4/D5 Euclidean construction proof before Levels 3–5 restoration.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- `spherical.rs` - bounded `S^2`/`S^3` construction,
realization-validation, and empty-cap Delaunay backend using the topology
space coordinate/metric backend.
Expand All @@ -186,8 +192,8 @@ coordinate model/API rather than loosening ordinary `f64` APIs.
`src/lib.rs` wires public modules, root re-exports, focused preludes, and the
crate-level documentation map. Public workflow modules are exposed directly as
`delaunay::builder`, `delaunay::construction`, `delaunay::flips`,
`delaunay::incremental_builder`, `delaunay::pachner`, `delaunay::repair`,
`delaunay::validation`, and focused preludes rather than through a nested
`delaunay::checkpoint`, `delaunay::incremental_builder`, `delaunay::pachner`,
`delaunay::repair`, `delaunay::validation`, and focused preludes rather than through a nested
`delaunay::delaunay` facade. The physical
location of `flips` and `pachner` under `src/triangulation/` records that these
operations require only the Levels 1–4 owner.
Expand Down
1 change: 1 addition & 0 deletions docs/architecture/prelude_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ they exercise.
| Unified Pachner move workflow / local topology edits | `use delaunay::prelude::pachner::*` |
| Collection aliases and small buffers | `use delaunay::prelude::collections::*` |
| Construct/configure Euclidean, toroidal, or spherical Delaunay triangulations | `use delaunay::prelude::construction::*` |
| Decode, migrate, inspect, and verify versioned Delaunay checkpoints | `use delaunay::prelude::checkpoint::*` |
| Build, validate, query, or repair generic triangulations | `use delaunay::prelude::triangulation::*` |
| Construction telemetry diagnostics | `use delaunay::prelude::diagnostics::*` |
| Export stable simplicial-complex primitives | `use delaunay::prelude::export::*` |
Expand Down
84 changes: 80 additions & 4 deletions docs/construction_and_validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,10 +577,86 @@ requires a full-dimensional simplex and an atomic cumulative audit.

The serde boundaries mirror those proof owners. Serializing `Tds` produces a
Levels 1–2 snapshot. Serializing `DelaunayTriangulation` produces a versioned
owner checkpoint containing that TDS plus its topology guarantee, global
topology, and validation policy. Loading the owner checkpoint reparses topology
metadata and re-proves Levels 3–5; a legacy TDS-only payload is rejected instead
of silently acquiring default higher-layer context.
schema-v2 owner checkpoint containing that TDS, its proof context, and a
scientific integrity manifest. Loading the owner checkpoint verifies the
manifest, reparses topology metadata, and re-proves Levels 3–5; a legacy
TDS-only payload is rejected instead of silently acquiring default higher-layer
context. Ordinary schema-v2 loading rejects schema-v1 owners explicitly because
they have no integrity manifest. The dedicated
`DelaunayCheckpointV1<U, V, D>` compatibility loader is the executable migration
bridge: deserialize the legacy owner, call `try_into_delaunay` (or
`try_into_delaunay_with_kernel`), then serialize the returned validated owner to
emit schema v2. Legacy outer codecs may already have rounded coordinates or
collapsed payload states, and schema v1 cannot retrospectively supply integrity
evidence.

### Owner checkpoint manifests

`DelaunayTriangulation::checkpoint_manifest` recomputes the evidence embedded
by serialization. It is not an authoritative mutable cache. The manifest stores
the compile-time dimension, the complete Level-3 f-vector, its alternating-sum
Euler characteristic, and a versioned SHA-256 digest. Every field remains
untrusted on load. In particular, no manifest value selects a proof path,
reconstructs topology, or suppresses Levels 1–5 validation.
Downstream tooling can call
`DelaunayTriangulation::verify_checkpoint_manifest` for typed integrity
failures against an existing owner; this evidence check does not replace
`DelaunayTriangulation::validate`.

Digest version 1 hashes a crate-defined canonical byte representation rather
than bytes from the outer serde codec. The representation includes:

- checkpoint schema, digest-representation version, and compile-time dimension;
- topology guarantee, global-topology variant, exact toroidal period bits and
construction mode when periodic, and validation policy;
- every vertex UUID, exact `f64::to_bits` coordinate, and serialized user
payload value;
- every simplex UUID and serialized user payload value, its ordered vertex-UUID
slots, ordered neighbor-UUID slots, and ordered periodic offsets.

Vertex and simplex records are sorted by UUID. The canonical payload serializer
streams scalar, declared-length sequence, tuple, and enum events directly into
the digest. It buffers only map key/value fragments needed for deterministic
sorting and rejects duplicate canonical map keys rather than accepting a
last-key-wins representation. Slotmap keys, hash-map iteration, snapshot record
order, and codec map order therefore do not affect the digest. Local simplex
slots remain ordered because vertex, neighbor, and periodic-offset positions
have aligned semantics. Ephemeral locate hints, spatial indexes, cached reports,
insertion scheduling state, and the derived manifest fields themselves do not
participate.

Schema v2 embeds the validated TDS as a CBOR byte image. JSON and
other outer codecs therefore transport exact coordinate bits without parsing
coordinate decimals and do not require downstream `serde_json/float_roundtrip`
feature unification. The envelope's toroidal periods are likewise stored as
`f64::to_bits` integers. Present user payloads whose Serde value contains a
null/unit state are rejected because the current canonical payload model cannot
distinguish `None`, `Some(())`, and unit injectively; absent payloads remain
supported. Custom sequences must declare their length so the streaming digest
can encode the collection prefix without buffering the entire value.

The f-vector records the face count in every dimension for the current
subdivision; it does not uniquely identify that subdivision. A valid bistellar
move may therefore change both the f-vector and digest. The
Euler characteristic is the alternating-sum PL invariant and is preserved by
such a move. Neither value alone is a checksum or proof of topology, and
different topology can share the same f-vector. Restoration verifies the digest,
recomputes the f-vector through the same periodic-aware Level-3 implementation,
binds those metrics to the restored TDS identity and generation, checks manifest
Euler consistency from that single proof pass, and still validates through
Level 5.

Deserialize `DelaunayCheckpoint<U, V, D>` first when tooling needs typed load
failures. `try_into_delaunay` restores `RobustKernel<f64>`;
`try_into_delaunay_with_kernel` restores the same TDS, topology guarantee,
global topology, and validation policy with a caller-supplied exact kernel.
The direct `Deserialize` implementation for `DelaunayTriangulation` is only the
convenience adapter for `RobustKernel<f64>` and necessarily converts domain
failures into the outer codec's error type. In D >= 4, multi-simplex Euclidean
PL-manifold restoration independently replays point construction and compares
the exact vertex/coordinate and maximal-cell signatures before reattaching
construction provenance. Periodic D >= 4 restoration remains a typed rejection
until trusted periodic construction is available in those dimensions.

### Methods

Expand Down
Loading
Loading