Esempio n. 1
0
    def argument(self, shape, dtype=np.float64):
        """
        Return an instance of :class:`numloopy.ArraySymbol` which the loop
        kernel expects as an input argument.
        """
        if isinstance(shape, int):
            shape = (shape, )
        assert isinstance(shape, tuple)

        inames = tuple(self.name_generator(based_on='i') for _ in shape)

        arg_name = self.name_generator(based_on='arr')

        rhs = Subscript(Variable(arg_name),
                        tuple(Variable(iname) for iname in inames))
        subst_name = self.name_generator(based_on='subst')
        self.register_substitution(lp.SubstitutionRule(subst_name, inames,
                                                       rhs))
        self.substs_to_arrays[subst_name] = arg_name

        self.data.append(lp.GlobalArg(name=arg_name, shape=shape, dtype=dtype))

        return ArraySymbol(stack=self,
                           name=subst_name,
                           dtype=dtype,
                           shape=shape)
Esempio n. 2
0
    def arange(self, stop):
        """
        Registers a substitution rule on to the stack with an array whose values
        are filled equivalent to ``numpy.arange``.

        :arg stop: An instance of :class:`int` denoting the extent of the
            array.

        :return: An instance of :class:`numloopy.ArraySymbol` of shape
            ``(stop,)``, corresponding to the substitution rule which was
            registered.
        """
        assert isinstance(stop, int)
        subst_name = self.name_generator(based_on="subst")
        arg = ArraySymbol(stack=self,
                          name=subst_name,
                          shape=(stop, ),
                          dtype=np.int)
        iname = self.name_generator(based_on="i")
        rhs = Variable(iname)
        rule = lp.SubstitutionRule(subst_name, (iname, ), rhs)

        self.register_substitution(rule)

        return arg
Esempio n. 3
0
    def cumsum(self, arg):
        """
        Registers  a substitution rule in order to cumulatively sum the
        elements of array ``arg`` along ``axis``. Mimics :func:`numpy.cumsum`.

        :return: An instance of :class:`numloopy.ArraySymbol` which is
            which is registered as the cumulative summed-substitution rule.
        """
        # Note: this can remain as a substitution but loopy does not have
        # support for translating inames for substitutions to the kernel
        # domains
        assert len(arg.shape) == 1
        i_iname = self.name_generator(based_on="i")
        j_iname = self.name_generator(based_on="i")

        space = isl.Space.create_from_names(isl.DEFAULT_CONTEXT,
                                            [i_iname, j_iname])
        domain = isl.BasicSet.universe(space)
        arg_name = self.name_generator(based_on="arr")
        subst_name = self.name_generator(based_on="subst")
        domain = domain & make_slab(space, i_iname, 0, arg.shape[0])
        domain = domain.add_constraint(
            isl.Constraint.ineq_from_names(space, {j_iname: 1}))
        domain = domain.add_constraint(
            isl.Constraint.ineq_from_names(space, {
                j_iname: -1,
                i_iname: 1,
                1: -1
            }))
        cumsummed_arg = ArraySymbol(stack=self,
                                    name=arg_name,
                                    shape=arg.shape,
                                    dtype=arg.dtype)
        cumsummed_subst = ArraySymbol(stack=self,
                                      name=subst_name,
                                      shape=arg.shape,
                                      dtype=arg.dtype)
        subst_iname = self.name_generator(based_on="i")
        rule = lp.SubstitutionRule(
            subst_name, (subst_iname, ),
            Subscript(Variable(arg_name), (Variable(subst_iname), )))

        from loopy.library.reduction import SumReductionOperation

        insn = lp.Assignment(assignee=Subscript(Variable(arg_name),
                                                (Variable(i_iname), )),
                             expression=lp.Reduction(
                                 SumReductionOperation(), (j_iname, ),
                                 parse('{}({})'.format(arg.name, j_iname))))
        self.data.append(cumsummed_arg)
        self.substs_to_arrays[subst_name] = arg_name
        self.register_implicit_assignment(insn)
        self.domains.append(domain)

        self.register_substitution(rule)
        return cumsummed_subst
