예제 #1
0
    def make_node(self, a, val, offset):
        a = aet.as_tensor_variable(a)
        val = aet.as_tensor_variable(val)
        offset = aet.as_tensor_variable(offset)
        if a.ndim != 2:
            raise TypeError(
                "%s: first parameter must have exactly"
                " two dimensions" % self.__class__.__name__
            )
        elif val.ndim != 0:
            raise TypeError(
                f"{self.__class__.__name__}: second parameter must be a scalar"
            )
        elif offset.ndim != 0:
            raise TypeError(
                f"{self.__class__.__name__}: third parameter must be a scalar"
            )
        val = aet.cast(val, dtype=upcast(a.dtype, val.dtype))
        if val.dtype != a.dtype:
            raise TypeError(
                "%s: type of second parameter must be the same"
                " as the first's" % self.__class__.__name__
            )
        elif offset.dtype not in integer_dtypes:
            raise TypeError(
                f"{self.__class__.__name__}: type of third parameter must be as integer"
                " use aesara.tensor.cast( input, 'int32/int64')"
            )

        return Apply(self, [a, val, offset], [a.type()])
예제 #2
0
 def make_node(self, x, w, v, gw, gv):
     x, w, v, gw, gv = map(as_tensor_variable, (x, w, v, gw, gv))
     assert x.ndim == 2
     assert w.ndim == 1
     assert v.ndim == 2
     assert gw.ndim == 1
     assert gv.ndim == 2
     out_dtype = aes.upcast(x.dtype, w.dtype, v.dtype, gw.dtype, gv.dtype)
     out = matrix(dtype=out_dtype)
     return Apply(self, [x, w, v, gw, gv], [out])
예제 #3
0
 def make_node(self, a, val):
     a = basic.as_tensor_variable(a)
     val = basic.as_tensor_variable(val)
     if a.ndim < 2:
         raise TypeError("%s: first parameter must have at least"
                         " two dimensions" % self.__class__.__name__)
     elif val.ndim != 0:
         raise TypeError("%s: second parameter must be a scalar" %
                         self.__class__.__name__)
     val = basic.cast(val, dtype=upcast(a.dtype, val.dtype))
     if val.dtype != a.dtype:
         raise TypeError("%s: type of second parameter must be the same as"
                         " the first's" % self.__class__.__name__)
     return Apply(self, [a, val], [a.type()])
