Esempio n. 1
0
    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))
Esempio n. 2
0
    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)
Esempio n. 3
0
    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)
Esempio n. 4
0
    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
Esempio n. 5
0
 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))
Esempio n. 6
0
    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
Esempio n. 7
0
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
Esempio n. 8
0
 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
Esempio n. 9
0
 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()
Esempio n. 10
0
 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))
Esempio n. 11
0
    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]))
Esempio n. 12
0
    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])
Esempio n. 13
0
 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
Esempio n. 14
0
 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
Esempio n. 15
0
 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
Esempio n. 16
0
    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