Skip to content

Fix HashGrid truncation error with negative coordinates#1281

Open
cluster2600 wants to merge 4 commits intoNVIDIA:mainfrom
cluster2600:fix/hashgrid-negative-coord-truncation
Open

Fix HashGrid truncation error with negative coordinates#1281
cluster2600 wants to merge 4 commits intoNVIDIA:mainfrom
cluster2600:fix/hashgrid-negative-coord-truncation

Conversation

@cluster2600
Copy link
Copy Markdown

@cluster2600 cluster2600 commented Mar 10, 2026

Summary

hash_grid_query silently misses valid neighbours when query bounds or point coordinates are negative. The root cause is float-to-int conversion using C++ int() cast (truncation toward zero) instead of floor() (toward negative infinity) in warp/native/hashgrid.h.

Three locations are affected:

  1. Point cell assignment (line 86) — during build(), points with negative fractional cell coords are binned into the wrong cell
  2. Query lower bounds (lines 150-152) — the start of the cell search range is computed incorrectly in negative space
  3. Query upper bounds (lines 155-157) — same issue for the end of the search range

For example, with cell_width=1.0 a point at -0.3 should be assigned to cell -1 but int(-0.3) = 0 places it in cell 0. A query centred at +0.2 with radius 0.6 has a lower bound of -0.4, which should search cell -1 but int(-0.4) = 0 skips it entirely.

The fix replaces int(expr) with (int)floor(Type(expr)) at all three locations, matching the pattern already used in noise.h, texture.h, and volume.h.

Test plan

  • New test_hashgrid_negative_coordinates — four subcases: cross-zero boundary, all-negative cluster, fractional negative cell, and negative on all axes
  • New test_hashgrid_negative_brute_force — cross-validates hash grid against brute-force reference kernel using points centred on the origin (half negative, half positive)
  • New test_hashgrid_negative_multiprecision — verifies the fix for float16, float32, and float64
  • Full test_hash_grid.py suite passes (23/23, zero regresssions) on both CPU and CUDA (RTX 5090, sm_120)

Closes #1256

Summary by CodeRabbit

  • Tests
    • Replaced previous wrapping test with three new negative-coordinate test suites: coordinate-boundary checks, brute-force cross-validation, and multiprecision validation.
    • Expanded scenarios to include points straddling zero, fully negative clusters, fractional-negative cell cases, and simultaneous-negative axes.
    • Increased grid resolution for these tests and added precision-specific checks across float16/32/64.

@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot bot commented Mar 10, 2026

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: e32ece9b-bd82-4ca9-bf09-f36ce2a1fcda

📥 Commits

Reviewing files that changed from the base of the PR and between 5578a4a and d269d08.

📒 Files selected for processing (1)
  • warp/tests/geometry/test_hash_grid.py

📝 Walkthrough

Walkthrough

Replaces a single negative-wrapping test with three new hash-grid tests that validate neighbor discovery for negative coordinates, cross-validate against a brute-force reference, and exercise multiple floating-point precisions (grid_dim increased to 64; wrapping-focused test removed).

Changes

Cohort / File(s) Summary
Hash Grid Negative Coordinate Tests
warp/tests/geometry/test_hash_grid.py
Removed test_hashgrid_negative_wrapping. Added test_hashgrid_negative_coordinates (subtests for cross-zero, fully-negative clusters, fractional negative cells, all-axes negative), test_hashgrid_negative_brute_force (brute-force cross-validation vs. reference counts), and test_hashgrid_negative_multiprecision (checks across float16/vec3h, float32/vec3, float64/vec3d). Increased grid_dim from 4 to 64 for new tests and updated test registration accordingly.

Sequence Diagram(s)

(silently skipped)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: fixing a HashGrid truncation error affecting negative coordinates, which is the core bug fix in this PR.
Linked Issues check ✅ Passed The PR addresses all objectives from #1256: replaces truncating int() casts with int(floor(Type(expr))) at three HashGrid locations, adds comprehensive tests for negative coordinates across multiple precisions, and validates correctness without regressions.
Out of Scope Changes check ✅ Passed All changes in the test file directly support the primary objective of validating the HashGrid truncation fix. The three new tests and removal of the replaced test are all in scope for the negative-coordinate bug fix.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 10, 2026

Greptile Summary

This PR adds three new test functions — test_hashgrid_negative_coordinates, test_hashgrid_negative_brute_force, and test_hashgrid_negative_multiprecision — to validate the floor()-based fix (already landed in warp/native/hashgrid.h) for the truncation-toward-zero bug that caused wrong cell assignments and query range miscalculations for negative coordinates. The native fix itself is correct and well-placed.

