From 89960d8586fb9a8d2fbac94d1c0a82f772250e55 Mon Sep 17 00:00:00 2001 From: Matthew Murray Date: Wed, 24 Jun 2026 21:55:08 +0000 Subject: [PATCH 1/3] Add a deterministic test for dropping dynamic predicates --- .../cudf_polars/cudf_polars/dsl/translate.py | 2 +- python/cudf_polars/tests/test_filter.py | 42 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/python/cudf_polars/cudf_polars/dsl/translate.py b/python/cudf_polars/cudf_polars/dsl/translate.py index 53e76ea6b89e..99e5142aa9eb 100644 --- a/python/cudf_polars/cudf_polars/dsl/translate.py +++ b/python/cudf_polars/cudf_polars/dsl/translate.py @@ -302,7 +302,7 @@ def _drop_dyn_pred_hints( left = _drop_dyn_pred_hints(translator, node.left, schema) right = _drop_dyn_pred_hints(translator, node.right, schema) if left is None: - return right # pragma: no cover + return right if right is None: return left return expr.BinOp( diff --git a/python/cudf_polars/tests/test_filter.py b/python/cudf_polars/tests/test_filter.py index 71781454e70d..b9150adfd024 100644 --- a/python/cudf_polars/tests/test_filter.py +++ b/python/cudf_polars/tests/test_filter.py @@ -1,11 +1,15 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations import pytest import polars as pl +from polars import polars as plrs # type: ignore[attr-defined] +from cudf_polars.dsl import expr, ir +from cudf_polars.dsl.translate import Translator +from cudf_polars.dsl.traversal import traversal from cudf_polars.testing.asserts import assert_gpu_result_equal @@ -40,3 +44,39 @@ def test_filter_drops_dynamic_predicate_hint(engine: pl.GPUEngine): # before executing on the GPU. q = ldf.filter((pl.col("a") > 1) & (pl.col("c") == 3)).sort("b").head(3) assert_gpu_result_equal(q, engine=engine) + + +def test_drop_dyn_pred_hints(): + class DynamicPredHint(Exception): + pass + + class DynamicPredVisitor: + def __init__(self, inner, op): + self.inner = inner + self.op = op + + def __getattr__(self, name): + return getattr(self.inner, name) + + def view_expression(self, n): + node = self.inner.view_expression(n) + if isinstance(node, plrs._expr_nodes.BinaryExpr) and node.op == self.op: + raise DynamicPredHint("dynamic_pred") + return node + + ldf = pl.LazyFrame( + {"a": [1, 2, 3, 4, 5], "b": [5, 4, 3, 2, 1], "c": [1, 1, 3, 3, 5]} + ) + query = ldf.filter( + ((pl.col("b") < 5) & (pl.col("a") > 1)) + & ((pl.col("c") == 3) & (pl.col("b") < 3)) + ) + + visitor = DynamicPredVisitor(query._ldf.visit(), plrs._expr_nodes.Operator.Lt) + translator = Translator(visitor, pl.GPUEngine()) + node = translator.translate_ir() + + assert translator.errors == [] + assert isinstance(node, ir.Filter) + columns = {n.name for n in traversal([node.mask.value]) if isinstance(n, expr.Col)} + assert columns == {"a", "c"} From 4f76f817a1c4d7128a6f0fb3608f74f666b21a83 Mon Sep 17 00:00:00 2001 From: Matthew Murray Date: Wed, 24 Jun 2026 22:26:27 +0000 Subject: [PATCH 2/3] skip code coverage where dyn pred are dropped --- .../cudf_polars/cudf_polars/dsl/translate.py | 6 ++- python/cudf_polars/tests/test_filter.py | 38 ------------------- 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/python/cudf_polars/cudf_polars/dsl/translate.py b/python/cudf_polars/cudf_polars/dsl/translate.py index 99e5142aa9eb..d9f2b03c3f74 100644 --- a/python/cudf_polars/cudf_polars/dsl/translate.py +++ b/python/cudf_polars/cudf_polars/dsl/translate.py @@ -301,9 +301,11 @@ def _drop_dyn_pred_hints( ): left = _drop_dyn_pred_hints(translator, node.left, schema) right = _drop_dyn_pred_hints(translator, node.right, schema) - if left is None: + # Which branch is taken depends on where the dynamic predicate lands in + # the AND tree, which is nondeterministic. + if left is None: # pragma: no cover return right - if right is None: + if right is None: # pragma: no cover return left return expr.BinOp( DataType(translator.visitor.get_dtype(n)), diff --git a/python/cudf_polars/tests/test_filter.py b/python/cudf_polars/tests/test_filter.py index b9150adfd024..6e54aa0fa093 100644 --- a/python/cudf_polars/tests/test_filter.py +++ b/python/cudf_polars/tests/test_filter.py @@ -5,11 +5,7 @@ import pytest import polars as pl -from polars import polars as plrs # type: ignore[attr-defined] -from cudf_polars.dsl import expr, ir -from cudf_polars.dsl.translate import Translator -from cudf_polars.dsl.traversal import traversal from cudf_polars.testing.asserts import assert_gpu_result_equal @@ -46,37 +42,3 @@ def test_filter_drops_dynamic_predicate_hint(engine: pl.GPUEngine): assert_gpu_result_equal(q, engine=engine) -def test_drop_dyn_pred_hints(): - class DynamicPredHint(Exception): - pass - - class DynamicPredVisitor: - def __init__(self, inner, op): - self.inner = inner - self.op = op - - def __getattr__(self, name): - return getattr(self.inner, name) - - def view_expression(self, n): - node = self.inner.view_expression(n) - if isinstance(node, plrs._expr_nodes.BinaryExpr) and node.op == self.op: - raise DynamicPredHint("dynamic_pred") - return node - - ldf = pl.LazyFrame( - {"a": [1, 2, 3, 4, 5], "b": [5, 4, 3, 2, 1], "c": [1, 1, 3, 3, 5]} - ) - query = ldf.filter( - ((pl.col("b") < 5) & (pl.col("a") > 1)) - & ((pl.col("c") == 3) & (pl.col("b") < 3)) - ) - - visitor = DynamicPredVisitor(query._ldf.visit(), plrs._expr_nodes.Operator.Lt) - translator = Translator(visitor, pl.GPUEngine()) - node = translator.translate_ir() - - assert translator.errors == [] - assert isinstance(node, ir.Filter) - columns = {n.name for n in traversal([node.mask.value]) if isinstance(n, expr.Col)} - assert columns == {"a", "c"} From 3ceb789ff5f7d2f5bbc2705fa41dfc73fdf4122b Mon Sep 17 00:00:00 2001 From: Matthew Murray Date: Wed, 24 Jun 2026 22:34:03 +0000 Subject: [PATCH 3/3] cleanup --- python/cudf_polars/tests/test_filter.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/cudf_polars/tests/test_filter.py b/python/cudf_polars/tests/test_filter.py index 6e54aa0fa093..113d82729b38 100644 --- a/python/cudf_polars/tests/test_filter.py +++ b/python/cudf_polars/tests/test_filter.py @@ -40,5 +40,3 @@ def test_filter_drops_dynamic_predicate_hint(engine: pl.GPUEngine): # before executing on the GPU. q = ldf.filter((pl.col("a") > 1) & (pl.col("c") == 3)).sort("b").head(3) assert_gpu_result_equal(q, engine=engine) - -