Skip to content

Commit

Permalink
Update condition for swapping iterators in PauliWord._matmul (#5301)
Browse files Browse the repository at this point in the history
**Context:**
We found that the wire order in `Sum/Prod.terms` was being swapped for
the first and second term in terms that were `Prod`s. This was due to
the use of the `pauli_rep`. While building the pauli rep, we use
`PauliWord._matmul` which swaps the order of `self` and `other` if
`len(other) >= len(self)` so that we iterate through the larger
`PauliWord`.

Example:
```python
H = qml.prod(X(0), X(1), X(2))
Sum_coeffs, Sum_ops = H.terms()
```
```pycon
>>> Sum_ops[0].wires, H.wires
(<Wires = [1, 0, 2]>, <Wires = [0, 1, 2]>)
```

**Description of the Change:**
Changed the condition for swapping from `len(other) >= len(self)` to
`len(other) > len(self)`.

**Benefits:**
More consistent `Sum/Prod.pauli_rep`, and `Sum/Prod.terms()` with the
original operands. The above example now behaves as follows:
```python
H = qml.prod(X(0), X(1), X(2))
Sum_coeffs, Sum_ops = H.terms()
```
```pycon
>>> Sum_ops[0].wires, H.wires
(<Wires = [0, 1, 2]>, <Wires = [0, 1, 2]>)
```

**Possible Drawbacks:**

**Related GitHub Issues:**

---------

Co-authored-by: qottmann <korbinian.kottmann@gmail.com>
  • Loading branch information
mudit2812 and Qottmann committed Apr 23, 2024
1 parent 9dd983f commit 1476cd2
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 23 deletions.
2 changes: 1 addition & 1 deletion pennylane/pauli/pauli_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ def __hash__(self):
def _matmul(self, other):
"""Private matrix multiplication that returns (pauli_word, coeff) tuple for more lightweight processing"""
base, iterator, swapped = (
(self, other, False) if len(self) > len(other) else (other, self, True)
(self, other, False) if len(self) >= len(other) else (other, self, True)
)
result = copy(dict(base))
coeff = 1
Expand Down
6 changes: 3 additions & 3 deletions tests/circuit_graph/test_qasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ def test_to_ApproxTimeEvolution(self):
include "qelib1.inc";
qreg q[2];
creg c[2];
cx q[0],q[1];
rz(2.0) q[1];
cx q[0],q[1];
cx q[1],q[0];
rz(2.0) q[0];
cx q[1],q[0];
measure q[0] -> c[0];
measure q[1] -> c[1];
"""
Expand Down
15 changes: 12 additions & 3 deletions tests/ops/op_math/test_prod.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,15 @@ def test_terms_pauli_rep(self, op, coeffs_true, ops_true):
assert coeffs == coeffs_true
assert ops1 == ops_true

def test_terms_pauli_rep_wire_order(self):
"""Test that the wire order of the terms is the same as the wire order of the original
operands when the Prod has a valid pauli_rep"""
H = qml.prod(X(0), X(1), X(2))
_, H_ops = H.terms()

assert len(H_ops) == 1
assert H_ops[0].wires == H.wires

def test_batch_size(self):
"""Test that batch size returns the batch size of a base operation if it is batched."""
x = qml.numpy.array([1.0, 2.0, 3.0])
Expand Down Expand Up @@ -1035,8 +1044,8 @@ def test_pauli_rep_order(self):
"""
op = qml.prod(qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2))
pw = list(op.pauli_rep.keys())[0]
assert list(pw.keys()) == [1, 0, 2]
assert list(pw.values()) == ["Y", "X", "Z"]
assert list(pw.keys()) == [0, 1, 2]
assert list(pw.values()) == ["X", "Y", "Z"]

