Repository: PennyLaneAI/pennylane
Pull request #1170 (2021-03-25 22:17:11)

Add deprecation warning to qnn module
Following this PR https://github.com/PennyLaneAI/pennylane/pull/1162 and this issue https://github.com/PennyLaneAI/pennylane/issues/1101, we would like to prevent the `qnn` module from being imported with PL because it also causes TF and Torch to be imported. This is a breaking change for users of `KerasLayer` and `TorchLayer`, and we will provide a deprecation warning for one release cycle before making the change.

github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [.github/CHANGELOG.md](/PennyLaneAI/pennylane/blob/master/.github/CHANGELOG.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/1170?src=pr&el=h1) Report
> Merging [#1170](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170?src=pr&el=desc) (7091052) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/94e809840cda97dbf7464469b58859738df505a9?el=desc) (94e8098) will **decrease** coverage by `0.96%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1170      +/-   ##
==========================================
- Coverage   99.08%   98.11%   -0.97%     
==========================================
  Files         141      144       +3     
  Lines       10274    10784     +510     
==========================================
+ Hits        10180    10581     +401     
- Misses         94      203     +109     
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pennylane/qnn/cost.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree#diff-cGVubnlsYW5lL3Fubi9jb3N0LnB5) | `100.00% <100.00%> (ø)` | |
| [pennylane/qnn/keras.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree#diff-cGVubnlsYW5lL3Fubi9rZXJhcy5weQ==) | `97.05% <100.00%> (-2.95%)` | :arrow_down: |
| [pennylane/qnn/torch.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree#diff-cGVubnlsYW5lL3Fubi90b3JjaC5weQ==) | `96.87% <100.00%> (-3.13%)` | :arrow_down: |
| [pennylane/tape/operation\_recorder.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree#diff-cGVubnlsYW5lL3RhcGUvb3BlcmF0aW9uX3JlY29yZGVyLnB5) | `64.51% <0.00%> (-35.49%)` | :arrow_down: |
| [pennylane/measure.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree#diff-cGVubnlsYW5lL21lYXN1cmUucHk=) | `91.76% <0.00%> (-8.24%)` | :arrow_down: |
| [pennylane/circuit\_drawer/grid.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree#diff-cGVubnlsYW5lL2NpcmN1aXRfZHJhd2VyL2dyaWQucHk=) | `88.70% <0.00%> (-7.72%)` | :arrow_down: |
| [pennylane/grouping/optimize\_measurements.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree#diff-cGVubnlsYW5lL2dyb3VwaW5nL29wdGltaXplX21lYXN1cmVtZW50cy5weQ==) | `92.30% <0.00%> (-7.70%)` | :arrow_down: |
| [pennylane/configuration.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree#diff-cGVubnlsYW5lL2NvbmZpZ3VyYXRpb24ucHk=) | `93.10% <0.00%> (-6.90%)` | :arrow_down: |
| [pennylane/vqe/vqe.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree#diff-cGVubnlsYW5lL3ZxZS92cWUucHk=) | `93.85% <0.00%> (-6.15%)` | :arrow_down: |
| [pennylane/about.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree#diff-cGVubnlsYW5lL2Fib3V0LnB5) | `95.45% <0.00%> (-4.55%)` | :arrow_down: |
| ... and [26 more](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170?src=pr&el=footer). Last update [94e8098...7091052](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1170?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

trbromley: This is a case where I don't understand what's going on with `codecov` :disappointed: 
trbromley: The coverage checks seem to be failing because of the `__str__` method: 
![image](https://user-images.githubusercontent.com/49409390/114097323-df5a9b80-988d-11eb-95a3-8d11d6d0d3a7.png)

@josh146, if this is just a case of codecov being bad (I think we discussed this a while back in the context of https://github.com/PennyLaneAI/pennylane/pull/1162), would you mind merging in?

josh146: @trbromley seems to be working today 🤔 Just needed master to be merged in

![Uploading image.png…]()


Patch:
diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index 0599f7b9a..eb939c0a7 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -658,6 +658,11 @@
 
 <h3>Breaking changes</h3>
 
+* A deprecation warning is now raised when loading content from the `qnn` module. In release 
+  `0.16.0`, the `qnn` module will no-longer be automatically loaded due to its dependency on
+  TensorFlow and Torch. Instead, users will need to do `from pennylane import qnn`.
+  [(#1170)](https://github.com/PennyLaneAI/pennylane/pull/1170)
+
 * Devices do not have an `analytic` argument or attribute anymore. 
   Instead, `shots` is the source of truth for whether a simulator 
   estimates return values from a finite number of shots, or whether 
diff --git a/pennylane/qnn/cost.py b/pennylane/qnn/cost.py
index 6238064ef..2057ae8eb 100644
--- a/pennylane/qnn/cost.py
+++ b/pennylane/qnn/cost.py
@@ -15,8 +15,16 @@
 This submodule contains frequently used loss and cost functions.
 """
 # pylint: disable=too-many-arguments
+from warnings import warn
+
 import pennylane as qml
 
+WARNING_STRING = (
+    "SquaredErrorLoss will no longer be directly imported in PennyLane from "
+    "release 0.16.0. It will be accessible by importing the qnn module. Consider adding "
+    "'from pennylane import qnn' to your existing code now."
+)
+
 
 class SquaredErrorLoss:
     r"""Squared error loss function for circuits with trainable parameters.
@@ -95,6 +103,7 @@ class SquaredErrorLoss:
         diff_method="best",
         **kwargs,
     ):
+        warn(WARNING_STRING, DeprecationWarning, stacklevel=2)
         self.qnodes = qml.map(
             ansatz,
             observables,
diff --git a/pennylane/qnn/keras.py b/pennylane/qnn/keras.py
index f7b6f1086..7757bf4d2 100644
--- a/pennylane/qnn/keras.py
+++ b/pennylane/qnn/keras.py
@@ -16,6 +16,7 @@ API."""
 import inspect
 from collections.abc import Iterable
 from typing import Optional
+from warnings import warn
 
 try:
     import tensorflow as tf
@@ -30,6 +31,12 @@ except ImportError:
     Layer = ABC
     CORRECT_TF_VERSION = False
 
+WARNING_STRING = (
+    "KerasLayer will no longer be directly imported in PennyLane from "
+    "release 0.16.0. It will be accessible by importing the qnn module. Consider adding "
+    "'from pennylane import qnn' to your existing code now."
+)
+
 
 class KerasLayer(Layer):
     """Converts a :func:`~.QNode` to a Keras
@@ -205,6 +212,8 @@ class KerasLayer(Layer):
                 "https://www.tensorflow.org/install for detailed instructions."
             )
 
+        warn(WARNING_STRING, DeprecationWarning, stacklevel=2)
+
         self.weight_shapes = {
             weight: (tuple(size) if isinstance(size, Iterable) else (size,) if size > 1 else ())
             for weight, size in weight_shapes.items()
diff --git a/pennylane/qnn/torch.py b/pennylane/qnn/torch.py
index d031bfc2b..17bf65f8a 100644
--- a/pennylane/qnn/torch.py
+++ b/pennylane/qnn/torch.py
@@ -18,6 +18,7 @@ import inspect
 import math
 from collections.abc import Iterable
 from typing import Callable, Optional
+from warnings import warn
 
 try:
     import torch
@@ -32,6 +33,12 @@ except ImportError:
     Module = Mock
     TORCH_IMPORTED = False
 
+WARNING_STRING = (
+    "TorchLayer will no longer be directly imported in PennyLane from "
+    "release 0.16.0. It will be accessible by importing the qnn module. Consider adding "
+    "'from pennylane import qnn' to your existing code now."
+)
+
 
 class TorchLayer(Module):
     r"""Converts a :func:`~.QNode` to a Torch layer.
@@ -204,6 +211,9 @@ class TorchLayer(Module):
                 "visit https://pytorch.org/get-started/locally/ for detailed "
                 "instructions."
             )
+
+        warn(WARNING_STRING, DeprecationWarning, stacklevel=2)
+
         super().__init__()
 
         weight_shapes = {


Test patch:
diff --git a/tests/qnn/test_cost.py b/tests/qnn/test_cost.py
index 716dc5ef9..a29aae827 100644
--- a/tests/qnn/test_cost.py
+++ b/tests/qnn/test_cost.py
@@ -18,7 +18,7 @@ import numpy as np
 import pennylane as qml
 import pytest
 
-from pennylane.qnn.cost import SquaredErrorLoss
+from pennylane.qnn.cost import SquaredErrorLoss, WARNING_STRING
 
 
 ALLOWED_INTERFACES = ["tf", "jax", "autograd", "torch"]
@@ -91,3 +91,15 @@ class TestSquaredErrorLoss:
         res = loss(phis, target=np.array([1.0, 0.5, 0.1]))
 
         assert np.allclose(res, np.array([0.21, 0.25, 0.03]), atol=0.01, rtol=0.01)
+
+
+def test_deprecation_warning():
+    """Test if deprecation warning is raised"""
+    if int(qml.__version__.split(".")[1]) >= 16:
+        pytest.fail("Deprecation warnings for the qnn module should be removed")
+
+    dev = qml.device("default.qubit", wires=3)
+    observables = [qml.PauliZ(0), qml.PauliX(0), qml.PauliZ(1) @ qml.PauliZ(2)]
+
+    with pytest.warns(DeprecationWarning, match=WARNING_STRING):
+        SquaredErrorLoss(rx_ansatz, observables, dev)
diff --git a/tests/qnn/test_keras.py b/tests/qnn/test_keras.py
index cc4798e05..ee2baa40c 100644
--- a/tests/qnn/test_keras.py
+++ b/tests/qnn/test_keras.py
@@ -18,7 +18,7 @@ import numpy as np
 import pytest
 
 import pennylane as qml
-from pennylane.qnn.keras import KerasLayer
+from pennylane.qnn.keras import KerasLayer, WARNING_STRING
 
 tf = pytest.importorskip("tensorflow", minversion="2")
 
@@ -497,6 +497,16 @@ class TestKerasLayer:
         output_shape = layer.compute_output_shape(inputs_shape)
         assert output_shape.as_list() == [None, 1]
 
+    @pytest.mark.parametrize("n_qubits, output_dim", indices_up_to(1))
+    def test_deprecation_warning(self, get_circuit, output_dim):
+        """Test if deprecation warning is raised"""
+        if int(qml.__version__.split(".")[1]) >= 16:
+            pytest.fail("Deprecation warnings for the qnn module should be removed")
+
+        c, w = get_circuit
+        with pytest.warns(DeprecationWarning, match=WARNING_STRING):
+            KerasLayer(c, w, output_dim)
+
 
 @pytest.mark.parametrize("interface", ["autograd", "torch", "tf"])
 @pytest.mark.parametrize("n_qubits, output_dim", indices_up_to(1))
diff --git a/tests/qnn/test_qnn_torch.py b/tests/qnn/test_qnn_torch.py
index 126c9cf7d..a8298718d 100644
--- a/tests/qnn/test_qnn_torch.py
+++ b/tests/qnn/test_qnn_torch.py
@@ -20,7 +20,7 @@ import numpy as np
 import pytest
 
 import pennylane as qml
-from pennylane.qnn.torch import TorchLayer
+from pennylane.qnn.torch import TorchLayer, WARNING_STRING
 
 torch = pytest.importorskip("torch")
 
@@ -416,6 +416,16 @@ class TestTorchLayer:
             except Exception:
                 pytest.fail("Exception raised in torch CUDA backward")
 
+    @pytest.mark.parametrize("n_qubits, output_dim", indices_up_to(1))
+    def test_deprecation_warning(self, get_circuit):
+        """Test if deprecation warning is raised"""
+        if int(qml.__version__.split(".")[1]) >= 16:
+            pytest.fail("Deprecation warnings for the qnn module should be removed")
+
+        c, w = get_circuit
+        with pytest.warns(DeprecationWarning, match=WARNING_STRING):
+            TorchLayer(c, w)
+
 
 @pytest.mark.parametrize("interface", ["autograd", "torch", "tf"])
 @pytest.mark.parametrize("n_qubits, output_dim", indices_up_to(1))