Integer grid-position columns for exact grid joins - #219
Conversation
a384ed2 to
7988025
Compare
|
🤖 Samudra's canonical loader would use this as The current diff emits A regression test using |
Review feedback (#219): a canonical loader filters on the integer grid position (e.g. `WHERE time_idx IN (...)`), so index columns need partition pruning in addition to exact joins. Previously `_block_metadata` reported bounds only for the original dimension coordinates, so an index filter stayed correct but scanned every partition. Emit `(start, start + n - 1, "int64")` bounds for each `<dim>_idx` column when index columns are enabled. Blocks are contiguous slices, so a partition's positions are exactly `[start, start+n-1]`; this holds for every coordinate dtype, including ones whose values are not prunable (strings, out-of-range datetimes), so index pruning can work even where coordinate pruning cannot. No Rust change: the native provider derives its prunable dimension-column set from the partition metadata keys, and `ScalarBound::Int64` already compares against `Int32` literals, so the new `<dim>_idx` keys become prunable automatically. Regression test registers one partition per time step and asserts, via the reader's iteration callback, that `WHERE time_idx IN (2, 5, 7)` instantiates only those three partition factories (time coordinate values are unrelated to the index, so pruning can only succeed via time_idx). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VuSeCio99NcME5eubcN3N
|
Good call — done in
No native change was needed: Added the regression test you described: one partition per time step, Verified locally (native build + tests): the 5 grid-index tests pass and the 21 Generated by Claude Code |
Review feedback (#219): a canonical loader filters on the integer grid position (e.g. `WHERE time_idx IN (...)`), so index columns need partition pruning in addition to exact joins. Previously `_block_metadata` reported bounds only for the original dimension coordinates, so an index filter stayed correct but scanned every partition. Emit `(start, start + n - 1, "int64")` bounds for each `<dim>_idx` column when index columns are enabled. Blocks are contiguous slices, so a partition's positions are exactly `[start, start+n-1]`; this holds for every coordinate dtype, including ones whose values are not prunable (strings, out-of-range datetimes), so index pruning can work even where coordinate pruning cannot. No Rust change: the native provider derives its prunable dimension-column set from the partition metadata keys, and `ScalarBound::Int64` already compares against `Int32` literals, so the new `<dim>_idx` keys become prunable automatically. Regression test registers one partition per time step and asserts, via the reader's iteration callback, that `WHERE time_idx IN (2, 5, 7)` instantiates only those three partition factories (time coordinate values are unrelated to the index, so pruning can only succeed via time_idx). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VuSeCio99NcME5eubcN3N
1636614 to
2ca3a88
Compare
`from_dataset(..., index_columns=True)` adds an int32 `<dim>_idx` column for every dimension, carrying each row's absolute integer position on that axis. Grids can then be joined on exact integer keys instead of floating-point coordinates: regridding and forecast alignment express a source-to-weight-table join on the source coordinate, and joining on the float value is fragile — a reproject/interp computed in float32 drifts sub-ULP and the equality join silently drops rows (at realistic scale a float32 round-trip of the weight coordinates drops ~99.9% of destination cells). Integer keys are exact, a bit faster (integer hashing), and half the key bytes; unlike dictionary-encoded coordinates they are plain Int32 columns, so nothing in DataFusion's join/aggregate/scalar-function paths is stressed. The indices are global, not per-partition: the reader adds each block's start offset so keys line up across chunks (a local index would restart at 0 in every partition and mis-join). Index columns also carry partition-pruning bounds `(start, start + n - 1, "int64")`, so `WHERE <dim>_idx IN (...)` prunes whole partitions like a dimension coordinate — the native provider already derives its prunable set from the metadata keys and compares Int64 bounds against Int32 literals, so no Rust change is needed. Coordinate columns stay dense and available for value predicates and display; the feature is off by default. - df.py: `_parse_schema(index_columns=)` appends the fields (collision-guarded); `iter_record_batches` / `dataset_to_record_batch` emit them from strided position plus a block offset, in both the full-pivot and per-batch paths; `_block_metadata(index_columns=)` emits the pruning bounds. - reader.py / sql.py: thread `index_columns` through `read_xarray_table` and `from_dataset`, computing per-block offsets so indices are global. - tests: indices global across chunks, exact index-keyed regrid, the float32-drift case where the float join drops cells but the index join stays exact, and a pruning regression asserting `time_idx IN (...)` instantiates only the matching partition factories. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VuSeCio99NcME5eubcN3N
0550829 to
283e9b2
Compare
|
Rebased onto What was failing: the branch had fallen ~7 commits behind Fix: re-implemented the feature on current Your pruning review is included in this commit: Generated by Claude Code |
Add opt-in
int32<dim>_idxcolumns so grid joins (regridding, forecast alignment) can key on exact integer positions instead of floating-point coordinate values.Why
Regridding is a sparse matmul expressed as
JOIN source grid TO weight table ON the source coordinate. Keying that join on the float coordinate value is fragile: any sub-ULP drift — e.g. a reproject/interp UDF that computes in float32 (cases 07/09) — makes the equality join silently drop rows and return a wrong answer with no error.A prototype at realistic scale (900×900 source, 3.24M weight rows) makes this concrete:
Speed is a secondary ~1.1× (the GROUP BY/SUM dominates) and the keys are 2× smaller (int32 vs float64).
What
from_dataset(..., index_columns=True)(andread_xarray_table(..., index_columns=True)) emit, for every dimension, anint32<dim>_idxcolumn carrying each row's absolute integer position on that axis:Int32columns — not dictionary-encoded — so none of DataFusion's join/aggregate/scalar-function paths are stressed (this is the safe alternative to the shelved dictionary-encoding approach in Dictionary-encode coordinate columns #217).WHERE lat > 45,date_part) and display. Index columns are opt-in, off by default, so nothing changes for existing users.Implementation
df.py:_parse_schema(index_columns=)appends the<dim>_idxfields (with a collision guard);iter_record_batches/dataset_to_record_batchemit them from the strided position plus a block offset.reader.py/sql.py: threadindex_columnsthroughread_xarray_tableandfrom_dataset, computing per-block offsets.Tests
tests/test_grid_index.py: schema/dtype, indices global across chunks, an exact index-keyed regrid matching a numpy gather, and the float32-drift case where the float-equality join drops cells but the index join stays exact. Full suite green (190), plusruffandmypy. No Rust changes.🤖 Generated with Claude Code
https://claude.ai/code/session_019VuSeCio99NcME5eubcN3N
Generated by Claude Code