Пример #1
0
def BGMM1_sampler(rstream,
                  weights,
                  mus,
                  sigmas,
                  low,
                  high,
                  draw_shape=None,
                  ndim=None,
                  dtype=None):
    rstate = rstream.new_shared_rstate()

    # shape prep
    if draw_shape is None:
        raise NotImplementedError()
    elif draw_shape is tensor.as_tensor_variable(draw_shape):
        shape = draw_shape
        if ndim is None:
            ndim = tensor.get_vector_length(shape)
    else:
        shape = tensor.hstack(*draw_shape)
        if ndim is None:
            ndim = len(draw_shape)
        assert tensor.get_vector_length(shape) == ndim

    # XXX: be smarter about inferring broadcastable
    op = BGMM1(
        tensor.TensorType(
            broadcastable=(False, ) * ndim,
            dtype=theano.config.floatX if dtype is None else dtype))
    rs, out = op(rstate, weights, mus, sigmas, low, high, shape)
    rstream.add_default_update(out, rstate, rs)
    return out
Пример #2
0
def BGMM1_sampler(rstream, weights, mus, sigmas, low, high,
        draw_shape=None, ndim=None, dtype=None):
    rstate = rstream.new_shared_rstate()

    # shape prep
    if draw_shape is None:
        raise NotImplementedError()
    elif draw_shape is tensor.as_tensor_variable(draw_shape):
        shape = draw_shape
        if ndim is None:
            ndim = tensor.get_vector_length(shape)
    else:
        shape = tensor.hstack(*draw_shape)
        if ndim is None:
            ndim = len(draw_shape)
        assert tensor.get_vector_length(shape) == ndim

    # XXX: be smarter about inferring broadcastable
    op = BGMM1(
            tensor.TensorType(
                broadcastable=(False,) * ndim,
                dtype=theano.config.floatX if dtype is None else dtype))
    rs, out = op(rstate, weights, mus, sigmas, low, high, shape)
    rstream.add_default_update(out, rstate, rs)
    return out
Пример #3
0
def quantized_lognormal_mixture_sampler(rstream, weights, mus, sigmas, step, draw_shape=None, ndim=None, dtype=None):
    rstate = rstream.new_shared_rstate()
    # shape prep
    if draw_shape is None:
        raise NotImplementedError()
    elif draw_shape is tensor.as_tensor_variable(draw_shape):
        shape = draw_shape
        if ndim is None:
            ndim = tensor.get_vector_length(shape)
    elif tuple(draw_shape) == ():
        ndim = 0
        shape = tensor.as_tensor_variable(numpy.asarray([], dtype="int"))
    else:
        shape = tensor.stack(*draw_shape)
        if ndim is None:
            ndim = len(draw_shape)
        assert tensor.get_vector_length(shape) == ndim

    # XXX: be smarter about inferring broadcastable
    op = QuantizedLognormalMixture(
        tensor.TensorType(broadcastable=(False,) * ndim, dtype=theano.config.floatX if dtype is None else dtype)
    )
    rs, out = op(rstate, shape, weights, mus, sigmas, step)
    rstream.add_default_update(out, rstate, rs)
    return out
Пример #4
0
def quantized_lognormal_mixture_sampler(rstream,
                                        weights,
                                        mus,
                                        sigmas,
                                        step,
                                        draw_shape=None,
                                        ndim=None,
                                        dtype=None):
    rstate = rstream.new_shared_rstate()
    # shape prep
    if draw_shape is None:
        raise NotImplementedError()
    elif draw_shape is tensor.as_tensor_variable(draw_shape):
        shape = draw_shape
        if ndim is None:
            ndim = tensor.get_vector_length(shape)
    elif tuple(draw_shape) == ():
        ndim = 0
        shape = tensor.as_tensor_variable(numpy.asarray([], dtype='int'))
    else:
        shape = tensor.stack(*draw_shape)
        if ndim is None:
            ndim = len(draw_shape)
        assert tensor.get_vector_length(shape) == ndim

    # XXX: be smarter about inferring broadcastable
    op = QuantizedLognormalMixture(
        tensor.TensorType(
            broadcastable=(False, ) * ndim,
            dtype=theano.config.floatX if dtype is None else dtype))
    rs, out = op(rstate, shape, weights, mus, sigmas, step)
    rstream.add_default_update(out, rstate, rs)
    return out