Esempio n. 4
0
    def sum(self, arg, axis=None):
        """
        Registers  a substitution rule in order to sum the elements of array
        ``arg`` along ``axis``.

        :return: An instance of :class:`numloopy.ArraySymbol` which is
            which is registered as the sum-substitution rule.
        """
        if isinstance(axis, int):
            axis = (axis, )

        if not axis:
            axis = tuple(range(len(arg.shape)))

        inames = [self.name_generator(based_on="i") for _ in arg.shape]

        space = isl.Space.create_from_names(isl.DEFAULT_CONTEXT, inames)
        domain = isl.BasicSet.universe(space)
        for axis_len, iname in zip(arg.shape, inames):
            domain &= make_slab(space, iname, 0, axis_len)

        self.domains.append(domain)

        reduction_inames = tuple(iname for i, iname in enumerate(inames)
                                 if i in axis)
        left_inames = tuple(iname for i, iname in enumerate(inames)
                            if i not in axis)

        def _one_if_empty(t):
            if t:
                return t
            else:
                return (1, )

        subst_name = self.name_generator(based_on="subst")

        summed_arg = ArraySymbol(
            stack=self,
            name=subst_name,
            shape=_one_if_empty(
                tuple(axis_len for i, axis_len in enumerate(arg.shape)
                      if i not in axis)),
            dtype=arg.dtype)

        from loopy.library.reduction import SumReductionOperation

        rule = lp.SubstitutionRule(
            subst_name, left_inames,
            lp.Reduction(SumReductionOperation(), reduction_inames,
                         parse('{}({})'.format(arg.name, ', '.join(inames)))))
        self.register_substitution(rule)

        return summed_arg
Esempio n. 5
0
    def __getitem__(self, index):
        """
        Registers a substitution rule pointing to the indices through `index`.

        :arg index: An instance of :class:`tuple` of int, slices or
            :class:`ArraySymbol`.
        """
        if isinstance(index, Number):
            index = (index, )
        assert isinstance(index, tuple)

        right_inames = []
        left_inames = []
        shape = []
        for axis_len, idx in zip(self.shape, index):
            if isinstance(idx, int):
                right_inames.append(idx)
            elif isinstance(idx, slice):
                # right now only support complete slices
                # future plan is to make it diverse by adding it more support
                # its easy, anyone intereseted can do it, with a small change
                # to make_slab, or wait for kernel_callables_v3 to go through
                # into master
                assert idx.start is None
                assert idx.stop is None
                assert idx.step is None
                iname = self.stack.name_generator(based_on='i')
                right_inames.append(Variable(iname))
                left_inames.append(iname)
                shape.append(axis_len)
            else:
                raise TypeError('can be subscripted only with slices or '
                                'integers')

        rhs = Call(Variable(self.name), tuple(right_inames))
        subst_name = self.stack.name_generator(based_on='subst')
        self.stack.register_substitution(
            lp.SubstitutionRule(subst_name, tuple(left_inames), rhs))

        def _one_if_empty(t):
            if t:
                return t
            else:
                return (1, )

        return ArraySymbol(stack=self.stack,
                           name=subst_name,
                           dtype=self.dtype,
                           shape=_one_if_empty(tuple(shape)))
Esempio n. 6
0
    def reshape(self, new_shape, order='C'):
        """
        Registers a substitution rule to reshape array with the shape
        ``new_shape``. Mimics :func:`numpy.ndarray.reshape`.

        :arg new_shape: An instance of :class:`tuple` of :class:`int`.
        :arg order: Either 'C' or 'F'
        """
        # need an error here complain if there is a shape mismatch
        # how to do this:
        # look at how loopy sets its dim tags, from shape and order.
        subst_name = self.stack.name_generator(based_on='subst')
        inames = tuple(
            self.stack.name_generator(based_on="i") for _ in new_shape)
        new_arg = self.copy(stack=self.stack,
                            name=subst_name,
                            shape=new_shape,
                            dim_tags=None,
                            order=order)

        linearized_idx = sum(
            Variable(iname) * dim_tag.stride
            for iname, dim_tag in zip(inames, new_arg.dim_tags))
        strides = tuple(dim_tag.stride for dim_tag in self.dim_tags)
        if self.dim_tags[0].stride == 1:
            pass
        elif self.dim_tags[-1].stride == 1:
            strides = strides[::-1]
        else:
            raise ValueError()

        indices = []
        for stride in strides[::-1]:
            current_idx = linearized_idx // stride
            indices.append(simplify_via_aff(current_idx))
            linearized_idx -= current_idx * stride

        # should we assert that the linearized index is 0?

        rule = lp.SubstitutionRule(subst_name,
                                   inames,
                                   expression=Variable(
                                       self.name)(*tuple(indices)))

        self.stack.register_substitution(rule)

        return ArraySymbol(stack=self.stack, name=subst_name, shape=new_shape)
