Repository: PennyLaneAI/pennylane
Pull request #3781 (2023-02-16 16:13:56)

Fold global with non-integer scale factors
Potentially easy fix for https://github.com/PennyLaneAI/pennylane/issues/3772
Had issues for local tests independent of this fix, trying here with CI

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.
codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3781?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#3781](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3781?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (865fcf6) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/d81e1a8a2bc26ed83c304ac641037f44592c2723?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (d81e1a8) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master    #3781   +/-   ##
=======================================
  Coverage   99.80%   99.80%           
=======================================
  Files         333      333           
  Lines       29264    29264           
=======================================
  Hits        29207    29207           
  Misses         57       57           
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3781?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/transforms/mitigate.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3781?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3RyYW5zZm9ybXMvbWl0aWdhdGUucHk=) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

Qottmann: Current failures are due to jax upgrade to 0.4.4 see https://github.com/PennyLaneAI/pennylane/pull/3784

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index e3c2048c3..1063830d2 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -619,9 +619,12 @@
 * `SProd.sparse_matrix` now supports interface-specific variables with a single element as the `scalar`.
   [(#3770)](https://github.com/PennyLaneAI/pennylane/pull/3770)
 
- * The `qchem.Molecule` class raises an error when the molecule has an odd number of electrons or 
-   when the spin multiplicity is not 1.
-   [(#3748)](https://github.com/PennyLaneAI/pennylane/pull/3748)
+* The `qchem.Molecule` class raises an error when the molecule has an odd number of electrons or 
+  when the spin multiplicity is not 1.
+  [(#3748)](https://github.com/PennyLaneAI/pennylane/pull/3748)
+
+* `transforms.fold_global` now supports non-integer values.
+  [(#3781)](https://github.com/PennyLaneAI/pennylane/pull/3781)
 
 <h3>Breaking changes</h3>
 
@@ -817,6 +820,7 @@ Isaac De Vlugt
 Olivia Di Matteo
 Lillian M. A. Frederiksen
 Soran Jahangiri
+Korbinian Kottmann
 Christina Lee
 Albert Mitjans Coma
 Romain Moyard
diff --git a/pennylane/transforms/mitigate.py b/pennylane/transforms/mitigate.py
index fa65cb530..00fa597b8 100644
--- a/pennylane/transforms/mitigate.py
+++ b/pennylane/transforms/mitigate.py
@@ -33,7 +33,7 @@ def fold_global(circuit, scale_factor):
 
     .. math:: \text{fold_global}(U) = U (U^\dagger U)^n (L^\dagger_d L^\dagger_{d-1} .. L^\dagger_s) (L_s .. L_d)
 
-    where :math:`n = \lfloor (\lambda - 1)/2 \rfloor` and :math:`s = \lfloor \left((\lambda -1) \mod 2 \right) (d/2) \rfloor` are determined via the ``scale_factor`` :math:`=\lambda`.
+    where :math:`n = \lfloor (\lambda - 1)/2 \rfloor` and :math:`s = \lfloor \left(\lambda - 1 \right) (d/2) \rfloor` are determined via the ``scale_factor`` :math:`=\lambda`.
     The purpose of folding is to artificially increase the noise for zero noise extrapolation, see :func:`~.pennylane.transforms.mitigate_with_zne`.
 
     Args:
@@ -190,7 +190,7 @@ def _divmod(a, b):
     """Performs divmod but in an all-interface compatible manner"""
     out1 = qml.math.floor(a / b)
     out2 = a - out1 * b
-    return int(out1), int(out2)
+    return int(out1), out2
 
 
 def fold_global_tape(circuit, scale_factor):


Test patch:
diff --git a/tests/transforms/test_mitigate.py b/tests/transforms/test_mitigate.py
index 6b81bb71d..853ec12aa 100644
--- a/tests/transforms/test_mitigate.py
+++ b/tests/transforms/test_mitigate.py
@@ -363,6 +363,23 @@ grad_ideal_0 = [-np.sqrt(2) / 2, -np.sqrt(2)]
 class TestDifferentiableZNE:
     """Testing differentiable ZNE"""
 
+    @pytest.mark.parametrize("lambda_", [1.0, 1.5, 1.7, 2.0, 2.5])
+    @pytest.mark.parametrize("num_ops", [4, 8, 9, 12, 16])
+    def test_correct_number_of_operators(self, lambda_, num_ops):
+        """Test the output corresponds to the right number of operators according to Sec. II A 1) in  https://arxiv.org/abs/2005.10921"""
+        x = 0.5
+        circuit = qml.tape.QuantumScript([qml.RX(x, wires=0) for i in range(num_ops)])
+        assert len(circuit._ops) == num_ops
+
+        folded, _ = qml.transforms.fold_global(circuit, lambda_)
+
+        n, s = divmod(lambda_ - 1, 2)
+
+        s = int(round(s * num_ops / 2))
+
+        exp_total_new = num_ops * (2 * n + 1) + 2 * s
+        assert len(folded[0]._ops) == exp_total_new
+
     def test_global_fold_constant_result(self):
         """Ensuring that the folded circuits always yields the same results."""
 
@@ -385,7 +402,8 @@ class TestDifferentiableZNE:
             return qml.expval(qml.PauliZ(0))
 
         res = [
-            fold_global(circuit, scale_factor=scale_factor)(w1, w2) for scale_factor in range(1, 5)
+            fold_global(circuit, scale_factor=scale_factor)(w1, w2)
+            for scale_factor in [1, 1.5, 2.0, 2.5, 3]
         ]
         # res = [qml.execute([folded], dev, None) for folded in folded_qnodes]
         assert np.allclose(res, 1)