Exemple #1
0
def assert_orthogonal(x):
    """Assert that a matrix is orthogonal."""
    # Check that matrix is square.
    assert B.shape(x)[0] == B.shape(x)[1]

    # Check that its transpose is its inverse.
    approx(B.matmul(x, x, tr_a=True), B.eye(x))
Exemple #2
0
def test_take_perm(dtype, check_lazy_shapes):
    a = B.range(dtype, 10)
    perm = B.randperm(B.dtype_int(dtype), 10)
    a2 = B.take(a, perm)
    assert B.dtype(perm) == B.dtype_int(dtype)
    assert B.shape(a) == B.shape(a2)
    assert B.dtype(a) == B.dtype(a2)
Exemple #3
0
def test_multiply_kron(kron1, kron2):
    if B.shape(kron1.left) == B.shape(kron2.left) and B.shape(
            kron1.right) == B.shape(kron2.right):
        check_bin_op(B.multiply, kron1, kron2, asserted_type=Kronecker)
    else:
        with pytest.raises(AssertionError):
            B.matmul(kron1, kron2)
Exemple #4
0
def approx(
    x: Union[tuple, B.Number, B.NPNumeric],
    y: Union[tuple, B.Number, B.NPNumeric],
    **kw_args,
):
    assert_allclose(x, y, **kw_args)
    assert B.shape(x) == B.shape(y)
Exemple #5
0
def test_matmul_kron(kron1, kron2):
    if (B.shape(kron1.left)[1] == B.shape(kron2.left)[0]
            and B.shape(kron1.right)[1] == B.shape(kron2.right)[0]):
        _check_matmul(kron1, kron2, asserted_type=Kronecker)
    else:
        with pytest.raises(AssertionError):
            B.matmul(kron1, kron2)
Exemple #6
0
def assert_lower_triangular(x):
    """Assert that a matrix is lower triangular."""
    # Check that matrix is square.
    assert B.shape(x)[0] == B.shape(x)[1]

    # Check that upper part is all zeros.
    upper = x[np.triu_indices(B.shape(x)[0], k=1)]
    approx(upper, B.zeros(upper))
Exemple #7
0
def test_normal_logpdf(normal1):
    normal1_sp = multivariate_normal(normal1.mean[:, 0], B.dense(normal1.var))
    x = B.randn(3, 10)
    approx(normal1.logpdf(x), normal1_sp.logpdf(x.T))

    # Test the the output of `logpdf` is flattened appropriately.
    assert B.shape(normal1.logpdf(B.ones(3, 1))) == ()
    assert B.shape(normal1.logpdf(B.ones(3, 2))) == (2, )
Exemple #8
0
def multiply(a: Kronecker, b: Kronecker):
    left_compatible = B.shape(a.left) == B.shape(b.left)
    right_compatible = B.shape(a.right) == B.shape(b.right)
    assert (
        left_compatible and right_compatible
    ), f"Kronecker products {a} and {b} must be compatible, but they are not."
    assert_compatible(a.left, b.left)
    assert_compatible(a.right, b.right)
    return Kronecker(B.multiply(a.left, b.left), B.multiply(a.right, b.right))
Exemple #9
0
def logdet(a: Kronecker):
    assert_square(
        a.left, f"Left factor of {a} must be square to compute the log-determinant."
    )
    assert_square(
        a.right,
        f"Right factor of {a} must be square to compute the log-determinant.",
    )
    n = B.shape(a.left)[0]
    m = B.shape(a.right)[0]
    return m * B.logdet(a.left) + n * B.logdet(a.right)
Exemple #10
0
def _attempt_zero(rows):
    # Check whether the result is just zeros.
    if all([all([isinstance(x, Zero) for x in row]) for row in rows]):
        # Determine the resulting data type and shape.
        dtype = B.dtype(rows[0][0])
        grid_rows = sum([B.shape(row[0])[0] for row in rows])
        grid_cols = sum([B.shape(x)[1] for x in rows[0]])

        return Zero(dtype, grid_rows, grid_cols)
    else:
        return None