Пример #5
0
def DM_sampler(rstream, alpha, draw_shape=None, ndim=None, dtype=None):
    shape = infer_shape(rstream.dirichlet(alpha, draw_shape=draw_shape))
    rstate = rstream.new_shared_rstate()
    op = DM(tensor.TensorType(broadcastable=(False,) * tensor.get_vector_length(shape), dtype=theano.config.floatX))
    rs, out = op(rstate, alpha)
    rstream.add_default_update(out, rstate, rs)
    return out
Пример #6
0
def DM_sampler(rstream, alpha, draw_shape=None, ndim=None, dtype=None):
    shape = infer_shape(rstream.dirichlet(alpha, draw_shape=draw_shape))
    rstate = rstream.new_shared_rstate()
    op = DM(
        tensor.TensorType(broadcastable=(False, ) *
                          tensor.get_vector_length(shape),
                          dtype=theano.config.floatX))
    rs, out = op(rstate, alpha)
    rstream.add_default_update(out, rstate, rs)
    return out
Пример #7
0
    def _infer_shape(self, size, dist_params, param_shapes=None):
        """Compute shapes and broadcasts values.

        Inspired by `tt.add.get_output_info`.

        """

        size_len = tt.get_vector_length(size)

        dummy_params = tuple(p if n == 0 else tt.ones(tuple(p.shape)[:-n])
                             for p, n in zip(dist_params, self.ndims_params))

        _, out_bcasts, bcastd_inputs = tt.add.get_output_info(
            tt.DimShuffle, *dummy_params)

        # _, out_bcasts, bcastd_inputs = tt.add.get_output_info(tt.DimShuffle, *dist_params)

        (bcast_ind, ) = out_bcasts
        ndim_ind = len(bcast_ind)
        shape_ind = bcastd_inputs[0].shape

        if self.ndim_supp == 0:
            shape_supp = tuple()

            # In the scalar case, `size` corresponds to the entire result's
            # shape. This implies the following:
            #     shape_ind[-ndim_ind] == size[:ndim_ind]
            # TODO: How do we add this constraint/check symbolically?

            ndim_reps = max(size_len - ndim_ind, 0)
            shape_reps = tuple(size)[ndim_ind:]
        else:
            shape_supp = self.supp_shape_fn(self.ndim_supp,
                                            self.ndims_params,
                                            dist_params,
                                            param_shapes=param_shapes)

            ndim_reps = size_len
            shape_reps = size

        ndim_shape = self.ndim_supp + ndim_ind + ndim_reps

        if ndim_shape == 0:
            shape = tt.constant([], dtype="int64")
        else:
            shape = tuple(shape_reps) + tuple(shape_ind) + tuple(shape_supp)

        # if shape is None:
        #     raise tt.ShapeError()

        return shape
Пример #8
0
def categorical_sampler(rstream, p, draw_shape, dtype="int32"):
    if not isinstance(p, theano.Variable):
        p = tensor._shared(numpy.asarray(p, dtype=theano.config.floatX))
    if p.ndim != 1:
        raise NotImplementedError()
    if draw_shape.ndim != 1:
        raise TypeError()
    op = Categorical(
        False, tensor.TensorType(broadcastable=(False,) * tensor.get_vector_length(draw_shape), dtype=dtype)
    )
    rstate = rstream.new_shared_rstate()
    new_rstate, out = op(rstate, p, draw_shape)
    rstream.add_default_update(out, rstate, new_rstate)
    return out
Пример #9
0
def categorical_sampler(rstream, p, draw_shape, dtype='int32'):
    if not isinstance(p, theano.Variable):
        p = tensor._shared(numpy.asarray(p, dtype=theano.config.floatX))
    if p.ndim != 1:
        raise NotImplementedError()
    if draw_shape.ndim != 1:
        raise TypeError()
    op = Categorical(
        False,
        tensor.TensorType(broadcastable=(False, ) *
                          tensor.get_vector_length(draw_shape),
                          dtype=dtype))
    rstate = rstream.new_shared_rstate()
    new_rstate, out = op(rstate, p, draw_shape)
    rstream.add_default_update(out, rstate, new_rstate)
    return out
