def _arg_check(self, args, interval): try: name = self.step.name except AttributeError: # `step` not a Symbol return value = args[name] if isinstance(self.parent, IncrDimension): # sub-IncrDimensions must be perfect divisors of their parent parent_value = args[self.parent.step.name] if parent_value % value > 0: raise InvalidArgument( "Illegal block size `%s=%d`: sub-block sizes " "must divide the parent block size evenly (`%s=%d`)" % (name, value, self.parent.step.name, parent_value)) else: if value < 0: raise InvalidArgument( "Illegal block size `%s=%d`: it should be > 0" % (name, value)) if value > args[self.root.max_name] - args[self.root.min_name] + 1: # Avoid OOB raise InvalidArgument( "Illegal block size `%s=%d`: it's greater than the " "iteration range and it will cause an OOB access" % (name, value))
def _arg_check(self, args, size, interval): """ :raises InvalidArgument: If any of the ``self``-related runtime arguments in ``args`` will cause an out-of-bounds access. """ if self.min_name not in args: raise InvalidArgument("No runtime value for %s" % self.min_name) if interval.is_Defined and args[self.min_name] + interval.lower < 0: raise InvalidArgument("OOB detected due to %s=%d" % (self.min_name, args[self.min_name])) if self.max_name not in args: raise InvalidArgument("No runtime value for %s" % self.max_name) if interval.is_Defined and args[self.max_name] + interval.upper >= size: raise InvalidArgument("OOB detected due to %s=%d" % (self.max_name, args[self.max_name])) # Allow the specific case of max=min-1, which disables the loop if args[self.max_name] < args[self.min_name] - 1: raise InvalidArgument("Illegal max=%s < min=%s" % (args[self.max_name], args[self.min_name])) elif args[self.max_name] == args[self.min_name] - 1: debug( "%s=%d and %s=%d might cause no iterations along Dimension %s", self.min_name, args[self.min_name], self.max_name, args[self.max_name], self.name)
def _arg_check(self, args, size, interval): """ Raises ------ InvalidArgument If any of the ``self``-related runtime arguments in ``args`` will cause an out-of-bounds access. """ if self.min_name not in args: raise InvalidArgument("No runtime value for %s" % self.min_name) if interval.is_Defined and args[self.min_name] + interval.lower < 0: raise InvalidArgument("OOB detected due to %s=%d" % (self.min_name, args[self.min_name])) if self.max_name not in args: raise InvalidArgument("No runtime value for %s" % self.max_name) if interval.is_Defined: if is_integer(interval.upper): upper = interval.upper else: # Autopadding causes non-integer upper limit upper = interval.upper.subs(args) if args[self.max_name] + upper >= size: raise InvalidArgument("OOB detected due to %s=%d" % (self.max_name, args[self.max_name])) # Allow the specific case of max=min-1, which disables the loop if args[self.max_name] < args[self.min_name]-1: raise InvalidArgument("Illegal %s=%d < %s=%d" % (self.max_name, args[self.max_name], self.min_name, args[self.min_name])) elif args[self.max_name] == args[self.min_name]-1: debug("%s=%d and %s=%d might cause no iterations along Dimension %s", self.min_name, args[self.min_name], self.max_name, args[self.max_name], self.name)
def arguments(self, **kwargs): """ Process any apply-time arguments passed to apply and derive values for any remaining arguments """ new_params = {} # If we've been passed CompositeData objects as kwargs, they might have children # that need to be substituted as well. for k, v in kwargs.items(): if isinstance(v, CompositeData): orig_param_l = [i for i in self.input if i.name == k] # If I have been passed a parameter, I must have seen it before if len(orig_param_l) == 0: raise InvalidArgument("Parameter %s does not exist in expressions " + "passed to this Operator" % k) # We've made sure the list isn't empty. Names should be unique so it # should have exactly one entry assert(len(orig_param_l) == 1) orig_param = orig_param_l[0] # Pull out the children and add them to kwargs for orig_child, new_child in zip(orig_param.children, v.children): new_params[orig_child.name] = new_child kwargs.update(new_params) # Derivation. It must happen in the order [tensors -> dimensions -> scalars] for i in self.parameters: if i.is_TensorArgument: assert(i.verify(kwargs.pop(i.name, None))) runtime_dimensions = [d for d in self.dimensions if d.value is not None] for d in runtime_dimensions: d.verify(kwargs.pop(d.name, None)) for i in self.parameters: if i.is_ScalarArgument: i.verify(kwargs.pop(i.name, None)) dim_sizes = OrderedDict([(d.name, d.value) for d in runtime_dimensions]) dle_arguments, autotune = self._dle_arguments(dim_sizes) dim_sizes.update(dle_arguments) autotune = autotune and kwargs.pop('autotune', False) # Make sure we've used all arguments passed if len(kwargs) > 0: raise InvalidArgument("Unknown arguments passed: " + ", ".join(kwargs.keys())) mapper = OrderedDict([(d.name, d) for d in self.dimensions]) for d, v in dim_sizes.items(): assert(mapper[d].verify(v)) arguments = self._default_args() if autotune: arguments = self._autotune(arguments) # Clear the temp values we stored in the arg objects since we've pulled them out # into the OrderedDict object above self._reset_args() return arguments, dim_sizes
def _arg_check(self, args, intervals): super(TimeFunction, self)._arg_check(args, intervals) key_time_size = args[self.name].shape[self._time_position] if self._time_buffering and self._time_size != key_time_size: raise InvalidArgument("Expected `time_size=%d` for runtime " "value `%s`, found `%d` instead" % (self._time_size, self.name, key_time_size))
def _derive_values(self, kwargs): """ Populate values for all the arguments. The values provided in kwargs will be used wherever provided. The values for the rest of the arguments will be derived from the ones provided. The default values for the tensors come from the data property of the symbols used in the Operator. """ values = OrderedDict() values = OrderedDict([(i, get_value(i, kwargs.pop(i.name, None), values)) for i in self.arguments]) dimension_values = OrderedDict([(i, kwargs.pop(i.name, None)) for i in self.dimension_params]) # Make sure we've used all arguments passed if len(kwargs) > 0: raise InvalidArgument("Unknown arguments passed: " + ", ".join(kwargs.keys())) # Derive values for other arguments for i in self.arguments: if values[i] is None: known_values = OrderedDict(chain(values.items(), dimension_values.items())) provided_values = [get_value(i, x, known_values) for x in i.gets_value_from] assert(len(provided_values) > 0) values[i] = reduce(max, provided_values) # Second pass to evaluate any Unevaluated dependencies from the first pass for k, v in values.items(): if isinstance(v, UnevaluatedDependency): values[k] = v.evaluate(values) return values
def infer_dimension_values_tuple(value, rtargs, offsets=None): """ Strictly, a dimension's value is a 3-tuple consisting of the values of each of its rtargs - currently size, start and end. However, for convenience, we may accept partial representations of the value, e.g. scalars and 2-tuples and interpret them in a certain way while assuming defaults for missing information. If value is: 3-tuple: it contains explicit values for all 3 rtargs and hence will be used directly 2-tuple: We assume we are being provided the (start, end) values. This will be promoted to a 3-tuple assuming size to be the same as end. scalar: We assume we are being provided the value of size. Promote to 3-tuple by assuming this scalar is the size and the end of the dimension. start will default to 0. """ size_arg, start_arg, end_arg = rtargs start_offset = 0 if offsets is None else offsets.get(start_arg.name, 0) end_offset = 0 if offsets is None else offsets.get(end_arg.name, 0) if not isinstance(value, tuple): # scalar value = (value, start_arg.default_value + start_offset, value + end_offset) else: if len(value) == 2: # 2-tuple # Assume we've been passed a (start, end) tuple start, end = value value = (end, start + start_offset, end + end_offset) elif len(value) != 3: raise InvalidArgument( "Expected either a scalar value or a tuple(2/3)") return value
def value(self): try: if self._value.is_SymbolicData: return self._value._data_buffer else: raise InvalidArgument("Unexpected data object %s" % type(self._value)) except AttributeError: return self._value
def _arg_values(self, **kwargs): if self.name in kwargs: v = kwargs.pop(self.name) if v < self.size: return {self.name: v} else: raise InvalidArgument("Illegal `%s=%d`. It must be `%s<%d`" % (self.name, v, self.name, self.size)) else: return self._arg_defaults()
def _arg_check(self, args, interval): """Check the block size won't cause OOB accesses.""" value = args[self.step.name] if isinstance(self.parent, BlockDimension): # sub-BlockDimensions must be perfect divisors of their parent parent_value = args[self.parent.step.name] if parent_value % value > 0: raise InvalidArgument("Illegal block size `%s=%d`: sub-block sizes " "must divide the parent block size evenly (`%s=%d`)" % (self.step.name, value, self.parent.step.name, parent_value)) else: if value < 0: raise InvalidArgument("Illegal block size `%s=%d`: it should be > 0" % (self.step.name, value)) if value > args[self.root.max_name] - args[self.root.min_name] + 1: # Avoid OOB raise InvalidArgument("Illegal block size `%s=%d`: it's greater than the " "iteration range and it will cause an OOB access" % (self.step.name, value))
def _arg_check(self, args, size, interval): """ :raises InvalidArgument: If any of the ``self``-related runtime arguments in ``args`` will cause an out-of-bounds access. """ if self.min_name not in args: raise InvalidArgument("No runtime value for %s" % self.min_name) if interval.is_Defined and args[self.min_name] + interval.lower < 0: raise InvalidArgument("OOB detected due to %s=%d" % (self.min_name, args[self.min_name])) if self.max_name not in args: raise InvalidArgument("No runtime value for %s" % self.max_name) if interval.is_Defined and args[self.max_name] + interval.upper >= size: raise InvalidArgument("OOB detected due to %s=%d" % (self.max_name, args[self.max_name])) if args[self.max_name] < args[self.min_name]: raise InvalidArgument("Illegal max=%s < min=%s" % (args[self.max_name], args[self.min_name]))
def _arg_check(self, args, intervals): """ Check that ``args`` contains legal runtime values bound to ``self``. Raises ------ InvalidArgument If, given the runtime values ``args``, an out-of-bounds array access would be performed, or if shape/dtype don't match with self's shape/dtype. """ if self.name not in args: raise InvalidArgument("No runtime value for `%s`" % self.name) key = args[self.name] if len(key.shape) != self.ndim: raise InvalidArgument("Shape %s of runtime value `%s` does not match " "dimensions %s" % (key.shape, self.name, self.indices)) if key.dtype != self.dtype: warning("Data type %s of runtime value `%s` does not match the " "Function data type %s" % (key.dtype, self.name, self.dtype)) for i, s in zip(self.indices, key.shape): i._arg_check(args, s, intervals[i])
def _arg_check(self, args, intervals): """ Check that ``args`` contains legal runtime values bound to ``self``. """ if self.name not in args: raise InvalidArgument("No runtime value for %s" % self.name) key = args[self.name] try: # Might be a plain number, w/o a dtype field if key.dtype != self.dtype: warning("Data type %s of runtime value `%s` does not match the " "Constant data type %s" % (key.dtype, self.name, self.dtype)) except AttributeError: pass
def _dle_arguments(self, dim_sizes): # Add user-provided block sizes, if any dle_arguments = OrderedDict() autotune = True for i in self.dle_arguments: dim_size = dim_sizes.get(i.original_dim.name, i.original_dim.size) if dim_size is None: error('Unable to derive size of dimension %s from defaults. ' 'Please provide an explicit value.' % i.original_dim.name) raise InvalidArgument('Unknown dimension size') if i.value: try: dle_arguments[i.argument.name] = i.value(dim_size) except TypeError: dle_arguments[i.argument.name] = i.value autotune = False else: dle_arguments[i.argument.name] = dim_size return dle_arguments, autotune
def _extract_children_of_composites(self, kwargs): new_params = {} # If we've been passed CompositeFunction objects as kwargs, # they might have children that need to be substituted as well. for k, v in kwargs.items(): if isinstance(v, CompositeFunction): orig_param_l = [i for i in self.parameters if i.name == k] # If I have been passed a parameter, I must have seen it before if len(orig_param_l) == 0: raise InvalidArgument("Parameter %s does not exist in expressions " + "passed to this Operator" % k) # We've made sure the list isn't empty. Names should be unique so it # should have exactly one entry assert(len(orig_param_l) == 1) orig_param = orig_param_l[0] # Pull out the children and add them to kwargs for orig_child, new_child in zip(orig_param.children, v.children): new_params[orig_child.name] = new_child kwargs.update(new_params) return kwargs
def arguments(self, *args, **kwargs): """ Return the arguments necessary to apply the Operator. """ if len(args) == 0: args = self.parameters # Will perform auto-tuning if the user requested it and loop blocking was used maybe_autotune = kwargs.get('autotune', False) # Initialise argument map and a map of dimension names to values arguments = OrderedDict([(arg.name, arg) for arg in self.parameters]) dim_sizes = dict([(arg.name, arg.size) for arg in self.parameters if isinstance(arg, Dimension)]) o_vals = {} for name, arg in kwargs.items(): # Override explicitly provided dim sizes from **kwargs if name in dim_sizes: dim_sizes[name] = arg # Override explicitly provided SymbolicData if name in arguments and isinstance(arguments[name], SymbolicData): # Override the original symbol o_vals[name] = arg original = arguments[name] if original.is_CompositeData: for orig, child in zip(original.children, arg.children): o_vals[orig.name] = child # Replace the overridden values with the provided ones for argname in o_vals.keys(): arguments[argname] = o_vals[argname] # Traverse positional args and infer loop sizes for open dimensions f_args = [(name, f) for name, f in arguments.items() if isinstance(f, SymbolicData)] for fname, f in f_args: arguments[fname] = self._arg_data(f) shape = self._arg_shape(f) # Ensure data dimensions match symbol dimensions for i, dim in enumerate(f.indices): # We don't need to check sizes for buffered dimensions # against data shapes, all we need is the size of the parent. if dim.is_Buffered: continue # Check data sizes for dimensions with a fixed size if dim.size is not None: if not shape[i] <= dim.size: error( 'Size of data argument for %s is greater than the size ' 'of dimension %s: %d' % (fname, dim.name, dim.size)) raise InvalidArgument('Wrong data shape encountered') else: continue if dim_sizes[dim.name] is None: # We haven't determined the size of this dimension yet, # try to infer it from the data shape. dim_sizes[dim.name] = shape[i] else: # We know the dimension size, check if data shape agrees if not dim_sizes[dim.name] <= shape[i]: error('Size of dimension %s was determined to be %d, ' 'but data for symbol %s has shape %d.' % (dim.name, dim_sizes[dim.name], fname, shape[i])) raise InvalidArgument('Wrong data shape encountered') # Make sure we have defined all buffered dimensions and their parents, # even if they are not explicitly given or used. d_args = [d for d in arguments.values() if isinstance(d, Dimension)] for d in d_args: if d.is_Buffered: if dim_sizes[d.parent.name] is None: dim_sizes[d.parent.name] = dim_sizes[d.name] if dim_sizes[d.name] is None: dim_sizes[d.name] = dim_sizes[d.parent.name] # Add user-provided block sizes, if any dle_arguments = OrderedDict() for i in self._dle_state.arguments: dim_size = dim_sizes.get(i.original_dim.name, i.original_dim.size) if dim_size is None: error('Unable to derive size of dimension %s from defaults. ' 'Please provide an explicit value.' % i.original_dim.name) raise InvalidArgument('Unknown dimension size') if i.value: try: dle_arguments[i.argument.name] = i.value(dim_size) except TypeError: dle_arguments[i.argument.name] = i.value # User-provided block size available, do not autotune maybe_autotune = False else: dle_arguments[i.argument.name] = dim_size dim_sizes.update(dle_arguments) # Insert loop size arguments from dimension values d_args = [d for d in arguments.values() if isinstance(d, Dimension)] for d in d_args: arguments[d.name] = dim_sizes[d.name] # Might have been asked to auto-tune the block size if maybe_autotune: arguments = self._autotune(arguments) # Add profiler structs arguments.update(self._extra_arguments()) # Sanity check argument derivation for name, arg in arguments.items(): if isinstance(arg, SymbolicData) or isinstance(arg, Dimension): raise ValueError('Runtime argument %s not defined' % arg) return arguments, dim_sizes