Exemple #11
0
def diag(a, b):
    # We could merge this with `block`, but `block` has a lot of overhead. It
    # seems advantageous to optimise this common case.
    warn_upmodule(
        f"Constructing a dense block-diagonal matrix from "
        f"{a} and {b}: converting to dense.",
        category=ToDenseWarning,
    )
    a = B.dense(a)
    b = B.dense(b)

    dtype = B.dtype(a)
    ar, ac = B.shape(a)
    br, bc = B.shape(b)
    return Dense(
        B.concat2d([a, B.zeros(dtype, ar, bc)], [B.zeros(dtype, br, ac), b]))
Exemple #12
0
def test_pack_unpack():
    a, b, c = B.randn(5, 10), B.randn(20), B.randn(5, 1, 15)

    # Test packing.
    package = pack(a, b, c)
    assert B.rank(package) == 1

    # Test unpacking.
    a2, b2, c2 = unpack(package, B.shape(a), B.shape(b), B.shape(c))
    approx(a, a2)
    approx(b, b2)
    approx(c, c2)

    # Check that the package must be a vector.
    with pytest.raises(ValueError):
        unpack(B.randn(2, 2), (2, 2))
Exemple #13
0
 def __str__(self):
     rows, cols = B.shape(self)
     return (
         f"<upper-triangular matrix:"
         f" shape={rows}x{cols},"
         f" dtype={dtype_str(self)}>"
     )
Exemple #14
0
def _attempt_diagonal(rows):
    # Check whether the result is diagonal.

    # Check that the blocks form a square.
    if not all([len(row) == len(rows) for row in rows]):
        return None

    # Collect the diagonal blocks.
    diagonal_blocks = []
    for r in range(len(rows)):
        for c in range(len(rows[0])):
            block_shape = B.shape(rows[r][c])
            if r == c:
                # Keep track of all the diagonal blocks.
                diagonal_blocks.append(rows[r][c])

                # All blocks on the diagonal must be diagonal or zero.
                if not isinstance(rows[r][c], (Diagonal, Zero)):
                    return None

                # All blocks on the diagonal must be square.
                if not block_shape[0] == block_shape[1]:
                    return None
            else:
                # All blocks not on the diagonal must be zero.
                if not isinstance(rows[r][c], Zero):
                    return None

    return Diagonal(B.concat(*[B.diag(x) for x in diagonal_blocks]))
Exemple #15
0
def _check_init_shape(init, shape):
    if init is None and shape is None:
        raise ValueError(
            f"The shape must be given to automatically initialise "
            f"a matrix variable.")
    if shape is None:
        shape = B.shape(init)
    return init, shape
Exemple #16
0
 def f(x, option=None):
     # Check that keyword arguments are passed on correctly. Only the compilation
     # with PyTorch does not pass on its keyword arguments.
     if not isinstance(x, B.TorchNumeric):
         assert option is not None
     # This requires lazy shapes:
     x = B.ones(B.dtype(x), *B.shape(x))
     # This requires control flow cache:
     return B.cond(x[0, 0] > 0, lambda: x[1], lambda: x[2])
Exemple #17
0
def matmul(a: Kronecker, b: AbstractMatrix, tr_a=False, tr_b=False):
    _assert_composable(a, b, tr_a, tr_b)
    a = _tr(a, tr_a)
    b = _tr(b, tr_b)

    # Extract the factors of the product.
    left = a.left
    right = a.right
    l_rows, l_cols = B.shape(left)
    r_rows, r_cols = B.shape(right)

    cost_first_left = l_rows * l_cols * r_cols + r_rows * r_cols * l_rows
    cost_first_right = r_rows * r_cols * l_cols + l_rows * l_cols * r_rows

    if cost_first_left <= cost_first_right:
        return _kron_id_a_b(right, _kron_a_id_b(left, b))
    else:
        return _kron_a_id_b(left, _kron_id_a_b(right, b))