Пример #10
0
    def new_auto_update(cls, generator, ndim, dtype, size, seed):
        """
        Return a symbolic sample from generator.

        cls dictates the random variable (e.g. uniform, normal).

        """
        v_size = theano.tensor.as_tensor_variable(size)
        if ndim is None:
            ndim = get_vector_length(v_size)
        self = cls(output_type=CudaNdarrayType((False,) * ndim), seed=seed, destructive=False)

        o_gen, sample = self(generator, cast(v_size, "int32"))

        sample.generator = generator  # for user
        sample.update = (generator, o_gen)  # for CURAND_RandomStreams
        generator.default_update = o_gen  # for pfunc uses this attribute
        return sample
Пример #11
0
    def new_auto_update(cls, generator, ndim, dtype, size, seed):
        """
        Return a symbolic sample from generator.

        cls dictates the random variable (e.g. uniform, normal)

        """
        v_size = theano.tensor.as_tensor_variable(size)
        if ndim is None:
            ndim = get_vector_length(v_size)
        self = cls(output_type=CudaNdarrayType((False, ) * ndim),
                   seed=seed,
                   destructive=False)

        o_gen, sample = self(generator, cast(v_size, 'int32'))

        sample.generator = generator  # for user
        sample.update = (generator, o_gen)  # for CURAND_RandomStreams
        generator.default_update = o_gen  # for pfunc uses this attribute
        return sample
Пример #12
0
 def new(cls, rstate, ndim, dtype, size):
     v_size = as_tensor_variable(size)
     if ndim is None:
         ndim = get_vector_length(v_size)
     op = cls(CudaNdarrayType((False,)*ndim))
     return op(rstate, cast(v_size, 'int32'))
Пример #13
0
 def new(cls, rstate, ndim, dtype, size):
     v_size = as_tensor_variable(size)
     if ndim is None:
         ndim = get_vector_length(v_size)
     op = cls(CudaNdarrayType((False, ) * ndim))
     return op(rstate, cast(v_size, 'int32'))
Пример #14
0
 def new(cls, rstate, ndim, dtype, size):
     v_size = as_tensor_variable(size)
     if ndim is None:
         ndim = get_vector_length(v_size)
     op = cls(TensorType(dtype, (False,) * ndim))
     return op(rstate, cast(v_size, "int32"))
Пример #15
0
def _infer_ndim_bcast(ndim, shape, *args):
    """
    Infer the number of dimensions from the shape or the other arguments.

    Returns
    -------
    (int, variable, tuple) triple, where the variable is an integer vector,
    and the tuple contains Booleans
        The first element returned is the inferred number of dimensions.
        The second element is the shape inferred (combining symbolic and
        constant informations from shape and args).
        The third element is a broadcasting pattern corresponding to that shape.

    """

    # Find the minimum value of ndim required by the *args
    if args:
        args_ndim = max(arg.ndim for arg in args)
    else:
        args_ndim = 0

    if isinstance(shape, (tuple, list)):
        # there is a convention that -1 means the corresponding shape of a
        # potentially-broadcasted symbolic arg
        #
        # This case combines together symbolic and non-symbolic shape
        # information
        shape_ndim = len(shape)
        if ndim is None:
            ndim = shape_ndim
        else:
            if shape_ndim != ndim:
                raise ValueError(
                    "ndim should be equal to len(shape), but\n",
                    "ndim = %s, len(shape) = %s, shape = %s" %
                    (ndim, shape_ndim, shape),
                )

        bcast = []
        pre_v_shape = []
        for i, s in enumerate(shape):
            if hasattr(s, "type"):  # s is symbolic
                bcast.append(False)  # todo - introspect further
                pre_v_shape.append(s)
            else:
                if s >= 0:
                    pre_v_shape.append(tensor.as_tensor_variable(s))
                    bcast.append((s == 1))
                elif s == -1:
                    n_a_i = 0
                    for a in args:
                        # ndim: _   _   _   _   _   _
                        # ashp:         s0  s1  s2  s3
                        #           i
                        if i >= ndim - a.ndim:
                            n_a_i += 1
                            a_i = i + a.ndim - ndim
                            if not a.broadcastable[a_i]:
                                pre_v_shape.append(a.shape[a_i])
                                bcast.append(False)
                                break
                    else:
                        if n_a_i == 0:
                            raise ValueError(
                                ("Auto-shape of -1 must overlap"
                                 "with the shape of one of the broadcastable"
                                 "inputs"))
                        else:
                            pre_v_shape.append(tensor.as_tensor_variable(1))
                            bcast.append(True)
                else:
                    ValueError("negative shape", s)
        # post-condition: shape may still contain both symbolic and
        # non-symbolic things
        if len(pre_v_shape) == 0:
            v_shape = tensor.constant([], dtype="int64")
        else:
            v_shape = tensor.stack(pre_v_shape)

    elif shape is None:
        # The number of drawn samples will be determined automatically,
        # but we need to know ndim
        if not args:
            raise TypeError(("_infer_ndim_bcast cannot infer shape without"
                             " either shape or args"))
        template = reduce(lambda a, b: a + b, args)
        v_shape = template.shape
        bcast = template.broadcastable
        ndim = template.ndim
    else:
        v_shape = tensor.as_tensor_variable(shape)
        if v_shape.ndim != 1:
            raise TypeError(
                "shape must be a vector or list of scalar, got '%s'" % v_shape)

        if ndim is None:
            ndim = tensor.get_vector_length(v_shape)
        bcast = [False] * ndim

    if v_shape.ndim != 1:
        raise TypeError("shape must be a vector or list of scalar, got '%s'" %
                        v_shape)

    if v_shape.dtype not in theano.tensor.integer_dtypes:
        raise TypeError("shape must be an integer vector or list",
                        v_shape.dtype)

    if args_ndim > ndim:
        raise ValueError(
            "ndim should be at least as big as required by args value",
            (ndim, args_ndim),
            args,
        )

    assert ndim == len(bcast)
    return ndim, tensor.cast(v_shape, "int64"), tuple(bcast)
