def block(*rows): """Construct a matrix from its blocks, preserving structure when possible. Assumes that every row has an equal number of blocks and that the sizes of the blocks align to form a grid. Args: *rows (list): Rows of the block matrix. Returns: matrix: Assembled matrix with as much structured as possible. """ if len(rows) == 1 and len(rows[0]) == 1: # There is just one block. Return it. return rows[0][0] res = _attempt_zero(rows) if res is not None: return res res = _attempt_diagonal(rows) if res is not None: return res # Could not preserve any structure. Simply concatenate them all densely. warn_upmodule( "Could not preserve structure in block matrix: converting to dense.", category=ToDenseWarning, ) return Dense(B.concat2d(*[[B.dense(x) for x in row] for row in rows]))
def test_diag_block_diag(diag1, diag2): approx( B.diag(diag1, diag2), B.concat2d( [B.dense(diag1), B.zeros(B.dense(diag2))], [B.zeros(B.dense(diag2)), B.dense(diag2)], ), ) assert isinstance(B.diag(diag1, diag2), Diagonal)
def test_diag_block_dense(dense1, dense2): with AssertDenseWarning(concat_warnings): res = B.diag(dense1, dense2) approx( res, B.concat2d( [B.dense(dense1), B.zeros(B.dense(dense1))], [B.zeros(B.dense(dense2)), B.dense(dense2)], ), ) assert isinstance(res, Dense)
def test_block_diag(): rows = [ [generate("diag:6"), generate("zero:6,6"), generate("zero:6,3")], [generate("zero:6,6"), generate("zero:6,6"), generate("zero:6,3")], [generate("zero:3,6"), generate("zero:3,6"), generate("diag:3")], ] res = B.block(*rows) approx(res, B.concat2d(*_dense(rows))) assert isinstance(res, Diagonal)
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]))
def test_mokernel(x1, x2, x3): m = Measure() p1 = GP(1 * EQ(), measure=m) p2 = GP(2 * EQ().stretch(2), measure=m) k = MultiOutputKernel(m, p1, p2) ks = m.kernels # Check representation. assert str(k) == "MultiOutputKernel(EQ(), 2 * (EQ() > 2))" # Input versus input: approx( k(x1, x2), B.concat2d( [ks[p1, p1](x1, x2), ks[p1, p2](x1, x2)], [ks[p2, p1](x1, x2), ks[p2, p2](x1, x2)], ), ) approx( k.elwise(x1, x3), B.concat(ks[p1, p1].elwise(x1, x3), ks[p2, p2].elwise(x1, x3), axis=0), ) # Input versus `FDD`: approx(k(p1(x1), x2), B.concat(ks[p1, p1](x1, x2), ks[p1, p2](x1, x2), axis=1)) approx(k(p2(x1), x2), B.concat(ks[p2, p1](x1, x2), ks[p2, p2](x1, x2), axis=1)) approx(k(x1, p1(x2)), B.concat(ks[p1, p1](x1, x2), ks[p2, p1](x1, x2), axis=0)) approx(k(x1, p2(x2)), B.concat(ks[p1, p2](x1, x2), ks[p2, p2](x1, x2), axis=0)) with pytest.raises(ValueError): k.elwise(x1, p2(x3)) with pytest.raises(ValueError): k.elwise(p1(x1), x3) # `FDD` versus `FDD`: approx(k(p1(x1), p1(x2)), ks[p1](x1, x2)) approx(k(p1(x1), p2(x2)), ks[p1, p2](x1, x2)) approx(k.elwise(p1(x1), p1(x3)), ks[p1].elwise(x1, x3)) approx(k.elwise(p1(x1), p2(x3)), ks[p1, p2].elwise(x1, x3)) # `MultiInput` versus input: approx( k(MultiInput(p2(x1), p1(x2)), x1), B.concat2d( [ks[p2, p1](x1, x1), ks[p2, p2](x1, x1)], [ks[p1, p1](x2, x1), ks[p1, p2](x2, x1)], ), ) approx( k(x1, MultiInput(p2(x1), p1(x2))), B.concat2d( [ks[p1, p2](x1, x1), ks[p1, p1](x1, x2)], [ks[p2, p2](x1, x1), ks[p2, p1](x1, x2)], ), ) with pytest.raises(ValueError): k.elwise(MultiInput(p2(x1), p1(x3)), p2(x1)) with pytest.raises(ValueError): k.elwise(p2(x1), MultiInput(p2(x1), p1(x3))) # `MultiInput` versus `FDD`: approx( k(MultiInput(p2(x1), p1(x2)), p2(x1)), B.concat(ks[p2, p2](x1, x1), ks[p1, p2](x2, x1), axis=0), ) approx( k(p2(x1), MultiInput(p2(x1), p1(x2))), B.concat(ks[p2, p2](x1, x1), ks[p2, p1](x1, x2), axis=1), ) with pytest.raises(ValueError): k.elwise(MultiInput(p2(x1), p1(x3)), p2(x1)) with pytest.raises(ValueError): k.elwise(p2(x1), MultiInput(p2(x1), p1(x3))) # `MultiInput` versus `MultiInput`: approx( k(MultiInput(p2(x1), p1(x2)), MultiInput(p2(x1))), B.concat(ks[p2, p2](x1, x1), ks[p1, p2](x2, x1), axis=0), ) with pytest.raises(ValueError): k.elwise(MultiInput(p2(x1), p1(x3)), MultiInput(p2(x1))) approx( k.elwise(MultiInput(p2(x1), p1(x3)), MultiInput(p2(x1), p1(x3))), B.concat(ks[p2, p2].elwise(x1, x1), ks[p1, p1].elwise(x3, x3), axis=0), )
def test_block_zero(): rows = [[generate("zero:6,6") for _ in range(3)] for _ in range(3)] res = B.block(*rows) approx(res, B.concat2d(*_dense(rows))) assert isinstance(res, Zero)
def test_block_dense(): rows = [[generate("dense:6,6") for _ in range(3)] for _ in range(3)] with AssertDenseWarning("could not preserve structure"): res = B.block(*rows) approx(res, B.concat2d(*_dense(rows)))
def test_mok(): x1 = B.linspace(0, 1, 10) x2 = B.linspace(1, 2, 5) x3 = B.linspace(1, 2, 10) m = Measure() p1 = GP(EQ(), measure=m) p2 = GP(2 * EQ().stretch(2), measure=m) k = MultiOutputKernel(m, p1, p2) ks = m.kernels # Check dimensionality. assert dimensionality(k) == 2 # Check representation. assert str(k) == "MultiOutputKernel(EQ(), 2 * (EQ() > 2))" # Input versus input: approx( k(x1, x2), B.concat2d( [ks[p1, p1](x1, x2), ks[p1, p2](x1, x2)], [ks[p2, p1](x1, x2), ks[p2, p2](x1, x2)], ), ) approx( k.elwise(x1, x3), B.concat(ks[p1, p1].elwise(x1, x3), ks[p2, p2].elwise(x1, x3), axis=0), ) # Input versus `FDD`: approx(k(p1(x1), x2), B.concat(ks[p1, p1](x1, x2), ks[p1, p2](x1, x2), axis=1)) approx(k(p2(x1), x2), B.concat(ks[p2, p1](x1, x2), ks[p2, p2](x1, x2), axis=1)) approx(k(x1, p1(x2)), B.concat(ks[p1, p1](x1, x2), ks[p2, p1](x1, x2), axis=0)) approx(k(x1, p2(x2)), B.concat(ks[p1, p2](x1, x2), ks[p2, p2](x1, x2), axis=0)) with pytest.raises(ValueError): k.elwise(x1, p2(x3)) with pytest.raises(ValueError): k.elwise(p1(x1), x3) # `FDD` versus `FDD`: approx(k(p1(x1), p1(x2)), ks[p1](x1, x2)) approx(k(p1(x1), p2(x2)), ks[p1, p2](x1, x2)) approx(k.elwise(p1(x1), p1(x3)), ks[p1].elwise(x1, x3)) approx(k.elwise(p1(x1), p2(x3)), ks[p1, p2].elwise(x1, x3)) # Multiple inputs versus input: approx( k((p2(x1), p1(x2)), x1), B.concat2d( [ks[p2, p1](x1, x1), ks[p2, p2](x1, x1)], [ks[p1, p1](x2, x1), ks[p1, p2](x2, x1)], ), ) approx( k(x1, (p2(x1), p1(x2))), B.concat2d( [ks[p1, p2](x1, x1), ks[p1, p1](x1, x2)], [ks[p2, p2](x1, x1), ks[p2, p1](x1, x2)], ), ) with pytest.raises(ValueError): k.elwise((p2(x1), p1(x3)), p2(x1)) with pytest.raises(ValueError): k.elwise(p2(x1), (p2(x1), p1(x3))) # Multiple inputs versus `FDD`: approx( k((p2(x1), p1(x2)), p2(x1)), B.concat(ks[p2, p2](x1, x1), ks[p1, p2](x2, x1), axis=0), ) approx( k(p2(x1), (p2(x1), p1(x2))), B.concat(ks[p2, p2](x1, x1), ks[p2, p1](x1, x2), axis=1), ) with pytest.raises(ValueError): k.elwise((p2(x1), p1(x3)), p2(x1)) with pytest.raises(ValueError): k.elwise(p2(x1), (p2(x1), p1(x3))) # Multiple inputs versus multiple inputs: approx( k((p2(x1), p1(x2)), (p2(x1))), B.concat(ks[p2, p2](x1, x1), ks[p1, p2](x2, x1), axis=0), ) with pytest.raises(ValueError): k.elwise((p2(x1), p1(x3)), (p2(x1),)) approx( k.elwise((p2(x1), p1(x3)), (p2(x1), p1(x3))), B.concat(ks[p2, p2].elwise(x1, x1), ks[p1, p1].elwise(x3, x3), axis=0), )