diff --git a/datashader/antialias.py b/datashader/antialias.py new file mode 100644 index 000000000..b6dc3ed8d --- /dev/null +++ b/datashader/antialias.py @@ -0,0 +1,97 @@ +from __future__ import annotations +from enum import Enum +from typing import NamedTuple, TYPE_CHECKING + +from datashader.utils import ( + nanfirst_in_place, nanlast_in_place, nanmax_in_place, + nanmin_in_place, nansum_in_place, ngjit, parallel_fill) +from numba import literal_unroll + + +# Enum used to specify how the second stage aggregation is performed +# for 2-stage antialiased lines. +class AntialiasCombination(Enum): + SUM_1AGG = 1 + SUM_2AGG = 2 + MIN = 3 + MAX = 4 + FIRST = 5 + LAST = 6 + + +class AntialiasStage2(NamedTuple): + """Configuration for second-stage combination of a single antialiased reduction.""" + combination: AntialiasCombination + zero: float + + +if TYPE_CHECKING: + UnzippedAntialiasStage2 = tuple[tuple[AntialiasCombination], tuple[float]] + + +def two_stage_agg(antialias_stage_2: UnzippedAntialiasStage2 | None): + """Information used to perform the correct stage 2 aggregation.""" + if not antialias_stage_2: + # Not using antialiased lines, doesn't matter what is returned. + return False, False + + # A single combination in (SUM_2AGG, FIRST, LAST, MIN) means that a 2-stage + # aggregation will be used, otherwise use a 1-stage aggregation that is + # faster. + use_2_stage_agg = False + for comb in antialias_stage_2[0]: + if comb in (AntialiasCombination.SUM_2AGG, AntialiasCombination.MIN, + AntialiasCombination.FIRST, AntialiasCombination.LAST): + use_2_stage_agg = True + break + + # Boolean overwrite flag is used in _full_antialias() is True to overwrite + # pixel values (using max of previous and new values) or False for the more + # complicated correction algorithm. Prefer overwrite=True for speed, but + # any SUM_1AGG implies overwrite=False. + overwrite = True + for comb in antialias_stage_2[0]: + if comb == AntialiasCombination.SUM_1AGG: + overwrite = False + break + + return overwrite, use_2_stage_agg + + +@ngjit +def _combine_in_place(accum_agg, other_agg, antialias_combination): + if antialias_combination == AntialiasCombination.MAX: + nanmax_in_place(accum_agg, other_agg) + elif antialias_combination == AntialiasCombination.MIN: + nanmin_in_place(accum_agg, other_agg) + elif antialias_combination == AntialiasCombination.FIRST: + nanfirst_in_place(accum_agg, other_agg) + elif antialias_combination == AntialiasCombination.LAST: + nanlast_in_place(accum_agg, other_agg) + else: + nansum_in_place(accum_agg, other_agg) + + +@ngjit +def aa_stage_2_accumulate(aggs_and_copies, antialias_combinations): + k = 0 + # Numba access to heterogeneous tuples is only permitted using literal_unroll. + for agg_and_copy in literal_unroll(aggs_and_copies): + _combine_in_place(agg_and_copy[1], agg_and_copy[0], antialias_combinations[k]) + k += 1 + + +@ngjit +def aa_stage_2_clear(aggs_and_copies, antialias_zeroes): + k = 0 + # Numba access to heterogeneous tuples is only permitted using literal_unroll. + for agg_and_copy in literal_unroll(aggs_and_copies): + parallel_fill(agg_and_copy[0], antialias_zeroes[k]) + k += 1 + + +@ngjit +def aa_stage_2_copy_back(aggs_and_copies): + # Numba access to heterogeneous tuples is only permitted using literal_unroll. + for agg_and_copy in literal_unroll(aggs_and_copies): + agg_and_copy[0][:] = agg_and_copy[1][:] diff --git a/datashader/compiler.py b/datashader/compiler.py index 4bbdf15f8..ffa69f214 100644 --- a/datashader/compiler.py +++ b/datashader/compiler.py @@ -1,6 +1,6 @@ from __future__ import annotations - from itertools import count +from typing import TYPE_CHECKING from toolz import unique, concat, pluck, get, memoize import numpy as np @@ -14,6 +14,9 @@ except ImportError: cuda_mutex_lock, cuda_mutex_unlock = None, None +if TYPE_CHECKING: + from datashader.antialias import UnzippedAntialiasStage2 + __all__ = ['compile_components'] @@ -362,7 +365,7 @@ def make_antialias_stage_2(reds, bases): self_intersect = False break - def antialias_stage_2(array_module): + def antialias_stage_2(array_module) -> UnzippedAntialiasStage2: return tuple(zip(*concat(b._antialias_stage_2(self_intersect, array_module) for b in bases))) return self_intersect, antialias_stage_2 diff --git a/datashader/enums.py b/datashader/enums.py deleted file mode 100644 index 8aed80596..000000000 --- a/datashader/enums.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations -from enum import Enum - - -# Enum used to specify how the second stage aggregation is performed -# for 2-stage antialiased lines. -class AntialiasCombination(Enum): - SUM_1AGG = 1 - SUM_2AGG = 2 - MIN = 3 - MAX = 4 - FIRST = 5 - LAST = 6 diff --git a/datashader/glyphs/__init__.py b/datashader/glyphs/__init__.py index c792956fd..c1be92102 100644 --- a/datashader/glyphs/__init__.py +++ b/datashader/glyphs/__init__.py @@ -1,7 +1,6 @@ from __future__ import annotations from .points import Point, MultiPointGeometry # noqa (API import) from .line import ( # noqa (API import) - AntialiasCombination, LineAxis0, LineAxis0Multi, LinesAxis1, diff --git a/datashader/glyphs/line.py b/datashader/glyphs/line.py index fbfe6b2d2..0ed09899e 100644 --- a/datashader/glyphs/line.py +++ b/datashader/glyphs/line.py @@ -3,12 +3,11 @@ import numpy as np from toolz import memoize -from datashader.enums import AntialiasCombination +from datashader.antialias import ( + aa_stage_2_accumulate, aa_stage_2_clear, aa_stage_2_copy_back, two_stage_agg) from datashader.glyphs.points import _PointLike, _GeometryLike -from datashader.utils import ( - isnull, isreal, nanfirst_in_place, nanlast_in_place, nanmax_in_place, - nanmin_in_place, nansum_in_place, ngjit, parallel_fill) -from numba import cuda, literal_unroll +from datashader.utils import isnull, isreal, ngjit +from numba import cuda import numba.types as nb_types @@ -26,35 +25,6 @@ spatialpandas = None -def _two_stage_agg(antialias_stage_2): - """Information used to perform the correct stage 2 aggregation.""" - if not antialias_stage_2: - # Not using antialiased lines, doesn't matter what is returned. - return False, False - - # A single combination in (SUM_2AGG, FIRST, LAST, MIN) means that a 2-stage - # aggregation will be used, otherwise use a 1-stage aggregation that is - # faster. - use_2_stage_agg = False - for comb in antialias_stage_2[0]: - if comb in (AntialiasCombination.SUM_2AGG, AntialiasCombination.MIN, - AntialiasCombination.FIRST, AntialiasCombination.LAST): - use_2_stage_agg = True - break - - # Boolean overwrite flag is used in _full_antialias() is True to overwrite - # pixel values (using max of previous and new values) or False for the more - # complicated correction algorithm. Prefer overwrite=True for speed, but - # any SUM_1AGG implies overwrite=False. - overwrite = True - for comb in antialias_stage_2[0]: - if comb == AntialiasCombination.SUM_1AGG: - overwrite = False - break - - return overwrite, use_2_stage_agg - - class _AntiAliasedLine(object): """ Methods common to all lines. """ _line_width = 0 # Use antialiasing if > 0. @@ -84,7 +54,7 @@ def _internal_build_extend( expand_aggs_and_cols = self.expand_aggs_and_cols(append) map_onto_pixel = _build_map_onto_pixel_for_line( x_mapper, y_mapper, antialias) - overwrite, use_2_stage_agg = _two_stage_agg(antialias_stage_2) + overwrite, use_2_stage_agg = two_stage_agg(antialias_stage_2) draw_segment = _build_draw_segment( append, map_onto_pixel, expand_aggs_and_cols, line_width, overwrite ) @@ -177,7 +147,7 @@ def _internal_build_extend( expand_aggs_and_cols = self.expand_aggs_and_cols(append) map_onto_pixel = _build_map_onto_pixel_for_line( x_mapper, y_mapper, antialias) - overwrite, use_2_stage_agg = _two_stage_agg(antialias_stage_2) + overwrite, use_2_stage_agg = two_stage_agg(antialias_stage_2) draw_segment = _build_draw_segment( append, map_onto_pixel, expand_aggs_and_cols, line_width, overwrite ) @@ -293,7 +263,7 @@ def _internal_build_extend( expand_aggs_and_cols = self.expand_aggs_and_cols(append) map_onto_pixel = _build_map_onto_pixel_for_line( x_mapper, y_mapper, antialias) - overwrite, use_2_stage_agg = _two_stage_agg(antialias_stage_2) + overwrite, use_2_stage_agg = two_stage_agg(antialias_stage_2) draw_segment = _build_draw_segment( append, map_onto_pixel, expand_aggs_and_cols, line_width, overwrite ) @@ -368,7 +338,7 @@ def _internal_build_extend( expand_aggs_and_cols = self.expand_aggs_and_cols(append) map_onto_pixel = _build_map_onto_pixel_for_line( x_mapper, y_mapper, antialias) - overwrite, use_2_stage_agg = _two_stage_agg(antialias_stage_2) + overwrite, use_2_stage_agg = two_stage_agg(antialias_stage_2) draw_segment = _build_draw_segment( append, map_onto_pixel, expand_aggs_and_cols, line_width, overwrite ) @@ -444,7 +414,7 @@ def _internal_build_extend( expand_aggs_and_cols = self.expand_aggs_and_cols(append) map_onto_pixel = _build_map_onto_pixel_for_line( x_mapper, y_mapper, antialias) - overwrite, use_2_stage_agg = _two_stage_agg(antialias_stage_2) + overwrite, use_2_stage_agg = two_stage_agg(antialias_stage_2) draw_segment = _build_draw_segment( append, map_onto_pixel, expand_aggs_and_cols, line_width, overwrite ) @@ -522,7 +492,7 @@ def _internal_build_extend( expand_aggs_and_cols = self.expand_aggs_and_cols(append) map_onto_pixel = _build_map_onto_pixel_for_line( x_mapper, y_mapper, antialias) - overwrite, use_2_stage_agg = _two_stage_agg(antialias_stage_2) + overwrite, use_2_stage_agg = two_stage_agg(antialias_stage_2) draw_segment = _build_draw_segment( append, map_onto_pixel, expand_aggs_and_cols, line_width, overwrite ) @@ -569,7 +539,7 @@ def _internal_build_extend( expand_aggs_and_cols = self.expand_aggs_and_cols(append) map_onto_pixel = _build_map_onto_pixel_for_line( x_mapper, y_mapper, antialias) - overwrite, use_2_stage_agg = _two_stage_agg(antialias_stage_2) + overwrite, use_2_stage_agg = two_stage_agg(antialias_stage_2) draw_segment = _build_draw_segment( append, map_onto_pixel, expand_aggs_and_cols, line_width, overwrite ) @@ -1064,45 +1034,6 @@ def extend_cuda(sx, tx, sy, ty, xmin, xmax, ymin, ymax, return extend_cpu, extend_cuda -@ngjit -def _combine_in_place(accum_agg, other_agg, antialias_combination): - if antialias_combination == AntialiasCombination.MAX: - nanmax_in_place(accum_agg, other_agg) - elif antialias_combination == AntialiasCombination.MIN: - nanmin_in_place(accum_agg, other_agg) - elif antialias_combination == AntialiasCombination.FIRST: - nanfirst_in_place(accum_agg, other_agg) - elif antialias_combination == AntialiasCombination.LAST: - nanlast_in_place(accum_agg, other_agg) - else: - nansum_in_place(accum_agg, other_agg) - - -@ngjit -def _aa_stage_2_accumulate(aggs_and_copies, antialias_combinations): - k = 0 - # Numba access to heterogeneous tuples is only permitted using literal_unroll. - for agg_and_copy in literal_unroll(aggs_and_copies): - _combine_in_place(agg_and_copy[1], agg_and_copy[0], antialias_combinations[k]) - k += 1 - - -@ngjit -def _aa_stage_2_clear(aggs_and_copies, antialias_zeroes): - k = 0 - # Numba access to heterogeneous tuples is only permitted using literal_unroll. - for agg_and_copy in literal_unroll(aggs_and_copies): - parallel_fill(agg_and_copy[0], antialias_zeroes[k]) - k += 1 - - -@ngjit -def _aa_stage_2_copy_back(aggs_and_copies): - # Numba access to heterogeneous tuples is only permitted using literal_unroll. - for agg_and_copy in literal_unroll(aggs_and_copies): - agg_and_copy[0][:] = agg_and_copy[1][:] - - def _build_extend_line_axis0_multi(draw_segment, expand_aggs_and_cols, use_2_stage_agg): @ngjit @@ -1168,12 +1099,12 @@ def cpu_antialias_2agg_impl(sx, tx, sy, ty, xmin, xmax, ymin, ymax, xs, ys, if ncols == 1: return - _aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) + aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) if j < ncols - 1: - _aa_stage_2_clear(aggs_and_accums, antialias_zeroes) + aa_stage_2_clear(aggs_and_accums, antialias_zeroes) - _aa_stage_2_copy_back(aggs_and_accums) + aa_stage_2_copy_back(aggs_and_accums) @cuda.jit @@ -1258,12 +1189,12 @@ def cpu_antialias_2agg_impl(sx, tx, sy, ty, xmin, xmax, ymin, ymax, xs, ys, if xs.shape[0] == 1: return - _aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) + aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) if i < xs.shape[0] - 1: - _aa_stage_2_clear(aggs_and_accums, antialias_zeroes) + aa_stage_2_clear(aggs_and_accums, antialias_zeroes) - _aa_stage_2_copy_back(aggs_and_accums) + aa_stage_2_copy_back(aggs_and_accums) @cuda.jit @expand_aggs_and_cols @@ -1349,12 +1280,12 @@ def cpu_antialias_2agg_impl(sx, tx, sy, ty, xmin, xmax, ymin, ymax, xs, ys, if ys.shape[0] == 1: return - _aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) + aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) if i < ys.shape[0] - 1: - _aa_stage_2_clear(aggs_and_accums, antialias_zeroes) + aa_stage_2_clear(aggs_and_accums, antialias_zeroes) - _aa_stage_2_copy_back(aggs_and_accums) + aa_stage_2_copy_back(aggs_and_accums) @cuda.jit @expand_aggs_and_cols @@ -1442,12 +1373,12 @@ def cpu_antialias_2agg_impl(sx, tx, sy, ty, xmin, xmax, ymin, ymax, xs, ys, if xs.shape[0] == 1: return - _aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) + aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) if i < xs.shape[0] - 1: - _aa_stage_2_clear(aggs_and_accums, antialias_zeroes) + aa_stage_2_clear(aggs_and_accums, antialias_zeroes) - _aa_stage_2_copy_back(aggs_and_accums) + aa_stage_2_copy_back(aggs_and_accums) @cuda.jit @expand_aggs_and_cols @@ -1616,12 +1547,12 @@ def extend_cpu_numba_antialias_2agg( if nrows == 1: return - _aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) + aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) if i < nrows - 1: - _aa_stage_2_clear(aggs_and_accums, antialias_zeroes) + aa_stage_2_clear(aggs_and_accums, antialias_zeroes) - _aa_stage_2_copy_back(aggs_and_accums) + aa_stage_2_copy_back(aggs_and_accums) if use_2_stage_agg: return extend_cpu_antialias_2agg @@ -1811,10 +1742,10 @@ def extend_cpu_numba_antialias_2agg( segment_start, segment_end, x0, x1, y0, y1, 0.0, 0.0, buffer, *aggs_and_cols) - _aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) - _aa_stage_2_clear(aggs_and_accums, antialias_zeroes) + aa_stage_2_accumulate(aggs_and_accums, antialias_combinations) + aa_stage_2_clear(aggs_and_accums, antialias_zeroes) - _aa_stage_2_copy_back(aggs_and_accums) + aa_stage_2_copy_back(aggs_and_accums) if use_2_stage_agg: return extend_cpu_antialias_2agg diff --git a/datashader/reductions.py b/datashader/reductions.py index d580e07f5..d1d575853 100644 --- a/datashader/reductions.py +++ b/datashader/reductions.py @@ -8,7 +8,7 @@ from toolz import concat, unique import xarray as xr -from datashader.enums import AntialiasCombination +from datashader.antialias import AntialiasCombination, AntialiasStage2 from datashader.utils import isminus1, isnull from numba import cuda as nb_cuda from numba.typed import List @@ -335,7 +335,7 @@ def _antialias_requires_2_stages(self): # Overridden in derived classes as appropriate. return False - def _antialias_stage_2(self, self_intersect, array_module): + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: # Only called if using antialiased lines. Overridden in derived classes. # Returns a tuple containing an item for each constituent reduction. # Each item is (AntialiasCombination, zero_value)). @@ -501,11 +501,11 @@ class count(SelfIntersectingOptionalFieldReduction): def out_dshape(self, in_dshape, antialias, cuda, partitioned): return dshape(ct.float32) if antialias else dshape(ct.uint32) - def _antialias_stage_2(self, self_intersect, array_module): + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: if self_intersect: - return ((AntialiasCombination.SUM_1AGG, array_module.nan),) + return (AntialiasStage2(AntialiasCombination.SUM_1AGG, array_module.nan),) else: - return ((AntialiasCombination.SUM_2AGG, array_module.nan),) + return (AntialiasStage2(AntialiasCombination.SUM_2AGG, array_module.nan),) # CPU append functions @staticmethod @@ -702,7 +702,7 @@ def uses_row_index(self, cuda, partitioned): def _antialias_requires_2_stages(self): return self.reduction._antialias_requires_2_stages() - def _antialias_stage_2(self, self_intersect, array_module): + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: return self.reduction._antialias_stage_2(self_intersect, array_module) def _build_create(self, required_dshape): @@ -746,8 +746,8 @@ class any(OptionalFieldReduction): def out_dshape(self, in_dshape, antialias, cuda, partitioned): return dshape(ct.float32) if antialias else dshape(ct.bool_) - def _antialias_stage_2(self, self_intersect, array_module): - return ((AntialiasCombination.MAX, array_module.nan),) + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: + return (AntialiasStage2(AntialiasCombination.MAX, array_module.nan),) # CPU append functions @staticmethod @@ -856,11 +856,11 @@ class _sum_zero(FloatingReduction): column : str Name of the column to aggregate over. Column data type must be numeric. """ - def _antialias_stage_2(self, self_intersect, array_module): + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: if self_intersect: - return ((AntialiasCombination.SUM_1AGG, 0),) + return (AntialiasStage2(AntialiasCombination.SUM_1AGG, 0),) else: - return ((AntialiasCombination.SUM_2AGG, 0),) + return (AntialiasStage2(AntialiasCombination.SUM_2AGG, 0),) def _build_create(self, required_dshape): return self._create_float64_zero @@ -951,11 +951,11 @@ class sum(SelfIntersectingFloatingReduction): Name of the column to aggregate over. Column data type must be numeric. ``NaN`` values in the column are skipped. """ - def _antialias_stage_2(self, self_intersect, array_module): + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: if self_intersect: - return ((AntialiasCombination.SUM_1AGG, array_module.nan),) + return (AntialiasStage2(AntialiasCombination.SUM_1AGG, array_module.nan),) else: - return ((AntialiasCombination.SUM_2AGG, array_module.nan),) + return (AntialiasStage2(AntialiasCombination.SUM_2AGG, array_module.nan),) def _build_bases(self, cuda, partitioned): if cuda: @@ -1067,8 +1067,8 @@ class min(FloatingReduction): def _antialias_requires_2_stages(self): return True - def _antialias_stage_2(self, self_intersect, array_module): - return ((AntialiasCombination.MIN, array_module.nan),) + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: + return (AntialiasStage2(AntialiasCombination.MIN, array_module.nan),) # CPU append functions @staticmethod @@ -1112,8 +1112,8 @@ class max(FloatingReduction): Name of the column to aggregate over. Column data type must be numeric. ``NaN`` values in the column are skipped. """ - def _antialias_stage_2(self, self_intersect, array_module): - return ((AntialiasCombination.MAX, array_module.nan),) + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: + return (AntialiasStage2(AntialiasCombination.MAX, array_module.nan),) # CPU append functions @staticmethod @@ -1288,8 +1288,8 @@ class first(_first_or_last): Name of the column to aggregate over. If the data type is floating point, ``NaN`` values in the column are skipped. """ - def _antialias_stage_2(self, self_intersect, array_module): - return ((AntialiasCombination.FIRST, array_module.nan),) + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: + return (AntialiasStage2(AntialiasCombination.FIRST, array_module.nan),) @staticmethod @ngjit @@ -1326,8 +1326,8 @@ class last(_first_or_last): Name of the column to aggregate over. If the data type is floating point, ``NaN`` values in the column are skipped. """ - def _antialias_stage_2(self, self_intersect, array_module): - return ((AntialiasCombination.LAST, array_module.nan),) + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: + return (AntialiasStage2(AntialiasCombination.LAST, array_module.nan),) @staticmethod @ngjit @@ -1424,8 +1424,8 @@ def _finalize(bases, cuda=False, **kwargs): class first_n(_first_n_or_last_n): - def _antialias_stage_2(self, self_intersect, array_module): - return ((AntialiasCombination.FIRST, array_module.nan),) + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: + return (AntialiasStage2(AntialiasCombination.FIRST, array_module.nan),) # CPU append functions @staticmethod @@ -1450,8 +1450,8 @@ def _create_row_index_selector(self): class last_n(_first_n_or_last_n): - def _antialias_stage_2(self, self_intersect, array_module): - return ((AntialiasCombination.LAST, array_module.nan),) + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: + return (AntialiasStage2(AntialiasCombination.LAST, array_module.nan),) # CPU append functions @staticmethod @@ -1475,8 +1475,8 @@ class max_n(FloatingNReduction): def uses_cuda_mutex(self): return True - def _antialias_stage_2(self, self_intersect, array_module): - return ((AntialiasCombination.MAX, array_module.nan),) + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: + return (AntialiasStage2(AntialiasCombination.MAX, array_module.nan),) # CPU append functions @staticmethod @@ -1547,8 +1547,8 @@ def uses_cuda_mutex(self): def _antialias_requires_2_stages(self): return True - def _antialias_stage_2(self, self_intersect, array_module): - return ((AntialiasCombination.MIN, array_module.nan),) + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: + return (AntialiasStage2(AntialiasCombination.MIN, array_module.nan),) # CPU append functions @staticmethod @@ -1709,11 +1709,11 @@ def validate(self, in_dshape): if self.column != SpecialColumn.RowIndex and self.column == self.selector.column: raise ValueError("where and its contained reduction cannot use the same column") - def _antialias_stage_2(self, self_intersect, array_module): + def _antialias_stage_2(self, self_intersect, array_module) -> tuple[AntialiasStage2]: ret = self.selector._antialias_stage_2(self_intersect, array_module) if self.column == SpecialColumn.RowIndex: # Override antialiased zero value when returning integer row index. - ret = ((ret[0][0], -1),) + ret = (AntialiasStage2(combination=ret[0].combination, zero=-1),) return ret # CPU append functions