From 9b609981a9617b77850622941db010e38e8b9038 Mon Sep 17 00:00:00 2001 From: Utkarsh Date: Fri, 12 Apr 2024 16:00:29 -0400 Subject: [PATCH] Add algorithmic errors tracking to `qml.Tracker` (#5465) **Context:** Update `qml.Tracker` to track and combine error **Description of the Change:** Add algorithmic error tracking in the `simulator_tracking` device modifier. **Benefits:** `tracker` will track algorithmic errors. **Possible Drawbacks:** N/A **Related GitHub Issues:** N/A --------- Co-authored-by: Jay Soni --- doc/releases/changelog-dev.md | 4 ++++ pennylane/devices/modifiers/simulator_tracking.py | 12 ++++++++---- pennylane/tracker.py | 4 +++- .../default_qubit/test_default_qubit_tracking.py | 3 +++ tests/devices/modifiers/test_all_modifiers.py | 2 +- tests/devices/modifiers/test_simulator_tracking.py | 2 +- tests/devices/test_default_clifford.py | 1 + tests/devices/test_null_qubit.py | 1 + tests/resource/test_error/test_error.py | 13 +++++++++++++ 9 files changed, 35 insertions(+), 7 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index ecda32c06ea..6cba45117fa 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -209,6 +209,10 @@ [stim](https://github.com/quantumlib/Stim) `v1.13.0`. [(#5409)](https://github.com/PennyLaneAI/pennylane/pull/5409) +* `qml.specs` and `qml.Tracker` now return information about algorithmic errors for the qnode as well. + [(#5464)](https://github.com/PennyLaneAI/pennylane/pull/5464) + [(#5465)](https://github.com/PennyLaneAI/pennylane/pull/5465) + * `qml.specs` now returns information regarding algorithmic errors for the qnode as well. [(#5464)](https://github.com/PennyLaneAI/pennylane/pull/5464) diff --git a/pennylane/devices/modifiers/simulator_tracking.py b/pennylane/devices/modifiers/simulator_tracking.py index 72f86d93547..0ebde382ead 100644 --- a/pennylane/devices/modifiers/simulator_tracking.py +++ b/pennylane/devices/modifiers/simulator_tracking.py @@ -46,6 +46,7 @@ def execute(self, circuits, execution_config=DefaultExecutionConfig): results=r, shots=shots, resources=c.specs["resources"], + errors=c.specs["errors"], ) else: self.tracker.update( @@ -53,6 +54,7 @@ def execute(self, circuits, execution_config=DefaultExecutionConfig): executions=qpu_executions, results=r, resources=c.specs["resources"], + errors=c.specs["errors"], ) self.tracker.record() return results @@ -85,7 +87,7 @@ def execute_and_compute_derivatives(self, circuits, execution_config=DefaultExec if self.tracker.active: batch = (circuits,) if isinstance(circuits, QuantumScript) else circuits for c in batch: - self.tracker.update(resources=c.specs["resources"]) + self.tracker.update(resources=c.specs["resources"], errors=c.specs["errors"]) self.tracker.update( execute_and_derivative_batches=1, executions=len(batch), @@ -119,7 +121,7 @@ def execute_and_compute_jvp(self, circuits, tangents, execution_config=DefaultEx if self.tracker.active: batch = (circuits,) if isinstance(circuits, QuantumScript) else circuits for c in batch: - self.tracker.update(resources=c.specs["resources"]) + self.tracker.update(resources=c.specs["resources"], errors=c.specs["errors"]) self.tracker.update(execute_and_jvp_batches=1, executions=len(batch), jvps=len(batch)) self.tracker.record() @@ -153,7 +155,7 @@ def execute_and_compute_vjp( if self.tracker.active: batch = (circuits,) if isinstance(circuits, QuantumScript) else circuits for c in batch: - self.tracker.update(resources=c.specs["resources"]) + self.tracker.update(resources=c.specs["resources"], errors=c.specs["errors"]) self.tracker.update(execute_and_vjp_batches=1, executions=len(batch), vjps=len(batch)) self.tracker.record() return untracked_execute_and_compute_vjp(self, circuits, cotangents, execution_config) @@ -176,6 +178,7 @@ def simulator_tracking(cls: type) -> type: * ``executions``: the number of unique circuits that would be required on quantum hardware * ``shots``: the number of shots * ``resources``: the :class:`~.resource.Resources` for the executed circuit. + * ``"errors"``: combined algorithmic errors from the quantum operations executed by the qnode. * ``simulations``: the number of simulations performed. One simulation can cover multiple QPU executions, such as for non-commuting measurements and batched parameters. * ``batches``: The number of times :meth:`~pennylane.devices.Device.execute` is called. @@ -218,7 +221,8 @@ def execute(self, circuits, execution_config = qml.devices.DefaultExecutionConfi 'shots': [100], 'resources': [Resources(num_wires=1, num_gates=1, gate_types=defaultdict(, {'S': 1}), gate_sizes=defaultdict(, {1: 1}), depth=1, shots=Shots(total_shots=50, - shot_vector=(ShotCopies(50 shots x 1),)))]} + shot_vector=(ShotCopies(50 shots x 1),)))], + 'errors': {}} """ if not issubclass(cls, Device): diff --git a/pennylane/tracker.py b/pennylane/tracker.py index 5b07867fe2b..24aeda55df1 100644 --- a/pennylane/tracker.py +++ b/pennylane/tracker.py @@ -82,7 +82,9 @@ def circuit(x): gate_types=defaultdict(, {'RX': 1}), gate_sizes=defaultdict(, {1: 1}), depth=1, - shots=Shots(total_shots=100, shot_vector=(ShotCopies(100 shots x 1),)))} + shots=Shots(total_shots=100, shot_vector=(ShotCopies(100 shots x 1),))), + 'errors': {} + } >>> tracker.history.keys() dict_keys(['batches', 'simulations', 'executions', 'results', 'shots', 'resources']) >>> tracker.history['results'] diff --git a/tests/devices/default_qubit/test_default_qubit_tracking.py b/tests/devices/default_qubit/test_default_qubit_tracking.py index 3975a42145c..ff52c6e7239 100644 --- a/tests/devices/default_qubit/test_default_qubit_tracking.py +++ b/tests/devices/default_qubit/test_default_qubit_tracking.py @@ -59,6 +59,7 @@ def test_tracking_batch(self): "resources": [Resources(num_wires=1), Resources(num_wires=1), Resources(num_wires=1)], "derivative_batches": [1], "derivatives": [1], + "errors": [{}, {}, {}], } assert tracker.totals == { "batches": 2, @@ -73,6 +74,7 @@ def test_tracking_batch(self): "simulations": 1, "results": 1, "resources": Resources(num_wires=1), + "errors": {}, } def test_tracking_execute_and_derivatives(self): @@ -103,6 +105,7 @@ def test_tracking_execute_and_derivatives(self): "vjp_batches": [1], "execute_and_vjp_batches": [1], "resources": [Resources(num_wires=1)] * 12, + "errors": [{}] * 12, } def test_tracking_resources(self): diff --git a/tests/devices/modifiers/test_all_modifiers.py b/tests/devices/modifiers/test_all_modifiers.py index 69a40f3cf0b..fa315dbbc81 100644 --- a/tests/devices/modifiers/test_all_modifiers.py +++ b/tests/devices/modifiers/test_all_modifiers.py @@ -44,7 +44,7 @@ def execute(self, circuits, execution_config=qml.devices.DefaultExecutionConfig) # result unwrapped assert out == 0.0 - assert len(dev.tracker.history) == 6 + assert len(dev.tracker.history) == 7 assert dev.tracker.history["batches"] == [1] assert dev.tracker.history["simulations"] == [1] assert dev.tracker.history["executions"] == [1] diff --git a/tests/devices/modifiers/test_simulator_tracking.py b/tests/devices/modifiers/test_simulator_tracking.py index 2d889c2d46b..88109eed8f6 100644 --- a/tests/devices/modifiers/test_simulator_tracking.py +++ b/tests/devices/modifiers/test_simulator_tracking.py @@ -44,7 +44,7 @@ def execute(self, circuits, execution_config=qml.devices.DefaultExecutionConfig) out = dev.execute((tape1, tape2)) assert out == ((0.0, 0.0), 0.0) - assert len(dev.tracker.history) == 6 + assert len(dev.tracker.history) == 7 assert dev.tracker.history["batches"] == [1] assert dev.tracker.history["simulations"] == [1, 1] assert dev.tracker.history["executions"] == [2, 2] diff --git a/tests/devices/test_default_clifford.py b/tests/devices/test_default_clifford.py index 9ceb1fdb492..1ff88630ed7 100644 --- a/tests/devices/test_default_clifford.py +++ b/tests/devices/test_default_clifford.py @@ -484,6 +484,7 @@ def test_tracker(): "batches": [1, 1], "simulations": [1, 1], "executions": [1, 1], + "errors": [{}, {}], } diff --git a/tests/devices/test_null_qubit.py b/tests/devices/test_null_qubit.py index 775ae7adde9..507fc9f2ea4 100644 --- a/tests/devices/test_null_qubit.py +++ b/tests/devices/test_null_qubit.py @@ -106,6 +106,7 @@ def test_tracking(): ) ] * 13, + "errors": [{}] * 13, } diff --git a/tests/resource/test_error/test_error.py b/tests/resource/test_error/test_error.py index d1b171bab44..4b690e49a18 100644 --- a/tests/resource/test_error/test_error.py +++ b/tests/resource/test_error/test_error.py @@ -282,3 +282,16 @@ def test_specs(self): assert algo_errors["MultiplicativeError"].error == 0.31 * 0.24 assert algo_errors["AdditiveError"].error == 0.73 + 0.12 assert algo_errors["SpectralNormError"].error == 0.25 + 0.17998560822421455 + + def test_tracker(self): + """Test that tracker are tracking errors as expected.""" + + with qml.Tracker(self.dev) as tracker: + self.circuit() + + algo_errors = tracker.latest["errors"] + assert len(algo_errors) == 3 + assert all(error in algo_errors for error in self.errors_types) + assert algo_errors["MultiplicativeError"].error == 0.31 * 0.24 + assert algo_errors["AdditiveError"].error == 0.73 + 0.12 + assert algo_errors["SpectralNormError"].error == 0.25 + 0.17998560822421455