Skip to content

Commit

Permalink
Shape error for tensor-like parameters in pauli arithmetic fixed (Add…
Browse files Browse the repository at this point in the history
…ition to PR: #6562) (#6587)

**Context:**
I added Pauli string decompositions for single qubit parametrized gates
in PR: #6562. Pennylane exploits these Pauli string representations
when, for example, computing

```python
(qml.RX(1.11, wires=0) @ qml.RY(1.23, wires=0)).matrix()
```
The example above works as intended, but

```python
(qml.RX([1.11, 2.11, 3.11], wires=0) @ qml.RY([1.23, 2.23, 3.23], wires=0)).matrix()     # shape error
```
or
```python
(qml.RX(np.array([1.11, 2.11, 3.11]), wires=0) @ qml.RY(np.array([1.23, 2.23, 3.23]), wires=0)).matrix()     # shape error
```
returns a shape error. The code above should work since `RX`and
`RY`support tensor-like (flattened) parameters as it can be seen from
```python
qml.RX([1.11, 2.11, 3.11], wires=0).matrix()    # works!
```


**Description of the Change:**

I added an outer product in `pauli_arithmetic`.

**Benefits:**
- Pauli representations can now be leveraged for the matrix conversion
of products of ops.
- A Pauli representation of the `PhaseShift gate` could be added - this
was not possible before as the `PCPhase` gate used the
`pauli_arithmetic`, which gave an error in the pytests.

**Possible Drawbacks:**
None

**Related GitHub Issues:**
#6243 and PR: #6562

---------

Co-authored-by: Lasse Dierich <[email protected]>
Co-authored-by: David Wierichs <[email protected]>
Co-authored-by: Christina Lee <[email protected]>
  • Loading branch information
4 people authored and mudit2812 committed Dec 13, 2024
1 parent 5b83f6c commit 0281ba2
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 6 deletions.
4 changes: 2 additions & 2 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,9 @@ such as `shots`, `rng` and `prng_key`.
visualizations, allowing global and per-wire customization with options like `color`, `linestyle`, and `linewidth`.
[(#6486)](https://github.com/PennyLaneAI/pennylane/pull/6486)

* Added Pauli String representations for the gates X, Y, Z, S, T, SX, SWAP, ISWAP, ECR, SISWAP.
* Added Pauli String representations for the gates X, Y, Z, S, T, SX, SWAP, ISWAP, ECR, SISWAP. Fixed a shape error in the matrix conversion of `PauliSentence`s with list or array input.
[(#6562)](https://github.com/PennyLaneAI/pennylane/pull/6562)
[(#6587)](https://github.com/PennyLaneAI/pennylane/pull/6587)

* `QNode` and `qml.execute` now forbid certain keyword arguments from being passed positionally.
[(#6610)](https://github.com/PennyLaneAI/pennylane/pull/6610)
Expand Down Expand Up @@ -465,7 +466,6 @@ same information.
* Fixed `Identity.__repr__` to return correct wires list.
[(#6506)](https://github.com/PennyLaneAI/pennylane/pull/6506)


<h3>Contributors ✍️</h3>

This release contains contributions from (in alphabetical order):
Expand Down
12 changes: 8 additions & 4 deletions pennylane/pauli/pauli_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -961,16 +961,18 @@ def _sum_same_structure_pws_dense(self, pauli_words, wire_order):
ml_interface = qml.math.get_interface(coeff)
if ml_interface == "torch":
data0 = qml.math.convert_like(data0, coeff)
data = coeff * data0
data = coeff * data0 if qml.math.ndim(coeff) == 0 else qml.math.outer(coeff, data0)
for pw in pauli_words[1:]:
coeff = self[pw]
csr_data = pw._get_csr_data(wire_order, 1)
ml_interface = qml.math.get_interface(coeff)
if ml_interface == "torch":
csr_data = qml.math.convert_like(csr_data, coeff)
data += self[pw] * csr_data
data += (
coeff * csr_data if qml.math.ndim(coeff) == 0 else qml.math.outer(coeff, csr_data)
)

return qml.math.einsum("ij,i->ij", base_matrix, data)
return qml.math.einsum("ij,...i->...ij", base_matrix, data)

def _sum_same_structure_pws(self, pauli_words, wire_order):
"""Sums Pauli words with the same sparse structure."""
Expand Down Expand Up @@ -1009,7 +1011,9 @@ def operation(self, wire_order=None):
for pw, coeff in self.items():
pw_op = pw.operation(wire_order=list(wire_order))
rep = PauliSentence({pw: coeff})
summands.append(pw_op if coeff == 1 else SProd(coeff, pw_op, _pauli_rep=rep))
summands.append(
pw_op if qml.math.all(coeff == 1) else SProd(coeff, pw_op, _pauli_rep=rep)
)
return summands[0] if len(summands) == 1 else Sum(*summands, _pauli_rep=self)

def simplify(self, tol=1e-8):
Expand Down
21 changes: 21 additions & 0 deletions tests/pauli/test_pauli_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,15 @@ def test_operation_wire_order(self):

qml.assert_equal(op, id)

# pylint: disable=W0621
@pytest.mark.parametrize("coeff0", [qml.math.array([0.6, 0.2, 4.3])])
@pytest.mark.parametrize("coeff1", [qml.math.array([1.2, -0.9, 2.7])])
def test_operation_array_input(self, coeff0, coeff1):
pw0 = qml.pauli.PauliWord({0: "X", "a": "Y"})
pw1 = qml.pauli.PauliWord({0: "Z", 1: "Y", "b": "Y"})
ps = qml.pauli.PauliSentence({pw0: coeff0, pw1: coeff1})
assert ps.operation() is not None

def test_pickling(self):
"""Check that paulisentences can be pickled and unpickled."""
word1 = PauliWord({2: "X", 3: "Y", 4: "Z"})
Expand Down Expand Up @@ -995,6 +1004,18 @@ def test_map_wires(self):
}
)

# pylint: disable=W0621
@pytest.mark.parametrize("coeff0", [[0.6, 0.2, 4.3], -0.7])
@pytest.mark.parametrize("coeff1", [[1.2, -0.9, 2.7], -0.7])
def test_to_mat_with_broadcasting(self, coeff0, coeff1):
wire_order = [0, 1, "a", "b"]
pw0 = qml.pauli.PauliWord({0: "X", "a": "Y"})
pw1 = qml.pauli.PauliWord({0: "Z", 1: "Y", "b": "Y"})
ps = qml.pauli.PauliSentence({pw0: coeff0, pw1: coeff1})
mat0 = ps.to_mat(wire_order=wire_order)
mat1 = qml.matrix(ps.operation(), wire_order=wire_order)
assert qml.math.allclose(mat0, mat1)


class TestPauliSentenceMatrix:
"""Tests for calculating the matrix of a pauli sentence."""
Expand Down

0 comments on commit 0281ba2

Please sign in to comment.