Пример #16
0
def _infer_ndim_bcast(ndim, shape, *args):
    """
    Infer the number of dimensions from the shape or the other arguments.

    :rtype: (int, variable, tuple) triple, where the variable is an integer
    vector, and the tuple contains Booleans.
    :returns: the first element returned is the inferred number of dimensions.
    The second element is the shape inferred (combining symbolic and constant
    informations from shape and args).
    The third element is a broadcasting pattern corresponding to that shape.
    """

    # Find the minimum value of ndim required by the *args
    if args:
        args_ndim = max(arg.ndim for arg in args)
    else:
        args_ndim = 0

    # there is a convention that -1 means the corresponding shape of a
    # potentially-broadcasted symbolic arg
    if (isinstance(shape, (tuple, list))
            and numpy.all(numpy.asarray(shape) >= 0)):
        bcast = [(s == 1) for s in shape]
        v_shape = tensor.TensorConstant(type=tensor.lvector,
                                        data=theano._asarray(shape,
                                                             dtype='int64'))
        shape_ndim = len(shape)
        if ndim is None:
            ndim = shape_ndim
        else:
            if shape_ndim != ndim:
                raise ValueError(
                    'ndim should be equal to len(shape), but\n',
                    'ndim = %s, len(shape) = %s, shape = %s' %
                    (ndim, shape_ndim, shape))
    elif isinstance(shape, (tuple, list)):
        # there is a convention that -1 means the corresponding shape of a
        # potentially-broadcasted symbolic arg
        #
        # This case combines together symbolic and non-symbolic shape
        # information
        if ndim is None:
            ndim = args_ndim
        else:
            ndim = max(args_ndim, ndim)
        ndim = max(args_ndim, len(shape))
        shape = [-1] * (ndim - len(shape)) + list(shape)
        bcast = []
        pre_v_shape = []
        for i, s in enumerate(shape):
            if hasattr(s, 'type'):  # s is symbolic
                bcast.append(False)  # todo - introspect further
                pre_v_shape.append(s)
            else:
                if s >= 0:
                    pre_v_shape.append(tensor.as_tensor_variable(s))
                    bcast.append((s == 1))
                elif s == -1:
                    n_a_i = 0
                    for a in args:
                        # ndim: _   _   _   _   _   _
                        # ashp:         s0  s1  s2  s3
                        #           i
                        if i >= ndim - a.ndim:
                            n_a_i += 1
                            a_i = i + a.ndim - ndim
                            if not a.broadcastable[a_i]:
                                pre_v_shape.append(a.shape[a_i])
                                bcast.append(False)
                                break
                    else:
                        if n_a_i == 0:
                            raise ValueError(
                                ('Auto-shape of -1 must overlap'
                                 'with the shape of one of the broadcastable'
                                 'inputs'))
                        else:
                            pre_v_shape.append(tensor.as_tensor_variable(1))
                            bcast.append(True)
                else:
                    ValueError('negative shape', s)
        # post-condition: shape may still contain both symbolic and
        # non-symbolic things
        v_shape = tensor.stack(*pre_v_shape)

    elif shape is None:
        # The number of drawn samples will be determined automatically,
        # but we need to know ndim
        if not args:
            raise TypeError(('_infer_ndim_bcast cannot infer shape without'
                             ' either shape or args'))
        template = reduce(lambda a, b: a + b, args)
        v_shape = template.shape
        bcast = template.broadcastable
        ndim = template.ndim
    else:
        v_shape = tensor.as_tensor_variable(shape)
        if ndim is None:
            ndim = tensor.get_vector_length(v_shape)
        bcast = [False] * ndim

    if (not (v_shape.dtype.startswith('int')
             or v_shape.dtype.startswith('uint'))):
        raise TypeError('shape must be an integer vector or list',
                        v_shape.dtype)

    if args_ndim > ndim:
        raise ValueError(
            'ndim should be at least as big as required by args value',
            (ndim, args_ndim), args)

    assert ndim == len(bcast)
    return ndim, tensor.cast(v_shape, 'int32'), tuple(bcast)