Exemple #18
0
    def project(self, x, y):
        """Project data.

        Args:
            x (matrix): Locations of data.
            y (matrix): Observations of data.

        Returns:
            tuple: Tuple containing the locations of the projection,
                the projection, weights associated with the projection, and
                a regularisation term.
        """
        n = B.shape(x)[0]
        available = ~B.isnan(B.to_numpy(y))

        # Optimise the case where all data is available.
        if B.all(available):
            return self._project_pattern(x, y, (True,) * self.p)

        # Extract patterns.
        patterns = list(set(map(tuple, list(available))))

        if len(patterns) > 30:
            warnings.warn(
                f"Detected {len(patterns)} patterns, which is more "
                f"than 30 and can be slow.",
                category=UserWarning,
            )

        # Per pattern, find data points that belong to it.
        patterns_inds = [[] for _ in range(len(patterns))]
        for i in range(n):
            patterns_inds[patterns.index(tuple(available[i]))].append(i)

        # Per pattern, perform the projection.
        proj_xs = []
        proj_ys = []
        proj_ws = []
        total_reg = 0

        for pattern, pattern_inds in zip(patterns, patterns_inds):
            proj_x, proj_y, proj_w, reg = self._project_pattern(
                B.take(x, pattern_inds), B.take(y, pattern_inds), pattern
            )

            proj_xs.append(proj_x)
            proj_ys.append(proj_y)
            proj_ws.append(proj_w)
            total_reg = total_reg + reg

        return (
            B.concat(*proj_xs, axis=0),
            B.concat(*proj_ys, axis=0),
            B.concat(*proj_ws, axis=0),
            total_reg,
        )
Exemple #19
0
def constant_to_lowrank(a):
    dtype = B.dtype(a)
    rows, cols = B.shape(a)
    middle = B.fill_diag(a.const, 1)
    if rows == cols:
        return LowRank(B.ones(dtype, rows, 1), middle=middle)
    else:
        return LowRank(B.ones(dtype, rows, 1),
                       B.ones(dtype, cols, 1),
                       middle=middle)
Exemple #20
0
    def __init__(
        self, model, u: AbstractMatrix, s_sqrt: AbstractMatrix, noise_obs: B.Numeric
    ):
        self.model = model
        self.u = u
        self.s_sqrt = s_sqrt
        self.h = u @ s_sqrt
        self.noise_obs = noise_obs

        self.p, self.m = B.shape(u)
Exemple #21
0
def multiply(a: Diagonal, b: AbstractMatrix):
    assert_compatible(a, b)
    # In the case of broadcasting, `B.diag(b)` will not get the diagonal of the
    # broadcasted version of `b`, so we exercise extra caution in that case.
    rows, cols = B.shape(b)
    if rows == 1 or cols == 1:
        b_diag = B.squeeze(B.dense(b))
    else:
        b_diag = B.diag(b)
    return Diagonal(a.diag * b_diag)
Exemple #22
0
    def _integrate_half2(self, name1, name2, **var_map):
        if self.poly.highest_power(name1) != 2 or self.poly.highest_power(
                name2) != 2:
            raise RuntimeError(f'Dependency on "{name1}" and {name2}" must '
                               f"be quadratic.")

        a11 = self.poly.collect_for(Factor(name1, 2))
        a22 = self.poly.collect_for(Factor(name2, 2))
        a12 = self.poly.collect_for(Factor(name1,
                                           1)).collect_for(Factor(name2, 1))
        b1 = self.poly.collect_for(Factor(name1, 1)).reject(name2)
        b2 = self.poly.collect_for(Factor(name2, 1)).reject(name1)
        c = self.poly.reject(name1).reject(name2)

        # Evaluate and scale A.
        a11 = -2 * a11.eval(**var_map)
        a22 = -2 * a22.eval(**var_map)
        a12 = -1 * a12.eval(**var_map)
        b1 = b1.eval(**var_map)
        b2 = b2.eval(**var_map)
        c = c.eval(**var_map)

        # Determinant of A:
        a_det = a11 * a22 - a12**2

        # Inverse of A, which corresponds to variance of distribution after
        # completing the square:
        ia11 = a22 / a_det
        ia12 = -a12 / a_det
        ia22 = a11 / a_det

        # Mean of distribution after completing the square:
        mu1 = ia11 * b1 + ia12 * b2
        mu2 = ia12 * b1 + ia22 * b2

        # Normalise and compute CDF part.
        x1 = -mu1 / safe_sqrt(ia11)
        x2 = -mu2 / safe_sqrt(ia22)
        rho = ia12 / safe_sqrt(ia11 * ia22)

        # Evaluate CDF for all `x1` and `x2`.
        orig_shape = B.shape(mu1)
        num = reduce(operator.mul, orig_shape, 1)
        x1 = B.reshape(x1, num)
        x2 = B.reshape(x2, num)
        rho = rho * B.ones(x1)
        cdf_part = B.reshape(B.bvn_cdf(x1, x2, rho), *orig_shape)

        # Compute exponentiated part.
        quad_form = 0.5 * (ia11 * b1**2 + ia22 * b2**2 + 2 * ia12 * b1 * b2)
        det_part = 2 * B.pi / safe_sqrt(a_det)
        exp_part = det_part * B.exp(quad_form + c)

        return self.const * cdf_part * exp_part
