From 40b6408effa19d183e31c0e6b1d9ce3fa3bff4ab Mon Sep 17 00:00:00 2001 From: Alexander Simm Date: Tue, 15 Jun 2021 22:58:23 +0200 Subject: [PATCH 1/7] add unit tests for basis function in qt utils --- test/test_qt_utils.py | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 test/test_qt_utils.py diff --git a/test/test_qt_utils.py b/test/test_qt_utils.py new file mode 100644 index 00000000..00582beb --- /dev/null +++ b/test/test_qt_utils.py @@ -0,0 +1,49 @@ +""" +Test Module for qt_utils +""" +import numpy as np +import pytest +from c3.utils.qt_utils import basis, xy_basis, get_basis_matrices +from numpy.testing import assert_array_almost_equal as almost_equal + + +@pytest.mark.unit +def test_basis() -> None: + """Testing orthonormality of basis vectors.""" + for dim in [3, 5, 10, 100]: + pairs = [(i, j) for i in range(dim) for j in range(dim)] + for p in pairs: + vi = basis(dim, p[0]) + vj = basis(dim, p[1]) + almost_equal(vi.T @ vj, 1 if p[0] == p[1] else 0) + + +@pytest.mark.unit +def test_xy_basis() -> None: + """Testing properties of basis vectors.""" + names = ["x", "y", "z"] + + for dim in [3, 5, 10, 100]: + # orthonormality of +/- vectors + for i in names: + vi_p = xy_basis(dim, i + "p") + vi_m = xy_basis(dim, i + "m") + almost_equal(np.linalg.norm(vi_p), 1) + almost_equal(np.linalg.norm(vi_m), 1) + almost_equal(np.vdot(vi_p.T, vi_m), 0) + + +@pytest.mark.unit +def test_basis_matrices() -> None: + """Testing properties of basis matrices.""" + for dim in [3, 5, 10]: + matrices = get_basis_matrices(dim) + + # orthogonality + pairs = [(a, b) for a in matrices for b in matrices if b is not a] + for p in pairs: + almost_equal(np.linalg.norm(np.multiply(p[0], p[1])), 0) + + # normalisation + for a in matrices: + almost_equal(np.linalg.norm(np.multiply(a, a)), 1) From 8059d02bef1e14e84a61fbb6c3815fa618868e4a Mon Sep 17 00:00:00 2001 From: Alexander Simm Date: Tue, 15 Jun 2021 22:59:28 +0200 Subject: [PATCH 2/7] update unit test for xy basis vectors --- test/test_qt_utils.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/test/test_qt_utils.py b/test/test_qt_utils.py index 00582beb..5e5caf64 100644 --- a/test/test_qt_utils.py +++ b/test/test_qt_utils.py @@ -12,10 +12,10 @@ def test_basis() -> None: """Testing orthonormality of basis vectors.""" for dim in [3, 5, 10, 100]: pairs = [(i, j) for i in range(dim) for j in range(dim)] - for p in pairs: - vi = basis(dim, p[0]) - vj = basis(dim, p[1]) - almost_equal(vi.T @ vj, 1 if p[0] == p[1] else 0) + for (i, j) in pairs: + vi = basis(dim, i) + vj = basis(dim, j) + almost_equal(vi.T @ vj, 1 if i == j else 0) @pytest.mark.unit @@ -32,6 +32,18 @@ def test_xy_basis() -> None: almost_equal(np.linalg.norm(vi_m), 1) almost_equal(np.vdot(vi_p.T, vi_m), 0) + # overlap + pairs = [(a, b) for a in names for b in names if b is not a] + for (a, b) in pairs: + va_p = xy_basis(dim, a + "p") + va_m = xy_basis(dim, a + "m") + vb_p = xy_basis(dim, b + "p") + vb_m = xy_basis(dim, b + "m") + almost_equal(np.linalg.norm(np.vdot(va_p.T, vb_p)), 1.0 / np.sqrt(2)) + almost_equal(np.linalg.norm(np.vdot(va_p.T, vb_m)), 1.0 / np.sqrt(2)) + almost_equal(np.linalg.norm(np.vdot(va_m.T, vb_p)), 1.0 / np.sqrt(2)) + almost_equal(np.linalg.norm(np.vdot(va_m.T, vb_m)), 1.0 / np.sqrt(2)) + @pytest.mark.unit def test_basis_matrices() -> None: @@ -41,8 +53,8 @@ def test_basis_matrices() -> None: # orthogonality pairs = [(a, b) for a in matrices for b in matrices if b is not a] - for p in pairs: - almost_equal(np.linalg.norm(np.multiply(p[0], p[1])), 0) + for (a, b) in pairs: + almost_equal(np.linalg.norm(np.multiply(a, b)), 0) # normalisation for a in matrices: From cc235c4a96ae62edcbd7789a316d4fa7497ed254 Mon Sep 17 00:00:00 2001 From: Alexander Simm Date: Tue, 15 Jun 2021 23:01:31 +0200 Subject: [PATCH 3/7] unit tests for rotation matrix and kronecker product --- test/test_qt_utils.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/test_qt_utils.py b/test/test_qt_utils.py index 5e5caf64..3535c462 100644 --- a/test/test_qt_utils.py +++ b/test/test_qt_utils.py @@ -3,7 +3,7 @@ """ import numpy as np import pytest -from c3.utils.qt_utils import basis, xy_basis, get_basis_matrices +from c3.utils.qt_utils import basis, xy_basis, get_basis_matrices, rotation, np_kron_n from numpy.testing import assert_array_almost_equal as almost_equal @@ -59,3 +59,35 @@ def test_basis_matrices() -> None: # normalisation for a in matrices: almost_equal(np.linalg.norm(np.multiply(a, a)), 1) + + +@pytest.mark.unit +def test_rotation() -> None: + """Testing properties of general rotation matrix""" + phase = 2 * np.pi * np.random.random() + xyz = np.random.random(3) + xyz /= np.linalg.norm(xyz) + matrix = rotation(phase, xyz) + + almost_equal(np.trace(matrix), 2 * np.cos(0.5 * phase)) + almost_equal(np.linalg.det(matrix), 1) + + +@pytest.mark.unit +def test_np_kron_n() -> None: + """Testing Kronecker product""" + for dim in [3, 5, 10]: + A = np.random.rand(dim, dim) + B = np.random.rand(dim, dim) + C = np.random.rand(dim, dim) + D = np.random.rand(dim, dim) + + # associativity and mixed product + almost_equal(np_kron_n([A, B + C]), np_kron_n([A, B]) + np_kron_n([A, C])) + almost_equal(np_kron_n([A, B]) * np_kron_n([C, D]), np_kron_n([A * C, B * D])) + # trace and determinant + almost_equal(np.trace(np_kron_n([A, B])), np.trace(A) * np.trace(B)) + almost_equal( + np.linalg.det(np_kron_n([A, B])), + np.linalg.det(A) ** dim * np.linalg.det(B) ** dim, + ) From 047f8271e84b9d1229b80d1196bb7b0d02720c32 Mon Sep 17 00:00:00 2001 From: Alexander Simm Date: Wed, 16 Jun 2021 10:46:58 +0200 Subject: [PATCH 4/7] unit tests for some qt_utils functions --- test/test_qt_utils.py | 91 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/test/test_qt_utils.py b/test/test_qt_utils.py index 3535c462..d5b23000 100644 --- a/test/test_qt_utils.py +++ b/test/test_qt_utils.py @@ -3,7 +3,17 @@ """ import numpy as np import pytest -from c3.utils.qt_utils import basis, xy_basis, get_basis_matrices, rotation, np_kron_n +from c3.utils.qt_utils import ( + basis, + xy_basis, + get_basis_matrices, + rotation, + np_kron_n, + kron_ids, + projector, + pad_matrix, + perfect_parametric_gate, +) from numpy.testing import assert_array_almost_equal as almost_equal @@ -77,10 +87,7 @@ def test_rotation() -> None: def test_np_kron_n() -> None: """Testing Kronecker product""" for dim in [3, 5, 10]: - A = np.random.rand(dim, dim) - B = np.random.rand(dim, dim) - C = np.random.rand(dim, dim) - D = np.random.rand(dim, dim) + (A, B, C, D) = [np.random.rand(dim, dim) for _ in range(4)] # associativity and mixed product almost_equal(np_kron_n([A, B + C]), np_kron_n([A, B]) + np_kron_n([A, C])) @@ -91,3 +98,77 @@ def test_np_kron_n() -> None: np.linalg.det(np_kron_n([A, B])), np.linalg.det(A) ** dim * np.linalg.det(B) ** dim, ) + + +@pytest.mark.unit +def test_kron_ids() -> None: + """Testing Kronecker product with identities""" + # create Kronecker product for some random dimensions and indices + dims = np.random.randint(2, 10, 3) + indices = np.where(np.random.rand(len(dims)) > 0.5)[0] + remaining_indices = np.delete(np.arange(len(dims)), indices) + matrices = [np.random.rand(dim, dim) for dim in dims[indices]] + result = kron_ids(dims, indices, matrices) + + # expected dimensions + assert result.shape[0] == result.shape[1] + assert result.shape[0], dims.prod() + + # trace + traces = np.array([np.trace(X) for X in matrices]) + almost_equal(np.trace(result), traces.prod() * np.prod(dims[remaining_indices])) + + +@pytest.mark.unit +def test_projector() -> None: + """Testing subspace projection matrix""" + # create projector for some random dimensions and indices + dims = np.random.randint(2, 10, 5) + indices = np.where(np.random.rand(len(dims)) > 0.5)[0] + result = projector(dims, indices) + + # check expected dimensions + assert result.shape[0], dims.prod() + expected_dims = np.array([2] * len(indices) + [1] * (len(dims) - len(indices))) + assert result.shape[1], expected_dims.prod() + + +@pytest.mark.unit +def test_pad_matrix() -> None: + """Testing padding of matrices""" + for dim in [3, 5, 10]: + M = np.random.rand(dim, dim) + padding_dim = np.random.randint(1, 10) + + # padding with unity + padded_ones = pad_matrix(M, padding_dim, "fulluni") + assert padded_ones.shape[0] == padded_ones.shape[1] + almost_equal(padded_ones.shape[0], M.shape[0] + padding_dim) + almost_equal(np.linalg.det(padded_ones), np.linalg.det(M)) + almost_equal(np.trace(padded_ones), np.trace(M) + padding_dim) + + # padding with zeros + padded_zeros = pad_matrix(M, padding_dim, "wzeros") + assert padded_zeros.shape[0] == padded_zeros.shape[1] + almost_equal(padded_ones.shape[0], M.shape[0] + padding_dim) + almost_equal(np.linalg.det(padded_zeros), 0) + almost_equal(np.trace(padded_zeros), np.trace(M)) + + +@pytest.mark.unit +def test_perfect_parametric_gate() -> None: + possible_gates = ["X", "Y", "Z", "Id"] + num_gates = np.random.randint(1, 5) + gates_str = ":".join( + np.take(possible_gates, np.random.randint(0, len(possible_gates), num_gates)) + ) + dims = np.random.randint(2, 5, num_gates) + angle = 2 * np.pi * np.random.rand() + result = perfect_parametric_gate(gates_str, angle, dims) + + # dimension + assert result.shape[0] == result.shape[1] + assert result.shape[0] == dims.prod() + + # unitarity + almost_equal(result * np.matrix(result).H, np.eye(dims.prod())) From 42f6d31590d3ecdb9d303badf2d8879c41958c06 Mon Sep 17 00:00:00 2001 From: Alexander Simm Date: Fri, 18 Jun 2021 09:47:00 +0200 Subject: [PATCH 5/7] unit tests for remaining qt_utils functions --- test/test_qt_utils.py | 63 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/test/test_qt_utils.py b/test/test_qt_utils.py index d5b23000..79df7a06 100644 --- a/test/test_qt_utils.py +++ b/test/test_qt_utils.py @@ -4,17 +4,32 @@ import numpy as np import pytest from c3.utils.qt_utils import ( + pauli_basis, basis, xy_basis, get_basis_matrices, rotation, np_kron_n, + hilbert_space_kron, kron_ids, projector, pad_matrix, perfect_parametric_gate, + T1_sequence, + ramsey_sequence, + ramsey_echo_sequence, ) from numpy.testing import assert_array_almost_equal as almost_equal +from c3.libraries.constants import GATES + + +@pytest.mark.unit +def test_pauli_basis() -> None: + """Testing dimensions of Pauli basis""" + dims = np.random.randint(2, 5, np.random.randint(1, 5)) + result = pauli_basis(dims) + assert result.shape[0] == result.shape[1] + assert result.shape[0] == dims.prod() ** 2 @pytest.mark.unit @@ -57,7 +72,7 @@ def test_xy_basis() -> None: @pytest.mark.unit def test_basis_matrices() -> None: - """Testing properties of basis matrices.""" + """Testing orthogonality and normalisation of basis matrices.""" for dim in [3, 5, 10]: matrices = get_basis_matrices(dim) @@ -73,7 +88,7 @@ def test_basis_matrices() -> None: @pytest.mark.unit def test_rotation() -> None: - """Testing properties of general rotation matrix""" + """Testing trace and determinant of general rotation matrix""" phase = 2 * np.pi * np.random.random() xyz = np.random.random(3) xyz /= np.linalg.norm(xyz) @@ -85,7 +100,7 @@ def test_rotation() -> None: @pytest.mark.unit def test_np_kron_n() -> None: - """Testing Kronecker product""" + """Testing properties of Kronecker product""" for dim in [3, 5, 10]: (A, B, C, D) = [np.random.rand(dim, dim) for _ in range(4)] @@ -100,6 +115,18 @@ def test_np_kron_n() -> None: ) +@pytest.mark.unit +def test_hilbert_space_kron() -> None: + """Testing dimensions of Kronecker product""" + dims = np.random.randint(1, 10, 5) + index = np.random.randint(0, len(dims)) + op_size = dims[index] + + result = hilbert_space_kron(np.random.rand(op_size, op_size), index, dims) + assert result.shape[0] == result.shape[1] + assert result.shape[0] == dims.prod() + + @pytest.mark.unit def test_kron_ids() -> None: """Testing Kronecker product with identities""" @@ -112,7 +139,7 @@ def test_kron_ids() -> None: # expected dimensions assert result.shape[0] == result.shape[1] - assert result.shape[0], dims.prod() + assert result.shape[0] == dims.prod() # trace traces = np.array([np.trace(X) for X in matrices]) @@ -128,14 +155,14 @@ def test_projector() -> None: result = projector(dims, indices) # check expected dimensions - assert result.shape[0], dims.prod() + assert result.shape[0] == dims.prod() expected_dims = np.array([2] * len(indices) + [1] * (len(dims) - len(indices))) - assert result.shape[1], expected_dims.prod() + assert result.shape[1] == expected_dims.prod() @pytest.mark.unit def test_pad_matrix() -> None: - """Testing padding of matrices""" + """Testing shape, trace, and determinant of matrices after padding""" for dim in [3, 5, 10]: M = np.random.rand(dim, dim) padding_dim = np.random.randint(1, 10) @@ -157,6 +184,7 @@ def test_pad_matrix() -> None: @pytest.mark.unit def test_perfect_parametric_gate() -> None: + """Testing shape and unitarity of the gate matrix""" possible_gates = ["X", "Y", "Z", "Id"] num_gates = np.random.randint(1, 5) gates_str = ":".join( @@ -172,3 +200,24 @@ def test_perfect_parametric_gate() -> None: # unitarity almost_equal(result * np.matrix(result).H, np.eye(dims.prod())) + + +@pytest.mark.unit +def test_sequence_generating_functions() -> None: + """Testing if the output of all utility functions that generate a sequence of gates contains strings + from the GATES list""" + length = np.random.randint(1, 100) + target = 1 + targetString = "[%d]" % target + + functions = [ + T1_sequence, + ramsey_sequence, + ramsey_echo_sequence, + ] + for func in functions: + sequence = func(length, target) + for gate in sequence: + assert type(gate) == str + gateWithoutTarget = gate.replace(targetString, "").lower() + assert gateWithoutTarget in GATES From d51a76cab22ad4e73b6ebbb02b3041317e27bf14 Mon Sep 17 00:00:00 2001 From: Alexander Simm Date: Fri, 18 Jun 2021 14:17:10 +0200 Subject: [PATCH 6/7] move list of test dimension for qt_utils to a fixture --- test/test_qt_utils.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/test/test_qt_utils.py b/test/test_qt_utils.py index 79df7a06..9259a4b4 100644 --- a/test/test_qt_utils.py +++ b/test/test_qt_utils.py @@ -23,6 +23,12 @@ from c3.libraries.constants import GATES +@pytest.fixture() +def test_dimensions() -> list: + """Functions that allow arbitrary numbers of dimensions will be tested for all dimensions in this list.""" + return [3, 5, 10, 50] + + @pytest.mark.unit def test_pauli_basis() -> None: """Testing dimensions of Pauli basis""" @@ -33,9 +39,9 @@ def test_pauli_basis() -> None: @pytest.mark.unit -def test_basis() -> None: +def test_basis(test_dimensions) -> None: """Testing orthonormality of basis vectors.""" - for dim in [3, 5, 10, 100]: + for dim in test_dimensions: pairs = [(i, j) for i in range(dim) for j in range(dim)] for (i, j) in pairs: vi = basis(dim, i) @@ -44,11 +50,11 @@ def test_basis() -> None: @pytest.mark.unit -def test_xy_basis() -> None: +def test_xy_basis(test_dimensions) -> None: """Testing properties of basis vectors.""" names = ["x", "y", "z"] - for dim in [3, 5, 10, 100]: + for dim in test_dimensions: # orthonormality of +/- vectors for i in names: vi_p = xy_basis(dim, i + "p") @@ -71,9 +77,9 @@ def test_xy_basis() -> None: @pytest.mark.unit -def test_basis_matrices() -> None: +def test_basis_matrices(test_dimensions) -> None: """Testing orthogonality and normalisation of basis matrices.""" - for dim in [3, 5, 10]: + for dim in test_dimensions[:3]: matrices = get_basis_matrices(dim) # orthogonality @@ -99,9 +105,9 @@ def test_rotation() -> None: @pytest.mark.unit -def test_np_kron_n() -> None: +def test_np_kron_n(test_dimensions) -> None: """Testing properties of Kronecker product""" - for dim in [3, 5, 10]: + for dim in test_dimensions: (A, B, C, D) = [np.random.rand(dim, dim) for _ in range(4)] # associativity and mixed product @@ -161,9 +167,9 @@ def test_projector() -> None: @pytest.mark.unit -def test_pad_matrix() -> None: +def test_pad_matrix(test_dimensions) -> None: """Testing shape, trace, and determinant of matrices after padding""" - for dim in [3, 5, 10]: + for dim in test_dimensions: M = np.random.rand(dim, dim) padding_dim = np.random.randint(1, 10) From 21309247bd34a1556829f6bdb5786b2d8f685552 Mon Sep 17 00:00:00 2001 From: Alexander Simm Date: Fri, 18 Jun 2021 14:47:36 +0200 Subject: [PATCH 7/7] move fixture to conftest --- test/conftest.py | 7 +++++++ test/test_qt_utils.py | 26 ++++++++++---------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index b2097cb8..d6481010 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -219,3 +219,10 @@ def get_test_signal() -> Dict: "TC1": {"values": tf.linspace(0, 100, 101)}, "TC2": {"values": tf.linspace(100, 200, 101)}, } + + +@pytest.fixture() +def get_test_dimensions() -> list: + """Functions in qt_utils that allow arbitrary numbers of dimensions will be tested for all dimensions in this + list.""" + return [3, 5, 10, 50] diff --git a/test/test_qt_utils.py b/test/test_qt_utils.py index 9259a4b4..94ddde8a 100644 --- a/test/test_qt_utils.py +++ b/test/test_qt_utils.py @@ -23,12 +23,6 @@ from c3.libraries.constants import GATES -@pytest.fixture() -def test_dimensions() -> list: - """Functions that allow arbitrary numbers of dimensions will be tested for all dimensions in this list.""" - return [3, 5, 10, 50] - - @pytest.mark.unit def test_pauli_basis() -> None: """Testing dimensions of Pauli basis""" @@ -39,9 +33,9 @@ def test_pauli_basis() -> None: @pytest.mark.unit -def test_basis(test_dimensions) -> None: +def test_basis(get_test_dimensions) -> None: """Testing orthonormality of basis vectors.""" - for dim in test_dimensions: + for dim in get_test_dimensions: pairs = [(i, j) for i in range(dim) for j in range(dim)] for (i, j) in pairs: vi = basis(dim, i) @@ -50,11 +44,11 @@ def test_basis(test_dimensions) -> None: @pytest.mark.unit -def test_xy_basis(test_dimensions) -> None: +def test_xy_basis(get_test_dimensions) -> None: """Testing properties of basis vectors.""" names = ["x", "y", "z"] - for dim in test_dimensions: + for dim in get_test_dimensions: # orthonormality of +/- vectors for i in names: vi_p = xy_basis(dim, i + "p") @@ -77,9 +71,9 @@ def test_xy_basis(test_dimensions) -> None: @pytest.mark.unit -def test_basis_matrices(test_dimensions) -> None: +def test_basis_matrices(get_test_dimensions) -> None: """Testing orthogonality and normalisation of basis matrices.""" - for dim in test_dimensions[:3]: + for dim in get_test_dimensions[:3]: matrices = get_basis_matrices(dim) # orthogonality @@ -105,9 +99,9 @@ def test_rotation() -> None: @pytest.mark.unit -def test_np_kron_n(test_dimensions) -> None: +def test_np_kron_n(get_test_dimensions) -> None: """Testing properties of Kronecker product""" - for dim in test_dimensions: + for dim in get_test_dimensions: (A, B, C, D) = [np.random.rand(dim, dim) for _ in range(4)] # associativity and mixed product @@ -167,9 +161,9 @@ def test_projector() -> None: @pytest.mark.unit -def test_pad_matrix(test_dimensions) -> None: +def test_pad_matrix(get_test_dimensions) -> None: """Testing shape, trace, and determinant of matrices after padding""" - for dim in test_dimensions: + for dim in get_test_dimensions: M = np.random.rand(dim, dim) padding_dim = np.random.randint(1, 10)