Repository: PennyLaneAI/pennylane (python)

Issue:
Allow QNodes to support `diff_method=None` option
As discussed in #1271 , add the option for `diff_method=None` to QNodes. This would create QNodes that accept NumPy input, return NumPy output, but cannot be differentiated.

Pull request description:
[Code Together] Allow QNodes to support `diff_method=None` option (#1274)
------------------------------------------------------------------------------------------------------------

**Context:**
Adds `diff_method=None`
**Description of the Change:**
Now you can specify `diff_method=None`. This works the same as `interface=None`.
Internally this is all that is being done.
**Benefits:**
QNode supports `diff_method=None` . It is more natural than `interface=None`.
**Possible Drawbacks:**
None
**Related GitHub Issues:**
Closes #1274 

Patch:
diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index d018e6758..1d62a188f 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -384,6 +384,10 @@ and requirements-ci.txt (unpinned). This latter would be used by the CI.
 * The `qml.ResetError` is now supported for `default.mixed` device. 
   [(#1541)](https://github.com/PennyLaneAI/pennylane/pull/1541)
 
+* QNodes now support `diff_method=None`. This works the same as `interface=None`. Such QNodes accept
+  floats, ints, lists and numpy arrays and return numpy output but can not be differentiated.
+  [(#1585)](https://github.com/PennyLaneAI/pennylane/pull/1585)
+
 
 <h3>Breaking changes</h3>
 
@@ -424,8 +428,8 @@ and requirements-ci.txt (unpinned). This latter would be used by the CI.
 This release contains contributions from (in alphabetical order):
 
 Vishnu Ajith, Akash Narayanan B, Thomas Bromley, Tanya Garg, Josh Izaac, Prateek Jain, Christina Lee,
-Johannes Jakob Meyer, Romain Moyard, Esteban Payares, Pratul Saini, Maria Schuld, Arshpreet Singh,
-Ingrid Strandberg, Slimane Thabet, David Wierichs, Vincent Wong.
+Ankit Khandelwal, Johannes Jakob Meyer, Romain Moyard, Esteban Payares, Pratul Saini, Maria Schuld,
+Arshpreet Singh, Ingrid Strandberg, Slimane Thabet, David Wierichs, Vincent Wong.
 
 # Release 0.17.0 (current release)
 
diff --git a/pennylane/qnode.py b/pennylane/qnode.py
index 5537b3c7f..ab5d5c632 100644
--- a/pennylane/qnode.py
+++ b/pennylane/qnode.py
@@ -106,6 +106,8 @@ class QNode:
             * ``"finite-diff"``: Uses numerical finite-differences for all quantum operation
               arguments.
 
+            * ``None``: QNode cannot be differentiated. Works the same as ``interface=None``.
+
         mutable (bool): If True, the underlying quantum circuit is re-constructed with
             every evaluation. This is the recommended approach, as it allows the underlying
             quantum structure to depend on (potentially trainable) QNode input arguments,
@@ -155,6 +157,13 @@ class QNode:
         **diff_options,
     ):
 
+        if diff_method is None:
+            # TODO: update this behaviour once the new differentiable pipeline is the default
+            # We set "best" to allow internals to work and leverage the interface = None
+            # feature to restrict differentiability
+            diff_method = "best"
+            interface = None
+
         if interface is not None and interface not in self.INTERFACE_MAP:
             raise qml.QuantumFunctionError(
                 f"Unknown interface {interface}. Interface must be "


Test patch:
diff --git a/tests/tape/test_qnode.py b/tests/tape/test_qnode.py
index 4504236df..d9fcea9c5 100644
--- a/tests/tape/test_qnode.py
+++ b/tests/tape/test_qnode.py
@@ -330,6 +330,44 @@ class TestValidation:
             == "<QNode: wires=1, device='default.qubit.autograd', interface='autograd', diff_method='best'>"
         )
 
+    @pytest.mark.parametrize("par", [None, 1, 1.1, np.array(1.2)])
+    def test_diff_method_none(self, par):
+        """Test if diff_method=None works as intended."""
+        dev = qml.device("default.qubit", wires=1)
+
+        def func(x):
+            qml.RX(x, wires=0)
+            return qml.expval(qml.PauliZ(0))
+
+        qn = qml.QNode(func, dev, diff_method=None)
+        assert qn.interface is None
+
+        grad = qml.grad(qn)
+
+        # Raise error in all cases
+        # Case 1: No input
+        # Case 2: int input
+        # Case 3: float input
+        # Case 4: numpy input
+        with pytest.raises(TypeError) as exp:
+            grad() if par is None else grad(par)
+
+    def test_diff_method_none_no_qnode_param(self):
+        """Test if diff_method=None works as intended."""
+        dev = qml.device("default.qubit", wires=1)
+
+        def func():
+            qml.PauliX(wires=0)
+            return qml.expval(qml.PauliZ(0))
+
+        qn = qml.QNode(func, dev, diff_method=None)
+        assert qn.interface is None
+
+        grad = qml.grad(qn)
+
+        # No differentiation required. No error raised.
+        grad()
+
 
 class TestTapeConstruction:
     """Tests for the tape construction"""