Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
5861da9
Support streaming over expression
Matt711 Apr 14, 2026
66be8e9
Add fast path for scalar aggs case, support preserving the full oreder
Matt711 Apr 16, 2026
3b15601
add more tests, protect against OOMs when preserving the full order
Matt711 Apr 17, 2026
6b91f1b
check style
Matt711 Apr 17, 2026
ac73ede
add pre-shuffle test, and clarifying comments
Matt711 Apr 17, 2026
0c8e3de
add multi-rank test
Matt711 Apr 17, 2026
696bbc9
style
Matt711 Apr 17, 2026
fdc40c9
Merge branch 'main' into fea/polars/streaming-over
Matt711 Apr 17, 2026
e477439
reject non decomposable aggregations
Matt711 Apr 17, 2026
4a6eb6d
fix xfail condition for mg test
Matt711 Apr 17, 2026
0c48e1d
Merge branch 'main' into fea/polars/streaming-over
Matt711 Apr 17, 2026
0d6d6b1
Merge branch 'main' into fea/polars/streaming-over
Matt711 Apr 17, 2026
01d8777
Merge branch 'main' into fea/polars/streaming-over
Matt711 Apr 17, 2026
f1cf13c
Add Over IR node
Matt711 Apr 21, 2026
4ac5e65
decompose select with over expressions with mixed partition by keys
Matt711 Apr 21, 2026
c02eea9
move IR to type checking block
Matt711 Apr 21, 2026
edc704e
add Over IR node
Matt711 Apr 21, 2026
5d28fb9
merge conflict
Matt711 Apr 22, 2026
4fc4d25
merge conflict
Matt711 May 1, 2026
e1442f2
Merge branch 'main' of https://github.com/rapidsai/cudf into fea/pola…
Matt711 May 2, 2026
d632dd4
add _decompose_grouped_window_node
rjzamora May 4, 2026
a7a914a
Merge remote-tracking branch 'upstream/main' into streaming-over-rick
rjzamora May 4, 2026
6bee1b8
cull unnecessary code
rjzamora May 4, 2026
4ca3e3c
address review
Matt711 May 4, 2026
d690b1e
heavy revisions
rjzamora May 4, 2026
9dab468
Merge remote-tracking branch 'upstream/main' into fea/polars/streamin…
rjzamora May 4, 2026
f8db130
Merge branch 'streaming-over-rick' into fea/polars/streaming-over
rjzamora May 4, 2026
3893253
Merge branch 'main' into fea/polars/streaming-over
rjzamora May 4, 2026
ca1e808
fix non-scalar over() by absorbing passthrough cols into Over node
Matt711 May 4, 2026
ec93d63
fix non-scalar over() with duplicated=True input
Matt711 May 5, 2026
f275788
update decompose condition for dynamic planning
Matt711 May 5, 2026
6051ac5
Fix scalar-over path: update _DECOMPOSABLE_AGG_NAMES, assert no-presh…
Matt711 May 5, 2026
b272f6c
Fix over_actor: remove stale key assertion, fix shuffle modulus to us…
Matt711 May 5, 2026
4d1b482
Add Over.do_evaluate and remove eval_ir indirection in over_actor
Matt711 May 5, 2026
85f77fd
Extract _allgather_and_broadcast and _shuffle_and_reassemble from ove…
Matt711 May 5, 2026
4c4a33f
Fix shuffle modulus: AllGather total size/count and compute from targ…
Matt711 May 5, 2026
563bac7
restoring _fuse_over_nodes
Matt711 May 6, 2026
dad1a79
merge conflict
Matt711 May 6, 2026
30e8826
update fixture name
Matt711 May 6, 2026
f9fdd46
add a second (reverse) shuffle for mg correctness
Matt711 May 6, 2026
c6a7b10
estimate the modulus
Matt711 May 6, 2026
3051291
remove xfail marker from test
Matt711 May 6, 2026
bd076c3
simplify skip condition in spmd over test
Matt711 May 6, 2026
12620c8
set allow_subset=True
Matt711 May 6, 2026
5f4dd3e
remove input_ir arg
Matt711 May 6, 2026
09a4586
simplify conditional statement
Matt711 May 6, 2026
ccaee76
TODO tracer API
Matt711 May 6, 2026
bc584d7
rename split* instead of boundaries
Matt711 May 6, 2026
a723dc8
use int32
Matt711 May 6, 2026
902a4cd
clean up docstring OriginStamps docstring
Matt711 May 6, 2026
338757a
Merge branch 'main' into fea/polars/streaming-over
Matt711 May 6, 2026
0a94101
fix upstream polars tests
Matt711 May 7, 2026
370fc21
Merge branch 'main' into fea/polars/streaming-over
Matt711 May 7, 2026
5b25eea
Merge branch 'main' into fea/polars/streaming-over
Matt711 May 7, 2026
7c81adf
address driveby nits
Matt711 May 7, 2026
68a7983
drop id() as dict key
Matt711 May 7, 2026
7975ff4
simplify _evaluate_window_with_stamps
Matt711 May 7, 2026
a6f397c
address more nits
Matt711 May 7, 2026
ea39bd7
docstrings & use names_to_indices
Matt711 May 7, 2026
658e4bf
simplify scalar-Over IR + cleanup
Matt711 May 7, 2026
d3b6562
colleect ir rewrite for scalar path ahead of time, comment improvemen…
Matt711 May 7, 2026
7b828be
add module doc string overviewing the algorithm, few smaller clean ups
Matt711 May 7, 2026
397d50f
more clean ups
Matt711 May 7, 2026
946582a
Merge branch 'main' into fea/polars/streaming-over
Matt711 May 7, 2026
f7687f1
oh yeah, dont use to_arrow
Matt711 May 7, 2026
beee7d9
merge conflict
Matt711 May 11, 2026
2b8b357
Merge branch 'main' of https://github.com/rapidsai/cudf into fea/pola…
Matt711 May 11, 2026
fc9f7bb
no allgather unecessarily
Matt711 May 11, 2026
3a23609
update NormalizedPartitioning.from_keys call site
Matt711 May 12, 2026
6f29e67
merge conflict
Matt711 May 12, 2026
564aaf0
move names_to_indices to avoid circular import
Matt711 May 12, 2026
4d10e42
Merge branch 'main' into fea/polars/streaming-over
Matt711 May 12, 2026
8cdf98b
shared name generator
Matt711 May 12, 2026
0582e29
address small nits
Matt711 May 12, 2026
191e537
more fuse over nodes simplifications
Matt711 May 12, 2026
5dbb1dc
Stream-sync the broadcast against global_agg_df in rapidsmpf over actor
Matt711 May 12, 2026
232b8ae
clean ups
Matt711 May 12, 2026
dfb3ffe
more review nits
Matt711 May 12, 2026
caadc2f
remove rename comment and clarify docstrings for scalar agg path
Matt711 May 12, 2026
0db0c7f
promote ChunkStore to a shared utility and use it in rapidsmpf over a…
Matt711 May 13, 2026
f63c7db
abstract allgather-post-reduce pattern
Matt711 May 13, 2026
80cb585
Use LocalRepartitioner to avoid materialising the full return shuffle
Matt711 May 13, 2026
5480a29
Merge branch 'main' into fea/polars/streaming-over
Matt711 May 13, 2026
b834838
add TODO for slicing the input when only rank 0 inserts
Matt711 May 13, 2026
4cc0003
Merge branch 'main' into fea/polars/streaming-over
Matt711 May 13, 2026
9491a9e
zip local partition ids with sequence numbers in the over emit loop
Matt711 May 13, 2026
92501aa
add a TODO for per stream work
Matt711 May 13, 2026
cf5b099
Merge branch 'main' into fea/polars/streaming-over
Matt711 May 13, 2026
96f24e0
distinct stream per chunk
Matt711 May 13, 2026
520badf
check is strictly partitioned
Matt711 May 14, 2026
0483abd
exercise both single- and multi-row chunks in test_over_many_partitions
Matt711 May 14, 2026
d2a0a60
merge conflict
Matt711 May 14, 2026
5b75ae3
address small nits
Matt711 May 15, 2026
590317f
prune gw_nodes; tighten bound for reserve_extra; use streaming fixtur…
Matt711 May 15, 2026
8162194
Merge branch 'release/26.06' into fea/polars/streaming-over
Matt711 May 15, 2026
bb0a75f
Merge branch 'release/26.06' into fea/polars/streaming-over
Matt711 May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions python/cudf_polars/cudf_polars/dsl/expressions/rolling.py
Original file line number Diff line number Diff line change
Expand Up @@ -615,13 +615,17 @@ def _grouped_window_scan_setup(
local = self._sorted_grouper(by_cols_for_scan)
return order_index, by_cols_for_scan, local

# TODO: this is an ordered left-join that drops the join keys.
# Rename it and replace the manual scatter+gather with the Join IR's
# _reorder_maps helper (lifted to a shared utility) so the streaming
# and in-memory over paths share the same primitive.
def _broadcast_agg_results(
self,
by_tbl: plc.Table,
group_keys_tbl: plc.Table,
value_tbls: list[plc.Table],
names: list[str],
dtypes: list[DataType],
value_tbl: plc.Table,
names: Sequence[str],
dtypes: Sequence[DataType],
stream: Stream,
) -> list[Column]:
# We do a left-join between the input keys to group-keys
Expand All @@ -647,7 +651,7 @@ def _broadcast_agg_results(

# Broadcast each scalar aggregated result back to row-shape using
# the aligned mapping between row indices and group indices.
out_cols = (t.columns()[0] for t in value_tbls)
out_cols = value_tbl.columns()
return [
Column(
plc.copying.gather(
Expand Down Expand Up @@ -773,7 +777,7 @@ def do_evaluate( # noqa: D102
broadcasted_cols = self._broadcast_agg_results(
by_tbl,
group_keys_tbl,
value_tables,
plc.Table([col for t in value_tables for col in t.columns()]),
out_names,
out_dtypes,
df.stream,
Expand Down Expand Up @@ -807,7 +811,7 @@ def do_evaluate( # noqa: D102
self._broadcast_agg_results(
by_tbl,
group_keys_tbl_local,
value_tables_local,
plc.Table([col for t in value_tables_local for col in t.columns()]),
out_names,
out_dtypes,
df.stream,
Expand Down
41 changes: 33 additions & 8 deletions python/cudf_polars/cudf_polars/dsl/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
)

if TYPE_CHECKING:
from collections.abc import Generator

from polars import GPUEngine

from cudf_polars.typing import NodeTraverser
Expand Down Expand Up @@ -90,6 +92,7 @@ def __init__(self, visitor: NodeTraverser, engine: GPUEngine):
self.errors: list[Exception] = []
self._cache_nodes: dict[int, ir.Cache] = {}
self._expr_context: ExecutionContext = ExecutionContext.FRAME
self._internal_name_gen: Generator[str, None, None] | None = None

def translate_ir(self, *, n: int | None = None) -> ir.IR:
"""
Expand Down Expand Up @@ -247,6 +250,24 @@ def __exit__(self, *args: Any) -> None:
self.translator._expr_context = self._prev


class set_internal_name_gen(AbstractContextManager[None]):
"""Share one internal-name generator across sibling expression translations."""

__slots__ = ("_prev", "schema", "translator")

def __init__(self, translator: Translator, schema: Schema) -> None:
self.translator = translator
self.schema = schema
self._prev: Generator[str, None, None] | None = None

def __enter__(self) -> None:
self._prev = self.translator._internal_name_gen
self.translator._internal_name_gen = unique_names(self.schema)

def __exit__(self, *args: Any) -> None:
self.translator._internal_name_gen = self._prev


@singledispatch
def _translate_ir(node: Any, translator: Translator, schema: Schema) -> ir.IR:
raise NotImplementedError(
Expand Down Expand Up @@ -362,9 +383,11 @@ def _(
def _(node: plrs._ir_nodes.Select, translator: Translator, schema: Schema) -> ir.IR:
with set_node(translator.visitor, node.input):
inp = translator.translate_ir(n=None)
exprs = [
translate_named_expr(translator, n=e, schema=inp.schema) for e in node.expr
]
with set_internal_name_gen(translator, inp.schema):
exprs = [
translate_named_expr(translator, n=e, schema=inp.schema)
for e in node.expr
]
return ir.Select(schema, exprs, node.should_broadcast, inp)


Expand Down Expand Up @@ -478,9 +501,11 @@ def _(node: plrs._ir_nodes.Join, translator: Translator, schema: Schema) -> ir.I
def _(node: plrs._ir_nodes.HStack, translator: Translator, schema: Schema) -> ir.IR:
with set_node(translator.visitor, node.input):
inp = translator.translate_ir(n=None)
exprs = [
translate_named_expr(translator, n=e, schema=inp.schema) for e in node.exprs
]
with set_internal_name_gen(translator, inp.schema):
exprs = [
translate_named_expr(translator, n=e, schema=inp.schema)
for e in node.exprs
]
return ir.HStack(schema, exprs, node.should_broadcast, inp)


Expand Down Expand Up @@ -830,7 +855,7 @@ def _(
# pl.col("a").rolling(...)
with set_expr_context(translator, ExecutionContext.ROLLING):
agg = translator.translate_expr(n=node.function, schema=schema)
name_generator = unique_names(schema)
name_generator = translator._internal_name_gen or unique_names(schema)
Comment thread
Matt711 marked this conversation as resolved.
aggs, named_post_agg = decompose_single_agg(
expr.NamedExpr(next(name_generator), agg),
name_generator,
Expand Down Expand Up @@ -875,7 +900,7 @@ def _(
# not exposed until polars 1.39.
with set_expr_context(translator, ExecutionContext.WINDOW):
agg = translator.translate_expr(n=node.function, schema=schema)
name_gen = unique_names(schema)
name_gen = translator._internal_name_gen or unique_names(schema)
aggs, post = decompose_single_agg(
expr.NamedExpr(next(name_gen), agg),
name_gen,
Expand Down
33 changes: 31 additions & 2 deletions python/cudf_polars/cudf_polars/dsl/utils/naming.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES.
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES.
# SPDX-License-Identifier: Apache-2.0

"""Name generation utilities."""
Expand All @@ -7,11 +7,15 @@

from typing import TYPE_CHECKING

from cudf_polars.dsl.expr import NamedExpr

if TYPE_CHECKING:
from collections.abc import Generator, Iterable

from cudf_polars.typing import Schema


__all__ = ["unique_names"]
__all__ = ["names_to_indices", "unique_names"]


def unique_names(names: Iterable[str]) -> Generator[str, None, None]:
Expand All @@ -32,3 +36,28 @@ def unique_names(names: Iterable[str]) -> Generator[str, None, None]:
while True:
yield f"{prefix}{i}"
i += 1


def names_to_indices(
Comment thread
rjzamora marked this conversation as resolved.
names: tuple[str | NamedExpr, ...], schema: Schema
) -> tuple[int, ...]:
"""
Return column indices for the given names in schema order.

Accepts either column names (str) or NamedExpr, so it can be used with
e.g. ir.left_on, ir.right_on as well as plain name tuples.

Parameters
----------
names
The names to get indices for.
schema
The schema to get indices from.

Returns
-------
The column indices for each name in schema order.
"""
keys = list(schema.keys())
str_names = [n.name if isinstance(n, NamedExpr) else n for n in names]
return tuple(keys.index(n) for n in str_names)
Comment on lines +61 to +63

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid repeated linear scans and raise a clearer missing-column error.

Line 63 does keys.index(...) for each requested name, which is quadratic in wide schemas, and missing names surface as a generic list error. Build a name→index map once and raise an explicit error for unknown names.

Suggested patch
 def names_to_indices(
     names: tuple[str | NamedExpr, ...], schema: Schema
 ) -> tuple[int, ...]:
@@
-    keys = list(schema.keys())
-    str_names = [n.name if isinstance(n, NamedExpr) else n for n in names]
-    return tuple(keys.index(n) for n in str_names)
+    index_by_name = {name: i for i, name in enumerate(schema.keys())}
+    str_names = [n.name if isinstance(n, NamedExpr) else n for n in names]
+    try:
+        return tuple(index_by_name[n] for n in str_names)
+    except KeyError as e:
+        missing = e.args[0]
+        raise KeyError(f"Column {missing!r} not found in schema") from None

As per coding guidelines "Missing validation causing crashes on invalid input - Add size and type validation before operations."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudf_polars/cudf_polars/dsl/utils/naming.py` around lines 61 - 63, The
code currently computes tuple(keys.index(n) ...) causing O(n*m) scans and a
generic ValueError when a name is missing; replace this with a single pass map:
build a name_to_index dict from keys (e.g., {name: idx for idx, name in
enumerate(keys)}), convert names to strings as you already do with str_names
(handling NamedExpr), then look up each name in name_to_index and if missing
raise a clear KeyError or ValueError like "Unknown column: {name}". Update the
block that defines keys/str_names and the return to use this lookup to avoid
quadratic behavior and provide an explicit missing-column error.

6 changes: 6 additions & 0 deletions python/cudf_polars/cudf_polars/experimental/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@
from cudf_polars.dsl.expressions.base import Col, ExecutionContext, NamedExpr
from cudf_polars.dsl.expressions.binaryop import BinOp
from cudf_polars.dsl.expressions.literal import Literal
from cudf_polars.dsl.expressions.rolling import GroupedWindow
from cudf_polars.dsl.expressions.ternary import Ternary
from cudf_polars.dsl.expressions.unary import Cast, Len, UnaryFunction
from cudf_polars.dsl.ir import Distinct, Empty, HConcat, Select
from cudf_polars.dsl.traversal import (
CachingVisitor,
)
from cudf_polars.experimental.base import PartitionInfo
from cudf_polars.experimental.over import _decompose_grouped_window_node
from cudf_polars.experimental.repartition import Repartition
from cudf_polars.experimental.utils import _dynamic_planning_on

Expand Down Expand Up @@ -462,6 +464,10 @@ def _decompose_expr_node(
)
(expr,) = columns
return expr, input_ir, partition_info
elif isinstance(expr, GroupedWindow) and _dynamic_planning_on(config_options):
return _decompose_grouped_window_node(
expr, input_ir, partition_info, config_options, names=names
)
else:
# This is an un-supported expression - raise.
raise NotImplementedError(
Expand Down
Loading
Loading