Key changes:

  • Replaces test_hashgrid_negative_wrapping (periodic, 4×4×4 grid) with broader non-periodic coverage across four boundary scenarios, a brute-force cross-validation, and all three floating-point precisions.
  • Updates the brute-force test's lower bound from (-8, −8, −8) to (−4, −4, −4) so the point cloud genuinely straddles the origin (addressed from the previous review thread).

Concern identified:

  • Because trunc is monotone non-decreasing, for any true neighbour pair |pa − pb| ≤ r, the build bug (wrong virtual cell) and the query bug (wrong lower bound) cancel each other out in non-periodic grids: trunc(pa/cw) always lands inside [trunc((pb−r)/cw), trunc((pb+r)/cw)]. All new test assertions therefore hold equally for the old int()-based code as for the new floor()-based code — they are not genuine regression tests that would fail if the native fix were reverted. The removed test_hashgrid_negative_wrapping used modulo aliasing in a small periodic grid to break this cancellation and produce an asymmetric [1, 2] vs. [2, 2] result.

Confidence Score: 4/5

Safe to merge but the test suite loses its only genuine regression test for the floor/trunc fix — reverting the native change would still produce a green suite.

The native fix in hashgrid.h is correct and already in the base branch. The new tests document expected behaviour and cover precisions and coordinate ranges well. However, due to the monotonicity-of-trunc cancellation, all new assertions pass with both the old int() and new floor() code, so a P1 regression-coverage gap remains.

warp/tests/geometry/test_hash_grid.py — the three new negative-coordinate test functions need redesign or supplementation to provide genuine regression coverage for the floor() fix.

Important Files Changed

Filename Overview
warp/tests/geometry/test_hash_grid.py Replaces test_hashgrid_negative_wrapping with three new negative-coordinate tests; due to the monotonicity of trunc, build and query bugs cancel out in non-periodic grids so the new assertions would pass with both fixed and unfixed native code, weakening regression coverage.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Point at pa (e.g. -0.3)"] --> B{"cell_func(pa/cw)"}
    B -- "floor: -1 ✓" --> C["Virtual cell -1\nPhysical cell 63"]
    B -- "trunc: 0 ✗" --> D["Virtual cell 0\nPhysical cell 0"]
    E["Query from pb (e.g. +0.2), r=0.6"] --> F{"Query lower bound\ncell_func((pb-r)/cw)"}
    F -- "floor(-0.4)=-1 ✓" --> G["Search range: [-1, 0]\nPhysical cells: {63, 0}"]
    F -- "trunc(-0.4)=0 ✗" --> H["Search range: [0, 0]\nPhysical cells: {0}"]
    C --> I{Point found?}
    G --> I
    I -- "Cell 63 searched ✓" --> J["Count = 2 ✓ (correct)"]
    D --> K{Point found?}
    H --> K
    K -- "Cell 0 searched ✓\n(bug cell == query cell)" --> L["Count = 2 ✓ (same result!)"]
    J --> M["Tests CANNOT distinguish\nfixed vs unfixed code"]
    L --> M
    style D fill:#ffcccc
    style H fill:#ffcccc
    style M fill:#ffe0b2
Loading

Reviews (5): Last reviewed commit: "Merge branch 'main' into fix/hashgrid-ne..." | Re-trigger Greptile

Comment on lines +474 to +476
# Generate points centred on the origin so half are in negative space
points = particle_grid(8, 8, 8, (-8.0, -8.0, -8.0), cell_width * 0.25, 0.1)
points_arr = wp.array(points, dtype=wp.vec3, device=device)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Misleading comment — points are not centred on the origin

The comment says "Generate points centred on the origin so half are in negative space", but the call is particle_grid(8, 8, 8, (-8.0, -8.0, -8.0), ...).

particle_grid builds a grid via np.linspace(0, dim, dim) (range [0, 8]) scaled by radius * 2.0 = 1.0, then shifted by lower = (-8.0, …). This yields coordinates in approximately [-8.0, 0.0] — nearly all negative, not half/half straddling zero.

To genuinely centre on the origin the lower bound should be -4.0:

Suggested change
# Generate points centred on the origin so half are in negative space
points = particle_grid(8, 8, 8, (-8.0, -8.0, -8.0), cell_width * 0.25, 0.1)
points_arr = wp.array(points, dtype=wp.vec3, device=device)
# Generate points centred on the origin so half are in negative space
points = particle_grid(8, 8, 8, (-4.0, -4.0, -4.0), cell_width * 0.25, 0.1)

The test still exercises negative coordinates correctly and the brute-force comparison remains valid, but the comment/lower-bound mismatch could mislead future readers or mask coverage gaps if the test is used as a template for further work.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch on the comment wording — you're right that with lower=(-8, -8, -8) and linspace(0, 8, 8) scaled by 1.0, the points span roughly [-8, 0] rather than straddeling the origin symmetrically. The comment is misleading there.

