Skip to content

Commit

Permalink
Adds ops.py and its test file (#533)
Browse files Browse the repository at this point in the history
* removes unused variables in test_tdmprogram

* Adds ops.py and its test file

* Update strawberryfields/backends/bosonicbackend/ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* minor updates

* adds parametrizations for most tests

* adds parametrizations for some tests

* parametrizes all the tests

* Docstrings+variable names

Rewrites docstrings correct style and provides more descriptive variable names

* Put tests into class

* typo fix

Co-authored-by: Nicolas Quesada <zeitus@gmail.com>

* Adds bosonic pytest mark

* Apply suggestions from code review

Co-authored-by: antalszava <antalszava@gmail.com>

Co-authored-by: antalszava <antalszava@gmail.com>
Co-authored-by: elib20 <53090166+elib20@users.noreply.github.com>
  • Loading branch information
3 people committed Jan 27, 2021
1 parent 097dbc3 commit 98898e8
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- {BATCHED: 0, OPTIONS: "tf"}
- {BATCHED: 1, OPTIONS: "tf and pure"}
- {BATCHED: 1, OPTIONS: "tf and mixed"}

- {BATCHED: 0, OPTIONS: "bosonic"}
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.4.1
Expand Down
101 changes: 101 additions & 0 deletions strawberryfields/backends/bosonicbackend/ops.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Copyright 2021 Xanadu Quantum Technologies Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
r"""Gaussian operations vectorizing commonly used operation on covariance matrices and vectors of means"""

import numpy as np


def chop_in_blocks_multi(m, id_to_delete):
r"""
Splits an array of (symmetric) matrices each into 3 blocks (``A``, ``B``, ``C``).
Blocks ``A`` and ``C`` are diagonal blocks and ``B`` is the offdiagonal block.
Args:
m (ndarray): array of matrices
id_to_delete (ndarray): array for the indices that go into ``C``
Returns:
tuple: tuple of the ``A``, ``B`` and ``C`` matrices
"""
A = np.delete(m, id_to_delete, axis=1)
A = np.delete(A, id_to_delete, axis=2)
B = np.delete(m[:, :, id_to_delete], id_to_delete, axis=1)
C = m[:, id_to_delete, :][:, :, id_to_delete]
return (A, B, C)


def chop_in_blocks_vector_multi(v, id_to_delete):
r"""
For an array of vectors ``v``, splits ``v`` into two arrays of vectors,
``va`` and ``vb``. ``vb`` contains the components of ``v`` specified by
``id_to_delete``, and ``va`` contains the remaining components.
Args:
v (ndarray): array of vectors
id_to_delete (ndarray): array for the indices that go into vb
Returns:
tuple: tuple of ``(va,vb)`` vectors
"""
id_to_keep = np.sort(list(set(np.arange(len(v[0]))) - set(id_to_delete)))
va = v[:, id_to_keep]
vb = v[:, id_to_delete]
return (va, vb)


def reassemble_multi(A, id_to_delete):
r"""
For an array of matrices ``A``, creates a new array of matrices, each with
dimension ``dim(A)+len(id_to_delete)``. The subspace of each new matrix
specified by indices ``id_to_delete`` is set to the identity matrix, while
the rest of each new matrix is filled with the matrices from ``A``.
Args:
m (ndarray): array of matrices
id_to_delete (ndarray): array of indices in the new matrices that will
be set to the identity
Returns:
array: array of new matrices, each filled with ``A`` and identity
"""
num_weights = len(A[:, 0, 0])
new_mat_dim = len(A[0]) + len(id_to_delete)
ind = np.sort(list(set(np.arange(new_mat_dim)) - set(id_to_delete)))
new_mat = np.tile(np.eye(new_mat_dim, dtype=complex), (num_weights, 1, 1))
new_mat[np.ix_(np.arange(new_mat.shape[0], dtype=int), ind, ind)] = A
return new_mat


def reassemble_vector_multi(va, id_to_delete):
r"""
For an array of vectors ``va``, creates a new array of vectors, each with
dimension ``dim(va)+len(id_to_delete)``. The subspace of each new vector
specified by indices ``id_to_delete`` is set to 0, while the rest of each
new vector is filled with the vectors from ``va``.
Args:
va (ndarray): array of vectors
id_to_delete (ndarray): array of indices in the new vectors that will
be set to 0
Returns:
array: array of new vectors, each filled with ``va`` and 0
"""
num_weights = len(va[:, 0])
new_vec_dim = len(va[0]) + len(id_to_delete)
ind = np.sort(list(set(np.arange(new_vec_dim)) - set(id_to_delete)))
new_vec = np.zeros((num_weights, new_vec_dim), dtype=complex)
new_vec[:, ind] = va
return new_vec
115 changes: 115 additions & 0 deletions tests/bosonic_files/test_backend_ops.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Copyright 2021 Xanadu Quantum Technologies Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

r"""
Unit tests for backends.bosonicbackend.ops.py .
"""

import pytest

import numpy as np
import strawberryfields.backends.bosonicbackend.ops as ops

pytestmark = pytest.mark.bosonic

class TestOpsFunctions:
r"""Tests all the functions inside backends.bosonicbackend.ops.py"""

@pytest.mark.parametrize("reps", [1, 2, 5, 10])
def test_chop_in_blocks_multi(self, reps):
r"""Checks that ops.chop_in_block_multi partitions arrays of matrices correctly"""
# Create submatrices
A = np.random.rand(2, 2)
B = np.random.rand(2, 3)
C = np.random.rand(3, 3)

# Repeat them in an array
Atile = np.tile(A, [reps, 1, 1])
Btile = np.tile(B, [reps, 1, 1])
Ctile = np.tile(C, [reps, 1, 1])

# Make a new block matrix out of them and repeat it
m = np.block([[A, B], [B.T, C]])
m = np.tile(m, [reps, 1, 1])

# Choose to delete the indices corresponding to C
id_to_delete = np.arange(2, 5, dtype=int)

A2, B2, C2 = ops.chop_in_blocks_multi(m, id_to_delete)

assert np.allclose(A2, Atile)
assert np.allclose(B2, Btile)
assert np.allclose(C2, Ctile)

@pytest.mark.parametrize("reps", [1, 2, 5, 10])
def test_chop_in_blocks_vector_multi(self, reps):
r"""Checks that ops.chop_in_block_vector_multi partitions arrays of vectors correctly"""

# Create vectors
va = np.random.rand(6)
vb = np.random.rand(4)

# Repeat them in an array
vatile = np.tile(va, [reps, 1])
vbtile = np.tile(vb, [reps, 1])

# Make a new vector out of them and repeat it
v = np.append(va, vb)
v = np.tile(v, [reps, 1])

# Choose to delete the indices corresponding to C
id_to_delete = np.arange(6, 10, dtype=int)

va2, vb2 = ops.chop_in_blocks_vector_multi(v, id_to_delete)

assert np.allclose(va2, vatile)
assert np.allclose(vb2, vbtile)

@pytest.mark.parametrize("id_to_delete", [[0], [0, 1], [2, 0, 1], [0, 2, 4, 5]])
def test_reassemble_multi(self, id_to_delete):
r"""Checks that ops.reassemble_multi generates the correct output"""

# Create matrix
A = np.random.rand(2, 2)
reps = np.random.randint(1, 10)
Atile = np.tile(A, [reps, 1, 1])
# Create indices
m = ops.reassemble_multi(Atile, id_to_delete)
dim = len(A) + len(id_to_delete)
id_to_keep = list(set(range(dim)) - set(id_to_delete))
id_to_keep.sort()
assert m.shape == (reps, dim, dim)

A2, B2, C2 = ops.chop_in_blocks_multi(m, id_to_keep)
assert np.allclose(C2, Atile)
assert np.allclose(B2, 0)
assert np.allclose(A2, np.tile(np.eye(len(id_to_delete)), [reps, 1, 1]))

@pytest.mark.parametrize("id_to_delete", [[0], [0, 1], [2, 0, 1], [0, 2, 4, 5]])
def test_reassemble_vector_multi(self, id_to_delete):
r"""Checks that ops.reassemble_vector_multi generates the correct output"""
# Create matrix
v = np.random.rand(4)
reps = np.random.randint(1, 10)
vtile = np.tile(v, [reps, 1])
# Create indices
m = ops.reassemble_vector_multi(vtile, id_to_delete)
dim = len(v) + len(id_to_delete)
id_to_keep = list(set(range(dim)) - set(id_to_delete))
id_to_keep.sort()
assert m.shape == (reps, dim)

va2, vb2 = ops.chop_in_blocks_vector_multi(m, id_to_keep)
assert np.allclose(vb2, vtile)
assert np.allclose(va2, 0)

0 comments on commit 98898e8

Please sign in to comment.