@@ -601,8 +601,14 @@ def assparse(self):
601
601
value_parts .append (_flat (values ))
602
602
index_parts .append (_flat (flatindex ))
603
603
if value_parts :
604
- flatindex , inverse = unique (concatenate (index_parts ), return_inverse = True )
605
- values = Inflate (concatenate (value_parts ), inverse , flatindex .shape [0 ])
604
+ # Guard the concatenation to avoid expensive swap-inflate-take
605
+ # operations due to take in unique
606
+ flatindex , inverse = unique (Guard (concatenate (index_parts )), return_inverse = True )
607
+ # The next three lines are equivalent to values = Inflate(concatenate(value_parts), inverse, flatindex.shape[0])
608
+ lengths = [arg .shape [0 ] for arg in value_parts ]
609
+ slices = [Range (length ) + offset for length , offset in zip (lengths , util .cumsum (lengths ))]
610
+ values = util .sum (Inflate (f , Take (inverse , s ), flatindex .shape [0 ]) for f , s in zip (value_parts , slices ))
611
+ # Unravel indices
606
612
indices = [flatindex ]
607
613
for n in reversed (self .shape [1 :]):
608
614
indices [:1 ] = divmod (indices [0 ], n )
@@ -3475,7 +3481,7 @@ def _unravel(self, axis, shape):
3475
3481
return Inflate (unravel (self .func , axis , shape ), self .dofmap , self .length )
3476
3482
3477
3483
def _sign (self ):
3478
- if isinstance (self .dofmap , Constant ) and _isunique ( self .dofmap .value ):
3484
+ if isinstance (self .dofmap , Constant ) and _ismonotonic ( numpy . sort ( self .dofmap .value , axis = None ) ):
3479
3485
return Inflate (Sign (self .func ), self .dofmap , self .length )
3480
3486
3481
3487
@cached_property
@@ -3521,8 +3527,8 @@ def __iter__(self):
3521
3527
3522
3528
@staticmethod
3523
3529
def evalf (inflateidx , takeidx ):
3524
- uniqueinflate = _isunique ( inflateidx )
3525
- uniquetake = _isunique ( takeidx )
3530
+ uniqueinflate = _ismonotonic ( numpy . sort ( inflateidx , axis = None ) )
3531
+ uniquetake = _ismonotonic ( numpy . sort ( takeidx , axis = None ) )
3526
3532
unique = uniqueinflate and uniquetake
3527
3533
# If both indices are unique (i.e. they do not contain duplicates) then the
3528
3534
# take and inflate operations can simply be restricted to the intersection,
@@ -5916,8 +5922,10 @@ def _inflate_scalar(arg, shape):
5916
5922
return arg
5917
5923
5918
5924
5919
- def _isunique (array ):
5920
- return numpy .unique (array ).size == array .size
5925
+ def _ismonotonic (indices ):
5926
+ 'check if indices are strict monotonic ascending'
5927
+ assert indices .ndim == 1 and indices .dtype == int
5928
+ return len (indices ) <= 1 or indices [- 1 ] - indices [0 ] >= len (indices ) - 1 and (indices [1 :] > indices [:- 1 ]).all ()
5921
5929
5922
5930
5923
5931
def _make_loop_ids_unique (funcs : typing .Tuple [Evaluable , ...]) -> typing .Tuple [Evaluable , ...]:
0 commit comments