예제 #4
0
파일: type.py 프로젝트: ricardoV94/aesara
    def filter(self, data, strict=False, allow_downcast=None):
        """
        Convert `data` to something which can be associated to a
        `TensorVariable`.

        This function is not meant to be called in user code. It is for
        `Linker` instances to use when running a compiled graph.

        """
        # Explicit error message when one accidentally uses a Variable as
        # input (typical mistake, especially with shared variables).
        if isinstance(data, Variable):
            raise TypeError(
                "Expected an array-like object, but found a Variable: "
                "maybe you are trying to call a function on a (possibly "
                "shared) variable instead of a numeric array?")

        if isinstance(data, np.memmap) and (data.dtype == self.numpy_dtype):
            # numpy.memmap is a "safe" subclass of ndarray,
            # so we can use it wherever we expect a base ndarray.
            # however, casting it would defeat the purpose of not
            # loading the whole data into memory
            pass
        elif isinstance(data, np.ndarray) and (data.dtype == self.numpy_dtype):
            if data.dtype.num != self.numpy_dtype.num:
                data = _asarray(data, dtype=self.dtype)
            # -- now fall through to ndim check
        elif strict:
            # If any of the two conditions above was not met,
            # we raise a meaningful TypeError.
            if not isinstance(data, np.ndarray):
                raise TypeError(
                    f"{self} expected a ndarray object (got {type(data)}).")
            if data.dtype != self.numpy_dtype:
                raise TypeError(
                    f"{self} expected an ndarray with dtype={self.numpy_dtype} (got {data.dtype})."
                )
        else:
            if allow_downcast:
                # Convert to self.dtype, regardless of the type of data
                data = _asarray(data, dtype=self.dtype)
                # TODO: consider to pad shape with ones to make it consistent
                # with self.broadcastable... like vector->row type thing
            else:
                if isinstance(data, np.ndarray):
                    # Check if self.dtype can accurately represent data
                    # (do not try to convert the data)
                    up_dtype = aes.upcast(self.dtype, data.dtype)
                    if up_dtype == self.dtype:
                        # Bug in the following line when data is a
                        # scalar array, see
                        # http://projects.scipy.org/numpy/ticket/1611
                        # data = data.astype(self.dtype)
                        data = _asarray(data, dtype=self.dtype)
                    if up_dtype != self.dtype:
                        err_msg = (
                            f"{self} cannot store a value of dtype {data.dtype} without "
                            "risking loss of precision. If you do not mind "
                            "this loss, you can: "
                            f"1) explicitly cast your data to {self.dtype}, or "
                            '2) set "allow_input_downcast=True" when calling '
                            f'"function". Value: "{repr(data)}"')
                        raise TypeError(err_msg)
                elif (allow_downcast is None
                      and isinstance(data, (float, np.floating))
                      and self.dtype == config.floatX):
                    # Special case where we allow downcasting of Python float
                    # literals to floatX, even when floatX=='float32'
                    data = _asarray(data, self.dtype)
                else:
                    # data has to be converted.
                    # Check that this conversion is lossless
                    converted_data = _asarray(data, self.dtype)
                    # We use the `values_eq` static function from TensorType
                    # to handle NaN values.
                    if TensorType.values_eq(np.asarray(data),
                                            converted_data,
                                            force_same_dtype=False):
                        data = converted_data
                    else:
                        # Do not print a too long description of data
                        # (ndarray truncates it, but it's not sure for data)
                        str_data = str(data)
                        if len(str_data) > 80:
                            str_data = str_data[:75] + "(...)"

                        err_msg = (
                            f"{self} cannot store accurately value {data}, "
                            f"it would be represented as {converted_data}. "
                            "If you do not mind this precision loss, you can: "
                            "1) explicitly convert your data to a numpy array "
                            f"of dtype {self.dtype}, or "
                            '2) set "allow_input_downcast=True" when calling '
                            '"function".')
                        raise TypeError(err_msg)

        if self.ndim != data.ndim:
            raise TypeError(
                f"Wrong number of dimensions: expected {self.ndim},"
                f" got {data.ndim} with shape {data.shape}.")
        if not data.flags.aligned:
            try:
                msg = "object buffer" + str(data.data)
            except AttributeError:
                msg = ""
            raise TypeError(
                "The numpy.ndarray object is not aligned."
                " Aesara C code does not support that.",
                msg,
                "object shape",
                data.shape,
                "object strides",
                data.strides,
                "object dtype",
                data.dtype,
            )

        i = 0
        for b in self.broadcastable:
            if b and data.shape[i] != 1:
                raise TypeError(
                    "Non-unit value on shape on a broadcastable"
                    " dimension.",
                    data.shape,
                    self.broadcastable,
                )
            i += 1
        if self.filter_checks_isfinite and not np.all(np.isfinite(data)):
            raise ValueError("non-finite elements not allowed")
        return data