Esempio n. 7
0
def fill_array(shape, value, name_generator):
    """
    Helper function to fill an array of shape ``shape`` with ``value`` .

    :arg shape: An instance of :class:`tuple` denoting the shape of the array.
    :arg value: The value with the array should be filled.
    :arg name_generator: An instance of :class:`pytools.UniqueNameGenerator`,
        for generating the name of the new array.

    :return: A tuple of the name of the subtitution, the substitution rule and
        the modified name generator.
    """
    inames = tuple(name_generator(based_on='i') for _ in shape)

    rhs = value
    subst_name = name_generator(based_on='subst')
    rule = lp.SubstitutionRule(subst_name, inames, rhs)

    return subst_name, rule, name_generator
Esempio n. 8
0
    def _arithmetic_op(self, other, op):
        """
        Registers a substitution rule that performs ``(self) op (other)``
        element-wise for the arrays. :mod:`numpy` broadcasting rules are
        followed while performing these operations.

        :return: An instance of :class:`ArraySymbol` corresponding to the
            registered substitution for the element-wise operation.
        """
        assert op in ['+', '-', '*', '/', '<', '<=', '>', '>=']
        assert self.stack == other.stack

        def _apply_op(var1, var2):
            if op == '+':
                return var1 + var2, self.dtype
            if op == '-':
                return var1 - var2, self.dtype
            if op == '*':
                return var1 * var2, self.dtype
            if op == '/':
                return var1 / var2, self.dtype
            if op == '<':
                return var1.lt(var2), np.int
            if op == '<=':
                return var1.le(var2), np.int
            if op == '>':
                return var1.gt(var2), np.int
            if op == '>=':
                return var1.ge(var2), np.int
            raise RuntimeError()

        if isinstance(other, Number):
            inames = tuple(
                self.stack.name_generator(based_on='i') for _ in self.shape)
            rhs, dtype = _apply_op(
                Call(function=Variable(self.name),
                     parameters=tuple(Variable(iname) for iname in inames)),
                other)
            subst_name = self.stack.name_generator(based_on='subst')
            self.stack.register_substitution(
                lp.SubstitutionRule(subst_name, inames, rhs))
            return self.copy(name=subst_name, dtype=dtype)
        elif isinstance(other, ArraySymbol):
            if self.shape == other.shape:
                inames = tuple(
                    self.stack.name_generator(based_on='i')
                    for _ in self.shape)
                rhs, dtype = _apply_op(
                    Variable(self.name)(*tuple(
                        Variable(iname) for iname in inames)),
                    Variable(other.name)(*tuple(
                        Variable(iname) for iname in inames)))
                subst_name = self.stack.name_generator(based_on='subst')
                self.stack.register_substitution(
                    lp.SubstitutionRule(subst_name, inames, rhs))
                return self.copy(name=subst_name, dtype=dtype)
            else:
                left = self
                right = other
                new_left_shape = self.shape[:]
                new_right_shape = other.shape[:]
                if len(left.shape) > len(right.shape):
                    new_right_shape = ((1, ) *
                                       (len(left.shape) - len(right.shape)) +
                                       new_right_shape)
                if len(left.shape) < len(right.shape):
                    new_left_shape = ((1, ) *
                                      (len(right.shape) - len(left.shape)) +
                                      new_left_shape)

                # now both have the same shapes
                # let compute the shape of these guys
                new_shape = [1] * len(new_left_shape)

                for i, (left_len, right_len) in enumerate(
                        zip(new_left_shape, new_right_shape)):
                    if left_len == right_len:
                        new_shape[i] = left_len
                    else:
                        if left_len != 1 and right_len != 1:
                            raise ValueError("Cannot be broadcasted.")
                        else:
                            new_shape[i] = left_len * right_len

                new_shape = tuple(new_shape)

                if new_shape != left.shape:
                    subst_name = self.stack.name_generator(based_on="subst")
                    inames = tuple(
                        self.stack.name_generator(based_on="i")
                        for _ in new_left_shape)

                    def _empty_if_zero(_idx):
                        if _idx == (0, ):
                            return ()
                        return _idx

                    indices = _empty_if_zero(
                        tuple(
                            Variable(iname) if axis_len != 1 else 0
                            for iname, axis_len in zip(
                                inames[-len(left.shape):], left.shape)))
                    rule = lp.SubstitutionRule(subst_name, inames,
                                               Variable(left.name)(*indices))
                    self.stack.register_substitution(rule)
                    new_left = left.copy(name=subst_name,
                                         shape=new_shape,
                                         dim_tags=None,
                                         order=left.order)
                else:
                    new_left = left

                if new_shape != right.shape:
                    subst_name = self.stack.name_generator(based_on="subst")
                    inames = tuple(
                        self.stack.name_generator(based_on="i")
                        for _ in new_right_shape)

                    def _empty_if_zero(_idx):
                        if _idx == (0, ):
                            return ()
                        return _idx

                    indices = _empty_if_zero(
                        tuple(
                            Variable(iname) if axis_len != 1 else 0
                            for iname, axis_len in zip(
                                inames[-len(right.shape):], right.shape)))
                    rule = lp.SubstitutionRule(subst_name, inames,
                                               Variable(right.name)(*indices))
                    self.stack.register_substitution(rule)
                    new_right = right.copy(name=subst_name,
                                           shape=new_shape,
                                           dim_tags=None,
                                           order=right.order)
                else:
                    new_right = right

                return new_left._arithmetic_op(new_right, op)

        else:
            raise NotImplementedError('__mul__ for', type(other))