Пример #17
0
    def _infer_shape(self, size, dist_params, param_shapes=None):
        """Compute shapes and broadcasts values.

        Inspired by `tt.add.get_output_info`.

        """

        param_shapes = param_shapes or [p.shape for p in dist_params]

        def slice_ind_dims(p, ps, n):
            shape = tuple(ps)

            if n == 0:
                return (p, shape, p.broadcastable)

            ind_slice = (np.s_[:], ) * (p.ndim - n) + (0, ) * n
            return (p[ind_slice], shape[:-n], p.broadcastable[:-n])

        # These are versions of our actual parameters with the expected
        # dimensions removed so that only the independent variate dimensions
        # are left.
        params_ind_slice = tuple(
            slice_ind_dims(p, ps, n)
            for p, ps, n in zip(dist_params, param_shapes, self.ndims_params))

        if len(params_ind_slice) == 1:
            ind_param, ind_shape, ind_bcast = params_ind_slice[0]
            ndim_ind = len(ind_shape)
            shape_ind = ind_shape
        elif len(params_ind_slice) > 1:
            # When there are multiple parameters with different dimensions
            # *and* independent dimensions, the independent dimensions should
            # broadcast together.  We simply add those independent dimension
            # slices and let `tt.add` work out the broadcasting logic.
            p_slices, p_shapes, p_bcasts = zip(*params_ind_slice)
            (shape_ind, ) = tt.add.infer_shape(
                tt.add(*p_slices).owner, p_shapes)
            ndim_ind = len(shape_ind)

        size_len = tt.get_vector_length(size)

        if self.ndim_supp == 0:
            shape_supp = tuple()

            # In the scalar case, `size` corresponds to the entire result's
            # shape. This implies the following:
            #     shape_ind == size[:ndim_ind]
            # TODO: Do we wan to constraint/check symbolically?

            shape_reps = tuple(size)

            if ndim_ind > 0:
                shape_reps = shape_reps[:-ndim_ind]

            ndim_reps = len(shape_reps)
        else:
            shape_supp = self.supp_shape_fn(self.ndim_supp,
                                            self.ndims_params,
                                            dist_params,
                                            param_shapes=param_shapes)

            ndim_reps = size_len
            shape_reps = size

        ndim_shape = self.ndim_supp + ndim_ind + ndim_reps

        if ndim_shape == 0:
            shape = tt.constant([], dtype="int64")
        else:
            shape = tuple(shape_reps) + tuple(shape_ind) + tuple(shape_supp)

        # if shape is None:
        #     raise tt.ShapeError()

        return shape
