Repository: PennyLaneAI/pennylane
Pull request #5039 (2024-01-10 22:14:38)

deprecate qml.matrix with wire_order=None
**Context:**
Wire-ordering can surprise people because PennyLane gives no promises. For example:
```pycon
>>> qs = qml.tape.QuantumScript([qml.Identity(1), qml.CNOT([0, 1])])
>>> qml.matrix(qs)
array([[1., 0., 0., 0.],
       [0., 0., 0., 1.],
       [0., 0., 1., 0.],
       [0., 1., 0., 0.]])
```
This is in wire-order `[1, 0]` because that's the order in which they appeared in the tape. Most users would expect the other way.

**Description of the Change:**
Deprecate the default `wire_order=None` value in `qml.matrix` in certain cases. The warning is raised if:
- the object is a tape with more than one wire
- the object is a QNode and the device does not provide wires
- the object is a qfunc

**Benefits:**
Users won't be surprised when they get a matrix in an unexpected wire order

**Possible Drawbacks:**
More deprecations, the user has to enter wire_order even though it's often safe to assume they meant ascending integers (but not often enough, so here we are)

**Related GitHub Issues:**
It was discussed in #3131

[sc-51277]

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://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5039?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 [(`919b6b3`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/919b6b34145c6949f9785fd816dc9036f71572e7?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.67% compared to head [(`4deaf11`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5039?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.66%.


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


