Repository: PennyLaneAI/pennylane
Pull request #1837 (2021-11-02 16:53:02)

Merge rc to master
Merge the following commits from the rc branch to master:
* https://github.com/PennyLaneAI/pennylane/commit/e768d39b1a4c433a875840c0eacc09697df187f3
* https://github.com/PennyLaneAI/pennylane/commit/b9fc6d4b1e94c521270106470434769074c29fc3

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/1837?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#1837](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1837?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (b8dcb63) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/48aa626c6f701d272e54be5b12144f5cc9cf50d1?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (48aa626) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1837/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1837?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

```diff
@@           Coverage Diff           @@
##           master    #1837   +/-   ##
=======================================
  Coverage   98.96%   98.96%           
=======================================
  Files         218      218           
  Lines       16364    16372    +8     
=======================================
+ Hits        16195    16203    +8     
  Misses        169      169           
```


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

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1837?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1837?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Last update [48aa626...b8dcb63](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1837?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).


Patch:
diff --git a/doc/releases/changelog-0.19.0.md b/doc/releases/changelog-0.19.0.md
index 2e36beddf..91fcd05bb 100644
--- a/doc/releases/changelog-0.19.0.md
+++ b/doc/releases/changelog-0.19.0.md
@@ -625,6 +625,10 @@
 
 <h3>Improvements</h3>
 
