예제 #1
0
    def draw_samples(self,
                     num_samples,
                     X1,
                     X0=None,
                     Y0=None,
                     base_rvs=None,
                     means=None,
                     cov=None,
                     name=None,
                     **kwargs):
        if (X0 is None or Y0 is None):
            raise NotImplementedError(
                "Sampling from the prior not yet supported")

        if (cov is None):
            means, cov = self.predict(X1, X0, Y0, full_cov=True, **kwargs)
        chol = self.compute_cholesky(X1, cov=cov)

        if (base_rvs is None):
            rvs_shape = [num_samples, tf.shape(chol)[-1]]
            base_rvs = tf.random_normal(rvs_shape, dtype=chol.dtype)

        # Produce MVN samples with shape [..., num_samples, mvn_dim]
        residuals = tf.tensordot(chol, base_rvs, [[-1], [-1]])
        if (means is None):
            # If cov is provided but means are not, samples will be zero-mean
            samples = utils.swap_axes(residuals, -1, -2, name=name)
        else:
            samples = utils.swap_axes(means + residuals, -1, -2, name=name)
        return samples
예제 #2
0
    def draw_samples(self,
                     num_samples,
                     means=None,
                     cov=None,
                     base_rvs=None,
                     parallelism=None,
                     chol=None,
                     model=None,
                     name=None,
                     **kwargs):
        if (model is None): model = self.model
        with tf.name_scope('draw_samples') as scope:
            # Compute Cholesky factor LL^{T} = K
            if (chol is None):
                jitter = getattr(model, 'jitter', None)
                chol = utils.jitter_cholesky(cov, jitter=jitter)

            # Generate i.i.d. standard normal random variables
            if (base_rvs is None):
                if (parallelism is None): parallelism = chol.get_shape()[-1]
                rvs_shape = tf.TensorShape((num_samples, parallelism))
                base_rvs = tf.random_normal(rvs_shape, dtype=chol.dtype)

            # Produce MVN samples
            residuals = tf.tensordot(chol, base_rvs, [[-1], [-1]])
            if (means is None):
                samples = utils.swap_axes(residuals, -1, -2, name=name)
            else:
                samples = utils.swap_axes(means + residuals, -1, -2, name=name)

            return samples
예제 #3
0
    def draw_samples(self,
                     num_samples,
                     means,
                     cov,
                     lower=None,
                     beta=None,
                     name=None,
                     **kwargs):
        '''
    Draw samples from the positive orthant of a multivariate normal
    distribution with rescaled covariance 
        \tilde{\Sigma} := (0.5 * \pi * \beta) * \Sigma.
    '''
        if (lower is None): lower = self.lower
        if (beta is None): beta = self.beta
        with tf.name_scope('draw_samples') as scope:
            # Sample and transform Gaussian residuals
            raw_residuals = super().draw_samples(num_samples, None, cov,
                                                 **kwargs)
            residuals = self.transform_residuals(raw_residuals, beta, lower)

            # Re-center samples
            if (means is not None):
                samples = tf.add(residuals,
                                 utils.swap_axes(means, -1, -2),
                                 name=name)
            else:
                samples = tf.identity(residuals, name=name)
            return samples
 def get_marginal_posterior(self, mean, var, xov, precis, resid):
   ''' 
   Compute marginal Gaussian posteriors given prior terms.
   # [?] use einsum for better parallel computation
   '''
   rank = utils.tensor_rank(xov)
   axes = [[max(0, rank - 2)], [0]]
   beta = tf.tensordot(xov, precis, axes)
   if rank > 1: xov = utils.swap_axes(xov, -1, -2) #matrix transpose
   return\
   (
     mean + tf.tensordot(beta, resid, [[-1], [0]]),
     var - tf.reduce_sum(beta*xov, axis=-1, keep_dims=True)
   )
예제 #5
0
    def _monte_carlo(self,
                     means,
                     cov,
                     samples=None,
                     weights=None,
                     beta=None,
                     lower=None,
                     num_fantasies=None,
                     transform_samples=None,
                     parallelism=None,
                     incumbents=None,
                     **kwargs):
        '''
    Monte Carlo estimate to q-LCB/q-UCB.
    '''
        if (lower is None): lower = self.lower
        if (num_fantasies is None): num_fantasies = self.num_fantasies
        if (transform_samples is None):
            transform_samples = self.transform_samples
        with tf.name_scope('monte_carlo') as scope:
            if (samples is None):
                samples = self.draw_samples(num_fantasies,
                                            means,
                                            cov,
                                            beta=beta,
                                            lower=lower,
                                            **kwargs)

            elif transform_samples:  #transform samples drawn from N(means, cov)
                # [!] Improve me, assumed shapes:
                #   `means`   = [..., parallelism, 1]
                #   `samples` = [..., num_samples, parallelism]
                mu = utils.swap_axes(means, axis1=-1, axis2=-2)
                residuals = self.transform_residuals(samples - mu, beta, lower)
                samples = mu + residuals

            # Get sample extrema
            extrema_fn = tf.reduce_min if lower else tf.reduce_max
            extrema = extrema_fn(samples, axis=-1)
            if (incumbents is not None):
                if lower: extrema = tf.minimum(extrema, incumbents)
                else: extrema = tf.reduce_max(extrema, incumbents)

            # (Weighted) sample average
            estimate = self.reduce_samples(extrema, weights, axis=-1)
            return estimate