```diff
@@            Coverage Diff             @@
##           master    #5039      +/-   ##
==========================================
- Coverage   99.67%   99.66%   -0.02%     
==========================================
  Files         394      394              
  Lines       35590    35325     -265     
==========================================
- Hits        35474    35206     -268     
- Misses        116      119       +3     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5039?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/development/deprecations.rst b/doc/development/deprecations.rst
index 2767e369a..4009c8eb4 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -9,6 +9,13 @@ deprecations are listed below.
 Pending deprecations
 --------------------
 
+* Calling ``qml.matrix`` without providing a ``wire_order`` on objects where the wire order could be
+  ambiguous now raises a warning. This includes tapes with multiple wires, QNodes with a device that
+  does not provide wires, or quantum functions.
+
+  - Deprecated in v0.35
+  - Will raise an error in v0.36
+
 * ``MeasurementProcess.name`` and ``MeasurementProcess.data`` have been deprecated, as they contain
   dummy values that are no longer needed.
 
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 84b7b11eb..a6d375140 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -69,6 +69,11 @@
   values that are no longer needed.
   [(#5047)](https://github.com/PennyLaneAI/pennylane/pull/5047)
 
+* Calling `qml.matrix` without providing a `wire_order` on objects where the wire order could be
+  ambiguous now raises a warning. In the future, the `wire_order` argument will be required in
+  these cases.
+  [(#5039)](https://github.com/PennyLaneAI/pennylane/pull/5039)
+
 <h3>Documentation 📝</h3>
 
 * A typo in a code example in the `qml.transforms` API has been fixed.
diff --git a/pennylane/ops/functions/matrix.py b/pennylane/ops/functions/matrix.py
index 04adb3160..3a8ca2600 100644
--- a/pennylane/ops/functions/matrix.py
+++ b/pennylane/ops/functions/matrix.py
@@ -17,12 +17,20 @@ This module contains the qml.matrix function.
 # pylint: disable=protected-access
 from typing import Sequence, Callable
 from functools import partial
+from warnings import warn
 
 import pennylane as qml
 from pennylane.transforms.op_transforms import OperationTransformError
 from pennylane import transform
 from pennylane.typing import TensorLike
 
+_wire_order_none_warning = (
+    "Calling qml.matrix() on a QuantumTape, quantum functions, or certain QNodes without "
+    "specifying a value for wire_order is deprecated. Please provide a wire_order value to avoid "
+    "future issues. Note that a wire order is not required for QNodes with devices that have "
+    "wires specified, because the wire order is taken from the device."
+)
+
 
 def matrix(op: qml.operation.Operator, wire_order=None) -> TensorLike:
     r"""The matrix representation of an operation or quantum circuit.
@@ -168,7 +176,16 @@ def matrix(op: qml.operation.Operator, wire_order=None) -> TensorLike:
 
     """
     if not isinstance(op, qml.operation.Operator):
-        if not isinstance(op, (qml.tape.QuantumScript, qml.QNode)) and not callable(op):
+        if isinstance(op, qml.tape.QuantumScript):
+            if wire_order is None and len(op.wires) > 1:
+                warn(_wire_order_none_warning, qml.PennyLaneDeprecationWarning)
+        elif isinstance(op, qml.QNode):
+            if wire_order is None and op.device.wires is None:
+                warn(_wire_order_none_warning, qml.PennyLaneDeprecationWarning)
+        elif callable(op):
+            if wire_order is None:
+                warn(_wire_order_none_warning, qml.PennyLaneDeprecationWarning)
+        else:
             raise OperationTransformError(
                 "Input is not an Operator, tape, QNode, or quantum function"
             )


Test patch:
diff --git a/tests/ops/functions/test_matrix.py b/tests/ops/functions/test_matrix.py
index b353b4046..511f5d0da 100644
--- a/tests/ops/functions/test_matrix.py
+++ b/tests/ops/functions/test_matrix.py
@@ -16,6 +16,7 @@ Unit tests for the get_unitary_matrix transform
 """
 # pylint: disable=too-few-public-methods,too-many-function-args
 from functools import reduce, partial
+from warnings import catch_warnings
 
 import pytest
 
@@ -737,6 +738,52 @@ class TestMeasurements:
         assert np.array_equal(qml.matrix(qscript), np.eye(N))
 
 
+class TestWireOrderDeprecation:
+    """Test that wire_order=None is deprecated for the qml.matrix transform."""
+
+    def test_warning_tape(self):
+        """Test that a warning is raised when calling qml.matrix without wire_order on a tape."""
+        qs = qml.tape.QuantumScript([qml.PauliX(1), qml.PauliX(0)])
+        with pytest.warns(qml.PennyLaneDeprecationWarning, match=r"Calling qml\.matrix\(\) on"):
+            _ = qml.matrix(qs)
+
+    def test_warning_qnode(self):
+        """Test that a warning is raised when calling qml.matrix without wire_order on a QNode."""
+
+        @qml.qnode(qml.device("default.qubit"))  # devices does not provide wire_order
+        def circuit():
+            qml.PauliX(0)
+            return qml.state()
+
+        with pytest.warns(qml.PennyLaneDeprecationWarning, match=r"Calling qml\.matrix\(\) on"):
+            _ = qml.matrix(circuit)
+
+    def test_warning_qfunc(self):
+        """Test that a warning is raised when calling qml.matrix without wire_order on a qfunc."""
+
+        def circuit():
+            qml.PauliX(0)
+
+        with pytest.warns(qml.PennyLaneDeprecationWarning, match=r"Calling qml\.matrix\(\) on"):
+            _ = qml.matrix(circuit)
+
+    def test_no_warning_cases(self):
+        """Test that a warning is not raised when calling qml.matrix on an operator, a
+        single-wire tape, or a QNode with a device that provides wires."""
+
+        @qml.qnode(qml.device("default.qubit", wires=2))  # device provides wire_order
+        def circuit():
+            qml.PauliX(0)
+            return qml.state()
+
+        with catch_warnings(record=True) as record:
+            qml.matrix(qml.PauliX(0))
+            qml.matrix(qml.tape.QuantumScript([qml.PauliX(1)]))
+            qml.matrix(circuit)
+
+        assert len(record) == 0
+
+
 @pytest.mark.jax
 def test_jitting_matrix():
     """Test that qml.matrix is jittable with jax."""