Repository: PennyLaneAI/pennylane
Pull request #5950 (2024-07-04 18:44:37)

Update orbital order in qubit observable
**Context:**
The `fermionic_hamiltonian` function returns fermionic terms in a sorted order following the order of spin orbitals. The construction of the qubit operator in `qubit_observable` follows the order of fermionic terms and in very occasional edge cases gives a Hamiltonians with a non-trivial wire order, which might be misleading for some users. This PR sorts the order of the terms in the qubit operator according to the orbital order.

**Description of the Change:**
The `qubit_observable` function is updated to sort qubit terms according to the orbital order.

**Benefits:**
Molecular Hamiltonians will always have a "trivial" wire order.  

[sc-67935]


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/5950?dropdown=coverage&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:
> Project coverage is 99.66%. Comparing base [(`865af05`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/865af05d40a85c27acab00b548cd02440abc6201?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`6ff88e8`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/6ff88e855e754aafd205ffecc6197590a202f475?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> Report is 2 commits behind head on master.

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


```diff
@@            Coverage Diff             @@
##           master    #5950      +/-   ##
==========================================
- Coverage   99.67%   99.66%   -0.01%     
==========================================
  Files         426      426              
  Lines       40869    40578     -291     
==========================================
- Hits        40737    40444     -293     
- Misses        132      134       +2     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5950?dropdown=coverage&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/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index cf17ad442..c07f4a75c 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -52,6 +52,10 @@
 * Molecules and Hamiltonians can now be constructed for all the elements present in the periodic table.
   [(#5821)](https://github.com/PennyLaneAI/pennylane/pull/5821)
 
+* The `qubit_observable` function is modified to return an ascending wire order for molecular 
+  Hamiltonians.
+  [(#5950)](https://github.com/PennyLaneAI/pennylane/pull/5950)
+
 <h4>Community contributions 🥳</h4>
 
 * `DefaultQutritMixed` readout error has been added using parameters `readout_relaxation_probs` and 
@@ -152,6 +156,7 @@ Lillian M. A. Frederiksen,
 Pietropaolo Frisoni,
 Emiliano Godinez,
 Renke Huang,
+Soran Jahangiri,
 Christina Lee,
 Austin Huang,
 Christina Lee,
diff --git a/pennylane/qchem/observable_hf.py b/pennylane/qchem/observable_hf.py
index 91b100de5..276bfac91 100644
--- a/pennylane/qchem/observable_hf.py
+++ b/pennylane/qchem/observable_hf.py
@@ -20,6 +20,7 @@ import numpy as np
 import pennylane as qml
 from pennylane.fermi import FermiSentence, FermiWord
 from pennylane.operation import active_new_opmath
+from pennylane.pauli import PauliSentence
 from pennylane.pauli.utils import simplify
 
 
@@ -81,12 +82,9 @@ def fermionic_observable(constant, one=None, two=None, cutoff=1.0e-12):
         coeffs = qml.math.concatenate((coeffs, coeffs_two))
         operators = operators + operators_two
 
-    indices_sort = [operators.index(i) for i in sorted(operators)]
-    if indices_sort:
-        indices_sort = qml.math.array(indices_sort)
-
     sentence = FermiSentence({FermiWord({}): constant[0]})
-    for c, o in zip(coeffs[indices_sort], sorted(operators)):
+    for c, o in sorted(zip(coeffs, operators), key=lambda item: item[1]):
+
         if len(o) == 2:
             sentence.update({FermiWord({(0, o[0]): "+", (1, o[1]): "-"}): c})
         if len(o) == 4:
@@ -145,6 +143,11 @@ def qubit_observable(o_ferm, cutoff=1.0e-12, mapping="jordan_wigner"):
         qubits = len(o_ferm.wires)
         h = qml.bravyi_kitaev(o_ferm, qubits, ps=True, tol=cutoff)
 
+    if list(h.wires) != sorted(list(h.wires)):
+        h = PauliSentence(
+            sorted(h.items(), key=lambda item: max(item[0].wires.tolist(), default=0))
+        )
+
     h.simplify(tol=cutoff)
 
     if active_new_opmath():


Test patch:
diff --git a/tests/qchem/test_hamiltonians.py b/tests/qchem/test_hamiltonians.py
index 1f71a8151..26c640b3f 100644
--- a/tests/qchem/test_hamiltonians.py
+++ b/tests/qchem/test_hamiltonians.py
@@ -267,6 +267,36 @@ def test_diff_hamiltonian_active_space():
     assert isinstance(h, qml.ops.Sum if active_new_opmath() else qml.Hamiltonian)
 
 
+@pytest.mark.parametrize(
+    ("symbols", "geometry", "core", "active", "charge"),
+    [
+        (
+            ["H", "H"],
+            np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 2.0]]),
+            None,
+            None,
+            0,
+        ),
+        (
+            ["H", "H", "H"],
+            np.array([[0.0, 0.0, 0.0], [2.0, 0.0, 1.0], [0.0, 2.0, 0.0]]),
+            [0],
+            [1, 2],
+            1,
+        ),
+    ],
+)
+def test_diff_hamiltonian_wire_order(symbols, geometry, core, active, charge):
+    r"""Test that diff_hamiltonian has an ascending wire order."""
+
+    mol = qchem.Molecule(symbols, geometry, charge)
+    args = [geometry]
+
+    h = qchem.diff_hamiltonian(mol, core=core, active=active)(*args)
+
+    assert h.wires.tolist() == sorted(h.wires.tolist())
+
+
 def test_gradient_expvalH():
     r"""Test that the gradient of expval(H) computed with ``qml.grad`` is equal to the value
     obtained with the finite difference method."""