예제 #6
0
        def mapped_fn(*sharded_args, kwargs=kwargs):
            pools = sharded_args[0]
            if use_rand_features:
                theta_mu = tf.transpose(self.mapping.mu)
                means = self.mapping(pools, **{**rf_kwargs, 'theta': theta_mu})

                samplesT = self.mapping(pools, **rf_kwargs)
                samples = utils.swap_axes(samplesT, -1, -2)
                return fn(means,
                          None,
                          samples=samples,
                          pools=pools,
                          inputs_old=inputs_old,
                          outputs_old=outputs_old,
                          **kwargs)

            if (posteriors is not None):
                # Reuse precomputed posteriors
                means, cov, chol = sharded_args[-1]
                sharded_args = sharded_args[:-1]
            else:
                # Compute marginal/joint posterior individual pools
                means, cov = model.get_or_create_node\
                (
                  group='predict',
                  fn=model.predict,
                  args=sharded_args + constant_args,
                  kwargs=predict_kwargs,
                  stateful=True,
                )
                chol = utils.jitter_cholesky(cov, jitter=model.jitter)

            losses = fn(means,
                        cov,
                        pools=pools,
                        inputs_old=inputs_old,
                        outputs_old=outputs_old,
                        chol=chol,
                        **kwargs)

            return losses, (means, cov, chol)
예제 #7
0
    def _predict_nd(self,
                    X1,
                    X0,
                    Y0,
                    full_cov=False,
                    chol=None,
                    precis=None,
                    K_10=None,
                    K_01=None,
                    **kwargs):
        '''
    Subroutine for computing broadcasted rank-n GP posteriors.

    [!] Bug: Hacks been added to handle the case where
              'X0' and 'Y0' have been padded to rank 4.
    '''
        with tf.name_scope('_predict_nd') as scope:
            if (K_10 is None):
                if (K_01 is None): K_10 = self.covar_fn(X1, X0, **kwargs)
                else: K_10 = K_10 = utils.swap_axes(K_01, -1, -2)

            # Solve for $\beta := K_10 (K_00 + nz*I)^{-1}$
            if (precis is not None):  #efficient but unstable...
                Beta = utils.broadcast_matmul(K_10, precis)
            else:
                if (chol is None):
                    chol = self.compute_cholesky(X0, noisy=True, **kwargs)
                if (K_01 is None):
                    K_01 = utils.swap_axes(K_10, -1, -2)

                # Solve for all systems of equations
                if (Y0.shape.ndims == 4):
                    # Hack to deal with padding
                    L = chol[0, 0]
                    B = tf.transpose(K_01, [2, 0, 1, 3])
                    X = tf.cholesky_solve(L,
                                          tf.reshape(B, [tf.shape(B)[0], -1]))
                    Beta = tf.transpose(tf.reshape(X, tf.shape(B)),
                                        [1, 2, 3, 0])
                else:
                    Beta = utils.swap_axes(utils.cholesky_solve(chol, K_01),
                                           -1, -2)

            resid = Y0 - self.mean_fn(X0, Y0)
            if (Y0.shape.ndims == 4):
                resid = self._auto_tile(resid, Beta, num_ranks=-2)

            Means = self.mean_fn(X1, Y0) + utils.broadcast_matmul(Beta, resid)
            if full_cov:
                if (K_01 is None): K_01 = utils.swap_axes(K_10, -1, -2)
                Covs = tf.subtract\
                (
                  self.covar_fn(X1, X1, **kwargs),
                  utils.broadcast_matmul(Beta, K_01)
                )
                return Means, Covs
            else:
                Vars = tf.subtract\
                (
                  self.covar_fn(X1, **kwargs),
                  tf.reduce_sum(Beta*K_10, axis=-1, keepdims=True),
                )
                return Means, Vars
예제 #8
0
    def _monte_carlo(self,
                     means_pool,
                     cov_pool,
                     xov_pd,
                     means_disc,
                     cov_disc,
                     chol_pool=None,
                     future_rvs=None,
                     fantasy_rvs=None,
                     parallelism=None,
                     num_futures=None,
                     num_fantasies=None,
                     **kwargs):
        '''
    Generic Monte Carlo subroutine for 1-step lookahead integrands.
    Computes updated discretization posteriors conditioned on each
    sample future.
    '''
        if (num_futures is None): num_futures = self.num_futures
        if (num_fantasies is None): num_fantasies = self.num_fantasies
        with tf.name_scope('monte_carlo'.format(self.name)) as scope:
            if (chol_pool is None): chol_pool = utils.jitter_cholesky(cov_pool)

            # Fantasize residuals (Y - \mu) at pool locations
            resid_pool = self.draw_samples\
            (
              num_futures,
              chol=chol_pool,
              base_rvs=future_rvs,
              parallelism=parallelism,
            )

            # Sample pool outcomes to condition upon
            futures = resid_pool + utils.swap_axes(means_pool, -1, -2)

            # Solve for beta := K(disc, pool) K_nz(pool, pool)^{-1}
            beta = utils.swap_axes(tf.cholesky_solve(chol_pool, xov_pd), -1,
                                   -2)

            # Compute changes in discretization posterior
            d_cov = -tf.einsum('ijk,ikl->ijl', beta, xov_pd)
            ds_means = tf.einsum('ijk,ilk->ilj', beta, resid_pool)

            # Posteriors conditional on each sampled future
            means_future = means_disc + tf.expand_dims(ds_means, -1)
            covs_future = cov_disc[None, None] + d_cov[:, None]

            if self.run_unit_test:
                means_future, covs_future = self.test_conditional_posterior\
                (
                  means_future,
                  covs_future,
                  futures
                )

            # Monte Carlo integration subroutine
            estimate = self.integrand\
            (
              means_future,
              covs_future,
              num_samples=num_fantasies,
              base_rvs=fantasy_rvs,
              futures=futures,
              means_pool=means_pool,
              means_disc=means_disc,
              cov_disc=cov_disc,
              **kwargs
            )
            return estimate