예제 #5
0
    def uniform(self,
                size,
                low=0.0,
                high=1.0,
                ndim=None,
                dtype=None,
                nstreams=None,
                **kwargs):
        # TODO : need description for parameter 'size', 'ndim', 'nstreams'
        """
        Sample a tensor of given size whose element from a uniform
        distribution between low and high.

        If the size argument is ambiguous on the number of dimensions,
        ndim may be a plain integer to supplement the missing information.

        Parameters
        ----------
        low
            Lower bound of the interval on which values are sampled.
            If the ``dtype`` arg is provided, ``low`` will be cast into
            dtype. This bound is excluded.
        high
            Higher bound of the interval on which values are sampled.
            If the ``dtype`` arg is provided, ``high`` will be cast into
            dtype. This bound is excluded.
        size
          Can be a list of integer or Aesara variable (ex: the shape
          of other Aesara Variable).
        dtype
            The output data type. If dtype is not specified, it will be
            inferred from the dtype of low and high, but will be at
            least as precise as floatX.

        """
        low = as_tensor_variable(low)
        high = as_tensor_variable(high)

        if dtype is None:
            dtype = aes.upcast(config.floatX, low.dtype, high.dtype)

        low = cast(low, dtype=dtype)
        high = cast(high, dtype=dtype)

        low = undefined_grad(low)
        high = undefined_grad(high)

        if isinstance(size, tuple):
            msg = "size must be a tuple of int or an Aesara variable"
            assert all(
                isinstance(i, (np.integer, int, Variable)) for i in size), msg
            if any(isinstance(i, (np.integer, int)) and i <= 0 for i in size):
                raise ValueError(
                    "The specified size contains a dimension with value <= 0",
                    size)

        else:
            if not (isinstance(size, Variable) and size.ndim == 1):
                raise TypeError("size must be a tuple of int or an Aesara "
                                "Variable with 1 dimension, got " + str(size) +
                                " of type " + str(type(size)))
        orig_nstreams = nstreams
        if nstreams is None:
            nstreams = self.n_streams(size)
        rstates = self.get_substream_rstates(nstreams, dtype)

        d = {}
        if "target" in kwargs:
            d = dict(target=kwargs.pop("target"))
        if len(kwargs) > 0:
            raise TypeError(
                f"uniform() got unexpected keyword arguments {kwargs.keys()}")
        node_rstate = shared(rstates, **d)
        u = self.pretty_return(
            node_rstate,
            *mrg_uniform.new(node_rstate, ndim, dtype, size),
            size=size,
            nstreams=orig_nstreams,
        )
        # Add a reference to distinguish from other shared variables
        node_rstate.tag.is_rng = True
        r = u * (high - low) + low

        if u.type.broadcastable != r.type.broadcastable:
            raise NotImplementedError(
                "Increase the size to match the broadcasting pattern of "
                "`low` and `high` arguments")

        assert r.dtype == dtype
        return r
