Exemple #1
0
 def test_broadcast_distribution_samples(self, samples_to_broadcast):
     size, samples, broadcast_shape = samples_to_broadcast
     if broadcast_shape is not None:
         outs = broadcast_distribution_samples(samples, size=size)
         assert all(o.shape == broadcast_shape for o in outs)
     else:
         with pytest.raises(ValueError):
             broadcast_distribution_samples(samples, size=size)
 def test_broadcast_distribution_samples(self, samples_to_broadcast):
     size, samples, broadcast_shape = samples_to_broadcast
     if broadcast_shape is not None:
         outs = broadcast_distribution_samples(samples, size=size)
         assert all((o.shape == broadcast_shape for o in outs))
     else:
         with pytest.raises(ValueError):
             broadcast_distribution_samples(samples, size=size)
Exemple #3
0
 def _comp_samples(self,
                   point=None,
                   size=None,
                   comp_dist_shapes=None,
                   broadcast_shape=None):
     if self.comp_is_distribution:
         samples = self._comp_dists.random(point=point, size=size)
     else:
         if comp_dist_shapes is None:
             comp_dist_shapes = self._comp_dist_shapes
         if broadcast_shape is None:
             broadcast_shape = self._sample_shape
         samples = []
         for dist_shape, generator in zip(comp_dist_shapes,
                                          self._generators):
             sample = generate_samples(
                 generator=generator,
                 dist_shape=dist_shape,
                 broadcast_shape=broadcast_shape,
                 point=point,
                 size=size,
                 not_broadcast_kwargs={"raw_size_": size},
             )
             samples.append(sample)
         samples = np.array(
             broadcast_distribution_samples(samples, size=size))
         # In the logp we assume the last axis holds the mixture components
         # so we move the axis to the last dimension
         samples = np.moveaxis(samples, 0, -1)
     return samples.astype(self.dtype)
Exemple #4
0
    def random(self, point=None, size=None):
        """
        Draw random values from defined Mixture distribution.

        Parameters
        ----------
        point: dict, optional
            Dict of variable values on which random values are to be
            conditioned (uses default point if not specified).
        size: int, optional
            Desired size of random sample (returns one sample if not
            specified).

        Returns
        -------
        array
        """
        # Convert size to tuple
        size = to_tuple(size)
        # Draw mixture weights and infer the comp_dists shapes
        with _DrawValuesContext() as draw_context:
            # We first need to check w and comp_tmp shapes and re compute size
            w = draw_values([self.w], point=point, size=size)[0]
            comp_dist_shapes, broadcast_shape = self.infer_comp_dist_shapes(point=point)

        # When size is not None, it's hard to tell the w parameter shape
        if size is not None and w.shape[: len(size)] == size:
            w_shape = w.shape[len(size) :]
        else:
            w_shape = w.shape

        # Try to determine parameter shape and dist_shape
        if self.comp_is_distribution:
            param_shape = np.broadcast(np.empty(w_shape), np.empty(broadcast_shape)).shape
        else:
            param_shape = np.broadcast(np.empty(w_shape), np.empty(broadcast_shape + (1,))).shape
        if np.asarray(self.shape).size != 0:
            dist_shape = np.broadcast(np.empty(self.shape), np.empty(param_shape[:-1])).shape
        else:
            dist_shape = param_shape[:-1]

        # Try to determine the size that must be used to get the mixture
        # components (i.e. get random choices using w).
        # 1. There must be size independent choices based on w.
        # 2. There must also be independent draws for each non singleton axis
        # of w.
        # 3. There must also be independent draws for each dimension added by
        # self.shape with respect to the w.ndim. These usually correspond to
        # observed variables with batch shapes
        wsh = (1,) * (len(dist_shape) - len(w_shape) + 1) + w_shape[:-1]
        psh = (1,) * (len(dist_shape) - len(param_shape) + 1) + param_shape[:-1]
        w_sample_size = []
        # Loop through the dist_shape to get the conditions 2 and 3 first
        for i in range(len(dist_shape)):
            if dist_shape[i] != psh[i] and wsh[i] == 1:
                # self.shape[i] is a non singleton dimension (usually caused by
                # observed data)
                sh = dist_shape[i]
            else:
                sh = wsh[i]
            w_sample_size.append(sh)
        if size is not None and w_sample_size[: len(size)] != size:
            w_sample_size = size + tuple(w_sample_size)
        # Broadcast w to the w_sample_size (add a singleton last axis for the
        # mixture components)
        w = broadcast_distribution_samples([w, np.empty(w_sample_size + (1,))], size=size)[0]

        # Semiflatten the mixture weights. The last axis is the number of
        # mixture mixture components, and the rest is all about size,
        # dist_shape and broadcasting
        w_ = np.reshape(w, (-1, w.shape[-1]))
        w_samples = random_choice(p=w_, size=None)  # w's shape already includes size
        # Now we broadcast the chosen components to the dist_shape
        w_samples = np.reshape(w_samples, w.shape[:-1])
        if size is not None and dist_shape[: len(size)] != size:
            w_samples = np.broadcast_to(w_samples, size + dist_shape)
        else:
            w_samples = np.broadcast_to(w_samples, dist_shape)

        # When size is not None, maybe dist_shape partially overlaps with size
        if size is not None:
            if size == dist_shape:
                size = None
            elif size[-len(dist_shape) :] == dist_shape:
                size = size[: len(size) - len(dist_shape)]

        # We get an integer _size instead of a tuple size for drawing the
        # mixture, then we just reshape the output
        if size is None:
            _size = None
        else:
            _size = int(np.prod(size))

        # Compute the total size of the mixture's random call with size
        if _size is not None:
            output_size = int(_size * np.prod(dist_shape) * param_shape[-1])
        else:
            output_size = int(np.prod(dist_shape) * param_shape[-1])
        # Get the size we need for the mixture's random call
        if self.comp_is_distribution:
            mixture_size = int(output_size // np.prod(broadcast_shape))
        else:
            mixture_size = int(output_size // (np.prod(broadcast_shape) * param_shape[-1]))
        if mixture_size == 1 and _size is None:
            mixture_size = None

        # Sample from the mixture
        with draw_context:
            mixed_samples = self._comp_samples(
                point=point,
                size=mixture_size,
                broadcast_shape=broadcast_shape,
                comp_dist_shapes=comp_dist_shapes,
            )
        # Test that the mixture has the same number of "samples" as w
        if w_samples.size != (mixed_samples.size // w.shape[-1]):
            raise ValueError(
                "Inconsistent number of samples from the "
                "mixture and mixture weights. Drew {} mixture "
                "weights elements, and {} samples from the "
                "mixture components.".format(w_samples.size, mixed_samples.size // w.shape[-1])
            )
        # Semiflatten the mixture to be able to zip it with w_samples
        w_samples = w_samples.flatten()
        mixed_samples = np.reshape(mixed_samples, (-1, w.shape[-1]))
        # Select the samples from the mixture
        samples = np.array([mixed[choice] for choice, mixed in zip(w_samples, mixed_samples)])
        # Reshape the samples to the correct output shape
        if size is None:
            samples = np.reshape(samples, dist_shape)
        else:
            samples = np.reshape(samples, size + dist_shape)
        return samples