Esempio n. 1
0
    def __init__(self, distribution, flow):
        """
        Construct a new :class:`FlowDistribution` from the given `distribution`.

        Args:
            distribution (Distribution): The distribution to transform from.
                It must be continuous,
            flow (Flow): A normalizing flow to transform the `distribution`.
        """
        if not isinstance(flow, Flow):
            raise TypeError('`flow` is not an instance of `tfsnippet.flows.'
                            'Flow`: {!r}'.format(flow))
        distribution = as_distribution(distribution)
        if not distribution.is_continuous:
            raise ValueError('{!r} cannot be transformed by a flow, because '
                             'it is not continuous.'.format(distribution))
        if not distribution.dtype.is_floating:
            raise ValueError(
                '{!r} cannot be transformed by a flow, because '
                'its data type is not float.'.format(distribution))

        self._flow = flow
        self._distribution = distribution
Esempio n. 2
0
    def add(self, name, distribution, n_samples=None, group_ndims=0,
            is_reparameterized=None, flow=None):
        """
        Add a stochastic node to the network.

        A :class:`StochasticTensor` will be created for this node.
        If `name` exists in `observed` dict, its value will be used as the
        observation of this node.  Otherwise samples will be taken from
        `distribution`.

        Args:
            name (str): Name of the stochastic node.
            distribution (Distribution or zhusuan.distributions.Distribution):
                Distribution where the samples should be taken from.
            n_samples (int or tf.Tensor): Number of samples to take.
                If specified, `n_samples` will be taken, with a dedicated
                sampling dimension ``[n_samples]`` at the front.
                If not specified, just one sample will be taken, without the
                dedicated dimension.
            group_ndims (int or tf.Tensor): Number of dimensions at the end of
                ``[n_samples] + batch_shape`` to be considered as events group.
                (default 0)
            is_reparameterized: Whether or not the re-parameterization trick
                should be applied? (default :obj:`None`, following the setting
                of `distribution`)
            flow (BaseFlow): If specified, transform `distribution` by `flow`.

        Returns:
            StochasticTensor: The sampled stochastic tensor.

        Raises:
            TypeError: If `name` is not a str, or `distribution` is a
                :class:`TransformedDistribution`.
            KeyError: If :class:`StochasticTensor` with `name` already exists.
            ValueError: If `transform` cannot be applied.

        See Also:
            :meth:`tfsnippet.distributions.Distribution.sample`
        """
        if not isinstance(name, six.string_types):
            raise TypeError('`name` must be a str')
        if name in self._stochastic_tensors:
            raise KeyError('StochasticTensor with name {!r} already exists in '
                           'the BayesianNet.  Names must be unique.'.
                           format(name))
        if flow is not None and name in self._observed and \
                not flow.explicitly_invertible:
            raise TypeError('The observed variable {!r} expects `flow` to be '
                            'explicitly invertible, but it is not: {!r}.'.
                            format(name, flow))

        distribution = as_distribution(distribution)
        if flow is not None:
            distribution = FlowDistribution(distribution, flow)

        if name in self._observed:
            t = StochasticTensor(
                distribution=distribution,
                tensor=self._observed[name],
                n_samples=n_samples,
                group_ndims=group_ndims,
                is_reparameterized=is_reparameterized,
            )
        else:
            t = distribution.sample(
                n_samples=n_samples,
                group_ndims=group_ndims,
                is_reparameterized=is_reparameterized,
            )
            assert(isinstance(t, StochasticTensor))

        self._stochastic_tensors[name] = t
        return t
Esempio n. 3
0
    def add(self,
            name,
            distribution,
            n_samples=None,
            group_ndims=0,
            is_reparameterized=None):
        """
        Add a stochastic node to the network.

        A :class:`StochasticTensor` will be created for this node.
        If `name` exists in `observed` dict, its value will be used as the
        observation of this node.  Otherwise samples will be taken from
        `distribution`.

        Args:
            name (str): Name of the stochastic node.
            distribution (Distribution or zhusuan.distributions.Distribution):
                Distribution where the samples should be taken from.
            n_samples (int or tf.Tensor): Number of samples to take.
                If specified, `n_samples` will be taken, with a dedicated
                sampling dimension ``[n_samples]`` at the front.
                If not specified, just one sample will be taken, without the
                dedicated dimension.
            group_ndims (int or tf.Tensor): Number of dimensions at the end of
                ``[n_samples] + batch_shape`` to be considered as events group.
                (default 0)
            is_reparameterized: If observation is not given for `name`, this
                argument will be used to determine whether or not
                re-parameterization trick should be applied when taking
                samples from `distribution` (if not specified,  use
                `distribution.is_reparameterized`).

                If observation is given for `name`, and this argument is
                set to :obj:`True`, it will be used to validate the observation.
                If this argument is set to :obj:`False`, `tf.stop_gradient`
                will be applied on the observation.

        Returns:
            StochasticTensor: The sampled stochastic tensor.

        Raises:
            TypeError: If `name` is not a str, or `distribution` is a
                :class:`TransformedDistribution`.
            KeyError: If :class:`StochasticTensor` with `name` already exists.
            ValueError: If `transform` cannot be applied,
                or `is_reparameterized = True`, but the observation is not
                re-parameterized.

        See Also:
            :meth:`tfsnippet.distributions.Distribution.sample`
        """
        if not isinstance(name, six.string_types):
            raise TypeError('`name` must be a str')
        if name in self._stochastic_tensors:
            raise KeyError(
                'StochasticTensor with name {!r} already exists in '
                'the BayesianNet.  Names must be unique.'.format(name))

        distribution = as_distribution(distribution)

        if name in self._observed:
            ob_tensor = self._observed[name]
            if isinstance(ob_tensor, StochasticTensor):
                if is_reparameterized and not ob_tensor.is_reparameterized:
                    raise ValueError(
                        '`is_reparameterized` is True, but the observation '
                        'for `{}` is not re-parameterized: {}'.format(
                            name, ob_tensor))
                if is_reparameterized is None:
                    is_reparameterized = ob_tensor.is_reparameterized

            if not is_reparameterized:
                ob_tensor = tf.stop_gradient(ob_tensor)

            t = StochasticTensor(
                distribution=distribution,
                tensor=ob_tensor,
                n_samples=n_samples,
                group_ndims=group_ndims,
                is_reparameterized=is_reparameterized,
            )
        else:
            t = distribution.sample(
                n_samples=n_samples,
                group_ndims=group_ndims,
                is_reparameterized=is_reparameterized,
            )
            assert (isinstance(t, StochasticTensor))

        self._stochastic_tensors[name] = t
        return t
