From ba30c071b33e4b481f289db08be9a40bd28dd785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Dao?= Date: Fri, 30 Aug 2024 15:10:40 +0200 Subject: [PATCH] Make `MultiQubitFrame.operators' attribute read-only --- .../quantum_info/multi_qubit_frame.py | 49 +++++++++---------- .../read-only-operators-bae10ffedf6b683c.yaml | 5 ++ test/quantum_info/test_multi_qubit_povm.py | 16 +++--- 3 files changed, 34 insertions(+), 36 deletions(-) create mode 100644 releasenotes/notes/read-only-operators-bae10ffedf6b683c.yaml diff --git a/povm_toolbox/quantum_info/multi_qubit_frame.py b/povm_toolbox/quantum_info/multi_qubit_frame.py index 7412706..363f2e0 100644 --- a/povm_toolbox/quantum_info/multi_qubit_frame.py +++ b/povm_toolbox/quantum_info/multi_qubit_frame.py @@ -64,7 +64,28 @@ def __init__(self, list_operators: list[Operator]) -> None: self._array: np.ndarray self._informationally_complete: bool - self.operators = list_operators + self._num_operators = len(list_operators) + self._dimension = list_operators[0].dim[0] + for frame_op in list_operators: + if not (self._dimension == frame_op.dim[0] and self._dimension == frame_op.dim[1]): + raise ValueError( + f"Frame operators need to be square ({frame_op.dim[0]},{frame_op.dim[1]}) and " + "all of the same dimension." + ) + + self._operators = list_operators + + self._pauli_operators = None + + self._array = np.ndarray((self.dimension**2, self.num_operators), dtype=complex) + for k, frame_op in enumerate(list_operators): + self._array[:, k] = matrix_to_double_ket(frame_op.data) + + self._informationally_complete = bool( + np.linalg.matrix_rank(self._array) == self.dimension**2 + ) + + self._check_validity() def __repr__(self) -> str: """Return the string representation of a :class:`.MultiQubitFrame` instance.""" @@ -91,32 +112,6 @@ def operators(self) -> list[Operator]: """Return the list of frame operators.""" return self._operators - @operators.setter - def operators(self, new_operators: list[Operator]): - """Set the frame operators.""" - self._num_operators = len(new_operators) - self._dimension = new_operators[0].dim[0] - for frame_op in new_operators: - if not (self._dimension == frame_op.dim[0] and self._dimension == frame_op.dim[1]): - raise ValueError( - f"Frame operators need to be square ({frame_op.dim[0]},{frame_op.dim[1]}) and " - "all of the same dimension." - ) - - self._operators = new_operators - - self._pauli_operators = None - - self._array = np.ndarray((self.dimension**2, self.num_operators), dtype=complex) - for k, frame_op in enumerate(new_operators): - self._array[:, k] = matrix_to_double_ket(frame_op.data) - - self._informationally_complete = bool( - np.linalg.matrix_rank(self._array) == self.dimension**2 - ) - - self._check_validity() - @property def pauli_operators(self) -> list[dict[str, complex]]: """Convert the internal frame operators to Pauli form. diff --git a/releasenotes/notes/read-only-operators-bae10ffedf6b683c.yaml b/releasenotes/notes/read-only-operators-bae10ffedf6b683c.yaml new file mode 100644 index 0000000..3e924a5 --- /dev/null +++ b/releasenotes/notes/read-only-operators-bae10ffedf6b683c.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + The :attr:`.MultiQubitFrame.operators` attribute of :class:`.MultiQubitFrame` objects is now + read-only. \ No newline at end of file diff --git a/test/quantum_info/test_multi_qubit_povm.py b/test/quantum_info/test_multi_qubit_povm.py index b42fa63..779d705 100644 --- a/test/quantum_info/test_multi_qubit_povm.py +++ b/test/quantum_info/test_multi_qubit_povm.py @@ -38,6 +38,13 @@ def test_invalid_operators(self): _ = MultiQubitPOVM( list_operators=[0.9 * Operator.from_label("0"), Operator.from_label("1")] ) + with self.subTest("Non-square operators") and self.assertRaises(ValueError): + _ = MultiQubitPOVM( + [ + Operator(np.array([[1, 0, 0], [0, 0, 0]])), + Operator(np.array([[0, 0, 0], [0, 1, 0]])), + ] + ) def test_dimension(self): """Test dimension attribute""" @@ -134,15 +141,6 @@ def test_repr(self): ) self.assertEqual(povm.__repr__(), f"MultiQubitPOVM(num_qubits=2)<4> at {hex(id(povm))}") - def test_operators_setter(self): - """Test the ``__repr__`` method.""" - povm = MultiQubitPOVM([Operator.from_label("0"), Operator.from_label("1")]) - with self.subTest("Non-square operators") and self.assertRaises(ValueError): - povm.operators = [ - Operator(np.array([[1, 0, 0], [0, 0, 0]])), - Operator(np.array([[0, 0, 0], [0, 1, 0]])), - ] - def test_pauli_operators(self): """Test errors are raised correctly for the ``pauli_operators`` attribute.""" povm = MultiQubitPOVM([Operator(np.eye(3))])