Esempio n. 9
0
    def __setitem__(self, index, value):
        """
        Registers an assignment in order to make the indices represented by
        ``index`` to ``value``.

        :arg index: An instance of :class:`int`, or :class:`slice` or
            :class:`numloopy.ArraySymbol`.

        :arg value: An instance of :class:`numloopy.ArraySymbol`, with the same
            shape as represented by ``index``.
        """
        if isinstance(index, (Number, slice, ArraySymbol)):
            index = (index, )
        assert isinstance(index, tuple)

        # current heuristic: assumes that the dereferenced guys are
        # always arguments and not temporary variables, maybe  we need to fix
        # this later?
        try:
            arg_name = self.stack.substs_to_arrays[self.name]
        except KeyError:
            inames = tuple(
                self.stack.name_generator(based_on="i") for _ in self.shape)
            arg_name = self.stack.name_generator(based_on="arr")
            insn = lp.Assignment(
                assignee=parse('{}[{}]'.format(arg_name, ', '.join(inames))),
                expression=parse('{}({})'.format(self.name,
                                                 ', '.join(inames))))
            self.stack.register_implicit_assignment(insn)

            space = isl.Space.create_from_names(isl.DEFAULT_CONTEXT, inames)
            domain = isl.BasicSet.universe(space)

            for iname_name, axis_length in zip(inames, self.shape):
                domain &= make_slab(space, iname_name, 0, axis_length)

            self.stack.domains.append(domain)

        # now handling the second assignment

        try:
            inames, iname_lens = zip(*tuple(
                (self.stack.name_generator(based_on="i"), axis_len)
                for idx, axis_len in zip(index, self.shape)
                if isinstance(idx, slice) or isinstance(idx, ArraySymbol)))
            space = isl.Space.create_from_names(isl.DEFAULT_CONTEXT, inames)
            domain = isl.BasicSet.universe(space)

            for iname_name, axis_length in zip(inames, iname_lens):
                domain &= make_slab(space, iname_name, 0, axis_length)

            self.stack.domains.append(domain)
        except ValueError:
            inames = ()
            iname_lens = ()

        indices = []
        _k = 0
        for idx in index:
            if isinstance(idx, slice):
                indices.append(Variable(inames[_k]))
                _k += 1
            elif isinstance(idx, ArraySymbol):
                indices.append(Variable(idx.name)(Variable(inames[_k])))
                _k += 1
            else:
                indices.append(idx)
        assert _k == len(inames)
        indices = tuple(indices)

        if isinstance(value, ArraySymbol):
            insn = lp.Assignment(assignee=Subscript(Variable(arg_name),
                                                    indices),
                                 expression='{}({})'.format(
                                     value.name,
                                     ', '.join(str(iname)
                                               for iname in inames)))
        elif isinstance(value, Number):
            insn = lp.Assignment(assignee=Subscript(Variable(arg_name),
                                                    indices),
                                 expression=value)
        else:
            raise TypeError("arrays can be only assigned with number or other "
                            "arrays")
        self.stack.register_implicit_assignment(insn)
        if self.name not in self.stack.substs_to_arrays:
            subst_name = self.stack.name_generator(based_on="subst")
            inames = tuple(
                self.stack.name_generator(based_on='i') for _ in self.shape)
            rule = lp.SubstitutionRule(
                subst_name,
                inames,
                expression=Subscript(
                    Variable(arg_name),
                    tuple(Variable(iname) for iname in inames)))
            self.stack.register_substitution(rule)
            self.stack.data.append(self.copy(name=arg_name))

            self.stack.substs_to_arrays[subst_name] = arg_name
            self.name = subst_name