Repository: PennyLaneAI/pennylane
Pull request #5115 (2024-01-26 22:40:07)

Add abstract `AlgorithmicError` class 
**Context:**
The first of a few PRs adding error propagation functionality to PennyLane

**Description of the Change:**
Add abstract `AlgorithmicError` class



github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [`doc/releases/changelog-dev.md`](/PennyLaneAI/pennylane/blob/master/doc/releases/changelog-dev.md) with:
* A one-to-two sentence description of the change. You may include a small working example for new features.
* A link back to this PR.
* Your name (or GitHub username) in the contributors section.
Jaybsoni: [sc-55439]
Jaybsoni: [sc-55154]
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5115?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`321ad70`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/321ad706c219ad689de4ee163cf8c37ef017e081?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.68% compared to head [(`2078e80`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5115?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.68%.


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #5115      +/-   ##
==========================================
- Coverage   99.68%   99.68%   -0.01%     
==========================================
  Files         391      392       +1     
  Lines       35793    35536     -257     
==========================================
- Hits        35682    35424     -258     
- Misses        111      112       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5115?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5115?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
Attention: `1 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`c1a813d`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/c1a813d3df83479527ad6751e5431a08294664a8?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.68% compared to head [(`f2e20ee`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5115?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.67%.

| [Files](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5115?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Patch % | Lines |
|---|---|---|
| [pennylane/resource/error.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5115?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3Jlc291cmNlL2Vycm9yLnB5) | 90.90% | [1 Missing :warning: ](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5115?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #5115      +/-   ##
==========================================
- Coverage   99.68%   99.67%   -0.01%     
==========================================
  Files         394      395       +1     
  Lines       36160    35900     -260     
==========================================
- Hits        36047    35785     -262     
- Misses        113      115       +2     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5115?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 96b4799e6..7aeec9a36 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -121,6 +121,12 @@
          [1, 0, 0, 1, 1]])
   ```
 
+* A function called `apply_operation` has been added to the new `qutrit_mixed` module found in `qml.devices` that applies operations to device-compatible states.
+  [(#5032)](https://github.com/PennyLaneAI/pennylane/pull/5032)
+
+* Added new error tracking and propagation functionality. 
+  [(#5115)](https://github.com/PennyLaneAI/pennylane/pull/5115)
+
 <h3>Improvements 🛠</h3>
 
 <h4>Faster gradients with VJPs and other performance improvements</h4>
@@ -512,4 +518,3 @@ Mudit Pandey,
 Alex Preciado,
 Matthew Silverman,
 Jay Soni.
-
diff --git a/pennylane/resource/__init__.py b/pennylane/resource/__init__.py
index 770deb6a4..d0a061cf7 100644
--- a/pennylane/resource/__init__.py
+++ b/pennylane/resource/__init__.py
@@ -49,6 +49,16 @@ Quantum Phase Estimation Resources
     ~FirstQuantization
     ~DoubleFactorization
 
+Error Tracking
+--------------
+
+.. currentmodule:: pennylane.resource
+
+.. autosummary::
+    :toctree: api
+
+    ~AlgorithmicError
+
 Resource Classes
 ----------------
 
@@ -112,3 +122,4 @@ from .first_quantization import FirstQuantization
 from .second_quantization import DoubleFactorization
 from .measurement import estimate_error, estimate_shots
 from .specs import specs
+from .error import AlgorithmicError
diff --git a/pennylane/resource/error.py b/pennylane/resource/error.py
new file mode 100644
index 000000000..c2f9c1110
--- /dev/null
+++ b/pennylane/resource/error.py
@@ -0,0 +1,59 @@
+# Copyright 2018-2024 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.
+"""
+Stores classes and logic to define and track algorithmic error in a quantum workflow.
+"""
+from abc import ABC, abstractmethod
+
+
+class AlgorithmicError(ABC):
+    """Abstract base class representing an abstract type of error.
+    This class can be used to create objects that track and propagate errors introduced by approximations and other algorithmic inaccuracies.
+
+    Args:
+        error (float): The numerical value of the error
+
+    .. note::
+        Child classes must implement the :func:`~.AlgorithmicError.combine` method which combines two
+        instances of this error type (as if the associated gates were applied in series).
+    """
+
+    def __init__(self, error: float):
+        self.error = error
+
+    @abstractmethod
+    def combine(self, other):
+        """A method to combine two errors of the same type.
+        (e.g., additive, square additive, multiplicative, etc.)
+
+        Args:
+            other (AlgorithmicError): The other instance of error being combined.
+
+        Returns:
+            AlgorithmicError: The total error after combination.
+        """
+
+    @staticmethod
+    def get_error(approximate_op, exact_op, **kwargs):
+        """A method to allow users to compute this type of error between two operators.
+
+        Args:
+            approximate_op (.Operator): The approximate operator.
+            exact_op (.Operator): The exact operator.
+
+        Returns:
+            float: The error between the exact operator and its
+            approximation.
+        """
+        raise NotImplementedError


Test patch:
diff --git a/tests/resource/test_error.py b/tests/resource/test_error.py
new file mode 100644
index 000000000..0d19c481b
--- /dev/null
+++ b/tests/resource/test_error.py
@@ -0,0 +1,83 @@
+# Copyright 2018-2024 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.
+"""
+Test base AlgorithmicError class and its associated methods.
+"""
+import pytest
+
+import pennylane as qml
+from pennylane.resource.error import AlgorithmicError
+
+
+class SimpleError(AlgorithmicError):
+    def combine(self, other):
+        return self.__class__(self.error + other.error)
+
+    @staticmethod
+    def get_error(approx_op, other_op):  # pylint: disable=unused-argument
+        return 0.5  # get simple error is always 0.5
+
+
+class ErrorNoGetError(AlgorithmicError):  # pylint: disable=too-few-public-methods
+    def combine(self, other):
+        return self.__class__(self.error + other.error)
+
+
+class TestAlgorithmicError:
+    """Test the methods and attributes of the AlgorithmicError class"""
+
+    @pytest.mark.parametrize("error", [1.23, 0.45, -6])
+    def test_error_attribute(self, error):
+        """Test that instantiation works"""
+        ErrorObj = SimpleError(error)
+        assert ErrorObj.error == error
+
+    def test_combine_not_implemented(self):
+        """Test can't instantiate Error if the combine method is not defined."""
+
+        with pytest.raises(TypeError, match="Can't instantiate abstract class"):
+
+            class ErrorNoCombine(AlgorithmicError):  # pylint: disable=too-few-public-methods
+                @staticmethod
+                def get_error(approx_op, other_op):  # pylint: disable=unused-argument
+                    return 0.5  # get simple error is always 0.5
+
+            _ = ErrorNoCombine(1.23)
+
+    @pytest.mark.parametrize("err1", [1.23, 0.45, -6])
+    @pytest.mark.parametrize("err2", [1.23, 0.45, -6])
+    def test_combine(self, err1, err2):
+        """Test that combine works as expected"""
+        ErrorObj1 = SimpleError(err1)
+        ErrorObj2 = SimpleError(err2)
+
+        res = ErrorObj1.combine(ErrorObj2)
+        assert res.error == err1 + err2
+        assert isinstance(res, type(ErrorObj1))
+
+    def test_get_error_not_implemented(self):
+        """Test NotImplementedError is raised if the method is not defined."""
+        approx_op = qml.RZ(0.01, 0)
+        exact_op = qml.PauliZ(0)
+
+        with pytest.raises(NotImplementedError):
+            _ = ErrorNoGetError.get_error(approx_op, exact_op)
+
+    def test_get_error(self):
+        """Test that get_error works as expected"""
+        approx_op = qml.RZ(0.01, 0)
+        exact_op = qml.PauliZ(0)
+
+        res = SimpleError.get_error(approx_op, exact_op)
+        assert res == 0.5