From 1142cb9d51944b47b4ac51b17aeaec97da0d8986 Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo Date: Mon, 7 Dec 2020 11:07:43 -0500 Subject: [PATCH 1/8] Adds bit and phase flip channels. --- doc/introduction/operations.rst | 2 + pennylane/devices/default_mixed.py | 2 + pennylane/ops/channel.py | 86 ++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/doc/introduction/operations.rst b/doc/introduction/operations.rst index a8f21b3310a..594c96458e9 100644 --- a/doc/introduction/operations.rst +++ b/doc/introduction/operations.rst @@ -115,6 +115,8 @@ Noisy channels ~pennylane.GeneralizedAmplitudeDamping ~pennylane.PhaseDamping ~pennylane.DepolarizingChannel + ~pennylane.BitFlipChannel + ~pennylane.PhaseFlipChannel ~pennylane.QubitChannel :html:`` diff --git a/pennylane/devices/default_mixed.py b/pennylane/devices/default_mixed.py index f629d462d82..49d2f9a660a 100644 --- a/pennylane/devices/default_mixed.py +++ b/pennylane/devices/default_mixed.py @@ -89,6 +89,8 @@ class DefaultMixed(QubitDevice): "GeneralizedAmplitudeDamping", "PhaseDamping", "DepolarizingChannel", + "BitFlipChannel", + "PhaseFlipChannel", "QubitChannel", } diff --git a/pennylane/ops/channel.py b/pennylane/ops/channel.py index ba5bca9eab0..267073993c3 100644 --- a/pennylane/ops/channel.py +++ b/pennylane/ops/channel.py @@ -223,6 +223,90 @@ def _kraus_matrices(cls, *params): return [K0, K1, K2, K3] +class BitFlipChannel(Channel): + r"""BitFlipChannel(p, wires) + Single-qubit bit flip (Pauli :math:`X`) error channel. + + This channel is modelled by the following Kraus matrices: + + .. math:: + K_0 = \sqrt{1-p} \begin{bmatrix} + 1 & 0 \\ + 0 & 1 + \end{bmatrix} + + .. math:: + K_1 = \sqrt{p}\begin{bmatrix} + 0 & 1 \\ + 1 & 0 + \end{bmatrix} + + where :math:`p \in [0, 1]` is the probability of a bit flip (Pauli :math:`X` error). + + **Details:** + + * Number of wires: 1 + * Number of parameters: 1 + + Args: + p (float): The probability for which a bit flip error occurs. + wires (Sequence[int] or int): the wire the channel acts on + """ + num_params = 1 + num_wires = 1 + par_domain = "R" + grad_method = "F" + + @classmethod + def _kraus_matrices(cls, *params): + p = params[0] + K0 = np.sqrt(1 - p) * np.eye(2) + K1 = np.sqrt(p) * np.array([[0, 1], [1, 0]]) + return [K0, K1] + + +class PhaseFlipChannel(Channel): + r"""PhaseFlipChannel(p, wires) + Single-qubit bit flip (Pauli :math:`Z`) error channel. + + This channel is modelled by the following Kraus matrices: + + .. math:: + K_0 = \sqrt{1-p} \begin{bmatrix} + 1 & 0 \\ + 0 & 1 + \end{bmatrix} + + .. math:: + K_1 = \sqrt{p}\begin{bmatrix} + 1 & 0 \\ + 0 & -1 + \end{bmatrix} + + where :math:`p \in [0, 1]` is the probability of a phase flip (Pauli :math:`Z`) error. + + **Details:** + + * Number of wires: 1 + * Number of parameters: 1 + + Args: + p (float): The probability for which a phase flip error occurs. + wires (Sequence[int] or int): the wire the channel acts on + """ + num_params = 1 + num_wires = 1 + par_domain = "R" + grad_method = "F" + + @classmethod + def _kraus_matrices(cls, *params): + p = params[0] + K0 = np.sqrt(1 - p) * np.eye(2) + K1 = np.sqrt(p) * np.array([[1, 0], [0, -1]]) + return [K0, K1] + + class QubitChannel(Channel): r"""QubitChannel(K_list, wires) Apply an arbitrary fixed quantum channel. @@ -282,6 +366,8 @@ def _kraus_matrices(cls, *params): "GeneralizedAmplitudeDamping", "PhaseDamping", "DepolarizingChannel", + "BitFlipChannel", + "PhaseFlipChannel", "QubitChannel", } From 0f8e0cfcda44abad34762eb425f9f650dd63a3e2 Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo Date: Mon, 7 Dec 2020 11:16:32 -0500 Subject: [PATCH 2/8] Add tests. --- tests/ops/test_channel_ops.py | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/ops/test_channel_ops.py b/tests/ops/test_channel_ops.py index 87514d06d67..83d5fec7b61 100644 --- a/tests/ops/test_channel_ops.py +++ b/tests/ops/test_channel_ops.py @@ -23,11 +23,14 @@ X = np.array([[0, 1], [1, 0]]) Y = np.array([[0, -1j], [1j, 0]]) +Z = np.array([[1, 0], [0, -1]]) ch_list = [ channel.AmplitudeDamping, channel.GeneralizedAmplitudeDamping, channel.PhaseDamping, + channel.BitFlipChannel, + channel.PhaseFlipChannel, channel.DepolarizingChannel, ] @@ -120,6 +123,48 @@ def test_gamma_arbitrary(self, tol): assert np.allclose(op(0.1, wires=0).kraus_matrices, expected, atol=tol, rtol=0) +class TestBitFlipChannel: + """Tests for the quantum channel BitFlipChannel""" + + def test_p_zero(self, tol): + """Test p=0 gives correct Kraus matrices""" + op = channel.BitFlipChannel + assert np.allclose(op(0, wires=0).kraus_matrices[0], np.eye(2), atol=tol, rtol=0) + assert np.allclose(op(0, wires=0).kraus_matrices[1], np.zeros((2, 2)), atol=tol, rtol=0) + + def test_p_arbitrary(self, tol): + """Test p=0.1 gives correct Kraus matrices""" + p = 0.1 + op = channel.BitFlipChannel + + expected_K0 = np.sqrt(1-p) * np.eye(2) + assert np.allclose(op(0.1, wires=0).kraus_matrices[0], expected_K0, atol=tol, rtol=0) + + expected_K1 = np.sqrt(p) * X + assert np.allclose(op(0.1, wires=0).kraus_matrices[1], expected_K1, atol=tol, rtol=0) + + +class TestPhaseFlipChannel: + """Tests for the quantum channel PhaseFlipChannel""" + + def test_p_zero(self, tol): + """Test p=0 gives correct Kraus matrices""" + op = channel.PhaseFlipChannel + assert np.allclose(op(0, wires=0).kraus_matrices[0], np.eye(2), atol=tol, rtol=0) + assert np.allclose(op(0, wires=0).kraus_matrices[1], np.zeros((2, 2)), atol=tol, rtol=0) + + def test_p_arbitrary(self, tol): + """Test p=0.1 gives correct Kraus matrices""" + p = 0.1 + op = channel.PhaseFlipChannel + + expected_K0 = np.sqrt(1-p) * np.eye(2) + assert np.allclose(op(0.1, wires=0).kraus_matrices[0], expected_K0, atol=tol, rtol=0) + + expected_K1 = np.sqrt(p) * Z + assert np.allclose(op(0.1, wires=0).kraus_matrices[1], expected_K1, atol=tol, rtol=0) + + class TestDepolarizingChannel: """Tests for the quantum channel DepolarizingChannel""" From 9575d7153da53e473bb3d91242767c08b4edf939 Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo Date: Mon, 7 Dec 2020 11:17:53 -0500 Subject: [PATCH 3/8] Run black. --- tests/ops/test_channel_ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ops/test_channel_ops.py b/tests/ops/test_channel_ops.py index 83d5fec7b61..41f4e17ff65 100644 --- a/tests/ops/test_channel_ops.py +++ b/tests/ops/test_channel_ops.py @@ -137,7 +137,7 @@ def test_p_arbitrary(self, tol): p = 0.1 op = channel.BitFlipChannel - expected_K0 = np.sqrt(1-p) * np.eye(2) + expected_K0 = np.sqrt(1 - p) * np.eye(2) assert np.allclose(op(0.1, wires=0).kraus_matrices[0], expected_K0, atol=tol, rtol=0) expected_K1 = np.sqrt(p) * X @@ -158,7 +158,7 @@ def test_p_arbitrary(self, tol): p = 0.1 op = channel.PhaseFlipChannel - expected_K0 = np.sqrt(1-p) * np.eye(2) + expected_K0 = np.sqrt(1 - p) * np.eye(2) assert np.allclose(op(0.1, wires=0).kraus_matrices[0], expected_K0, atol=tol, rtol=0) expected_K1 = np.sqrt(p) * Z From 1f3ba279b586403fe91f064578bbe6b66fa2fcb4 Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo Date: Mon, 7 Dec 2020 11:49:52 -0500 Subject: [PATCH 4/8] Update changelog. --- .github/CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 2ab5b800558..9075f1615f3 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,6 +2,23 @@

New features since last release

+* Two new error channels, `BitFlipError` and `PhaseFlipError` have been + added. [#954](https://github.com/PennyLaneAI/pennylane/pull/954) + + They can be used in the same manner as existing error channels, like so: + + ```python + dev = qml.device("default.mixed", wires=2) + + @qml.qnode(dev) + def circuit(): + qml.RX(0.3, wires=0) + qml.RY(0.5, wires=1) + qml.BitFlipChannel(0.01, wires=0) + qml.PhaseFlipChannel(0.01, wires=1) + return qml.expval(qml.PauliZ(0)) + ``` +

Improvements

Breaking changes

@@ -14,6 +31,8 @@ This release contains contributions from (in alphabetical order): +Olivia Di Matteo. + # Release 0.13.0 (current release)

New features since last release

From 4621cbbb204ee8141aec4af60e8351d90b7c5b17 Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo <2068515+glassnotes@users.noreply.github.com> Date: Tue, 8 Dec 2020 13:25:39 -0500 Subject: [PATCH 5/8] Apply suggestions from code review Co-authored-by: ixfoduap <40441298+ixfoduap@users.noreply.github.com> --- pennylane/ops/channel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/ops/channel.py b/pennylane/ops/channel.py index 267073993c3..e59fa6ba266 100644 --- a/pennylane/ops/channel.py +++ b/pennylane/ops/channel.py @@ -249,7 +249,7 @@ class BitFlipChannel(Channel): * Number of parameters: 1 Args: - p (float): The probability for which a bit flip error occurs. + p (float): The probability that a bit flip error occurs. wires (Sequence[int] or int): the wire the channel acts on """ num_params = 1 @@ -291,7 +291,7 @@ class PhaseFlipChannel(Channel): * Number of parameters: 1 Args: - p (float): The probability for which a phase flip error occurs. + p (float): The probability that a phase flip error occurs. wires (Sequence[int] or int): the wire the channel acts on """ num_params = 1 From e4fb1c468f2b72c0c80f2df7d89153caa0485a33 Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo <2068515+glassnotes@users.noreply.github.com> Date: Tue, 8 Dec 2020 13:25:53 -0500 Subject: [PATCH 6/8] Update .github/CHANGELOG.md Co-authored-by: ixfoduap <40441298+ixfoduap@users.noreply.github.com> --- .github/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 9075f1615f3..ef1a8e96706 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -5,7 +5,7 @@ * Two new error channels, `BitFlipError` and `PhaseFlipError` have been added. [#954](https://github.com/PennyLaneAI/pennylane/pull/954) - They can be used in the same manner as existing error channels, like so: + They can be used in the same manner as existing error channels: ```python dev = qml.device("default.mixed", wires=2) From 0468de2ec971cf3cce33713cd6fdb9ae67657e80 Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo Date: Tue, 8 Dec 2020 13:34:02 -0500 Subject: [PATCH 7/8] Make tests parameterized. --- tests/ops/test_channel_ops.py | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/tests/ops/test_channel_ops.py b/tests/ops/test_channel_ops.py index 41f4e17ff65..482b5cae736 100644 --- a/tests/ops/test_channel_ops.py +++ b/tests/ops/test_channel_ops.py @@ -126,43 +126,31 @@ def test_gamma_arbitrary(self, tol): class TestBitFlipChannel: """Tests for the quantum channel BitFlipChannel""" - def test_p_zero(self, tol): - """Test p=0 gives correct Kraus matrices""" - op = channel.BitFlipChannel - assert np.allclose(op(0, wires=0).kraus_matrices[0], np.eye(2), atol=tol, rtol=0) - assert np.allclose(op(0, wires=0).kraus_matrices[1], np.zeros((2, 2)), atol=tol, rtol=0) - - def test_p_arbitrary(self, tol): - """Test p=0.1 gives correct Kraus matrices""" - p = 0.1 + @pytest.mark.parametrize("p", [0, 0.1, 0.5, 1]) + def test_p_arbitrary(self, p, tol): + """Test that various values of p give correct Kraus matrices""" op = channel.BitFlipChannel expected_K0 = np.sqrt(1 - p) * np.eye(2) - assert np.allclose(op(0.1, wires=0).kraus_matrices[0], expected_K0, atol=tol, rtol=0) + assert np.allclose(op(p, wires=0).kraus_matrices[0], expected_K0, atol=tol, rtol=0) expected_K1 = np.sqrt(p) * X - assert np.allclose(op(0.1, wires=0).kraus_matrices[1], expected_K1, atol=tol, rtol=0) + assert np.allclose(op(p, wires=0).kraus_matrices[1], expected_K1, atol=tol, rtol=0) class TestPhaseFlipChannel: - """Tests for the quantum channel PhaseFlipChannel""" + """Test that various values of p give correct Kraus matrices""" - def test_p_zero(self, tol): - """Test p=0 gives correct Kraus matrices""" - op = channel.PhaseFlipChannel - assert np.allclose(op(0, wires=0).kraus_matrices[0], np.eye(2), atol=tol, rtol=0) - assert np.allclose(op(0, wires=0).kraus_matrices[1], np.zeros((2, 2)), atol=tol, rtol=0) - - def test_p_arbitrary(self, tol): + @pytest.mark.parametrize("p", [0, 0.1, 0.5, 1]) + def test_p_arbitrary(self, p, tol): """Test p=0.1 gives correct Kraus matrices""" - p = 0.1 op = channel.PhaseFlipChannel expected_K0 = np.sqrt(1 - p) * np.eye(2) - assert np.allclose(op(0.1, wires=0).kraus_matrices[0], expected_K0, atol=tol, rtol=0) + assert np.allclose(op(p, wires=0).kraus_matrices[0], expected_K0, atol=tol, rtol=0) expected_K1 = np.sqrt(p) * Z - assert np.allclose(op(0.1, wires=0).kraus_matrices[1], expected_K1, atol=tol, rtol=0) + assert np.allclose(op(p, wires=0).kraus_matrices[1], expected_K1, atol=tol, rtol=0) class TestDepolarizingChannel: From 15852c3999d0aaaea0767d5b0b2389fc436f535f Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo Date: Wed, 9 Dec 2020 10:34:29 -0500 Subject: [PATCH 8/8] Remove 'channel' prefix from names. --- .github/CHANGELOG.md | 8 ++++---- doc/introduction/operations.rst | 4 ++-- pennylane/devices/default_mixed.py | 4 ++-- pennylane/ops/channel.py | 12 ++++++------ tests/ops/test_channel_ops.py | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index ef1a8e96706..1810a3f8741 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,8 +2,8 @@

New features since last release

-* Two new error channels, `BitFlipError` and `PhaseFlipError` have been - added. [#954](https://github.com/PennyLaneAI/pennylane/pull/954) +* Two new error channels, `BitFlip` and `PhaseFlip` have been added. + [#954](https://github.com/PennyLaneAI/pennylane/pull/954) They can be used in the same manner as existing error channels: @@ -14,8 +14,8 @@ def circuit(): qml.RX(0.3, wires=0) qml.RY(0.5, wires=1) - qml.BitFlipChannel(0.01, wires=0) - qml.PhaseFlipChannel(0.01, wires=1) + qml.BitFlip(0.01, wires=0) + qml.PhaseFlip(0.01, wires=1) return qml.expval(qml.PauliZ(0)) ``` diff --git a/doc/introduction/operations.rst b/doc/introduction/operations.rst index 594c96458e9..325e463bd57 100644 --- a/doc/introduction/operations.rst +++ b/doc/introduction/operations.rst @@ -115,8 +115,8 @@ Noisy channels ~pennylane.GeneralizedAmplitudeDamping ~pennylane.PhaseDamping ~pennylane.DepolarizingChannel - ~pennylane.BitFlipChannel - ~pennylane.PhaseFlipChannel + ~pennylane.BitFlip + ~pennylane.PhaseFlip ~pennylane.QubitChannel :html:`` diff --git a/pennylane/devices/default_mixed.py b/pennylane/devices/default_mixed.py index 49d2f9a660a..d35aa6dcc79 100644 --- a/pennylane/devices/default_mixed.py +++ b/pennylane/devices/default_mixed.py @@ -89,8 +89,8 @@ class DefaultMixed(QubitDevice): "GeneralizedAmplitudeDamping", "PhaseDamping", "DepolarizingChannel", - "BitFlipChannel", - "PhaseFlipChannel", + "BitFlip", + "PhaseFlip", "QubitChannel", } diff --git a/pennylane/ops/channel.py b/pennylane/ops/channel.py index e59fa6ba266..24cf44bd3d0 100644 --- a/pennylane/ops/channel.py +++ b/pennylane/ops/channel.py @@ -223,8 +223,8 @@ def _kraus_matrices(cls, *params): return [K0, K1, K2, K3] -class BitFlipChannel(Channel): - r"""BitFlipChannel(p, wires) +class BitFlip(Channel): + r"""BitFlip(p, wires) Single-qubit bit flip (Pauli :math:`X`) error channel. This channel is modelled by the following Kraus matrices: @@ -265,8 +265,8 @@ def _kraus_matrices(cls, *params): return [K0, K1] -class PhaseFlipChannel(Channel): - r"""PhaseFlipChannel(p, wires) +class PhaseFlip(Channel): + r"""PhaseFlip(p, wires) Single-qubit bit flip (Pauli :math:`Z`) error channel. This channel is modelled by the following Kraus matrices: @@ -366,8 +366,8 @@ def _kraus_matrices(cls, *params): "GeneralizedAmplitudeDamping", "PhaseDamping", "DepolarizingChannel", - "BitFlipChannel", - "PhaseFlipChannel", + "BitFlip", + "PhaseFlip", "QubitChannel", } diff --git a/tests/ops/test_channel_ops.py b/tests/ops/test_channel_ops.py index 482b5cae736..1e6f2857bd0 100644 --- a/tests/ops/test_channel_ops.py +++ b/tests/ops/test_channel_ops.py @@ -29,8 +29,8 @@ channel.AmplitudeDamping, channel.GeneralizedAmplitudeDamping, channel.PhaseDamping, - channel.BitFlipChannel, - channel.PhaseFlipChannel, + channel.BitFlip, + channel.PhaseFlip, channel.DepolarizingChannel, ] @@ -123,13 +123,13 @@ def test_gamma_arbitrary(self, tol): assert np.allclose(op(0.1, wires=0).kraus_matrices, expected, atol=tol, rtol=0) -class TestBitFlipChannel: +class TestBitFlip: """Tests for the quantum channel BitFlipChannel""" @pytest.mark.parametrize("p", [0, 0.1, 0.5, 1]) def test_p_arbitrary(self, p, tol): """Test that various values of p give correct Kraus matrices""" - op = channel.BitFlipChannel + op = channel.BitFlip expected_K0 = np.sqrt(1 - p) * np.eye(2) assert np.allclose(op(p, wires=0).kraus_matrices[0], expected_K0, atol=tol, rtol=0) @@ -138,13 +138,13 @@ def test_p_arbitrary(self, p, tol): assert np.allclose(op(p, wires=0).kraus_matrices[1], expected_K1, atol=tol, rtol=0) -class TestPhaseFlipChannel: +class TestPhaseFlip: """Test that various values of p give correct Kraus matrices""" @pytest.mark.parametrize("p", [0, 0.1, 0.5, 1]) def test_p_arbitrary(self, p, tol): """Test p=0.1 gives correct Kraus matrices""" - op = channel.PhaseFlipChannel + op = channel.PhaseFlip expected_K0 = np.sqrt(1 - p) * np.eye(2) assert np.allclose(op(p, wires=0).kraus_matrices[0], expected_K0, atol=tol, rtol=0)