2929
3030from __future__ import annotations
3131
32+ import math
3233from dataclasses import dataclass
3334from typing import TYPE_CHECKING , Any , Literal , cast
3435
@@ -398,43 +399,33 @@ def inverted(self) -> IndexTransform:
398399 f"{ self .input_rank } and { self .output_rank } "
399400 )
400401
402+ # Nothing below touches `self`, so validating and building in one pass
403+ # leaves no partial state behind when a map is rejected.
401404 referenced : set [int ] = set ()
405+ inverse_min : list [int ] = []
406+ inverse_max : list [int ] = []
407+ inverse_output : dict [int , OutputIndexMap ] = {}
402408 for output_dimension , output_map in enumerate (self .output ):
403409 if isinstance (output_map , ArrayMap ):
404410 raise ValueError ( # noqa: TRY004 - valid map, invalid inverse
405411 f"cannot invert transform: output[{ output_dimension } ] is an ArrayMap"
406412 )
407- if isinstance (output_map , DimensionMap ):
408- if output_map .stride not in (- 1 , 1 ):
409- raise ValueError (
410- "cannot invert transform: DimensionMap stride must be +1 or -1, "
411- f"got { output_map .stride } for output[{ output_dimension } ]"
412- )
413- if output_map .input_dimension in referenced :
414- raise ValueError (
415- "cannot invert transform: input dimension "
416- f"{ output_map .input_dimension } is referenced more than once"
417- )
418- referenced .add (output_map .input_dimension )
419-
420- for input_dimension , extent in enumerate (self .domain .shape ):
421- if input_dimension not in referenced and extent != 1 :
422- raise ValueError (
423- "cannot invert transform: unreferenced input dimension "
424- f"{ input_dimension } has extent { extent } , not 1"
425- )
426-
427- inverse_min : list [int ] = []
428- inverse_max : list [int ] = []
429- inverse_output : dict [int , OutputIndexMap ] = {}
430- for output_dimension , output_map in enumerate (self .output ):
431413 if isinstance (output_map , ConstantMap ):
432414 inverse_min .append (output_map .offset )
433415 inverse_max .append (output_map .offset + 1 )
434416 continue
435-
436- assert isinstance (output_map , DimensionMap )
417+ if output_map .stride not in (- 1 , 1 ):
418+ raise ValueError (
419+ "cannot invert transform: DimensionMap stride must be +1 or -1, "
420+ f"got { output_map .stride } for output[{ output_dimension } ]"
421+ )
437422 input_dimension = output_map .input_dimension
423+ if input_dimension in referenced :
424+ raise ValueError (
425+ "cannot invert transform: input dimension "
426+ f"{ input_dimension } is referenced more than once"
427+ )
428+ referenced .add (input_dimension )
438429 lower = self .domain .inclusive_min [input_dimension ]
439430 upper = self .domain .exclusive_max [input_dimension ]
440431 if output_map .stride == 1 :
@@ -453,9 +444,17 @@ def inverted(self) -> IndexTransform:
453444 stride = - 1 ,
454445 )
455446
456- for input_dimension , lower in enumerate (self .domain .inclusive_min ):
457- if input_dimension not in referenced :
458- inverse_output [input_dimension ] = ConstantMap (lower )
447+ for input_dimension , (lower , extent ) in enumerate (
448+ zip (self .domain .inclusive_min , self .domain .shape , strict = True )
449+ ):
450+ if input_dimension in referenced :
451+ continue
452+ if extent != 1 :
453+ raise ValueError (
454+ "cannot invert transform: unreferenced input dimension "
455+ f"{ input_dimension } has extent { extent } , not 1"
456+ )
457+ inverse_output [input_dimension ] = ConstantMap (lower )
459458
460459 return IndexTransform (
461460 domain = IndexDomain (tuple (inverse_min ), tuple (inverse_max )),
@@ -1164,14 +1163,12 @@ def _intersect_general(
11641163 broadcast_shape = block .broadcast_shape
11651164
11661165 # Joint bounds mask over the broadcast block.
1167- combined : np .ndarray [ Any , np . dtype [ np .bool_ ]] | None = None
1166+ combined = np .ones ( math . prod ( broadcast_shape ), dtype = np .bool_ )
11681167 for out_dim in correlated_dims :
11691168 storage = block .flat_storage [out_dim ]
11701169 lo = output_domain .inclusive_min [out_dim ]
11711170 hi = output_domain .exclusive_max [out_dim ]
1172- mask = (storage >= lo ) & (storage < hi )
1173- combined = mask if combined is None else (combined & mask )
1174- assert combined is not None
1171+ combined &= (storage >= lo ) & (storage < hi )
11751172 surviving = np .flatnonzero (combined ).astype (np .intp )
11761173 if surviving .size == 0 :
11771174 return None
@@ -1222,19 +1219,18 @@ def _intersect_general(
12221219 corr_shape = points_shape + (1 ,) * n_slice
12231220 new_output : list [OutputIndexMap ] = []
12241221 for out_dim , m in enumerate (transform .output ):
1225- if out_dim in correlated_dims :
1226- corr = cast ( " ArrayMap" , m )
1222+ if isinstance ( m , ArrayMap ) :
1223+ # `correlated_dims` is exactly the ArrayMap output dimensions.
12271224 new_output .append (
12281225 ArrayMap (
12291226 index_array = corr_values [out_dim ].reshape (corr_shape ),
1230- offset = corr .offset ,
1231- stride = corr .stride ,
1227+ offset = m .offset ,
1228+ stride = m .stride ,
12321229 )
12331230 )
12341231 elif isinstance (m , ConstantMap ):
12351232 new_output .append (m )
12361233 else :
1237- assert isinstance (m , DimensionMap )
12381234 new_output .append (
12391235 DimensionMap (
12401236 input_dimension = new_input_dim_of [m .input_dimension ],
@@ -1780,7 +1776,7 @@ def _apply_vindex(transform: IndexTransform, selection: Any) -> IndexTransform:
17801776
17811777 # Separate array dims and slice dims
17821778 array_dims : list [int ] = []
1783- slice_dims : list [int ] = []
1779+ slices : dict [int , slice ] = {}
17841780 arrays : list [np .ndarray [Any , np .dtype [np .intp ]]] = []
17851781
17861782 for i , sel in enumerate (processed ):
@@ -1791,7 +1787,8 @@ def _apply_vindex(transform: IndexTransform, selection: Any) -> IndexTransform:
17911787 array_dims .append (i )
17921788 arrays .append (sel )
17931789 else :
1794- slice_dims .append (i )
1790+ slices [i ] = sel
1791+ slice_dims = list (slices )
17951792
17961793 # Determine the shared domain without expanding the index arrays: their
17971794 # singleton axes describe independent dependencies during grid partitioning.
@@ -1800,12 +1797,10 @@ def _apply_vindex(transform: IndexTransform, selection: Any) -> IndexTransform:
18001797 # Slice dimensions (preserved-domain literal semantics, like basic indexing)
18011798 slice_dim_params : dict [int , tuple [int , int , int ]] = {}
18021799 slice_bounds : list [tuple [int , int ]] = []
1803- for old_dim in slice_dims :
1804- sel = processed [old_dim ]
1805- assert isinstance (sel , slice )
1800+ for old_dim , sl in slices .items ():
18061801 lo = transform .domain .inclusive_min [old_dim ]
18071802 hi = transform .domain .exclusive_max [old_dim ]
1808- start , step , origin , size = _resolve_slice_ts (sel , old_dim , lo , hi )
1803+ start , step , origin , size = _resolve_slice_ts (sl , old_dim , lo , hi )
18091804 slice_bounds .append ((origin , origin + size ))
18101805 slice_dim_params [old_dim ] = (start , step , origin )
18111806
0 commit comments