Пример #18
0
def _infer_ndim_bcast(ndim, shape, *args):
    """
    Infer the number of dimensions from the shape or the other arguments.

    Returns
    -------
    (int, variable, tuple) triple, where the variable is an integer vector,
    and the tuple contains Booleans
        The first element returned is the inferred number of dimensions.
        The second element is the shape inferred (combining symbolic and
        constant informations from shape and args).
        The third element is a broadcasting pattern corresponding to that shape.

    """

    # Find the minimum value of ndim required by the *args
    if args:
        args_ndim = max(arg.ndim for arg in args)
    else:
        args_ndim = 0

    if isinstance(shape, (tuple, list)):
        # there is a convention that -1 means the corresponding shape of a
        # potentially-broadcasted symbolic arg
        #
        # This case combines together symbolic and non-symbolic shape
        # information
        shape_ndim = len(shape)
        if ndim is None:
            ndim = shape_ndim
        else:
            if shape_ndim != ndim:
                raise ValueError('ndim should be equal to len(shape), but\n',
                                 'ndim = %s, len(shape) = %s, shape = %s'
                                 % (ndim, shape_ndim, shape))

        bcast = []
        pre_v_shape = []
        for i, s in enumerate(shape):
            if hasattr(s, 'type'):  # s is symbolic
                bcast.append(False)  # todo - introspect further
                pre_v_shape.append(s)
            else:
                if s >= 0:
                    pre_v_shape.append(tensor.as_tensor_variable(s))
                    bcast.append((s == 1))
                elif s == -1:
                    n_a_i = 0
                    for a in args:
                        # ndim: _   _   _   _   _   _
                        # ashp:         s0  s1  s2  s3
                        #           i
                        if i >= ndim - a.ndim:
                            n_a_i += 1
                            a_i = i + a.ndim - ndim
                            if not a.broadcastable[a_i]:
                                pre_v_shape.append(a.shape[a_i])
                                bcast.append(False)
                                break
                    else:
                        if n_a_i == 0:
                            raise ValueError((
                                'Auto-shape of -1 must overlap'
                                'with the shape of one of the broadcastable'
                                'inputs'))
                        else:
                            pre_v_shape.append(tensor.as_tensor_variable(1))
                            bcast.append(True)
                else:
                    ValueError('negative shape', s)
        # post-condition: shape may still contain both symbolic and
        # non-symbolic things
        if len(pre_v_shape) == 0:
            v_shape = tensor.constant([], dtype='int64')
        else:
            v_shape = tensor.stack(pre_v_shape)

    elif shape is None:
        # The number of drawn samples will be determined automatically,
        # but we need to know ndim
        if not args:
            raise TypeError(('_infer_ndim_bcast cannot infer shape without'
                             ' either shape or args'))
        template = reduce(lambda a, b: a + b, args)
        v_shape = template.shape
        bcast = template.broadcastable
        ndim = template.ndim
    else:
        v_shape = tensor.as_tensor_variable(shape)
        if v_shape.ndim != 1:
            raise TypeError(
                "shape must be a vector or list of scalar, got '%s'" % v_shape)

        if ndim is None:
            ndim = tensor.get_vector_length(v_shape)
        bcast = [False] * ndim

    if v_shape.ndim != 1:
        raise TypeError("shape must be a vector or list of scalar, got '%s'" %
                        v_shape)

    if v_shape.dtype not in theano.tensor.integer_dtypes:
        raise TypeError('shape must be an integer vector or list',
                        v_shape.dtype)

    if args_ndim > ndim:
        raise ValueError(
            'ndim should be at least as big as required by args value',
            (ndim, args_ndim), args)

    assert ndim == len(bcast)
    return ndim, tensor.cast(v_shape, 'int64'), tuple(bcast)
