https://github.com/GPflow/GPflow
Revision d9951946d64542268b85610593495e187343d42e authored by gustavocmv on 30 July 2020, 11:58:40 UTC, committed by GitHub on 30 July 2020, 11:58:40 UTC
* WIP: quadrature refactoring

* Removing old ndiagquad code

* deleted test code

* formatting and type-hint

* merge modules

* black formatting

* formatting

* solving failing tests

* fixing failing tests

* fixes

* adapting tests for new syntax, keeping numerical behavior

* black formatting

* remove printf

* changed code for compiled tf compatibility

* black

* restored to original version

* undoing changes

* renaming

* renaming

* renaming

* reshape kwargs

* quadrature along axis=-2, simplified broadcasting

* black

* docs

* docs

* helper function

* docstrings and typing

* added new and old quadrature equivalence tests

* black

* Removing comments

Co-authored-by: Vincent Dutordoir <dutordoirv@gmail.com>

* Typo

Co-authored-by: Vincent Dutordoir <dutordoirv@gmail.com>

* notation

Co-authored-by: Vincent Dutordoir <dutordoirv@gmail.com>

* reshape_Z_dZ return docstring fix

* FIX: quad_old computed with the ndiagquad_old

Co-authored-by: Vincent Dutordoir <dutordoirv@gmail.com>

* more readable implementation

Co-authored-by: Vincent Dutordoir <dutordoirv@gmail.com>

* tf.ensure_shape added

* removed ndiagquad

* removed ndiagquad

* Revert "removed ndiagquad"

This reverts commit 7bb0e9f1e0f2b0e225a2b8a5b3092c4c2f24ba91.

* FIX: shape checking of dZ

* Revert "removed ndiagquad"

This reverts commit 8e235241a697696e361158c30ff9aa9b4cc69f8a.

Co-authored-by: Gustavo Carvalho <gustavo.carvalho@delfosim.com>
Co-authored-by: ST John <st@prowler.io>
Co-authored-by: Vincent Dutordoir <dutordoirv@gmail.com>
1 parent 65aea21
Raw File
Tip revision: d9951946d64542268b85610593495e187343d42e authored by gustavocmv on 30 July 2020, 11:58:40 UTC
Quadrature Refactoring (#1505)
Tip revision: d995194
cross_kernels.py
import tensorflow as tf

from . import dispatch
from .. import kernels
from ..inducing_variables import InducingPoints
from ..probability_distributions import DiagonalGaussian, Gaussian
from .expectations import expectation


@dispatch.expectation.register(
    (Gaussian, DiagonalGaussian),
    kernels.SquaredExponential,
    InducingPoints,
    kernels.Linear,
    InducingPoints,
)
def _E(p, sqexp_kern, feat1, lin_kern, feat2, nghp=None):
    """
    Compute the expectation:
    expectation[n] = <Ka_{Z1, x_n} Kb_{x_n, Z2}>_p(x_n)
        - K_lin_{.,.} :: SqExp kernel
        - K_sqexp_{.,.} :: Linear kernel
    Different Z1 and Z2 are handled if p is diagonal and K_lin and K_sqexp have disjoint
    active_dims, in which case the joint expectations simplify into a product of expectations

    :return: NxM1xM2
    """
    if sqexp_kern.on_separate_dims(lin_kern) and isinstance(
        p, DiagonalGaussian
    ):  # no joint expectations required
        eKxz1 = expectation(p, (sqexp_kern, feat1))
        eKxz2 = expectation(p, (lin_kern, feat2))
        return eKxz1[:, :, None] * eKxz2[:, None, :]

    if feat1 != feat2:
        raise NotImplementedError("inducing_variables have to be the same for both kernels.")

    if sqexp_kern.active_dims != lin_kern.active_dims:
        raise NotImplementedError("active_dims have to be the same for both kernels.")

    # use only active dimensions
    Xcov = sqexp_kern.slice_cov(tf.linalg.diag(p.cov) if isinstance(p, DiagonalGaussian) else p.cov)
    Z, Xmu = sqexp_kern.slice(feat1.Z, p.mu)

    N = tf.shape(Xmu)[0]
    D = tf.shape(Xmu)[1]

    def take_with_ard(value):
        if not sqexp_kern.ard:
            return tf.zeros((D,), dtype=value.dtype) + value
        return value

    lin_kern_variances = take_with_ard(lin_kern.variance)
    sqexp_kern_lengthscales = take_with_ard(sqexp_kern.lengthscales)

    chol_L_plus_Xcov = tf.linalg.cholesky(
        tf.linalg.diag(sqexp_kern_lengthscales ** 2) + Xcov
    )  # NxDxD

    Z_transpose = tf.transpose(Z)
    all_diffs = Z_transpose - tf.expand_dims(Xmu, 2)  # NxDxM
    exponent_mahalanobis = tf.linalg.triangular_solve(
        chol_L_plus_Xcov, all_diffs, lower=True
    )  # NxDxM
    exponent_mahalanobis = tf.reduce_sum(tf.square(exponent_mahalanobis), 1)  # NxM
    exponent_mahalanobis = tf.exp(-0.5 * exponent_mahalanobis)  # NxM

    sqrt_det_L = tf.reduce_prod(sqexp_kern_lengthscales)
    sqrt_det_L_plus_Xcov = tf.exp(
        tf.reduce_sum(tf.math.log(tf.linalg.diag_part(chol_L_plus_Xcov)), axis=1)
    )
    determinants = sqrt_det_L / sqrt_det_L_plus_Xcov  # N
    eKxz_sqexp = sqexp_kern.variance * (
        determinants[:, None] * exponent_mahalanobis
    )  ## NxM <- End RBF eKxz code

    tiled_Z = tf.tile(tf.expand_dims(Z_transpose, 0), (N, 1, 1))  # NxDxM
    z_L_inv_Xcov = tf.linalg.matmul(
        tiled_Z, Xcov / sqexp_kern_lengthscales[:, None] ** 2.0, transpose_a=True
    )  # NxMxD

    cross_eKzxKxz = tf.linalg.cholesky_solve(
        chol_L_plus_Xcov, (lin_kern_variances * sqexp_kern_lengthscales ** 2.0)[..., None] * tiled_Z
    )  # NxDxM

    cross_eKzxKxz = tf.linalg.matmul(
        (z_L_inv_Xcov + Xmu[:, None, :]) * eKxz_sqexp[..., None], cross_eKzxKxz
    )  # NxMxM
    return cross_eKzxKxz


@dispatch.expectation.register(
    (Gaussian, DiagonalGaussian),
    kernels.Linear,
    InducingPoints,
    kernels.SquaredExponential,
    InducingPoints,
)
def _E(p, lin_kern, feat1, sqexp_kern, feat2, nghp=None):
    """
    Compute the expectation:
    expectation[n] = <Ka_{Z1, x_n} Kb_{x_n, Z2}>_p(x_n)
        - K_lin_{.,.} :: Linear kernel
        - K_sqexp_{.,.} :: sqexp kernel
    Different Z1 and Z2 are handled if p is diagonal and K_lin and K_sqexp have disjoint
    active_dims, in which case the joint expectations simplify into a product of expectations

    :return: NxM1xM2
    """
    return tf.linalg.adjoint(expectation(p, (sqexp_kern, feat2), (lin_kern, feat1)))
back to top