Skip to content

Commit

Permalink
Replace inv by adjoint (#260)
Browse files Browse the repository at this point in the history
* Replace inv with adjoint

* Replace inv with adjoint

* Black

* Remove recorder

* Changelog
  • Loading branch information
rmoyard authored Jan 24, 2023
1 parent bde31f3 commit a4eee1c
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 51 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

### Breaking changes

* `.inv` is replaced by `qml.adjoint` in PennyLane `0.30.0` and therefore the plugin is adapted as well.
[(#260)](https://github.com/PennyLaneAI/pennylane-qiskit/pull/260)

### Improvements

### Documentation
Expand All @@ -14,6 +17,8 @@

This release contains contributions from (in alphabetical order):

Romain Moyard

---
# Release 0.28.0

Expand Down
48 changes: 33 additions & 15 deletions pennylane_qiskit/qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from ._version import __version__


QISKIT_OPERATION_MAP = {
QISKIT_OPERATION_MAP_SELF_ADJOINT = {
# native PennyLane operations also native to qiskit
"PauliX": ex.XGate,
"PauliY": ex.YGate,
Expand All @@ -45,12 +45,6 @@
"RX": ex.RXGate,
"RY": ex.RYGate,
"RZ": ex.RZGate,
"S": ex.SGate,
"Adjoint(S)": ex.SdgGate,
"T": ex.TGate,
"Adjoint(T)": ex.TdgGate,
"SX": ex.SXGate,
"Adjoint(SX)": ex.SXdgGate,
"Identity": ex.IGate,
"CSWAP": ex.CSwapGate,
"CRX": ex.CRXGate,
Expand All @@ -68,9 +62,27 @@
"IsingXX": ex.RXXGate,
}

QISKIT_OPERATION_INVERSES_MAP_SELF_ADJOINT = {
"Adjoint(" + k + ")": v for k, v in QISKIT_OPERATION_MAP_SELF_ADJOINT.items()
}

# Separate dictionary for the inverses as the operations dictionary needs
# to be invertible for the conversion functionality to work
QISKIT_OPERATION_INVERSES_MAP = {k + ".inv": v for k, v in QISKIT_OPERATION_MAP.items()}
QISKIT_OPERATION_MAP_NON_SELF_ADJOINT = {"S": ex.SGate, "T": ex.TGate, "SX": ex.SXGate}
QISKIT_OPERATION_INVERSES_MAP_NON_SELF_ADJOINT = {
"Adjoint(S)": ex.SdgGate,
"Adjoint(T)": ex.TdgGate,
"Adjoint(SX)": ex.SXdgGate,
}

QISKIT_OPERATION_MAP = {
**QISKIT_OPERATION_MAP_SELF_ADJOINT,
**QISKIT_OPERATION_MAP_NON_SELF_ADJOINT,
}
QISKIT_OPERATION_INVERSES_MAP = {
**QISKIT_OPERATION_INVERSES_MAP_SELF_ADJOINT,
**QISKIT_OPERATION_INVERSES_MAP_NON_SELF_ADJOINT,
}


class QiskitDevice(QubitDevice, abc.ABC):
Expand Down Expand Up @@ -299,17 +311,23 @@ def apply_operations(self, operations):

qregs = [self._reg[i] for i in device_wires.labels]

if operation.split(".inv")[0] in ("QubitUnitary", "QubitStateVector"):
# Need to revert the order of the quantum registers used in
# Qiskit such that it matches the PennyLane ordering
qregs = list(reversed(qregs))
adjoint = operation.startswith("Adjoint(")
split_op = operation.split("Adjoint(")

if adjoint:
if split_op[1] in ("QubitUnitary)", "QubitStateVector)"):
# Need to revert the order of the quantum registers used in
# Qiskit such that it matches the PennyLane ordering
qregs = list(reversed(qregs))
else:
if split_op[0] in ("QubitUnitary", "QubitStateVector"):
# Need to revert the order of the quantum registers used in
# Qiskit such that it matches the PennyLane ordering
qregs = list(reversed(qregs))

dag = circuit_to_dag(QuantumCircuit(self._reg, self._creg, name=""))
gate = mapped_operation(*par)

if operation.endswith(".inv"):
gate = gate.inverse()

dag.apply_operation_back(gate, qargs=qregs)
circuit = dag_to_circuit(dag)
circuits.append(circuit)
Expand Down
20 changes: 13 additions & 7 deletions pennylane_qiskit/vqe_runtime_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,10 +411,19 @@ def _qiskit_ansatz(num_params, num_qubits, wires, tape):

qregs = [reg[i] for i in wires.labels]

if operation.split(".inv")[0] in ("QubitUnitary", "QubitStateVector"):
# Need to revert the order of the quantum registers used in
# Qiskit such that it matches the PennyLane ordering
qregs = list(reversed(qregs))
adjoint = operation.startswith("Adjoint(")
split_op = operation.split("Adjoint(")

if adjoint:
if split_op[1] in ("QubitUnitary)", "QubitStateVector)"):
# Need to revert the order of the quantum registers used in
# Qiskit such that it matches the PennyLane ordering
qregs = list(reversed(qregs))
else:
if split_op[0] in ("QubitUnitary", "QubitStateVector"):
# Need to revert the order of the quantum registers used in
# Qiskit such that it matches the PennyLane ordering
qregs = list(reversed(qregs))

dag = circuit_to_dag(QuantumCircuit(reg, name=""))

Expand All @@ -432,9 +441,6 @@ def _qiskit_ansatz(num_params, num_qubits, wires, tape):

gate = mapped_operation(*par)

if operation.endswith(".inv"):
gate = gate.inverse()

dag.apply_operation_back(gate, qargs=qregs)
circuit = dag_to_circuit(dag)
circuits.append(circuit)
Expand Down
35 changes: 16 additions & 19 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,17 +401,15 @@ def circuit(phi=None):
return qml.expval(qml.PauliZ(0))

phi = tensor([[0.04439891, 0.14490549, 3.29725643, 2.51240058]])

with qml.tape.OperationRecorder() as rec:
circuit(phi=phi)

circuit(phi)
ops = circuit.tape.operations
for i in range(phi.shape[1]):
# Test each rotation applied
assert rec.queue[i].name == "RX"
assert len(rec.queue[i].parameters) == 1
assert ops[i].name == "RX"
assert len(ops[i].parameters) == 1

# Test that the gate parameter is a PennyLane tensor
assert isinstance(rec.queue[i].parameters[0], tensor)
assert isinstance(ops[i].parameters[0], tensor)

def test_multiple_gate_parameter(self):
"""Test that when supplied a PennyLane tensor, a QNode passes arguments
Expand All @@ -426,19 +424,18 @@ def circuit(phi=None):

phi = tensor([[0.04439891, 0.14490549, 3.29725643]])

with qml.tape.OperationRecorder() as rec:
circuit(phi=phi)

circuit(phi)
ops = circuit.tape.operations
# Test the rotation applied
assert rec.queue[0].name == "Rot"
assert len(rec.queue[0].parameters) == 3
assert ops[0].name == "Rot"
assert len(ops[0].parameters) == 3

# Test that the gate parameters are PennyLane tensors,
assert isinstance(rec.queue[0].parameters[0], tensor)
assert isinstance(ops[0].parameters[0], tensor)

assert isinstance(rec.queue[0].parameters[1], tensor)
assert isinstance(ops[0].parameters[1], tensor)

assert isinstance(rec.queue[0].parameters[2], tensor)
assert isinstance(ops[0].parameters[2], tensor)


class TestInverses:
Expand All @@ -455,14 +452,14 @@ def test_inverse_of_operation(self):

@qml.qnode(dev)
def circuit_with_inverses(angle):
qml.Hadamard(0).inv()
qml.RX(angle, wires=0).inv()
qml.adjoint(qml.Hadamard(0))
qml.adjoint(qml.RX(angle, wires=0))
return qml.expval(qml.PauliZ(0))

@qml.qnode(dev2)
def circuit_with_inverses_default_qubit(angle):
qml.Hadamard(0).inv()
qml.RX(angle, wires=0).inv()
qml.adjoint(qml.Hadamard(0))
qml.adjoint(qml.RX(angle, wires=0))
return qml.expval(qml.PauliZ(0))

for x in angles:
Expand Down
18 changes: 9 additions & 9 deletions tests/test_inverses.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_supported_gate_inverse_single_wire_no_parameters(self, name, expected_o

@qml.qnode(dev)
def circuit():
op(wires=0).inv()
qml.adjoint(op(wires=0))
return qml.expval(qml.PauliZ(0))

assert np.isclose(circuit(), expected_output, atol=tol, rtol=0)
Expand All @@ -62,7 +62,7 @@ def test_supported_gate_inverse_two_wires_no_parameters(self, name, expected_out
@qml.qnode(dev)
def circuit():
qml.QubitStateVector(np.array([1 / 2, 0, 0, math.sqrt(3) / 2]), wires=[0, 1])
op(wires=[0, 1]).inv()
qml.adjoint(op(wires=[0, 1]))
return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

assert np.allclose(circuit(), expected_output, atol=tol, rtol=0)
Expand All @@ -85,7 +85,7 @@ def test_supported_gate_inverse_three_wires_no_parameters(self, name, expected_o
@qml.qnode(dev)
def circuit():
qml.BasisState(np.array([1, 0, 1]), wires=[0, 1, 2])
op(wires=[0, 1, 2]).inv()
qml.adjoint(op(wires=[0, 1, 2]))
return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)), qml.expval(qml.PauliZ(2))

assert np.allclose(circuit(), expected_output, atol=tol, rtol=0)
Expand Down Expand Up @@ -139,7 +139,7 @@ def test_supported_gate_inverse_single_wire_with_parameters(self, name, par, exp

@qml.qnode(dev)
def circuit():
op(*np.negative(par), wires=0).inv()
qml.adjoint(op(*np.negative(par), wires=0))
return qml.expval(qml.PauliZ(0))

assert np.isclose(circuit(), expected_output, atol=tol, rtol=0)
Expand Down Expand Up @@ -193,7 +193,7 @@ def test_supported_gate_inverse_two_wires_with_parameters(self, name, par, expec
@qml.qnode(dev)
def circuit():
qml.QubitStateVector(np.array([1 / 2, 0, 0, math.sqrt(3) / 2]), wires=[0, 1])
op(*np.negative(par), wires=[0, 1]).inv()
qml.adjoint(op(*np.negative(par), wires=[0, 1]))
return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

assert np.allclose(circuit(), expected_output, atol=tol, rtol=0)
Expand All @@ -218,7 +218,7 @@ def test_unsupported_gate_inverses(self, name, par, expected_output):

@qml.qnode(dev)
def circuit():
op(*np.negative(par), wires=0).inv()
qml.adjoint(op(*np.negative(par), wires=0))
return qml.expval(qml.PauliZ(0))

assert np.isclose(circuit(), expected_output, atol=tol, rtol=0)
Expand All @@ -235,7 +235,7 @@ def test_s_gate_inverses(self, par):
def circuit():
qml.Hadamard(0)
qml.RZ(par, wires=[0])
qml.S(wires=[0]).inv()
qml.adjoint(qml.S(wires=[0]))
return qml.expval(qml.PauliX(0))

assert np.allclose(circuit(), expected_output, atol=tol, rtol=0)
Expand All @@ -251,7 +251,7 @@ def test_t_gate_inverses(self, par):
@qml.qnode(dev)
def circuit():
qml.RX(par, wires=[0])
qml.T(wires=[0]).inv()
qml.adjoint(qml.T(wires=[0]))
return qml.expval(qml.PauliX(0))

assert np.allclose(circuit(), expected_output, atol=tol, rtol=0)
Expand All @@ -267,7 +267,7 @@ def test_sx_gate_inverses(self, par):
@qml.qnode(dev)
def circuit():
qml.RY(par, wires=[0])
qml.SX(wires=[0]).inv()
qml.adjoint(qml.SX(wires=[0]))
return qml.expval(qml.PauliX(0))

assert np.allclose(circuit(), expected_output, atol=tol, rtol=0)
2 changes: 1 addition & 1 deletion tests/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ def test_inverse(self, token, tol, shots):
IBMQ.enable_account(token)

def vqe_circuit(params):
qml.RX(params[0], wires=0).inv()
qml.adjoint(qml.RX(params[0], wires=0))
qml.RX(params[1], wires=1)

coeffs = [1, 1]
Expand Down

0 comments on commit a4eee1c

Please sign in to comment.