77import numpy as np
88
99from devito .data import LEFT , RIGHT
10+ from devito .deprecations import deprecations
1011from devito .exceptions import InvalidArgument
1112from devito .logger import debug
12- from devito .tools import Pickable , is_integer , memoized_meth
13+ from devito .tools import Pickable , is_integer , is_number , memoized_meth
1314from devito .types .args import ArgProvider
1415from devito .types .basic import Symbol , DataSymbol , Scalar
1516from devito .types .constant import Constant
@@ -822,6 +823,10 @@ def bound_symbols(self):
822823 return self .parent .bound_symbols
823824
824825
826+ class SubsamplingFactor (Scalar ):
827+ pass
828+
829+
825830class ConditionalDimension (DerivedDimension ):
826831
827832 """
@@ -898,7 +903,8 @@ class ConditionalDimension(DerivedDimension):
898903 is_NonlinearDerived = True
899904 is_Conditional = True
900905
901- __rkwargs__ = DerivedDimension .__rkwargs__ + ('factor' , 'condition' , 'indirect' )
906+ __rkwargs__ = DerivedDimension .__rkwargs__ + \
907+ ('factor' , 'condition' , 'indirect' )
902908
903909 def __init_finalize__ (self , name , parent = None , factor = None , condition = None ,
904910 indirect = False , ** kwargs ):
@@ -909,27 +915,49 @@ def __init_finalize__(self, name, parent=None, factor=None, condition=None,
909915
910916 super ().__init_finalize__ (name , parent )
911917
912- # Always make the factor symbolic to allow overrides with different factor.
918+ # Process subsampling factor
913919 if factor is None or factor == 1 :
914920 self ._factor = None
915- elif is_integer (factor ):
916- self ._factor = Constant (name = "%sf" % name , value = factor , dtype = np .int32 )
917- elif factor .is_Constant and is_integer (factor .data ):
921+ elif is_number (factor ):
922+ self ._factor = int (factor )
923+ elif factor .is_Constant :
924+ deprecations .constant_factor_warn
918925 self ._factor = factor
919926 else :
920- raise ValueError ("factor must be an integer or integer Constant " )
927+ raise ValueError ("factor must be an integer" )
921928
922929 self ._condition = condition
923930 self ._indirect = indirect
924931
932+ @property
933+ def uses_symbolic_factor (self ):
934+ return self ._factor is not None
935+
936+ @property
937+ def factor_data (self ):
938+ if isinstance (self .factor , Constant ):
939+ return self .factor .data
940+ else :
941+ return self .factor
942+
925943 @property
926944 def spacing (self ):
927- s = self ._factor .data if self ._factor is not None else 1
928- return s * self .parent .spacing
945+ return self .factor_data * self .parent .spacing
929946
930947 @property
931948 def factor (self ):
932- return self ._factor if self ._factor is not None else 1
949+ return self ._factor if self .uses_symbolic_factor else 1
950+
951+ @cached_property
952+ def symbolic_factor (self ):
953+ if not self .uses_symbolic_factor :
954+ return None
955+ elif isinstance (self .factor , Constant ):
956+ return self .factor
957+ else :
958+ return SubsamplingFactor (
959+ name = f'{ self .name } f' , dtype = np .int32 , is_const = True
960+ )
933961
934962 @property
935963 def condition (self ):
@@ -950,9 +978,14 @@ def free_symbols(self):
950978 pass
951979 return retval
952980
953- def _arg_values (self , interval , grid = None , ** kwargs ):
954- # Parent dimension define the interval
955- fact = self ._factor .data if self ._factor is not None else 1
981+ def _arg_values (self , interval , grid = None , args = None , ** kwargs ):
982+ if not self .uses_symbolic_factor :
983+ return {}
984+
985+ args = args or {}
986+ fname = self .symbolic_factor .name
987+ fact = kwargs .get (fname , args .get (fname , self .factor_data ))
988+
956989 toint = lambda x : math .ceil (x / fact )
957990 vals = {}
958991 try :
@@ -965,6 +998,8 @@ def _arg_values(self, interval, grid=None, **kwargs):
965998 except (KeyError , TypeError ):
966999 pass
9671000
1001+ vals [self .symbolic_factor .name ] = fact
1002+
9681003 return vals
9691004
9701005 def _arg_defaults (self , _min = None , size = None , alias = None ):
@@ -974,15 +1009,9 @@ def _arg_defaults(self, _min=None, size=None, alias=None):
9741009 # `factor` endpoints are legal, so we return them all. It's then
9751010 # up to the caller to decide which one to pick upon reduction
9761011 dim = alias or self
977- if dim .condition is not None or size is None or dim ._factor is None :
978- return defaults
979- try :
980- # Is it a symbolic factor?
981- factor = defaults [dim ._factor .name ] = self ._factor .data
982- except AttributeError :
983- factor = dim ._factor
984-
985- defaults [dim .parent .max_name ] = range (0 , factor * size - 1 )
1012+ if dim .uses_symbolic_factor :
1013+ factor = defaults [dim .symbolic_factor .name ] = self .factor_data
1014+ defaults [dim .parent .max_name ] = range (0 , factor * size - 1 )
9861015
9871016 return defaults
9881017
0 commit comments