That said, the test's primary purpose is exercising the floor() fix in negative coordinate space and cross-validating against the brute-force reference kernel. Since the entire cluster sits in negative space the coverage for the bug scenario is actually slightly better than a symmetric distribution would give. The brute-force comparison remains valid regardless of the offset.

I'll update the comment to accurately describe the coordinate range in a follow-up to avoid confusing future readers. Cheers.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good spot — updated the lower bound to (-4, -4, -4) so the points actually straddle the origin as the comment says. Cheers.

Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
warp/tests/geometry/test_hash_grid.py (1)

363-370: Use repo docstring markup for code identifiers.

Please wrap code elements like int(), floor(), and test_hashgrid_query in double backticks to match the Python docstring convention used in this repo.

As per coding guidelines, "Use double backticks for code elements in docstrings (RST syntax): ``.nvdb``, ``"none"``".

Also applies to: 465-469

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@warp/tests/geometry/test_hash_grid.py` around lines 363 - 370, Update the
docstring to use reStructuredText code markup by wrapping the inline code
elements with double backticks: replace occurrences of int(), floor(), and
test_hashgrid_query with ``int()``, ``floor()``, and ``test_hashgrid_query``
(also update the other instances noted at lines 465-469) so the docstring
follows the repo's RST convention for code identifiers; keep the surrounding
text unchanged and ensure punctuation remains outside the backticks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@warp/tests/geometry/test_hash_grid.py`:
- Around line 363-370: Update the docstring to use reStructuredText code markup
by wrapping the inline code elements with double backticks: replace occurrences
of int(), floor(), and test_hashgrid_query with ``int()``, ``floor()``, and
``test_hashgrid_query`` (also update the other instances noted at lines 465-469)
so the docstring follows the repo's RST convention for code identifiers; keep
the surrounding text unchanged and ensure punctuation remains outside the
backticks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 6ce80138-54dc-446f-9f08-2c9709dc24f9

📥 Commits

Reviewing files that changed from the base of the PR and between 3af8dfa and 59394a8.

📒 Files selected for processing (2)
  • warp/native/hashgrid.h
  • warp/tests/geometry/test_hash_grid.py

cluster2600 and others added 2 commits March 12, 2026 07:23
Replace C++ int() cast (truncation toward zero) with
(int)floor() (toward negative infinity) for float-to-int
conversion in hashgrid.h.  Three locations are affected:

  1. Point cell assignment during grid build (line 86)
  2. Query lower bounds (lines 150-152)
  3. Query upper bounds (lines 155-157)

Without this fix, points with negative fractional cell
coordinates are assigned to the wrong grid cell, and query
bounds that cross the zero boundary miss valid cells.  For
example, with cell_width=1.0 a point at -0.3 should be in
cell -1 but int(-0.3)=0 places it in cell 0.

The (int)floor() pattern is already used elsewhere in the
Warp native code (noise.h, texture.h, volume.h).

Closes NVIDIA#1256

Signed-off-by: Maxime Grenu <maxime.grenu@gmail.com>
- Run clang-format on hashgrid.h to fix pre-commit.ci failure
- Use double backticks for code elements in docstrings per repo convention

Signed-off-by: Maxime Kawawa-Beaudan <maxkawab@gmail.com>
@cluster2600 cluster2600 force-pushed the fix/hashgrid-negative-coord-truncation branch from 63cc6ee to 5578a4a Compare March 12, 2026 06:24
Copy link
Copy Markdown

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@warp/tests/geometry/test_hash_grid.py`:
- Around line 607-615: Remove the stale registration that refers to the
undefined test function: find the add_function_test call registering
"test_hashgrid_negative_wrapping" (associated with TestHashGrid) and delete that
line so only existing functions like test_hashgrid_negative_coordinates,
test_hashgrid_negative_brute_force, and test_hashgrid_negative_multiprecision
are registered; ensure no other references to test_hashgrid_negative_wrapping
remain in the file to avoid NameError on import.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 11225c32-d2c0-4477-a868-399e4c637d14

📥 Commits

Reviewing files that changed from the base of the PR and between 63cc6ee and 5578a4a.

📒 Files selected for processing (1)
  • warp/tests/geometry/test_hash_grid.py

cluster2600 and others added 2 commits March 23, 2026 08:25
Correct the lower bound in test_hashgrid_negative_brute_force from
(-8, -8, -8) to (-4, -4, -4) so the generated points genuinely straddle
the origin, matching the comment that says "half are in negative space".

Remove the stale add_function_test registration for the renamed
test_hashgrid_negative_wrapping (now test_hashgrid_negative_coordinates),
which would raise NameError at import time.

Signed-off-by: Maxime Grenu <maxime.grenu@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] HashGrid truncation error causes missed neighbors with negative coordinates

1 participant