Esempio n. 4
0
 def test_type_error(self):
     with pytest.raises(TypeError,
                        match='Type `int` cannot be casted into `tfsnippet.'
                        'distributions.Distribution`'):
         _ = as_distribution(1)
Esempio n. 5
0
 def test_zs_distribution(self):
     normal = zd.Normal(mean=0., std=1.)
     distrib = as_distribution(normal)
     self.assertIsInstance(distrib, Distribution)
     self.assertIsInstance(distrib, ZhuSuanDistribution)
     self.assertIs(distrib._distribution, normal)
Esempio n. 6
0
 def test_distribution(self):
     d = Distribution()
     distrib = as_distribution(d)
     self.assertIs(distrib, d)
Esempio n. 7
0
    def add(self,
            name,
            distribution,
            n_samples=None,
            group_ndims=0,
            is_reparameterized=None,
            transform=None):
        """
        Add a stochastic node to the network.

        A :class:`StochasticTensor` will be created for this node.
        If `name` exists in `observed` dict, its value will be used as the
        observation of this node.  Otherwise samples will be taken from
        `distribution`.

        Args:
            name (str): Name of the stochastic node.
            distribution (Distribution or zhusuan.distributions.Distribution):
                Distribution where the samples should be taken from.
            n_samples (int or tf.Tensor): Number of samples to take.
                If specified, `n_samples` will be taken, with a dedicated
                sampling dimension ``[n_samples]`` at the front.
                If not specified, just one sample will be taken, without the
                dedicated dimension.
            group_ndims (int or tf.Tensor): Number of dimensions at the end of
                ``[n_samples] + batch_shape`` to be considered as events group.
                (default 0)
            is_reparameterized: Whether or not the re-parameterization trick
                should be applied? (default :obj:`None`, following the setting
                of `distribution`)
            transform ((Tensor, Tensor) -> (tf.Tensor, tf.Tensor)):
                The function to transform (x, log_p) to (x', log_p').
                If specified, a :class:`StochasticTensor` will be sampled,
                then transformed, then wrapped by a :class:`StochasticTensor`
                with :class:`TransformedDistribution`.

        Returns:
            StochasticTensor: The sampled stochastic tensor.

        Raises:
            TypeError: If `name` is not a str, or `distribution` is a
                :class:`TransformedDistribution`.
            KeyError: If :class:`StochasticTensor` with `name` already exists.
            ValueError: If `transform` cannot be applied.

        See Also:
            :meth:`tfsnippet.distributions.Distribution.sample`
        """
        if not isinstance(name, six.string_types):
            raise TypeError('`name` must be a str')
        if name in self._stochastic_tensors:
            raise KeyError(
                'StochasticTensor with name {!r} already exists in '
                'the BayesianNet.  Names must be unique.'.format(name))
        if isinstance(distribution, TransformedDistribution):
            raise TypeError('Cannot add `TransformedDistribution`.')
        if transform is not None and \
                (not distribution.is_continuous or
                 not distribution.is_reparameterized or
                 is_reparameterized is False):
            raise ValueError('`transform` can only be applied on continuous, '
                             're-parameterized variables.')
        if transform is not None and name in self._observed:
            raise ValueError('`observed` variable cannot be transformed.')

        distribution = as_distribution(distribution)
        if name in self._observed:
            t = StochasticTensor(
                distribution=distribution,
                tensor=self._observed[name],
                n_samples=n_samples,
                group_ndims=group_ndims,
                is_reparameterized=is_reparameterized,
            )
        else:
            t = distribution.sample(
                n_samples=n_samples,
                group_ndims=group_ndims,
                is_reparameterized=is_reparameterized,
            )
            assert (isinstance(t, StochasticTensor))

            # do transformation
            if transform is not None:
                t_log_p = t.log_prob()
                ft, ft_log_p = transform(t, t_log_p)
                ft = tf.convert_to_tensor(ft)
                ft_log_p = tf.convert_to_tensor(ft_log_p)
                if not ft.dtype.is_floating:
                    raise ValueError('The transformed samples must be '
                                     'continuous: got {!r}'.format(ft))
                t = StochasticTensor(distribution=TransformedDistribution(
                    origin=t,
                    transformed=ft,
                    transformed_log_p=ft_log_p,
                    is_reparameterized=t.is_reparameterized,
                    is_continuous=True),
                                     tensor=ft,
                                     n_samples=t.n_samples,
                                     group_ndims=t.group_ndims,
                                     is_reparameterized=t.is_reparameterized)

        self._stochastic_tensors[name] = t
        return t
Esempio n. 8
0
 def test_distribution(self):
     d = Normal(mean=0., std=1.)
     distrib = as_distribution(d)
     self.assertIs(distrib, d)