Пример #19
0
def local_dimshuffle_rv_lift(fgraph, node):
    """Lift `DimShuffle`s through `RandomVariable` `Op`s.

    For example, ``normal(mu, std).T == normal(mu.T, std.T)``.

    The basic idea behind this optimization is that we need to separate the
    `DimShuffle`ing into independent `DimShuffle`s that each occur in two
    distinct sub-spaces: the parameters and ``size`` (i.e. replications)
    sub-spaces.

    If a `DimShuffle` exchanges dimensions across those two sub-spaces, then we
    don't do anything.

    Otherwise, if the `DimShuffle` only exchanges dimensions within each of
    those sub-spaces, we can break it apart and apply the parameter-space
    `DimShuffle` to the `RandomVariable`'s distribution parameters, and the
    apply the replications-space `DimShuffle` to the `RandomVariable`'s``size``
    tuple.  The latter is a particularly simple rearranging of a tuple, but the
    former requires a little more work.
    """

    ds_op = node.op

    if not isinstance(ds_op, DimShuffle):
        return False

    base_rv = node.inputs[0]
    rv_node = base_rv.owner

    if not (rv_node and isinstance(rv_node.op, RandomVariable)
            and rv_node.op.ndim_supp == 0):
        return False

    # If no one else is using the underlying `RandomVariable`, then we can
    # do this; otherwise, the graph would be internally inconsistent.
    if not all((n == node or isinstance(n.op, Shape))
               for n, i in fgraph.clients[base_rv]):
        return False

    rv_op = rv_node.op
    rng, size, dtype, *dist_params = rv_node.inputs

    # We need to know the dimensions that were *not* added by the `size`
    # parameter (i.e. the dimensions corresponding to independent variates with
    # different parameter values)
    num_ind_dims = None
    if len(dist_params) == 1:
        num_ind_dims = dist_params[0].ndim
    else:
        # When there is more than one distribution parameter, assume that all
        # of them will broadcast to the maximum number of dimensions
        num_ind_dims = max(d.ndim for d in dist_params)

    # If the indices in `ds_new_order` are entirely within the replication
    # indices group or the independent variates indices group, then we can apply
    # this optimization.

    ds_new_order = ds_op.new_order
    # Create a map from old index order to new/`DimShuffled` index order
    dim_orders = [(n, d) for n, d in enumerate(ds_new_order)
                  if isinstance(d, int)]

    # Find the index at which the replications/independents split occurs
    reps_ind_split_idx = len(dim_orders) - (num_ind_dims + rv_op.ndim_supp)

    ds_reps_new_dims = dim_orders[:reps_ind_split_idx]
    ds_ind_new_dims = dim_orders[reps_ind_split_idx:]
    ds_only_in_ind = ds_ind_new_dims and all(d >= reps_ind_split_idx
                                             for n, d in ds_ind_new_dims)

    if ds_only_in_ind:

        # Update the `size` array to reflect the `DimShuffle`d dimensions,
        # since the trailing dimensions in `size` represent the independent
        # variates dimensions (for univariate distributions, at least)
        new_size = ([
            tt.constant(1, dtype="int64") if o == "x" else size[o]
            for o in ds_new_order
        ] if tt.get_vector_length(size) > 0 else size)

        # Compute the new axes parameter(s) for the `DimShuffle` that will be
        # applied to the `RandomVariable` parameters (they need to be offset)
        rv_params_new_order = [
            d - reps_ind_split_idx if isinstance(d, int) else d
            for d in ds_new_order[ds_ind_new_dims[0][0]:]
        ]

        # Lift the `DimShuffle`s into the parameters
        # NOTE: The parameters might not be broadcasted against each other, so
        # we can only apply the parts of the `DimShuffle` that are relevant.
        new_dist_params = []
        for d in dist_params:
            if d.ndim < len(ds_ind_new_dims):
                _rv_params_new_order = [
                    o for o in rv_params_new_order
                    if (isinstance(o, int) and o < d.ndim) or o == "x"
                ]
            else:
                _rv_params_new_order = rv_params_new_order

            new_dist_params.append(
                type(ds_op)(d.type.broadcastable, _rv_params_new_order)(d))
        new_node = rv_op.make_node(rng, new_size, dtype, *new_dist_params)

        if config.compute_test_value != "off":
            compute_test_value(new_node)

        return [new_node.outputs[1]]

    ds_only_in_reps = ds_reps_new_dims and all(d < reps_ind_split_idx
                                               for n, d in ds_reps_new_dims)

    if ds_only_in_reps:
        # Update the `size` array to reflect the `DimShuffle`d dimensions.
        # There should be no need to `DimShuffle` now.
        new_size = [
            tt.constant(1, dtype="int64") if o == "x" else size[o]
            for o in ds_new_order
        ]

        new_node = rv_op.make_node(rng, new_size, dtype, *dist_params)

        if config.compute_test_value != "off":
            compute_test_value(new_node)

        return [new_node.outputs[1]]

    return False
Пример #20
0
 def new(cls, rstate, ndim, dtype, size):
     v_size = as_tensor_variable(size)
     if ndim is None:
         ndim = get_vector_length(v_size)
     op = cls(GpuArrayType(dtype, (False, ) * ndim))
     return op(rstate, v_size)
Пример #21
0
 def new(cls, rstate, ndim, dtype, size):
     v_size = as_tensor_variable(size)
     if ndim is None:
         ndim = get_vector_length(v_size)
     op = cls(GpuArrayType(dtype, (False,) * ndim))
     return op(rstate, v_size)