Skip to content

Commit

Permalink
Use new op math in generator of GlobalPhase and Controlled (PennyLane…
Browse files Browse the repository at this point in the history
…AI#5194)

**Context:**
The `Tensor` and `Hamiltonian` does not work with `Identity` on no
wires, causing issues with `ControlledGlobalPhase` when the
`GlobalPhase` acts on no wires.

**Description of the Change:**
1. Use `prod` and `s_prod` in place of straight multiplication in the
`generator` of `Controlled` and `GlobalPhase`
2. The generator of `GlobalPhase` now contains an identity that acts on
the same wires as the `GlobalPhase`.

**Benefits:**
BugFix

**Related GitHub Issues:**
PennyLaneAI#4578

**Related Shortcut Story:**
[sc-44933]
  • Loading branch information
astralcai authored and Gabriel-Bottrill committed Feb 14, 2024
1 parent bf1eb53 commit 5f20ca3
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 31 deletions.
3 changes: 3 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,9 @@
operators have a valid `pauli_rep` property.
[(#5177)](https://github.com/PennyLaneAI/pennylane/pull/5177)

* Controlled `GlobalPhase` with non-zero control wire no longer throws an error.
[(#5194)](https://github.com/PennyLaneAI/pennylane/pull/5194)

* A `QNode` transformed with `mitigate_with_zne` now accepts batch parameters.
[(#5195)](https://github.com/PennyLaneAI/pennylane/pull/5195)

Expand Down
3 changes: 1 addition & 2 deletions pennylane/ops/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,5 +389,4 @@ def pow(self, z):
return [GlobalPhase(z * self.data[0], self.wires)]

def generator(self):
wires = self.wires or [0]
return -1 * qml.Identity(wires)
return qml.s_prod(-1, qml.Identity(self.wires))
6 changes: 1 addition & 5 deletions pennylane/ops/op_math/controlled.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,11 +666,7 @@ def generator(self):
projectors = (
qml.Projector([val], wires=w) for val, w in zip(self.control_values, self.control_wires)
)

if qml.operation.active_new_opmath():
return qml.prod(*projectors, sub_gen)

return 1.0 * operation.Tensor(*projectors) @ sub_gen
return qml.prod(*projectors, sub_gen)

@property
def has_adjoint(self):
Expand Down
52 changes: 28 additions & 24 deletions tests/ops/qubit/test_parametric_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -2007,18 +2007,19 @@ def circuit(phi):

@pytest.mark.autograd
@pytest.mark.parametrize("dev_name,diff_method", device_methods)
def test_globalphase_autograd_grad(self, tol, dev_name, diff_method):
@pytest.mark.parametrize("wires", [(0, 1), (1, 0)])
def test_globalphase_autograd_grad(self, tol, dev_name, diff_method, wires):
"""Test the gradient with Autograd for a controlled GlobalPhase."""

dev = qml.device(dev_name, wires=2)

@qml.qnode(dev, diff_method=diff_method)
def circuit(x):
qml.Identity(0)
qml.Hadamard(1)
qml.ctrl(qml.GlobalPhase(x), 1)
qml.Hadamard(1)
return qml.expval(qml.PauliZ(1))
qml.Identity(wires[0])
qml.Hadamard(wires[1])
qml.ctrl(qml.GlobalPhase(x), control=wires[1])
qml.Hadamard(wires[1])
return qml.expval(qml.PauliZ(wires[1]))

phi = npp.array(2.1, requires_grad=True)

Expand Down Expand Up @@ -2351,7 +2352,8 @@ def circuit(phi):

@pytest.mark.tf
@pytest.mark.parametrize("dev_name,diff_method", device_methods)
def test_globalphase_tf_grad(self, tol, dev_name, diff_method):
@pytest.mark.parametrize("wires", [(0, 1), (1, 0)])
def test_globalphase_tf_grad(self, tol, dev_name, diff_method, wires):
"""Test the gradient with Tensorflow for a controlled GlobalPhase."""

import tensorflow as tf
Expand All @@ -2360,11 +2362,11 @@ def test_globalphase_tf_grad(self, tol, dev_name, diff_method):

@qml.qnode(dev, diff_method=diff_method)
def circuit(x):
qml.Identity(0)
qml.Hadamard(1)
qml.ctrl(qml.GlobalPhase(x), 1)
qml.Hadamard(1)
return qml.expval(qml.PauliZ(1))
qml.Identity(wires[0])
qml.Hadamard(wires[1])
qml.ctrl(qml.GlobalPhase(x), control=wires[1])
qml.Hadamard(wires[1])
return qml.expval(qml.PauliZ(wires[1]))

phi = tf.Variable(2.1, dtype=tf.complex128)

Expand Down Expand Up @@ -2505,7 +2507,8 @@ def circ(phi):

@pytest.mark.jax
@pytest.mark.parametrize("dev_name,diff_method", device_methods)
def test_globalphase_jax_grad(self, tol, dev_name, diff_method):
@pytest.mark.parametrize("wires", [(1, 0), (0, 1)])
def test_globalphase_jax_grad(self, tol, dev_name, diff_method, wires):
"""Test the gradient with JAX for a controlled GlobalPhase."""

import jax
Expand All @@ -2517,11 +2520,11 @@ def test_globalphase_jax_grad(self, tol, dev_name, diff_method):

@qml.qnode(dev, diff_method=diff_method)
def circuit(x):
qml.Identity(0)
qml.Hadamard(1)
qml.ctrl(qml.GlobalPhase(x), 1)
qml.Hadamard(1)
return qml.expval(qml.PauliZ(1))
qml.Identity(wires[0])
qml.Hadamard(wires[1])
qml.ctrl(qml.GlobalPhase(x), control=wires[1])
qml.Hadamard(wires[1])
return qml.expval(qml.PauliZ(wires[1]))

phi = jnp.array(2.1)

Expand All @@ -2532,7 +2535,8 @@ def circuit(x):

@pytest.mark.torch
@pytest.mark.parametrize("dev_name,diff_method", device_methods)
def test_globalphase_torch_grad(self, tol, dev_name, diff_method):
@pytest.mark.parametrize("wires", [(1, 0), (0, 1)])
def test_globalphase_torch_grad(self, tol, dev_name, diff_method, wires):
"""Test the gradient with Torch for a controlled GlobalPhase."""

import torch
Expand All @@ -2541,11 +2545,11 @@ def test_globalphase_torch_grad(self, tol, dev_name, diff_method):

@qml.qnode(dev, diff_method=diff_method)
def circuit(x):
qml.Identity(0)
qml.Hadamard(1)
qml.ctrl(qml.GlobalPhase(x), 1)
qml.Hadamard(1)
return qml.expval(qml.PauliZ(1))
qml.Identity(wires[0])
qml.Hadamard(wires[1])
qml.ctrl(qml.GlobalPhase(x), control=wires[1])
qml.Hadamard(wires[1])
return qml.expval(qml.PauliZ(wires[1]))

phi = torch.tensor(2.1, requires_grad=True, dtype=torch.float64)

Expand Down

0 comments on commit 5f20ca3

Please sign in to comment.