Skip to content

Changed: Refactors Gram determinant calculation with LDLT#167

Merged
acgetchell merged 6 commits intomainfrom
feat/la-stack-ldlt-factorization
Jan 7, 2026
Merged

Changed: Refactors Gram determinant calculation with LDLT#167
acgetchell merged 6 commits intomainfrom
feat/la-stack-ldlt-factorization

Conversation

@acgetchell
Copy link
Owner

Refactors the Gram determinant calculation to use LDLT factorization from the la-stack crate for improved efficiency and numerical stability by exploiting symmetry.

Also, updates the la-stack dependency version.

Refactors the Gram determinant calculation to use LDLT factorization from the `la-stack` crate for improved efficiency and numerical stability by exploiting symmetry.

Also, updates the `la-stack` dependency version.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

Walkthrough

Adds LDLT-based Gram determinant computation, tightens cavity-boundary extraction with ridge incidence/connectivity checks and new ConflictError variants, temporarily hardens validation during insertion, renames SimplexCounts→FVector across topology APIs, updates tests/examples/tooling, and bumps la-stack dependency.

Changes

Cohort / File(s) Summary
Geometry: Gram & LDLT
src/geometry/util.rs
Add gram_determinant_ldlt using la-stack Matrix/LDLT; replace determinant call sites; add LDLT-aware tests and docs describing clamping/defensive handling.
Boundary extraction & locate
src/core/algorithms/locate.rs
Introduce RidgeInfo bookkeeping, build ridge incidence map and adjacency graph, detect OpenBoundary/RidgeFan/DisconnectedBoundary, and return new ConflictError variants when boundary manifoldness/connectivity fail.
Insertion retry logic
src/core/algorithms/incremental_insertion.rs
Treat new ConflictError variants (DisconnectedBoundary, OpenBoundary) as retryable in InsertionError::is_retryable.
Triangulation validation
src/core/triangulation.rs
Add BoundaryRidgeMultiplicity error and validate_closed_boundary_with_map to ensure boundary ridges are incident to exactly two boundary facets; integrate into Level‑3 is_valid flow and adjust logging.
Triangulation construction
src/core/delaunay_triangulation.rs
Temporarily set ValidationPolicy::DebugOnly during batch construction/insert and restore original policy afterward.
Topology API & counting
src/topology/characteristics/euler.rs, src/topology/characteristics/validation.rs, docs/topology.md
Rename public SimplexCountsFVector; update public APIs, impls, docs, examples, and tests; refactor counting to a vectorized, single-pass per-dimension approach and add insert_simplices_of_size.
Tests & regression data
tests/delaunay_edge_cases.rs, tests/proptest_delaunay_triangulation.proptest-regressions
Add 4D Euler-regression test (duplicated block present) and add a 4-component regression data block for proptest regressions.
Examples: topology output
examples/triangulation_3d_20_points.rs
Print topology/Euler summary (classification, χ, f‑vector) via topology::characteristics::validation.
Build & Tooling Automation
justfile
Major rewrite: centralized _coverage_base_args, new setup/setup-tools, new CI targets, Python lint/typecheck targets, prettier/npx-driven YAML/MD formatting, shell lint/fmt, and expanded validation targets.
Dependency & config
Cargo.toml, cspell.json
Bump la-stack 0.1.1 → 0.1.2 and serde_json; reorganize/expand cspell.json word list (many reordered/duplicated entries).

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant Inserter as Insertion routine
participant Extract as extract_cavity_boundary
participant RidgeMap as RidgeInfo map
participant Adjacency as Boundary adjacency graph

Note over Extract,RidgeMap: Aggregate per-ridge metrics (vertex_count, facet_count, first/second_facet)
Inserter->>Extract: request cavity boundary (conflict_cells)
Extract->>RidgeMap: record ridge metrics for each ridge
RidgeMap-->>Extract: aggregated ridge info
Extract->>Adjacency: connect boundary facets via shared ridges
Adjacency-->>Extract: component count
alt all ridges facet_count == 2 and single component
    Extract-->>Inserter: Ok(CavityBoundaryBuffer)
else ridge facet_count == 1
    Extract-->>Inserter: Err(OpenBoundary { facet_count, ridge_vertex_count })
else ridge facet_count >= 3
    Extract-->>Inserter: Err(RidgeFan)
else multiple components
    Extract-->>Inserter: Err(DisconnectedBoundary { visited, total })
end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
I hopped through matrices, LDLT in paw,
I counted faces, mended ridges I saw.
Tests spun, validators woke in the night—
One tidy patch, and the triangulation’s right.
Carrots for code, and bugs put to flight.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change (refactoring Gram determinant with LDLT), which is confirmed by the PR description and the substantial changes to src/geometry/util.rs involving LDLT-based determinant computation.
Description check ✅ Passed The description relates to the changeset by explaining the Gram determinant refactoring and la-stack dependency update, though it omits significant other changes (topology API refactoring, validation enhancements, tooling updates) that comprise a large portion of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb65889 and 9366191.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • Cargo.toml
  • src/topology/characteristics/euler.rs
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-21T22:40:08.556Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 124
File: justfile:111-112
Timestamp: 2025-11-21T22:40:08.556Z
Learning: In the delaunay repository justfile, the clippy recipe runs cargo clippy twice: once without --all-features to check default code paths, and once with --all-features to check feature-gated code. This is intentional and necessary because the repository has feature-gated code behind count-allocations, dense-slotmap, bench, and test-debug features.

Applied to files:

  • Cargo.toml
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • src/topology/characteristics/euler.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Codacy Security Scan
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (windows-latest)
  • GitHub Check: Code Coverage
  • GitHub Check: performance-regression
🔇 Additional comments (8)
src/topology/characteristics/euler.rs (6)

33-113: LGTM! Clean renaming to standard topology terminology.

The rename from SimplexCounts to FVector aligns with standard topology literature where the f-vector represents the sequence (f₀, f₁, ..., f_D) of simplex counts. Documentation and examples are consistently updated.


251-283: Excellent vectorized refactoring for performance.

The single-pass approach over cells (filling all intermediate dimension sets at once) eliminates redundant iteration. Pre-sorting each cell's vertices once (line 269) ensures canonical ordering without per-combination sorting overhead.


288-342: Correct lexicographic combination generator.

The algorithm correctly generates all C(n, k) combinations using standard lexicographic enumeration. The condition at line 332 (indices[pivot] < pivot + n - simplex_size) properly ensures each index has room for subsequent indices in the sequence.

The debug_assert (line 302) enforcing sorted input is satisfied by callers (lines 269, 461 pre-sort vertex keys).


433-475: Consistent vectorized approach for boundary counting.

The boundary simplex counting mirrors the main counting logic with the same single-pass optimization. Correctly extracts facet vertices (excluding the opposite vertex) and pre-sorts them (line 461) before generating sub-simplices.


641-699: Thorough test coverage for combination generation.