@pytest.mark.parametrize("op, rep", op_pauli_reps)
def test_pauli_rep(self, op, rep):
Expand Down Expand Up @@ -1119,7 +1128,7 @@ def test_simplify_method_product_of_sums(self):
"""Test the simplify method with a product of sums."""
prod_op = Prod(qml.PauliX(0) + qml.RX(1, 0), qml.PauliX(1) + qml.RX(1, 1), qml.Identity(3))
final_op = qml.sum(
Prod(qml.PauliX(1), qml.PauliX(0)),
Prod(qml.PauliX(0), qml.PauliX(1)),
qml.PauliX(0) @ qml.RX(1, 1),
qml.PauliX(1) @ qml.RX(1, 0),
qml.RX(1, 0) @ qml.RX(1, 1),
Expand Down
18 changes: 18 additions & 0 deletions tests/ops/op_math/test_sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,24 @@ def test_terms_pauli_rep(self, op, coeffs_true, ops_true):
assert coeffs == coeffs_true
assert ops1 == ops_true

def test_terms_pauli_rep_wire_order(self):
"""Test that the wire order of the terms is the same as the wire order of the original
operands when the Sum has a valid pauli_rep"""
w0, w1, w2, w3 = [0, 1, 2, 3]
coeffs = [0.5, -0.5]

obs = [
qml.X(w0) @ qml.Y(w1) @ qml.X(w2) @ qml.Z(w3),
qml.X(w0) @ qml.X(w1) @ qml.Y(w2) @ qml.Z(w3),
]

H = qml.dot(coeffs, obs)
_, H_ops = H.terms()

assert all(o1.wires == o2.wires for o1, o2 in zip(obs, H_ops))
assert H_ops[0] == qml.prod(qml.X(w0), qml.Y(w1), qml.X(w2), qml.Z(w3))
assert H_ops[1] == qml.prod(qml.X(w0), qml.X(w1), qml.Y(w2), qml.Z(w3))

coeffs_ = [1.0, 1.0, 1.0, 3.0, 4.0, 4.0, 5.0]
h6 = qml.sum(
qml.s_prod(2.0, qml.prod(qml.Hadamard(0), qml.PauliZ(10))),
Expand Down
2 changes: 1 addition & 1 deletion tests/pauli/test_measurement_transformations.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def test_diagonalize_pauli_word_catch_non_pauli_word(self, non_pauli_word):
(
[PauliX(0) @ PauliY(1), PauliX(0) @ PauliZ(2)],
(
[RX(np.pi / 2, wires=[1]), RY(-np.pi / 2, wires=[0])],
[RY(-np.pi / 2, wires=[0]), RX(np.pi / 2, wires=[1])],
[PauliZ(wires=[0]) @ PauliZ(wires=[1]), PauliZ(wires=[0]) @ PauliZ(wires=[2])],
),
),
Expand Down
5 changes: 1 addition & 4 deletions tests/pauli/test_pauli_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -926,10 +926,7 @@ def test_diagonalize_pauli_word_catch_non_pauli_word(self, non_pauli_word):
(
[PauliX(0) @ PauliY(1), PauliX(0) @ PauliZ(2)],
(
[
RX(np.pi / 2, wires=[1]),
RY(-np.pi / 2, wires=[0]),
],
[RY(-np.pi / 2, wires=[0]), RX(np.pi / 2, wires=[1])],
[PauliZ(wires=[0]) @ PauliZ(wires=[1]), PauliZ(wires=[0]) @ PauliZ(wires=[2])],
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class TestDecomposition:
),
(
2,
qml.Hamiltonian([2, 0.5], [qml.PauliX("a"), qml.PauliZ("b") @ qml.PauliX("a")]),
qml.Hamiltonian([2, 0.5], [qml.PauliX("a"), qml.PauliX("a") @ qml.PauliZ("b")]),
2,
[
qml.PauliRot(4.0, "X", wires=["a"]),
Expand All @@ -87,7 +87,7 @@ class TestDecomposition:
[2, 0.5, 0.5],
[
qml.PauliX("a"),
qml.PauliZ(-15) @ qml.PauliX("a"),
qml.PauliX("a") @ qml.PauliZ(-15),
qml.Identity(0) @ qml.PauliY(-15),
],
),
Expand Down
18 changes: 9 additions & 9 deletions tests/test_qaoa.py
Original file line number Diff line number Diff line change
Expand Up @@ -1166,12 +1166,12 @@ def make_mixer_layer_test_cases():
[
qaoa.xy_mixer(Graph([(0, 1), (1, 2), (2, 0)])),
[
qml.PauliRot(1.0, "XX", wires=[1, 0]),
qml.PauliRot(1.0, "YY", wires=[1, 0]),
qml.PauliRot(1.0, "XX", wires=[2, 0]),
qml.PauliRot(1.0, "YY", wires=[2, 0]),
qml.PauliRot(1.0, "XX", wires=[2, 1]),
qml.PauliRot(1.0, "YY", wires=[2, 1]),
qml.PauliRot(1.0, "XX", wires=[0, 1]),
qml.PauliRot(1.0, "YY", wires=[0, 1]),
qml.PauliRot(1.0, "XX", wires=[0, 2]),
qml.PauliRot(1.0, "YY", wires=[0, 2]),
qml.PauliRot(1.0, "XX", wires=[1, 2]),
qml.PauliRot(1.0, "YY", wires=[1, 2]),
],
],
]
Expand All @@ -1186,9 +1186,9 @@ def make_cost_layer_test_cases():
[
qaoa.maxcut(Graph([(0, 1), (1, 2), (2, 0)]))[0],
[
qml.PauliRot(1.0, "ZZ", wires=[1, 0]),
qml.PauliRot(1.0, "ZZ", wires=[2, 0]),
qml.PauliRot(1.0, "ZZ", wires=[2, 1]),
qml.PauliRot(1.0, "ZZ", wires=[0, 1]),
qml.PauliRot(1.0, "ZZ", wires=[0, 2]),
qml.PauliRot(1.0, "ZZ", wires=[1, 2]),
],
],
]
Expand Down

0 comments on commit 1476cd2

Please sign in to comment.