+* Updated the `qml.QNGOptimizer.step_and_cost` method to avoid the use of
+  deprecated functionality.
+  [(#1834)](https://github.com/PennyLaneAI/pennylane/pull/1834)
+
 * The default for an `Operation`'s `control_wires` attribute is now an empty `Wires`
   object instead of the attribute raising a `NonImplementedError`.
   [(#1821)](https://github.com/PennyLaneAI/pennylane/pull/1821)
@@ -891,12 +895,6 @@
 
   If `hybrid=False`, the changed expansion rule might lead to a changed output.
 
-* The `qml.metric_tensor` keyword argument `diag_approx` is deprecated.
-  Approximations can be controlled with the more fine-grained `approx`
-  keyword argument, with `approx="block-diag"` (the default) reproducing
-  the old behaviour.
-  [(#1721)](https://github.com/PennyLaneAI/pennylane/pull/1721)
-
 * The `default.qubit.torch` device automatically determines if computations
   should be run on a CPU or a GPU and doesn't take a `torch_device` argument
   anymore.
@@ -923,6 +921,14 @@
 
 <h3>Deprecations</h3>
 
+* The `qml.metric_tensor` and `qml.QNGOptimizer` keyword argument `diag_approx`
+  is deprecated.
+  Approximations can be controlled with the more fine-grained `approx` keyword
+  argument, with `approx="block-diag"` (the default) reproducing the old
+  behaviour.
+  [(#1721)](https://github.com/PennyLaneAI/pennylane/pull/1721)
+  [(#1834)](https://github.com/PennyLaneAI/pennylane/pull/1834)
+
 * The `template` decorator is now deprecated with a warning message and will be removed
   in release `v0.20.0`. It has been removed from different PennyLane functions.
   [(#1794)](https://github.com/PennyLaneAI/pennylane/pull/1794)
@@ -997,6 +1003,22 @@
   which can then be generated manually.
   [(#1689)](https://github.com/PennyLaneAI/pennylane/pull/1689)
 
+  To generate the parameter tensors, the `np.random.normal` and
+  `np.random.uniform` functions can be used (just like in the `init` module).
+  Considering the default arguments of these functions as of NumPy v1.21, some
+  non-default options were used by the `init` module:
+
+  * All functions generating normally distributed parameters used
+    `np.random.normal` by passing `scale=0.1`;
+
+  * Most functions generating uniformly distributed parameters (except for
+    certain CVQNN initializers) used `np.random.uniform` by passing
+    `high=2*math.pi`;
+
+  * The `cvqnn_layers_r_uniform`, `cvqnn_layers_a_uniform`,
+    `cvqnn_layers_kappa_uniform` functions used `np.random.uniform` by passing
+    `high=0.1`.
+
 * The `QNode.draw` method has been deprecated, and will be removed in an upcoming release.
   Please use the `qml.draw` transform instead.
   [(#1746)](https://github.com/PennyLaneAI/pennylane/pull/1746)
diff --git a/pennylane/optimize/qng.py b/pennylane/optimize/qng.py
index 17a46e309..5f9f85fd0 100644
--- a/pennylane/optimize/qng.py
+++ b/pennylane/optimize/qng.py
@@ -14,8 +14,9 @@
 """Quantum natural gradient optimizer"""
 # pylint: disable=too-many-branches
 # pylint: disable=too-many-arguments
+import warnings
 
-import numpy as np
+from pennylane import numpy as np
 
 import pennylane as qml
 from pennylane.utils import _flatten, unflatten
@@ -150,9 +151,23 @@ class QNGOptimizer(GradientDescentOptimizer):
             to be applied at each optimization step
     """
 
-    def __init__(self, stepsize=0.01, diag_approx=False, lam=0):
+    def __init__(self, stepsize=0.01, approx="block-diag", diag_approx=None, lam=0):
         super().__init__(stepsize)
-        self.diag_approx = diag_approx
+
+        approx_set = False
+        if diag_approx is not None:
+
+            warnings.warn(
+                "The keyword argument diag_approx is deprecated. Please use approx='diag' instead.",
+                UserWarning,
+            )
+            if diag_approx:
+                self.approx = "diag"
+                approx_set = True
+
+        if not approx_set:
+            self.approx = approx
+
         self.metric_tensor = None
         self.lam = lam
 
@@ -192,13 +207,14 @@ class QNGOptimizer(GradientDescentOptimizer):
 
         if recompute_tensor or self.metric_tensor is None:
             if metric_tensor_fn is None:
-                metric_tensor_fn = qml.metric_tensor(qnode, diag_approx=self.diag_approx)
+
+                metric_tensor_fn = qml.metric_tensor(qnode, approx=self.approx)
 
             self.metric_tensor = metric_tensor_fn(*args, **kwargs)
             self.metric_tensor += self.lam * np.identity(self.metric_tensor.shape[0])
 
         g, forward = self.compute_grad(qnode, args, kwargs, grad_fn=grad_fn)
-        new_args = self.apply_grad(g, args)
+        new_args = np.array(self.apply_grad(g, args), requires_grad=True)
 
         if forward is None:
             forward = qnode(*args, **kwargs)


Test patch:
diff --git a/tests/optimize/test_qng.py b/tests/optimize/test_qng.py
index 345e6cdd6..253cb7866 100644
--- a/tests/optimize/test_qng.py
+++ b/tests/optimize/test_qng.py
@@ -1,4 +1,4 @@
-# Copyright 2018-2020 Xanadu Quantum Technologies Inc.
+# Copyright 2018-2021 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.
@@ -14,6 +14,7 @@
 """Tests for the QNG optimizer"""
 import pytest
 import scipy as sp
+import warnings
 
 import pennylane as qml
 from pennylane import numpy as np
@@ -228,7 +229,7 @@ class TestOptimize:
         # check final cost
         assert np.allclose(cost_fn(theta), -1.41421356, atol=tol, rtol=0)
 
-    def test_single_qubit_vqe_using_vqecost(self, tol):
+    def test_single_qubit_vqe_using_vqecost(self, tol, recwarn):
         """Test single-qubit VQE using ExpvalCost
         has the correct QNG value every step, the correct parameter updates,
         and correct cost after 200 steps"""
@@ -252,7 +253,7 @@ class TestOptimize:
             return np.array([da, db])
 
         eta = 0.01
-        init_params = np.array([0.011, 0.012])
+        init_params = np.array([0.011, 0.012], requires_grad=True)
         num_steps = 200
 
         opt = qml.QNGOptimizer(eta)
@@ -275,3 +276,65 @@ class TestOptimize:
 
         # check final cost
         assert np.allclose(cost_fn(theta), -1.41421356, atol=tol, rtol=0)
+        assert len(recwarn) == 0
+
+    def test_single_qubit_vqe_using_expval_h_multiple_input_params(self, tol, recwarn):
+        """Test single-qubit VQE by returning qml.expval(H) in the QNode and
+        check for the correct QNG value every step, the correct parameter updates, and
+        correct cost after 200 steps"""
+        dev = qml.device("default.qubit", wires=1)
+        coeffs = [1, 1]
+        obs_list = [qml.PauliX(0), qml.PauliZ(0)]
+
+        H = qml.Hamiltonian(coeffs=coeffs, observables=obs_list)
+
+        @qml.qnode(dev)
+        def circuit(x, y, wires=0):
+            qml.RX(x, wires=wires)
+            qml.RY(y, wires=wires)
+            return qml.expval(H)
+
+        eta = 0.01
+        x = np.array(0.011, requires_grad=True)
+        y = np.array(0.022, requires_grad=True)
+
+        def gradient(params):
+            """Returns the gradient"""
+            da = -np.sin(params[0]) * (np.cos(params[1]) + np.sin(params[1]))
+            db = np.cos(params[0]) * (np.cos(params[1]) - np.sin(params[1]))
+            return np.array([da, db])
+
+        eta = 0.01
+        num_steps = 200
+
+        opt = qml.QNGOptimizer(eta)
+
+        # optimization for 200 steps total
+        for t in range(num_steps):
+            theta = np.array([x, y])
+            x, y = opt.step(circuit, x, y)
+
+            # check metric tensor
+            res = opt.metric_tensor
+            exp = np.diag([0.25, (np.cos(x) ** 2) / 4])
+            assert np.allclose(res, exp, atol=0.00001, rtol=0)
+
+            # check parameter update
+            theta_new = np.array([x, y])
+            dtheta = eta * sp.linalg.pinvh(exp) @ gradient(theta)
+            assert np.allclose(dtheta, theta - theta_new, atol=0.000001, rtol=0)
+
+        # check final cost
+        assert np.allclose(circuit(x, y), -1.41421356, atol=tol, rtol=0)
+        assert len(recwarn) == 0
+
+    @pytest.mark.parametrize(
+        "diag_approx, approx_expected", [(True, "diag"), (False, "block-diag")]
+    )
+    def test_deprecate_diag_approx(self, diag_approx, approx_expected):
+        """Test that using the diag_approx argument raises a warning due to
+        deprecation."""
+        with pytest.warns(UserWarning, match="keyword argument diag_approx is deprecated"):
+            opt = qml.QNGOptimizer(0.1, diag_approx=True)
+
+        assert opt.approx == "diag"