Repository: PennyLaneAI/pennylane
Pull request #7773 (2025-07-02 20:48:28)

sc-92296-table-rendering-broken-in-latest-docs-due-to-sphinx-upgrade
## Ticket
- [sc-92296-table-rendering-broken-in-latest-docs-due-to-sphinx-upgrade](https://app.shortcut.com/xanaduai/iteration/93741?group_by=owner_ids&hide_subtasks=false)

## Summary
- Fix table styles to be consistent with stable version of the doc prior to sphix upgrade

## Description
- With the update to `docutils 0.18` and later tables now default to using the "auto" width algorithm. This means the column widths are determined by the content within the cells, which can result in tables that are not as wide as the available content area.
- Added configuration `docutils.conf` to fall back to previous styling for tables with full-width
- Also sphinx upgrade drops support for JQuery, so updated custom style implementation to be in JavaScript

## Testing
- Compare the tables on stable, latest and preview links below to confirm that preview and stable table styles match
- Latest Link - https://docs.pennylane.ai/en/latest/introduction/interfaces.html (broken)
- Stable Link - https://docs.pennylane.ai/en/stable/introduction/interfaces.html (looks nice)
- Preview Link: https://xanaduai-pennylane--7773.com.readthedocs.build/en/7773/introduction/interfaces.html (looks nice)

## Snaps

### Before
![Screenshot 2025-07-03 at 9 19 57 AM](https://github.com/user-attachments/assets/a496e70f-80d1-457c-9749-924083b79438)


### After
![Screenshot 2025-07-03 at 9 19 26 AM](https://github.com/user-attachments/assets/e588c939-168c-445a-b7d1-b32948646655)


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.

Patch:
diff --git a/doc/docutils.conf b/doc/docutils.conf
new file mode 100644
index 000000000..d22dcc571
--- /dev/null
+++ b/doc/docutils.conf
@@ -0,0 +1,2 @@
+[html writers]
+table-style: colwidths-grid
diff --git a/doc/introduction/dynamic_quantum_circuits.rst b/doc/introduction/dynamic_quantum_circuits.rst
index 5f69c541b..30f3ed7d8 100644
--- a/doc/introduction/dynamic_quantum_circuits.rst
+++ b/doc/introduction/dynamic_quantum_circuits.rst
@@ -241,13 +241,26 @@ scalings  with respect to the number of mid-circuit measurements (and shots) are
 
 .. raw:: html
 
-   <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script>
-     $(document).ready(function() {
-       $('.gr').parent().parent().addClass('gr-parent');
-       $('.or').parent().parent().addClass('or-parent');
-       $('.rd').parent().parent().addClass('rd-parent');
-     });
+    document.addEventListener('DOMContentLoaded', function() {
+        document.querySelectorAll('.gr').forEach(function(element) {
+            if (element.parentElement && element.parentElement.parentElement) {
+                element.parentElement.parentElement.classList.add('gr-parent');
+            }
+        });
+
+        document.querySelectorAll('.or').forEach(function(element) {
+            if (element.parentElement && element.parentElement.parentElement) {
+                element.parentElement.parentElement.classList.add('or-parent');
+            }
+        });
+
+        document.querySelectorAll('.rd').forEach(function(element) {
+            if (element.parentElement && element.parentElement.parentElement) {
+                element.parentElement.parentElement.classList.add('rd-parent');
+            }
+        });
+    });
    </script>
    <style>
        .gr-parent {background-color:#e1eba8}
diff --git a/doc/introduction/interfaces.rst b/doc/introduction/interfaces.rst
index f59789a57..4e9117290 100644
--- a/doc/introduction/interfaces.rst
+++ b/doc/introduction/interfaces.rst
@@ -397,12 +397,19 @@ Supported configurations
 
 .. raw:: html
 
-   <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script>
-     $(document).ready(function() {
-       $('.gr').parent().parent().addClass('gr-parent');
-       $('.rd').parent().parent().addClass('rd-parent');
-     });
+    document.addEventListener('DOMContentLoaded', function() {
+        document.querySelectorAll('.gr').forEach(function(element) {
+            if (element.parentElement && element.parentElement.parentElement) {
+                element.parentElement.parentElement.classList.add('gr-parent');
+            }
+        });
+        document.querySelectorAll('.rd').forEach(function(element) {
+            if (element.parentElement && element.parentElement.parentElement) {
+                element.parentElement.parentElement.classList.add('rd-parent');
+            }
+        });
+    });
    </script>
    <style>
        .gr-parent {background-color:#bbffbb}
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index a526659d1..a32d6e297 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -745,6 +745,14 @@
 
 * Updated documentation check to remove duplicate docstring references. [(#7453)](https://github.com/PennyLaneAI/pennylane/pull/7453)
 
+* Improved performance for `qml.clifford_t_decomposition` transform by introducing caching support and changed the
+  default basis set of `qml.ops.sk_decomposition` to `(H, S, T)`, resulting in shorter decomposition sequences.
+  [(#7454)](https://github.com/PennyLaneAI/pennylane/pull/7454)
+
+* The decomposition of `qml.BasisState` with capture and the graph-based decomposition system enabled is more efficient. 
+  Additionally, the resource params of `qml.BasisState` is simplified to the number of wires.
+  [(#7722)](https://github.com/PennyLaneAI/pennylane/pull/7722)
+
 <h3>Labs: a place for unified and rapid prototyping of research software 🧪</h3>
 
 * The imports of dependencies introduced by ``labs`` functionalities have been modified such that
@@ -1229,6 +1237,9 @@ may move operations across a `Snapshot`.
   off from the original tape by a global phase.
   [(#7619)](https://github.com/PennyLaneAI/pennylane/pull/7619)
 
+* Fixes a bug where an error is raised from the decomposition graph when the resource params of an operator contains lists.
+  [(#7722)](https://github.com/PennyLaneAI/pennylane/pull/7722)
+
 * Updated documentation for mid-circuit measurements using the Tree Traversal algorithm
   to reflect supported devices and usage in analytic simulations,
   in the :doc:`/introduction/dynamic_quantum_circuits` page.
diff --git a/pennylane/decomposition/resources.py b/pennylane/decomposition/resources.py
index a6666e1f8..3cce6ed37 100644
--- a/pennylane/decomposition/resources.py
+++ b/pennylane/decomposition/resources.py
@@ -157,7 +157,9 @@ def _make_hashable(d):
             sorted(((str(k), _make_hashable(v)) for k, v in d.items()), key=lambda x: x[0])
         )
     if hasattr(d, "tolist"):
-        return d.tolist()
+        d = d.tolist()
+    if isinstance(d, list):
+        return tuple(_make_hashable(v) for v in d)
     return d
 
 
diff --git a/pennylane/ops/qubit/state_preparation.py b/pennylane/ops/qubit/state_preparation.py
index b20c1694b..dc85d2fcd 100644
--- a/pennylane/ops/qubit/state_preparation.py
+++ b/pennylane/ops/qubit/state_preparation.py
@@ -75,11 +75,11 @@ class BasisState(StatePrepBase):
     [0.+0.j 0.+0.j 0.+0.j 1.+0.j]
     """
 
-    resource_keys = {"state", "wires"}
+    resource_keys = {"num_wires"}
 
     @property
     def resource_params(self) -> dict:
-        return {"state": self.parameters[0], "wires": self.wires}
+        return {"num_wires": len(self.wires)}
 
     def __init__(self, state, wires: WiresLike, id=None):
 
@@ -185,23 +185,32 @@ class BasisState(StatePrepBase):
         return math.convert_like(ket, prep_vals)
 
 
-def _basis_state_decomp_resources(state, wires):
-    if not qml.math.is_abstract(state):
-        return {qml.X: len([wire for wire, basis in zip(wires, state, strict=True) if basis == 1])}
-    return {qml.PhaseShift: 2 * len(wires), qml.RX: len(wires)}
+def _basis_state_decomp_resources(num_wires):
+    # Represent one of the X gates as an RX and a GlobalPhase because RX is
+    # used when jax-jit is enabled without capture/qjit.
+    return {qml.X: num_wires - 1 or num_wires, qml.RX: 1, qml.GlobalPhase: 1}
 
 
 @register_resources(_basis_state_decomp_resources)
 def _basis_state_decomp(state, wires, **__):
-    if not qml.math.is_abstract(state):
-        for wire, basis in zip(wires, state, strict=True):
-            if basis == 1:
-                qml.X(wire)
-    else:
+
+    if qml.math.is_abstract(state) and not (qml.capture.enabled() or qml.compiler.active()):
+        # This branch is for supporting jax-jit without capture/qjit.
+        global_phase = 0.0
         for wire, basis in zip(wires, state):
-            qml.PhaseShift(basis * np.pi / 2, wire)
-            qml.RX(basis * np.pi, wire)
-            qml.PhaseShift(basis * np.pi / 2, wire)
+            qml.RX(basis * np.pi, wires=wire)
+            global_phase += basis * np.pi / 2
+        qml.GlobalPhase(-global_phase)
+        return
+
+    def _X(w):
+        qml.X(w)
+
+    @qml.for_loop(0, len(wires), 1)
+    def _loop(i):
+        qml.cond(qml.math.allclose(state[i], 1), _X)(wires[i])
+
+    _loop()  # pylint: disable=no-value-for-parameter
 
 
 add_decomps(BasisState, _basis_state_decomp)
diff --git a/pennylane/templates/embeddings/basis.py b/pennylane/templates/embeddings/basis.py
index e254f85ac..9bc9075a6 100644
--- a/pennylane/templates/embeddings/basis.py
+++ b/pennylane/templates/embeddings/basis.py
@@ -16,7 +16,8 @@ Contains the BasisEmbedding template.
 """
 # pylint: disable-msg=too-many-branches,too-many-arguments,protected-access
 
-from pennylane.ops.qubit.state_preparation import BasisState
+from pennylane.decomposition import add_decomps
+from pennylane.ops.qubit.state_preparation import BasisState, _basis_state_decomp
 
 
 class BasisEmbedding(BasisState):
@@ -69,3 +70,6 @@ class BasisEmbedding(BasisState):
 
     def __init__(self, features, wires, id=None):
         super().__init__(features, wires=wires, id=id)
+
+
+add_decomps(BasisEmbedding, _basis_state_decomp)


Test patch:
diff --git a/tests/decomposition/test_resources.py b/tests/decomposition/test_resources.py
index ddd4e902a..1c91ae080 100644
--- a/tests/decomposition/test_resources.py
+++ b/tests/decomposition/test_resources.py
@@ -172,6 +172,20 @@ class TestCompressedResourceOp:
         )
         assert isinstance(hash(op), int)
 
+    def test_hash_list_params(self):
+        """Tests when the resource params contains a list."""
+
+        class CustomOp(qml.operation.Operator):  # pylint: disable=too-few-public-methods
+
+            resource_keys = {"foo", "bar"}
+
+            @property
+            def resource_params(self) -> dict:
+                return {"foo": [1, 2, 3], "bar": [1, 2, [3, 4, 5]]}
+
+        op = CompressedResourceOp(CustomOp, {"foo": [1, 2, 3], "bar": [1, 2, [3, 4, 5]]})
+        assert isinstance(hash(op), int)
+
     def test_same_params_same_hash(self):
         """Tests that two ops with the same params have the same hash."""
 
diff --git a/tests/ops/functions/conftest.py b/tests/ops/functions/conftest.py
index 8ef514f3e..b47c84497 100644
--- a/tests/ops/functions/conftest.py
+++ b/tests/ops/functions/conftest.py
@@ -40,7 +40,7 @@ def _trotterize_qfunc_dummy(time, theta, phi, wires, flip=False):
 _INSTANCES_TO_TEST = [
     (qml.sum(qml.PauliX(0), qml.PauliZ(0)), {}),
     (qml.sum(qml.X(0), qml.X(0), qml.Z(0), qml.Z(0)), {}),
-    (qml.BasisState([1], wires=[0]), {"skip_differentiation": True}),
+    (qml.BasisState([1], wires=[0]), {"skip_differentiation": True, "heuristic_resources": True}),
     (
         qml.ControlledQubitUnitary(np.eye(2), wires=[1, 0]),
         {"skip_differentiation": True, "heuristic_resources": True},
diff --git a/tests/ops/qubit/test_state_prep.py b/tests/ops/qubit/test_state_prep.py
index 413947306..d20ecf68c 100644
--- a/tests/ops/qubit/test_state_prep.py
+++ b/tests/ops/qubit/test_state_prep.py
@@ -40,7 +40,7 @@ def test_assert_valid():
     """Tests that BasisState operators are valid"""
 
     op = qml.BasisState(np.array([0, 1]), wires=[0, 1])
-    qml.ops.functions.assert_valid(op, skip_differentiation=True)
+    qml.ops.functions.assert_valid(op, skip_differentiation=True, heuristic_resources=True)
 
     def abstract_check(state):
         op = qml.BasisState(state, wires=[0, 1])
@@ -56,7 +56,7 @@ def test_assert_valid():
             for _op in tape.operations:
                 resource_rep = qml.resource_rep(type(_op), **_op.resource_params)
                 actual_gate_counts[resource_rep] += 1
-            assert gate_counts == actual_gate_counts
+            assert all(_op in gate_counts for _op in actual_gate_counts)
 
             # Tests that the decomposition produces the same matrix
             op_matrix = qml.matrix(op)
diff --git a/tests/templates/test_embeddings/test_basis.py b/tests/templates/test_embeddings/test_basis.py
index c552cc254..be37de9d3 100644
--- a/tests/templates/test_embeddings/test_basis.py
+++ b/tests/templates/test_embeddings/test_basis.py
@@ -25,7 +25,7 @@ def test_standard_validity():
     """Check the operation using the assert_valid function."""
     wires = qml.wires.Wires((0, 1, 2))
     op = qml.BasisEmbedding(features=np.array([1, 1, 1]), wires=wires)
-    qml.ops.functions.assert_valid(op, skip_differentiation=True)
+    qml.ops.functions.assert_valid(op, skip_differentiation=True, heuristic_resources=True)
 
 
 # pylint: disable=protected-access