예제 #6
0
    def normal(
        self,
        size,
        avg=0.0,
        std=1.0,
        ndim=None,
        dtype=None,
        nstreams=None,
        truncate=False,
        **kwargs,
    ):
        """
        Sample a tensor of values from a normal distribution.

        Parameters
        ----------
        size : int_vector_like
            Array dimensions for the output tensor.
        avg : float_like, optional
            The mean value for the truncated normal to sample from (defaults to 0.0).
        std : float_like, optional
            The standard deviation for the truncated normal to sample from (defaults to 1.0).
        truncate : bool, optional
            Truncates the normal distribution at 2 standard deviations if True (defaults to False).
            When this flag is set, the standard deviation of the result will be less than the one specified.
        ndim : int, optional
            The number of dimensions for the output tensor (defaults to None).
            This argument is necessary if the size argument is ambiguous on the number of dimensions.
        dtype : str, optional
            The data-type for the output tensor. If not specified,
            the dtype is inferred from avg and std, but it is at least as precise as floatX.
        kwargs
            Other keyword arguments for random number generation (see uniform).

        Returns
        -------
        samples : TensorVariable
            A Aesara tensor of samples randomly drawn from a normal distribution.

        """
        size = _check_size(size)
        avg = undefined_grad(as_tensor_variable(avg))
        std = undefined_grad(as_tensor_variable(std))

        if dtype is None:
            dtype = aes.upcast(config.floatX, avg.dtype, std.dtype)

        avg = at.cast(avg, dtype=dtype)
        std = at.cast(std, dtype=dtype)

        # generate even number of uniform samples
        # Do manual constant folding to lower optiimizer work.
        if isinstance(size, Constant):
            n_odd_samples = size.prod(dtype="int64")
        else:
            n_odd_samples = prod(size, dtype="int64")
        n_even_samples = n_odd_samples + n_odd_samples % 2
        uniform = self.uniform(
            (n_even_samples, ),
            low=0.0,
            high=1.0,
            ndim=1,
            dtype=dtype,
            nstreams=nstreams,
            **kwargs,
        )

        # box-muller transform
        u1 = uniform[:n_even_samples // 2]
        u2 = uniform[n_even_samples // 2:]
        r = sqrt(-2.0 * log(u1))
        theta = np.array(2.0 * np.pi, dtype=dtype) * u2
        cos_theta, sin_theta = cos(theta), sin(theta)
        z0 = r * cos_theta
        z1 = r * sin_theta

        if truncate:
            # use valid samples
            to_fix0 = (z0 < -2.0) | (z0 > 2.0)
            to_fix1 = (z1 < -2.0) | (z1 > 2.0)
            z0_valid = z0[at.nonzero(~to_fix0)]
            z1_valid = z1[at.nonzero(~to_fix1)]

            # re-sample invalid samples
            to_fix0 = at.nonzero(to_fix0)[0]
            to_fix1 = at.nonzero(to_fix1)[0]
            n_fix_samples = to_fix0.size + to_fix1.size
            lower = at.constant(1.0 / np.e**2, dtype=dtype)
            u_fix = self.uniform(
                (n_fix_samples, ),
                low=lower,
                high=1.0,
                ndim=1,
                dtype=dtype,
                nstreams=nstreams,
                **kwargs,
            )
            r_fix = sqrt(-2.0 * log(u_fix))
            z0_fixed = r_fix[:to_fix0.size] * cos_theta[to_fix0]
            z1_fixed = r_fix[to_fix0.size:] * sin_theta[to_fix1]

            # pack everything together to a useful result
            norm_samples = at.join(0, z0_valid, z0_fixed, z1_valid, z1_fixed)
        else:
            norm_samples = at.join(0, z0, z1)
        if isinstance(n_odd_samples, Variable):
            samples = norm_samples[:n_odd_samples]
        elif n_odd_samples % 2 == 1:
            samples = norm_samples[:-1]
        else:
            samples = norm_samples
        samples = reshape(samples, newshape=size, ndim=ndim)
        samples *= std
        samples += avg

        return samples
예제 #7
0
 def make_node(self, a, b):
     a = as_tensor_variable(a)
     b = as_tensor_variable(b)
     out_dtype = aes.upcast(a.dtype, b.dtype)
     x = matrix(dtype=out_dtype)
     return Apply(self, [a, b], [x])
예제 #8
0
    def __init__(self, comp_dists, states, *args, **kwargs):
        """Initialize a `SwitchingProcess` instance.

        Each `Distribution` object in `comp_dists` must have a
        `Distribution.random_subset` method that takes a list of indices and
        returns a sample for only that subset.  Unfortunately, since PyMC3
        doesn't provide such a method, you'll have to implement it yourself and
        monkey patch a `Distribution` class.

        Parameters
        ----------
        comp_dists : list of Distribution
            A list containing `Distribution` objects for each mixture component.
            These are essentially the emissions distributions.
        states : DiscreteMarkovChain
            The hidden state sequence.  It should have a number of states
            equal to the size of `comp_dists`.

        """
        self.states = at.as_tensor_variable(pm.intX(states))

        if len(comp_dists) > 31:
            warnings.warn(
                "There are too many mixture distributions to properly"
                " determine their combined shape.")

        self.comp_dists = comp_dists

        states_tv = get_test_value(self.states)
        bcast_comps = np.broadcast(
            states_tv, *[get_and_check_comp_value(x) for x in comp_dists[:31]])
        shape = bcast_comps.shape

        defaults = kwargs.pop("defaults", [])

        out_dtype = upcast(*[x.type.dtype for x in comp_dists])
        dtype = kwargs.pop("dtype", out_dtype)

        if not all_discrete(comp_dists):
            try:
                bcast_means = tt_broadcast_arrays(
                    *([self.states] +
                      [d.mean.astype(dtype) for d in self.comp_dists]))
                self.mean = at.choose(self.states, bcast_means[1:])

                if "mean" not in defaults:
                    defaults.append("mean")

            except (AttributeError, ValueError,
                    IndexError):  # pragma: no cover
                pass

        try:
            bcast_modes = tt_broadcast_arrays(
                *([self.states] +
                  [d.mode.astype(dtype) for d in self.comp_dists]))
            self.mode = at.choose(self.states, bcast_modes[1:])

            if "mode" not in defaults:
                defaults.append("mode")

        except (AttributeError, ValueError, IndexError):  # pragma: no cover
            pass

        super().__init__(shape=shape, dtype=dtype, defaults=defaults, **kwargs)
예제 #9
0
    def filter_inplace(self,
                       data,
                       old_data,
                       strict=False,
                       allow_downcast=None):
        if isinstance(data,
                      gpuarray.GpuArray) and data.typecode == self.typecode:
            # This is just to make this condition not enter the
            # following branches
            pass
        elif strict:
            if not isinstance(data, gpuarray.GpuArray):
                raise TypeError("%s expected a GpuArray object." % self, data,
                                type(data))
            if self.typecode != data.typecode:
                raise TypeError("%s expected typecode %d (dtype %s), "
                                "got %d (dtype %s)." %
                                (self, self.typecode, self.dtype,
                                 data.typecode, str(data.dtype)))
            if self.context != data.context:
                raise TypeError("data context does not match type context")
            # fallthrough to ndim check
        elif allow_downcast or (allow_downcast is None and type(data) == float
                                and self.dtype == config.floatX):
            if not isinstance(data, gpuarray.GpuArray):
                data = np.array(data,
                                dtype=self.dtype,
                                copy=False,
                                ndmin=len(self.broadcastable))
            else:
                data = gpuarray.array(
                    data,
                    dtype=self.typecode,
                    copy=False,
                    ndmin=len(self.broadcastable),
                    context=self.context,
                )
        else:
            if not hasattr(data, "dtype"):
                converted_data = aesara._asarray(data, self.dtype)
                # We use the `values_eq` static function from TensorType
                # to handle NaN values.
                if TensorType.values_eq(np.asarray(data),
                                        converted_data,
                                        force_same_dtype=False):
                    data = converted_data

            up_dtype = scalar.upcast(self.dtype, data.dtype)
            if up_dtype == self.dtype:
                if not isinstance(data, gpuarray.GpuArray):
                    data = np.array(data, dtype=self.dtype, copy=False)
                else:
                    data = gpuarray.array(data, dtype=self.dtype, copy=False)
            else:
                raise TypeError("%s cannot store a value of dtype %s "
                                "without risking loss of precision." %
                                (self, data.dtype))

        if self.ndim != data.ndim:
            raise TypeError(
                "Wrong number of dimensions: expected %s, "
                "got %s with shape %s." % (self.ndim, data.ndim, data.shape),
                data,
            )
        shp = data.shape
        for i, b in enumerate(self.broadcastable):
            if b and shp[i] != 1:
                raise TypeError(
                    "Non-unit value on shape on a broadcastable"
                    " dimension.",
                    shp,
                    self.broadcastable,
                )
        if not isinstance(data, gpuarray.GpuArray):
            if (old_data is not None and old_data.shape == data.shape and (
                    # write() only work if the destitation is contiguous.
                    old_data.flags["C_CONTIGUOUS"]
                    or old_data.flags["F_CONTIGUOUS"])):
                old_data.write(data)
                data = old_data
            else:
                data = pygpu.array(data, context=self.context)
        return data