The test validates critical edge cases:

  • Empty result when n < simplex_size
  • Single combination when n == simplex_size
  • All singleton combinations when simplex_size == 1
  • Correct binomial coefficient C(3,2) = 3

Deliberately uses unsorted input (lines 658, 672, 686) then sorts to verify the helper's pre-sorting requirement.


738-788: Excellent test coverage for closed surface topology.

The build_closed_2d_surface_tds helper correctly constructs a tetrahedron's boundary (4 triangles on 4 vertices) where each edge appears in exactly 2 faces, forming a closed S² with no boundary.

Tests properly verify:

  • Zero boundary facets (line 771)
  • Correct classification as ClosedSphere(2) (line 774)
  • Expected f-vector [4, 6, 4] and χ = 2 for S² (lines 777-778)
  • Boundary simplex counting returns zero for closed surfaces (line 786)

These additions align with the PR's goal of validating topological classification and boundary detection.

Cargo.toml (2)

34-34: Version 1.0.149 is valid and has no known security advisories.

Serde_json 1.0.149 exists on crates.io as the latest version (released 2026-01-06) with no reported security vulnerabilities in the Rust Security Advisory Database. The dependency update is safe to merge.


25-25: Dependency update is valid.

Confirmed that la-stack 0.1.2 exists and includes LDLᵀ factorization. Release notes explicitly mention "Add LDL^T factorization" (released Jan 6, 2026), and the API provides the required ldlt() method with solve_vec() and det() for the Gram determinant calculation.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot added the rust Pull requests that update rust code label Jan 6, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
justfile (1)

527-557: spell-check rename handling is incorrect with git status --porcelain -z

With --porcelain -z, Git does not emit the "oldname -> newname" textual form; instead, renames are reported as two NUL-separated paths. In this loop:

while IFS= read -r -d '' status_line; do
    if [[ "$status_line" =~ ^..[[:space:]](.*)$ ]]; then
        filename="${BASH_REMATCH[1]}"
        if [[ "$filename" == *" -> "* ]]; then
            filename="${filename#* -> }"
        fi
        files+=("$filename")
    fi
done < <(git status --porcelain -z --ignored=no)

rename entries will only contribute the old path (and the *" -> "* branch is never hit), so:

  • cspell may be invoked on paths that no longer exist after a git mv, and
  • the renamed-to path is never spell-checked.

It would be better to either:

  • switch to a NUL-safe command that directly lists changed file paths (e.g., git diff --name-only -z plus git ls-files --others for untracked), or
  • explicitly handle the two-path rename format in --porcelain -z and always enqueue the final/new path.

As-is, this is a functional bug for rename workflows.

🧹 Nitpick comments (1)
justfile (1)

17-21: Tooling, setup, and lint/fix workflow refactor is coherent and extensible

This is a substantial but well-structured upgrade of the developer workflow:

  • _ensure-* helpers for actionlint, Prettier/npx, shfmt, taplo, uv, yamllint are consistent and provide clear remediation messages.
  • fix now composes toml-fmt, Rust fmt, Python fixes, shell formatting, markdown-fix, and yaml-fix into a single “apply auto-fixes” entrypoint, which matches the help text.
  • markdown-fix and markdown-lint wrap markdownlint (via npx) in a way that scales to many files and keeps config centralized.
  • python-check/python-lint/python-typecheck give a clear separation of format, lint, and type checks under uv, and lint-code now routes through them.
  • shell-check vs shell-fmt cleanly split validation from mutation with appropriate shfmt and shellcheck usage.
  • setup delegating to setup-tools and then running uv sync --group dev plus cargo build makes onboarding much smoother.
  • validate-json via jq empty and the YAML yaml-fix/yaml-lint pair round out config validation nicely.
  • test-integration using cargo test --tests --verbose better matches the description (“all integration tests”).

Two minor refinements to consider:

  • On non-macOS systems without Homebrew, setup-tools currently exits early once it discovers brew is missing, without running the later “Verifying required commands are available…” section. If a Linux developer has already installed uv, jq, etc. by other means, it would be useful to still run the verification loop instead of hard-failing once brew is absent.
  • For yaml-fix, you’re already guarding Prettier vs npx carefully; if you ever add more Prettier-based formatters, consider centralizing that logic into a shared helper to avoid subtle drift.

Overall, the structure and messaging look solid.

Because this wiring depends on external tools (uv, ruff, mypy, prettier, yamllint, jq, etc.), please:

  • Verify just setup-tools behaves sensibly on at least one non-Homebrew Linux environment (e.g., tools preinstalled via distro packages).
  • Run just lint and just fix end-to-end to ensure there are no ordering or missing-tool edge cases.

Also applies to: 33-43, 50-54, 55-60, 61-66, 206-209, 220-220, 263-264, 290-306, 372-376, 382-382, 389-493, 494-524, 579-582, 636-649, 663-695, 696-708

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b5f723 and c2fc197.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml
  • cspell.json
  • justfile
  • src/geometry/util.rs
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • justfile
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • justfile
📚 Learning: 2025-11-21T22:40:08.556Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 124
File: justfile:111-112
Timestamp: 2025-11-21T22:40:08.556Z
Learning: In the delaunay repository justfile, the clippy recipe runs cargo clippy twice: once without --all-features to check default code paths, and once with --all-features to check feature-gated code. This is intentional and necessary because the repository has feature-gated code behind count-allocations, dense-slotmap, bench, and test-debug features.

Applied to files:

  • Cargo.toml
📚 Learning: 2025-09-02T20:32:05.985Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 60
File: cspell.json:103-103
Timestamp: 2025-09-02T20:32:05.985Z
Learning: In cspell.json for the delaunay project, the word "itional" is intentionally added to the dictionary because it comes from a regex pattern, not a typo.

Applied to files:

  • cspell.json
📚 Learning: 2025-12-17T07:10:54.838Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 156
File: cspell.json:325-325
Timestamp: 2025-12-17T07:10:54.838Z
Learning: In cspell.json for this project, include both 'retriable' and 'retryable' in the customWords (or equivalent custom dictionary) so both spellings are recognized. Do not remove either; maintain both variants to ensure consistency across code, tests, and documentation.

Applied to files:

  • cspell.json
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • src/geometry/util.rs
🧬 Code graph analysis (1)
src/geometry/util.rs (2)
src/geometry/matrix.rs (2)
  • matrix_get (169-172)
  • matrix_set (175-178)