Exemple #23
0
def _per_output(x, y, w):
    p = B.shape(y)[1]

    for i in range(p):
        yi = y[:, i]
        wi = w[:, i]

        # Only return available observations.
        available = ~B.isnan(yi)

        yield x[available], yi[available], wi[available]
Exemple #24
0
def matmul(a: LowRank, b: LowRank, tr_a=False, tr_b=False):
    _assert_composable(a, b, tr_a=tr_a, tr_b=tr_b)
    a = _tr(a, tr_a)
    b = _tr(b, tr_b)
    middle = B.matmul(a.right, b.left, tr_a=True)
    middle = B.matmul(a.middle, middle, b.middle)
    # Let `middle` be of type `Diagonal` if possible.
    if B.shape(middle) == (1, 1):
        return LowRank(a.left, b.right, Diagonal(middle[0]))
    else:
        return LowRank(a.left, b.right, middle)
Exemple #25
0
def test_lazy_shape():
    a = B.randn(2, 2)

    # By default, it should be off.
    assert isinstance(B.shape(a), tuple)

    # Turn on.
    with B.lazy_shapes():
        assert isinstance(B.shape(a), Shape)

        # Force lazy shapes to be off again.
        B.lazy_shapes.enabled = False
        assert isinstance(B.shape(a), tuple)

        # Turn on again.
        with B.lazy_shapes():
            assert isinstance(B.shape(a), Shape)

        # Should remain off.
        assert isinstance(B.shape(a), tuple)
Exemple #26
0
def assert_square(x, message):
    """Assert that a tensor is a square matrix.

    Args
        x (tensor): Tensor that must be a matrix.
        message (str): Error message to raise in the case that `x` is not a
            square matrix.
    """
    shape = B.shape(x)
    if len(shape) != 2 or shape[0] != shape[1]:
        raise AssertionError(message)
Exemple #27
0
    def normalise_logdet(self, y):
        """Compute the log-determinant of the Jacobian of the normalisation.

        Accepts multiple arguments.

        Args:
            y (matrix): Data that was transformed.

        Returns:
            scalar: Log-determinant.
        """
        return -B.shape(y)[0] * B.sum(B.log(self.scale))
Exemple #28
0
def sample(a, num=1):  # pragma: no cover
    """Sample from covariance matrices.

    Args:
        a (tensor): Covariance matrix to sample from.
        num (int): Number of samples.

    Returns:
        tensor: Samples as rank 2 column vectors.
    """
    chol = B.cholesky(a)
    return B.matmul(chol, B.randn(B.dtype_float(a), B.shape(chol)[1], num))
Exemple #29
0
def test_recurrent():
    vs = Vars(np.float32)

    # Test setting the initial hidden state.
    layer = Recurrent(GRU(10), B.zeros(1, 10))
    layer.initialise(5, vs)
    approx(layer.h0, B.zeros(1, 10))

    layer = Recurrent(GRU(10))
    layer.initialise(5, vs)
    assert layer.h0 is not None

    # Check batch consistency.
    check_batch_consistency(layer, B.randn(30, 20, 5))

    # Test preservation of rank upon calls.
    assert B.shape(layer(B.randn(20, 5))) == (20, 10)
    assert B.shape(layer(B.randn(30, 20, 5))) == (30, 20, 10)

    # Check that zero-dimensional calls fail.
    with pytest.raises(ValueError):
        layer(0)
Exemple #30
0
def test_subshape(check_lazy_shapes):
    assert B.shape(B.zeros(2), 0) == 2
    assert B.shape(B.zeros(2, 3, 4), 1) == 3
    assert B.shape(B.zeros(2, 3, 4), 0, 2) == (2, 4)
    assert B.shape(B.zeros(2, 3, 4), 0, 1, 2) == (2, 3, 4)

    # Check for possible infinite recursion.
    with pytest.raises(NotFoundLookupError):
        B.shape(None, 1)