tests/circumsphere_debug_tools.rs (2)
  • matrix_get (70-73)
  • matrix_set (66-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Code Coverage
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: Codacy Security Scan
  • GitHub Check: performance-regression
🔇 Additional comments (4)
cspell.json (1)

71-71: Allowing LDLT identifiers in spell-check config is appropriate

Adding LDLT/ldlt keeps cspell from flagging the new linear-algebra API and tests; this is consistent with how other technical acronyms are handled here.
Based on learnings, this matches the existing approach of whitelisting domain-specific terms in cspell.json.

Also applies to: 254-254

src/geometry/util.rs (1)

13-25: LDLT-based Gram determinant integration and clamping look consistent

The new gram_determinant_ldlt helper and imports cleanly centralize LDLT usage:

  • ldlt(DEFAULT_SINGULAR_TOL)det() / 0.0 (singular) / NaN (non-finite), with clamp_gram_determinant converting those into clear CircumcenterErrors.
  • simplex and facet Gram-based volume paths now share this helper, keeping determinant handling symmetric and PSD-aware.
  • Updated clamp docs correctly describe the “non-finite or (near-)negative ⇒ error; zero ⇒ degenerate simplex error” behavior, which matches the implementation.
  • Tests (gram_det_from_edges, test_gram_determinant_ldlt_known_spd, and the existing Gram-matrix volume tests) exercise the new path appropriately.

I don’t see correctness or stability regressions in these changes.

Given the reliance on la-stack’s LDLT implementation and DEFAULT_SINGULAR_TOL, please confirm that:

  • Near-singular but valid Gram matrices in test_gram_matrix_debug still succeed instead of being classified as singular.
  • The new SPD test passes across your supported platforms and toolchains.

Also applies to: 1124-1129, 1167-1179, 1224-1225, 1578-1579, 3178-3196, 3198-3204

justfile (1)

9-15: Centralizing coverage args and adding CI variants is a good cleanup

Factoring common tarpaulin flags into _coverage_base_args and wiring coverage / coverage-ci through it removes duplication and keeps timeouts/exclusions in sync. The new check-fast and ci-baseline/ci-slow recipes also give a nice separation between quick local checks, baseline CI, and slow/stress runs.

Also applies to: 141-143, 150-157

Cargo.toml (1)

25-25: la-stack 0.1.2 correctly aligns with LDLT implementation

The version bump to la-stack = "0.1.2" is consistent with the new LDLT-based determinant computations in src/geometry/util.rs (e.g., gram_determinant_ldlt function using matrix.ldlt(DEFAULT_SINGULAR_TOL) at lines 1174–1225, 1578, 3194). Imports and method signatures match the crate's API.

@codacy-production
Copy link

codacy-production bot commented Jan 6, 2026

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
+0.03% (target: -1.00%) 74.85%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (6b5f723) 4969 3539 71.22%
Head commit (9366191) 5055 (+86) 3602 (+63) 71.26% (+0.03%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#167) 167 125 74.85%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

@codecov
Copy link

codecov bot commented Jan 6, 2026

Codecov Report

❌ Patch coverage is 74.85030% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.28%. Comparing base (6b5f723) to head (9366191).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/core/algorithms/locate.rs 60.78% 20 Missing ⚠️
src/core/triangulation.rs 70.21% 14 Missing ⚠️
src/topology/characteristics/euler.rs 88.13% 7 Missing ⚠️
src/geometry/util.rs 85.71% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #167      +/-   ##
==========================================
+ Coverage   71.25%   71.28%   +0.03%     
==========================================
  Files          28       28              
  Lines        4967     5053      +86     
==========================================
+ Hits         3539     3602      +63     
- Misses       1428     1451      +23     
Flag Coverage Δ
unittests 71.28% <74.85%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Addresses rare topological invalidations during incremental
insertion by:

-   Adding connectedness validation to conflict region checks.

-   Adding codimension-2 boundary manifoldness validation
    ("no boundary of boundary") to triangulation's `is_valid`
     method.

-   Ensuring that strict Level 3 validation is enabled during
    batch construction in debug builds.
Refs: feat/la-stack-ldlt-factorization
@coderabbitai coderabbitai bot added documentation Improvements or additions to documentation geometry Geometry-related issues labels Jan 7, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
justfile (2)

464-473: Verify cargo-tarpaulin binary detection.

The check have cargo-tarpaulin looks for a binary named cargo-tarpaulin on PATH. Cargo subcommands installed via cargo install typically create binaries with the crate name (e.g., cargo-tarpaulin), so this should work. However, the later verification loop (line 478) doesn't include cargo-tarpaulin in the checked commands, creating an inconsistency.

Consider either adding cargo-tarpaulin to the verification loop for Linux systems, or documenting that coverage commands require manual verification.


519-533: Minor: Consider batching shfmt invocations.

shell-check uses -n4 for shellcheck while shell-fmt uses -n1 for shfmt. While functionally correct, batching with -n4 or higher would reduce process spawn overhead for repositories with many shell scripts.

🔎 Proposed fix
-        printf '%s\0' "${files[@]}" | xargs -0 -n1 shfmt -w
+        printf '%s\0' "${files[@]}" | xargs -0 shfmt -w
src/core/algorithms/locate.rs (1)

1346-1413: Existing cavity-boundary tests remain valid under new implementation

The dimension-parameterized test_cavity_boundary_dimension! and test_extract_cavity_boundary_empty_conflict still exercise:

  • Correct facet counts for a single simplex across D=2–5.
  • The empty-conflict → empty-boundary case.

These provide a good regression baseline for the refactored extract_cavity_boundary; adding explicit tests for DisconnectedBoundary/OpenBoundary in the future would further strengthen coverage, but is not strictly required here.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2fc197 and 6443ee2.

📒 Files selected for processing (10)
  • cspell.json
  • docs/topology.md
  • docs/validation.md
  • justfile
  • src/core/algorithms/incremental_insertion.rs
  • src/core/algorithms/locate.rs
  • src/core/delaunay_triangulation.rs
  • src/core/triangulation.rs
  • tests/delaunay_edge_cases.rs
  • tests/proptest_delaunay_triangulation.proptest-regressions
✅ Files skipped from review due to trivial changes (3)
  • src/core/delaunay_triangulation.rs
  • docs/topology.md
  • tests/proptest_delaunay_triangulation.proptest-regressions
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • src/core/algorithms/incremental_insertion.rs
  • src/core/triangulation.rs
  • tests/delaunay_edge_cases.rs
  • src/core/algorithms/locate.rs
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • src/core/algorithms/incremental_insertion.rs
  • justfile
  • src/core/triangulation.rs
  • tests/delaunay_edge_cases.rs
📚 Learning: 2025-11-21T22:40:08.556Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 124
File: justfile:111-112
Timestamp: 2025-11-21T22:40:08.556Z
Learning: In the delaunay repository justfile, the clippy recipe runs cargo clippy twice: once without --all-features to check default code paths, and once with --all-features to check feature-gated code. This is intentional and necessary because the repository has feature-gated code behind count-allocations, dense-slotmap, bench, and test-debug features.

Applied to files:

  • justfile
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • justfile
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • tests/delaunay_edge_cases.rs
  • src/core/algorithms/locate.rs
📚 Learning: 2025-09-02T20:32:05.985Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 60
File: cspell.json:103-103
Timestamp: 2025-09-02T20:32:05.985Z
Learning: In cspell.json for the delaunay project, the word "itional" is intentionally added to the dictionary because it comes from a regex pattern, not a typo.

Applied to files:

  • cspell.json
📚 Learning: 2025-12-17T07:10:54.838Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 156
File: cspell.json:325-325
Timestamp: 2025-12-17T07:10:54.838Z
Learning: In cspell.json for this project, include both 'retriable' and 'retryable' in the customWords (or equivalent custom dictionary) so both spellings are recognized. Do not remove either; maintain both variants to ensure consistency across code, tests, and documentation.

Applied to files:

  • cspell.json
🧬 Code graph analysis (2)
src/core/triangulation.rs (1)
src/core/facet.rs (2)
  • facet_key_from_vertices (933-946)
  • vertices (496-518)
tests/delaunay_edge_cases.rs (2)
src/core/delaunay_triangulation.rs (4)
  • vertices (597-599)
  • new (168-172)
  • number_of_vertices (480-482)
  • number_of_cells (506-508)
src/core/triangulation_data_structure.rs (3)
  • vertices (933-935)
  • number_of_vertices (1026-1028)
  • number_of_cells (1166-1168)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (30)
justfile (12)

9-15: LGTM!

Well-documented coverage configuration with appropriate timeout for slow CI environments and clear exclusion rationale.


17-71: LGTM!

Consistent pattern for tooling validation with helpful error messages that guide users to just setup or manual installation.


150-157: LGTM!

Useful CI workflow extensions: ci-baseline for saving performance baselines and ci-slow for comprehensive testing including stress tests.


542-580: Well-implemented git status parsing with rename/copy handling.

The spell-check recipe correctly handles the nuances of git status --porcelain -z output, including proper handling of renames (R) and copies (C) where the destination path is a separate NUL-delimited token. The fallback from global cspell to npx cspell provides good flexibility.


682-713: LGTM!

Good defensive programming with npx --yes flag detection for compatibility across npm versions, and clean command array construction pattern.


598-600: LGTM!

Clear separation between test (lib + doc tests for fast feedback) and test-integration (full integration test suite including property tests).


655-667: LGTM!

Correct use of jq empty for JSON validation, following the consistent pattern used throughout the justfile.


167-176: LGTM!

The triple clippy run (no-default-features, default, all-features) is intentional and necessary to catch warnings in feature-gated code paths. Based on learnings, this covers the count-allocations, dense-slotmap, bench, and test-debug features.


388-400: LGTM!

Clean separation of concerns: setup-tools handles external tooling installation while setup handles project-specific Python dependencies and verification. The dependency chain ensures tools are available before attempting uv sync.


372-382: LGTM!

Correct ordering of ruff operations: check formatting first, then lint, then typecheck. The python-lint alias to python-check provides intuitive naming.


206-208: LGTM!

Comprehensive fix command that applies all formatters in a logical order: configuration files → code → scripts → documentation.


1-7: Well-structured justfile with comprehensive development workflow.

Good practices observed:

  • Strict shell error handling (set -euo pipefail)
  • Consistent _ensure-* pattern for tooling validation
  • Clear separation between mutating (fix) and non-mutating (check, lint) operations
  • Comprehensive help documentation via help-workflows
  • Appropriate use of internal recipes (underscore prefix) for helpers
cspell.json (1)

35-407: Dictionary additions look consistent with project terminology

New words (e.g., Algorithmica, Antisymmetry, LDLT/ldlt, topology/authors) are appropriate and non-problematic. Existing special cases like itional, retriable, and retryable are preserved as expected.

tests/delaunay_edge_cases.rs (1)

107-192: 4D Euler-regression test is well targeted

The new 4D regression test exercises construction, basic size bounds, and full Level 1–3 validation on the previously failing configuration, which is exactly what you want to guard against the χ=0 shell regression. No issues spotted.

src/core/triangulation.rs (7)

31-37: Level‑3 topology docs correctly reflect new boundary checks

The updated Level‑3 documentation (codimension‑1 manifoldness and codimension‑2 closed boundary) matches the implemented validation steps and clarifies the invariants enforced by is_valid().


104-106: New imports are appropriate for boundary and adjacency validation

Adding VertexKeyBuffer, VertexToCellsMap, fast hash helpers, and AllFacetsIter/BoundaryFacetsIter/FacetHandle/facet_key_from_vertices is consistent with the new boundary-closure and adjacency APIs. No issues with visibility or coupling here.

Also applies to: 108-113


246-258: BoundaryRidgeMultiplicity error type is well-structured

The new TriangulationValidationError::BoundaryRidgeMultiplicity cleanly encapsulates codimension‑2 boundary failures with a canonical ridge key and multiplicity count, giving useful diagnostics without exposing internal data structures.


1458-1476: is_valid() integration of closed-boundary check is sound

Plumbing validate_closed_boundary_with_map immediately after manifold facet validation ensures codimension‑2 boundary issues are caught early, before global connectedness and Euler checks. The reuse of a single facet_to_cells map keeps the cost reasonable and matches the documented Level‑3 guarantees.

Also applies to: 1501-1529


1745-1843: Closed-boundary validator correctly enforces “no boundary of boundary”

validate_closed_boundary_with_map:

  • Skips irrelevant cases (D<2, no boundary facets).
  • Derives each boundary facet’s vertex set from the owning cell and facet_index with robust consistency checks.
  • Enumerates all boundary ridges and requires each to have incidence 2, flagging any 1- or >2‑incidence ridge as BoundaryRidgeMultiplicity.

The logic is dimension-agnostic, doesn’t rely on neighbor pointers, and aligns with the new tests, so manifold-with-boundary semantics are enforced as intended.


2371-2382: Debug logging now only fires for genuinely suspicious operations

Conditioning log_validation_trigger_if_enabled on both should_validate() and suspicion.is_suspicious() avoids debug-log spam for Always/DebugOnly policies while still logging when a suspicious insertion actually triggers Level‑3 validation. Behavior of validation itself is unchanged.


3778-3897: New closed-boundary tests cover core OK and error paths

The added tests:

  • Validate the happy path for a single tetrahedron.
  • Confirm the 2D closed surface (tetra boundary as S²) is a no-op for boundary-closure checks.
  • Construct a 3D configuration with an edge shared by four boundary facets and verify both the direct validate_closed_boundary_with_map error and that is_valid() surfaces BoundaryRidgeMultiplicity before connectedness.

This gives good coverage of the new codimension‑2 invariant.

src/core/algorithms/locate.rs (5)

79-126: ConflictError extensions capture new boundary-degeneracy modes

Adding DisconnectedBoundary and OpenBoundary (plus the existing RidgeFan) provides clear, structured error cases for non-ball cavity boundaries and open boundary surfaces during conflict extraction. The messages strongly indicate “retryable degeneracy” semantics, which fits with the perturbation-based insertion strategy.

Please ensure wherever ConflictError is wrapped into InsertionError (e.g., in InsertionError::is_retryable or similar helpers) these new variants are treated consistently with other degeneracy cases so that they participate correctly in the perturbation retry path, not as hard structural failures.

Also applies to: 127-156


158-165: RidgeInfo is a minimal, effective container for ridge incidence

RidgeInfo compactly records per-ridge vertex count, facet incidence count, and up to two incident boundary facets. This is exactly what’s needed to both detect fans/open ridges and build a boundary-facet adjacency graph without extra passes.


616-625: Facet-incidence–based cavity boundary classification is robust

The reworked extract_cavity_boundary:

  • Avoids relying on neighbor pointers (which may be inconsistent during local updates), instead computing boundary facets purely from facet incidence within conflict_cells.
  • Uses a conflict-set hash set and a facet-hash → FacetHandle map to determine which facets are internal vs. on the cavity boundary.
  • Caches canonical facet vertex sets for later ridge analysis, avoiding redundant cell vertex lookups.

This is a solid foundation for the new topology checks and makes boundary extraction more resilient to transient TDS states.

Also applies to: 631-690


694-767: Ridge counting for boundary facets correctly detects non-manifold ridges

The ridge enumeration code:

  • Derives ridges (D‑2 simplices) by omitting each facet vertex in turn.
  • Hashes each ridge via the sorted vertex keys and accumulates facet_count plus association back to the boundary-facet indices.
  • Leaves internal facets untouched, focusing solely on the cavity boundary.

This aligns with the global BoundaryRidgeMultiplicity logic and prepares the adjacency graph for connectivity checks.


768-838: Boundary adjacency and connectivity checks match intended topology invariant

The final phase of extract_cavity_boundary:

  • Rejects ridges with facet_count == 1 as OpenBoundary and facet_count >= 3 as RidgeFan.
  • Builds a symmetric adjacency list between boundary facets for all ridges with exactly two incident facets.
  • Performs a simple DFS to assert that all boundary facets lie in one connected component, otherwise returning DisconnectedBoundary.

This ensures the cavity boundary is a single closed manifold component before insertion proceeds, which directly addresses the previous shell/Euler issues. Implementation is straightforward and dimension-independent.

src/core/algorithms/incremental_insertion.rs (1)

241-250: LGTM! Logical extension of retry logic for boundary topology issues.

The addition of DisconnectedBoundary and OpenBoundary to the retryable conflict errors is consistent with the existing pattern—these represent topological degeneracies (disconnected cavity boundaries, unclosed boundaries) that may be resolved via coordinate perturbation, similar to how NonManifoldFacet and RidgeFan are handled. The updated comment accurately reflects the expanded scope.

docs/validation.md (3)

42-48: Excellent clarification of automatic Level 3 validation scope.

The expanded enumeration makes it immediately clear what checks are included in automatic validation (codimension-1/2 manifoldness, connectedness, isolated vertices, Euler characteristic) versus what is excluded (Level 4 Delaunay property). This helps users understand exactly what ValidationPolicy controls.


251-265: Outstanding breakdown of Level 3 validation checks.

The numbered list with detailed explanations for each check (facet degree, boundary neighbor consistency, closed boundary, connectedness, isolated vertices, Euler characteristic) significantly improves the documentation. The distinction between codimension-1 and codimension-2 manifoldness is mathematically precise, and the explanatory notes (e.g., "no boundary of boundary," "interior ridges can have higher degree") provide helpful context without overwhelming the reader.


482-487: Troubleshooting section properly updated to match new validation checks.

The expanded problem description now covers boundary closure issues, boundary ridge degree requirements, and isolated vertices, which aligns with the enhanced Level 3 checks described earlier in the document. This ensures users encountering these new validation failures will have guidance on how to address them.

Renames the `SimplexCounts` struct to `FVector` to better reflect
its mathematical meaning as the f-vector in topology, representing
the counts of simplices of different dimensions.

This change improves code readability and aligns the naming
convention with standard topological terminology.
(Internal refactoring, no API change.)
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
justfile (2)

403-509: Consider splitting setup-tools for better maintainability.

The setup-tools recipe is comprehensive and functional, providing excellent developer experience with OS detection, Homebrew integration, and clear error messages. However, at ~106 lines, it's quite long and handles multiple concerns (package manager detection, tool installation, verification).

Consider optionally refactoring into smaller helper recipes for easier maintenance, though this works well as-is.


543-586: Good handling of git status edge cases.

The spell-check recipe now properly handles renames, copies, and deletions when parsing git status --porcelain -z. The logic is correct, and the existing comments help explain the approach.

One optional improvement: The comment could more explicitly show the git status format for renames (R old_path\0new_path\0) to make the two-token read pattern clearer.

src/topology/characteristics/euler.rs (1)

281-321: Consider documenting the combination generation algorithm.

The implementation is correct and uses the standard lexicographic combination generation algorithm. However, the pivot-based logic (lines 305-320) is subtle and may not be immediately clear to maintainers.

Optional: Add explanatory comment

Consider adding a brief comment explaining the algorithm, such as:

 fn insert_simplices_of_size(
     vertex_keys: &[VertexKey],
     simplex_size: usize,
     simplex_set: &mut FastHashSet<SmallBuffer<VertexKey, MAX_PRACTICAL_DIMENSION_SIZE>>,
 ) {
     let n = vertex_keys.len();
     if n < simplex_size {
         return;
     }
 
-    // Generate all C(n, simplex_size) combinations of vertex keys using indices.
+    // Generate all C(n, simplex_size) combinations using the standard lexicographic algorithm.
+    // We maintain indices[0..simplex_size] representing positions in vertex_keys.
+    // To get the next combination, find the rightmost index that can be incremented,
+    // increment it, and reset all subsequent indices to consecutive values.
     let mut indices: SmallBuffer<usize, MAX_PRACTICAL_DIMENSION_SIZE> = (0..simplex_size).collect();
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6443ee2 and e6499ab.

📒 Files selected for processing (5)
  • docs/topology.md
  • examples/triangulation_3d_20_points.rs
  • justfile
  • src/topology/characteristics/euler.rs
  • src/topology/characteristics/validation.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/topology.md
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The user corrected that finitecheck.rs, hashcoordinate.rs, and orderedeq.rs trait files do not exist in the current version of the delaunay repository under src/geometry/traits/. The current structure only contains coordinate.rs, which matches the documented structure in CONTRIBUTING.md.

Applied to files:

  • examples/triangulation_3d_20_points.rs
  • src/topology/characteristics/validation.rs
  • src/topology/characteristics/euler.rs
📚 Learning: 2025-08-30T02:40:22.032Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 55
File: CONTRIBUTING.md:152-167
Timestamp: 2025-08-30T02:40:22.032Z
Learning: The delaunay repository has stale documentation in WARP.md that references non-existent trait files (finitecheck.rs, hashcoordinate.rs, orderedeq.rs) in src/geometry/traits/, while the actual directory only contains coordinate.rs. The CONTRIBUTING.md documentation is accurate and reflects the current state.

Applied to files:

  • examples/triangulation_3d_20_points.rs
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • examples/triangulation_3d_20_points.rs
  • justfile
📚 Learning: 2025-08-20T21:55:04.552Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 50
File: src/core/algorithms/robust_bowyer_watson.rs:191-195
Timestamp: 2025-08-20T21:55:04.552Z
Learning: CoordinateScalar trait guarantees PartialOrd, making direct comparisons between CoordinateScalar values valid without needing conversion to f64.

Applied to files:

  • examples/triangulation_3d_20_points.rs
  • src/topology/characteristics/validation.rs
📚 Learning: 2025-08-20T21:55:04.552Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 50
File: src/core/algorithms/robust_bowyer_watson.rs:191-195
Timestamp: 2025-08-20T21:55:04.552Z
Learning: CoordinateScalar trait includes PartialOrd through its Float supertrait bound, making direct comparisons between CoordinateScalar values valid without needing conversion to f64.

Applied to files:

  • examples/triangulation_3d_20_points.rs
  • src/topology/characteristics/validation.rs
📚 Learning: 2025-11-21T22:40:08.556Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 124
File: justfile:111-112
Timestamp: 2025-11-21T22:40:08.556Z
Learning: In the delaunay repository justfile, the clippy recipe runs cargo clippy twice: once without --all-features to check default code paths, and once with --all-features to check feature-gated code. This is intentional and necessary because the repository has feature-gated code behind count-allocations, dense-slotmap, bench, and test-debug features.

Applied to files:

  • justfile
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • justfile
🧬 Code graph analysis (2)
src/topology/characteristics/validation.rs (1)
src/topology/characteristics/euler.rs (1)
  • count_simplices_with_facet_to_cells_map (226-279)
src/topology/characteristics/euler.rs (3)
src/core/triangulation.rs (3)
  • default (359-361)
  • boundary_facets (739-747)
  • from (279-281)
src/core/triangulation_data_structure.rs (4)
  • default (280-282)
  • vertex_keys (942-944)
  • from (457-459)
  • from (463-465)
src/core/edge.rs (2)
  • new (55-66)
  • from (162-164)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Code Coverage
  • GitHub Check: build (macos-latest)
  • GitHub Check: performance-regression
  • GitHub Check: Codacy Security Scan
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (ubuntu-latest)
🔇 Additional comments (17)
justfile (12)

9-15: LGTM! Good centralization of coverage configuration.

The new _coverage_base_args variable eliminates duplication and makes coverage configuration easier to maintain. The comment explaining the timeout is helpful.


17-65: LGTM! Well-structured tooling dependency checks.

The helper functions follow a consistent pattern with clear error messages that guide users to just setup or manual installation. The _ensure-prettier-or-npx fallback logic is particularly good for flexibility.


141-158: LGTM! Useful CI workflow additions.

The new recipes provide good granularity for different CI scenarios:

  • check-fast for quick compile checks
  • ci-baseline for performance tracking
  • ci-slow for comprehensive validation

206-208: LGTM! Comprehensive fix command.

The fix recipe now consolidates all auto-formatting across Rust, Python, Shell, Markdown, and YAML. This provides a convenient one-command fix for developers.


290-305: LGTM! Clean markdown formatting integration.

The markdown-fix recipe properly handles file enumeration and provides clear feedback. The use of git ls-files ensures only tracked files are formatted.


372-386: LGTM! Well-structured Python quality checks.

The Python recipes follow the established pattern:

  • python-check for non-mutating validation (ruff + type checking)
  • python-fix for auto-fixes
  • Clear separation of concerns

510-541: LGTM! Comprehensive shell script quality checks.

The shell script recipes properly combine shellcheck (linting) and shfmt (formatting) with appropriate guards for tool availability and empty file lists. The note about justfiles is helpful.


604-607: LGTM! Clear test organization.

The new test-integration recipe provides explicit separation between fast unit/doc tests and slower integration tests, improving CI efficiency and developer workflow.


661-673: LGTM! Simple and effective JSON validation.

Using jq empty is a clean approach to validate JSON syntax. The recipe follows the established pattern for file enumeration and error handling.


688-733: LGTM! Robust YAML formatting and linting.

The YAML recipes handle multiple tool scenarios gracefully:

  • yaml-fix intelligently detects prettier/npx availability and the --yes flag support
  • yaml-lint uses yamllint with strict checking
  • The comment explaining the CLI-flag-only approach (line 715) is helpful

The defensive programming for npx version differences is good practice.


187-193: LGTM! Proper use of centralized coverage configuration.

Both coverage recipes correctly use the _coverage_base_args variable with appropriate output formats (HTML for local development, XML for CI).


216-263: LGTM! Comprehensive and well-organized help documentation.

The updated help text clearly documents all new recipes and workflows, providing excellent developer experience. The note about running just setup-tools or just setup first is helpful.

src/topology/characteristics/validation.rs (1)

12-12: LGTM - Consistent rename to FVector.

The rename from SimplexCounts to FVector is clean and mathematically accurate, aligning with standard topology terminology for the f-vector (face vector) that counts simplices by dimension.

Also applies to: 53-53, 74-74, 83-83, 211-211, 222-222, 233-233

examples/triangulation_3d_20_points.rs (1)

30-30: LGTM - Enhanced validation output with topology details.

The added topology validation section provides valuable diagnostic information (classification, Euler characteristic, f-vector) that helps users understand the topological structure of their triangulation. The error handling is appropriate.

Also applies to: 208-230

src/topology/characteristics/euler.rs (3)

43-113: LGTM - Clean rename to standard topology terminology.

The rename from SimplexCounts to FVector better reflects the mathematical concept of the f-vector in combinatorial topology. The updated documentation appropriately explains that this represents (f₀, f₁, ..., f_D).


251-279: LGTM - Single-pass optimization improves performance.

The refactoring consolidates intermediate simplex counting into a single pass over cells, eliminating redundant iterations. This is a solid performance improvement, especially for higher dimensions where the number of intermediate dimensions grows.


413-449: LGTM - Consistent single-pass optimization for boundary simplices.

The boundary simplex counting uses the same single-pass approach as the main counting function, maintaining consistency and achieving similar performance improvements.

Improves the algorithm for generating simplex combinations in
the Euler characteristic calculation. This change enhances
efficiency by using a lexicographic approach to generate
combinations, reducing unnecessary computations.

Refs: feat/la-stack-ldlt-factorization
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
justfile (2)

402-511: Well-structured tooling setup with good OS awareness.

The setup-tools recipe is comprehensive and properly handles platform differences. The verification step (lines 479-508) ensures all required tools are available before proceeding.

Minor optional suggestion: Consider extracting the Homebrew installation logic (lines 417-425) into a separate function if you plan to add more package managers in the future.


546-592: Complex but correct git status parsing for spell-check.

The refactored spell-check recipe properly handles git renames and copies (lines 567-571). The logic correctly reads two NUL-delimited tokens for renamed/copied files and uses the destination path.

The fallback from cspell to npx (lines 581-589) provides good flexibility.

Optional: Consider adding a comment explaining the R/C status handling

The git status rename/copy parsing is subtle. Consider adding a brief inline comment for future maintainers:

         # For renames/copies, consume the destination path (the next NUL-delimited token).
+        # Git emits: "R  old_path\0new_path\0" for renames
         if [[ "$status" == *"R"* || "$status" == *"C"* ]]; then
src/topology/characteristics/euler.rs (2)

281-328: Lexicographic combination generation is correct.

The algorithm correctly generates all C(n, simplex_size) combinations using the standard lexicographic approach. The pivot-finding logic (lines 313-321) properly identifies the rightmost position that can be incremented and handles loop termination when all combinations are exhausted.

Optional: Consider pre-sorting vertex_keys to eliminate per-combination sorting

If vertex_keys were provided in sorted order (or sorted once at the start of insert_simplices_of_size), line 307's per-combination sort could be eliminated. The lexicographic generation would then naturally produce combinations in canonical order. This micro-optimization may be beneficial if profiling shows sorting overhead in high-dimensional cases.

 fn insert_simplices_of_size(
     vertex_keys: &[VertexKey],
     simplex_size: usize,
     simplex_set: &mut FastHashSet<SmallBuffer<VertexKey, MAX_PRACTICAL_DIMENSION_SIZE>>,
 ) {
     let n = vertex_keys.len();
     if n < simplex_size {
         return;
     }
+    
+    // Sort once to ensure canonical order
+    let mut sorted_keys: SmallBuffer<VertexKey, MAX_PRACTICAL_DIMENSION_SIZE> = 
+        vertex_keys.iter().copied().collect();
+    sorted_keys.sort();
 
-    let mut indices: SmallBuffer<usize, MAX_PRACTICAL_DIMENSION_SIZE> = (0..simplex_size).collect();
+    let mut indices: SmallBuffer<usize, MAX_PRACTICAL_DIMENSION_SIZE> = 
+        (0..simplex_size).collect();
 
     'outer: loop {
         let mut simplex_vertices: SmallBuffer<VertexKey, MAX_PRACTICAL_DIMENSION_SIZE> =
             SmallBuffer::new();
         for &vertex_index in &indices {
-            simplex_vertices.push(vertex_keys[vertex_index]);
+            simplex_vertices.push(sorted_keys[vertex_index]);
         }
-        simplex_vertices.sort();
         simplex_set.insert(simplex_vertices);
         
         // ... rest unchanged

606-659: Tests updated consistently for FVector.

All unit tests have been properly updated to use FVector. The existing test coverage validates the correctness of the type rename and the counting functionality through integration tests.

Consider adding a focused unit test for insert_simplices_of_size to explicitly verify the lexicographic combination generation with various edge cases (e.g., simplex_size == n, simplex_size == 1, small n values). While the function is tested indirectly through count_simplices, a dedicated test would make the expected behavior more explicit and aid future maintenance.

Example test structure:

#[test]
fn test_insert_simplices_of_size() {
    use crate::core::triangulation_data_structure::VertexKey;
    let mut set = FastHashSet::default();
    
    // Test case: C(3, 2) = 3 combinations
    let keys: Vec<VertexKey> = vec![/* ... */];
    insert_simplices_of_size(&keys, 2, &mut set);
    assert_eq!(set.len(), 3);
    
    // Verify specific combinations exist
    // ...
}
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6499ab and c81455a.

📒 Files selected for processing (2)
  • justfile
  • src/topology/characteristics/euler.rs
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-11-21T22:40:08.556Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 124
File: justfile:111-112
Timestamp: 2025-11-21T22:40:08.556Z
Learning: In the delaunay repository justfile, the clippy recipe runs cargo clippy twice: once without --all-features to check default code paths, and once with --all-features to check feature-gated code. This is intentional and necessary because the repository has feature-gated code behind count-allocations, dense-slotmap, bench, and test-debug features.

Applied to files:

  • justfile
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • justfile
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • justfile
🧬 Code graph analysis (1)
src/topology/characteristics/euler.rs (3)
src/core/triangulation_data_structure.rs (2)
  • default (280-282)
  • vertex_keys (942-944)
src/geometry/algorithms/convex_hull.rs (1)
  • default (1539-1547)
src/core/edge.rs (1)
  • new (55-66)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: performance-regression
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (windows-latest)
  • GitHub Check: Codacy Security Scan
  • GitHub Check: Code Coverage
🔇 Additional comments (13)
justfile (9)

9-15: LGTM! Excellent consolidation of coverage configuration.

Centralizing tarpaulin arguments eliminates duplication and makes coverage configuration easier to maintain. The 5-minute timeout is appropriate for slow CI environments.


17-65: LGTM! Well-structured tooling dependency checks.

The new _ensure-* helpers follow a consistent pattern with helpful error messages. The _ensure-prettier-or-npx fallback logic (lines 33-43) is particularly well-designed, checking for prettier first before falling back to npx.


141-157: LGTM! Useful additions to the CI workflow.

The new recipes provide good workflow flexibility:

  • check-fast enables quick iteration during development
  • ci-baseline integrates performance tracking into CI
  • ci-slow provides comprehensive validation including stress tests

206-305: LGTM! Comprehensive fix workflow.

The updated fix recipe now provides a one-stop command for all formatting needs (lines 206-208). The new markdown-fix recipe follows the established pattern with proper null-delimited file handling and informative output.


372-401: LGTM! Clean Python tooling organization.

The refactored Python recipes properly separate non-mutating checks from mutating fixes. The updated setup recipe has a logical flow: tooling setup → Python dependencies → project build, with clear status messaging at each step.


513-545: LGTM! Clean separation of shell quality checks.

The shell recipes properly separate non-mutating checks (shell-check) from mutating formatting (shell-fmt). The note on line 542 correctly clarifies that justfiles are excluded from shellcheck.


610-612: LGTM! Clear integration test target.

The new test-integration recipe provides an explicit way to run all integration tests, complementing the existing test (lib + doc) and test-all recipes.


667-679: LGTM! Simplified JSON validation.

Using jq empty for validation is a clean approach that efficiently validates JSON syntax without producing output.


694-739: LGTM! Comprehensive YAML tooling.

The new YAML recipes provide both formatting and linting:

  • yaml-fix uses prettier with smart fallback to npx (lines 704-719)
  • yaml-lint uses yamllint with strict mode
  • The npx --yes flag detection (lines 711-713) handles version differences gracefully

The note on line 721 correctly explains why CLI flags are used instead of a config file (scoped to YAML only).

src/topology/characteristics/euler.rs (4)

43-64: LGTM! Type rename aligns with topology literature.

The rename from SimplexCounts to FVector is well-motivated and consistent with standard topology terminology. The documentation clearly explains the f-vector concept, and all examples have been updated accordingly.


251-276: Excellent optimization with single-pass approach.

The refactoring to fill all intermediate-dimension sets in a single pass over cells is a clear improvement over the previous approach of iterating cells once per dimension. This reduces the algorithmic complexity from O(N·D) to O(N) cell iterations while maintaining correctness through set-based deduplication.


420-457: Consistent single-pass optimization for boundary simplices.

The refactoring applies the same efficient single-pass approach to boundary counting, maintaining consistency with count_simplices_with_facet_to_cells_map. The vertex extraction logic (lines 437-443) correctly excludes the opposite vertex to obtain facet vertex keys.


199-224: Public API updated consistently for FVector.

All public functions have been updated to use FVector in their signatures:

  • count_simplices returns Result<FVector, TopologyError>
  • euler_characteristic accepts &FVector

The API changes are consistent and maintain backward compatibility in terms of functionality while improving terminology.

Also applies to: 492-502

Speeds up simplex generation within Euler characteristic calculation by
pre-sorting vertex keys for cells and facets. This eliminates
per-combination sorting during simplex insertion, as combinations are
generated in sorted order from sorted input. Adds a test case for
`insert_simplices_of_size`.

Refs: feat/la-stack-ldlt-factorization
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/topology/characteristics/euler.rs (1)

324-333: Consider clarifying the pivot-finding logic.

The combination generation algorithm is correct, but the pivot-finding control flow at lines 324-333 is slightly confusing with the nested condition checking pivot == 0 inside the while loop. The logic works correctly (when all indices are maxed out, we exit), but it requires careful reading to verify.

♻️ Optional refactor for clarity

Consider restructuring for more explicit intent:

-        let mut pivot = simplex_size;
-        while pivot > 0 {
-            pivot -= 1;
-            if indices[pivot] != pivot + n - simplex_size {
-                break;
-            }
-            if pivot == 0 {
-                break 'outer;
-            }
-        }
+        // Find the rightmost index that can be incremented
+        let mut pivot = simplex_size;
+        loop {
+            if pivot == 0 {
+                break 'outer;  // All indices at maximum values
+            }
+            pivot -= 1;
+            if indices[pivot] < pivot + n - simplex_size {
+                break;  // Found index that can be incremented
+            }
+        }

This makes the termination condition more explicit: we check for completion before attempting to find a pivot, making the loop invariant clearer.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c81455a and eb65889.

📒 Files selected for processing (2)
  • justfile
  • src/topology/characteristics/euler.rs
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-11-21T22:40:08.556Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 124
File: justfile:111-112
Timestamp: 2025-11-21T22:40:08.556Z
Learning: In the delaunay repository justfile, the clippy recipe runs cargo clippy twice: once without --all-features to check default code paths, and once with --all-features to check feature-gated code. This is intentional and necessary because the repository has feature-gated code behind count-allocations, dense-slotmap, bench, and test-debug features.

Applied to files:

  • justfile
📚 Learning: 2025-09-02T02:40:54.439Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 58
File: scripts/tests/test_subprocess_utils.py:92-101
Timestamp: 2025-09-02T02:40:54.439Z
Learning: In Rust projects, cargo is expected to be available in development and CI environments, so conditional skipping of cargo-dependent tests is typically unnecessary.

Applied to files:

  • justfile
📚 Learning: 2025-09-04T20:03:49.896Z
Learnt from: acgetchell
Repo: acgetchell/delaunay PR: 65
File: WARP.md:249-254
Timestamp: 2025-09-04T20:03:49.896Z
Learning: The delaunay repository contains all the specialized integration tests documented in WARP.md: circumsphere_debug_tools.rs, robust_predicates_comparison.rs, convex_hull_bowyer_watson_integration.rs, and allocation_api.rs, plus additional test files like robust_predicates_showcase.rs and coordinate_conversion_errors.rs.

Applied to files:

  • justfile
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: performance-regression
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (macos-latest)
  • GitHub Check: Codacy Security Scan
  • GitHub Check: Code Coverage
🔇 Additional comments (10)
src/topology/characteristics/euler.rs (4)

43-64: LGTM! API rename improves clarity.

The rename from SimplexCounts to FVector aligns with standard topology literature terminology. The documentation clearly explains the f-vector concept with good examples.


251-283: Excellent performance optimization!

The refactoring to use a single pass over cells with per-dimension sets is a significant improvement over the previous approach that would enumerate all cells once per intermediate dimension. Pre-sorting vertex keys once per cell eliminates redundant per-combination sorting.


432-472: LGTM! Consistent optimization applied to boundary counting.

The refactoring consistently applies the same single-pass optimization to boundary simplex counting. The pre-sorting and set-based deduplication approach matches the main counting logic.


639-697: Excellent test coverage!

The test comprehensively covers edge cases (empty, single combination) and normal cases (multiple combinations). Testing with deliberately unsorted input that gets sorted before calling the function demonstrates the API contract clearly.

justfile (6)

9-15: LGTM! Good DRY refactoring.

Extracting the common tarpaulin arguments into a reusable variable improves maintainability and ensures consistent coverage settings across recipes. The inline documentation of the timeout setting is helpful.


33-43: LGTM! Flexible verification helper.

The fallback logic (prettier → npx → error) provides good flexibility while maintaining clear requirements. Error messages guide users to the recommended installation method.


406-512: Comprehensive tooling setup with good platform awareness.

The setup-tools recipe provides thorough cross-platform tooling installation. The size (~100 lines) is acknowledged in the comments (line 404) as something that may be split later if it grows further. The conditional handling of cargo-tarpaulin (Linux-only) and brew availability is well-structured.


551-593: LGTM! Robust handling of git status edge cases.

The updated spell-check recipe properly handles file renames/copies in git status output (lines 568-572) and uses NUL-delimited output for filenames with spaces. The detailed comments explaining the git status format make the parsing logic clear.


695-726: LGTM! Well-structured YAML formatting with good CI support.

The recipe properly detects and uses the --yes flag for npx in CI environments (lines 712-715). The comment at line 722 explains the rationale for using CLI flags over a config file, keeping the scope limited to YAML files only.


1-740: Well-organized expansion of development workflows.

The justfile has grown significantly but maintains clear organization with consistent patterns across similar recipe groups (shell-, markdown-, yaml-*). The separation between mutating (fix/fmt) and non-mutating (check/lint) operations is consistent throughout. The updated help text (lines 216-263) effectively guides users through the expanded capabilities.

Updates the serde_json dependency from v1.0.148 to v1.0.149.

Also, fixes a bug in the `insert_simplices_of_size` function in
`euler.rs`, where the loop condition was incorrect, causing
potential issues when generating combinations.

Adds tests for classifying a closed 2D surface (sphere) and counting
boundary simplices to verify correct topological classification and
boundary detection.
@acgetchell acgetchell merged commit 561a259 into main Jan 7, 2026
14 of 15 checks passed
@acgetchell acgetchell deleted the feat/la-stack-ldlt-factorization branch January 7, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation geometry Geometry-related issues rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant