Skip to content

Commit

Permalink
Add the Missing Gates for Conversion (#449)
Browse files Browse the repository at this point in the history
* add more gates

* fix tests?

* get more info?

* fix integration tests

* fix missing coverage

* add `changelog`

* add test
  • Loading branch information
obliviateandsurrender authored Feb 22, 2024
1 parent 7908dba commit 40121d0
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
a ``QuantumCircuit`` using `load`.
[(#417)](https://github.com/PennyLaneAI/pennylane-qiskit/pull/417)

* Added conversion support for more Qiskit gates to native PennyLane operations -
``Barrier``, ``CYGate``, ``CHGate``, ``CPhase``, ``CCZGate``, ``ECRGate``, and ``GlobalPhaseGate``.
[(#449)](https://github.com/PennyLaneAI/pennylane-qiskit/pull/449)

### Breaking changes 💔

### Deprecations 👋
Expand Down
8 changes: 7 additions & 1 deletion pennylane_qiskit/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@

inv_map = {v.__name__: k for k, v in QISKIT_OPERATION_MAP.items()}

dagger_map = {"SdgGate": qml.S, "TdgGate": qml.T, "SXdgGate": qml.SX}
dagger_map = {
"SdgGate": qml.S,
"TdgGate": qml.T,
"SXdgGate": qml.SX,
"GlobalPhaseGate": qml.GlobalPhase,
}

referral_to_forum = (
"\n \nIf you are experiencing any difficulties with converting circuits from Qiskit, you can reach out "
Expand Down Expand Up @@ -434,6 +439,7 @@ def _function(*args, params: dict = None, wires: list = None, **kwargs):

if instruction_name in dagger_map:
operation_class = qml.adjoint(dagger_map[instruction_name])
operation_args.extend(operation_params)

elif instruction_name in inv_map:
operation_class = getattr(pennylane_ops, inv_map[instruction_name])
Expand Down
12 changes: 12 additions & 0 deletions pennylane_qiskit/qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@
"Adjoint(S)": lib.SdgGate,
"Adjoint(T)": lib.TdgGate,
"Adjoint(SX)": lib.SXdgGate,
"CY": lib.CYGate,
"CH": lib.CHGate,
"CPhase": lib.CPhaseGate,
"CCZ": lib.CCZGate,
"ECR": lib.ECRGate,
"Barrier": lib.Barrier,
"Adjoint(GlobalPhase)": lib.GlobalPhaseGate,
}


Expand Down Expand Up @@ -351,7 +358,12 @@ def apply_operations(self, operations):
# Qiskit such that it matches the PennyLane ordering
qregs = list(reversed(qregs))

if operation in ("Barrier",):
# Need to add the num_qubits for instantiating Barrier in Qiskit
par = [len(self._reg)]

dag = circuit_to_dag(QuantumCircuit(self._reg, self._creg, name=""))

gate = mapped_operation(*par)

dag.apply_operation_back(gate, qargs=qregs)
Expand Down
69 changes: 60 additions & 9 deletions tests/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ def test_operations_adjoint_ops(self, recorder):
qc.sdg([0])
qc.tdg([0])
qc.sxdg([0])
qc.append(lib.GlobalPhaseGate(1.2))

quantum_circuit = load(qc)
with recorder:
Expand All @@ -671,21 +672,67 @@ def test_operations_adjoint_ops(self, recorder):
assert len(recorder.queue[2].parameters) == 0
assert recorder.queue[2].wires == Wires([0])

assert recorder.queue[3].name == "Adjoint(GlobalPhase)"
assert recorder.queue[3].parameters == [1.2]
assert recorder.queue[3].wires == Wires([])

def test_controlled_gates(self, recorder):
"""Tests loading a circuit with controlled gates."""

qc = QuantumCircuit(3)
qc.cy(0, 1)
qc.ch(1, 2)
qc.cp(1.2, 2, 1)
qc.ccz(0, 2, 1)
qc.barrier()
qc.ecr(1, 0)

quantum_circuit = load(qc)

with recorder:
quantum_circuit()

assert len(recorder.queue) == 6

assert recorder.queue[0].name == "CY"
assert len(recorder.queue[0].parameters) == 0
assert recorder.queue[0].wires == Wires([0, 1])

assert recorder.queue[1].name == "CH"
assert len(recorder.queue[1].parameters) == 0
assert recorder.queue[1].wires == Wires([1, 2])

assert recorder.queue[2].name == "ControlledPhaseShift"
assert recorder.queue[2].parameters == [1.2]
assert recorder.queue[2].wires == Wires([2, 1])

assert recorder.queue[3].name == "CCZ"
assert len(recorder.queue[3].parameters) == 0
assert recorder.queue[3].wires == Wires([0, 2, 1])

assert recorder.queue[4].name == "Barrier"
assert len(recorder.queue[4].parameters) == 0
assert recorder.queue[4].wires == Wires([0, 1, 2])

assert recorder.queue[5].name == "ECR"
assert len(recorder.queue[5].parameters) == 0
assert recorder.queue[5].wires == Wires([1, 0])

def test_operation_transformed_into_qubit_unitary(self, recorder):
"""Tests loading a circuit with operations that can be converted,
but not natively supported by PennyLane."""

qc = QuantumCircuit(3, 1)

qc.ch([0], [1])
qc.cs([0], [1])

quantum_circuit = load(qc)
with recorder:
quantum_circuit()

assert recorder.queue[0].name == "QubitUnitary"
assert len(recorder.queue[0].parameters) == 1
assert np.array_equal(recorder.queue[0].parameters[0], lib.CHGate().to_matrix())
assert np.array_equal(recorder.queue[0].parameters[0], lib.CSGate().to_matrix())
assert recorder.queue[0].wires == Wires([0, 1])


Expand Down Expand Up @@ -1017,7 +1064,7 @@ def test_qasm_from_file(self, tmpdir, recorder):
with recorder:
quantum_circuit()

assert len(recorder.queue) == 10
assert len(recorder.queue) == 11

assert recorder.queue[0].name == "PauliX"
assert recorder.queue[0].parameters == []
Expand All @@ -1027,21 +1074,25 @@ def test_qasm_from_file(self, tmpdir, recorder):
assert recorder.queue[1].parameters == []
assert recorder.queue[1].wires == Wires([2])

assert recorder.queue[2].name == "Hadamard"
assert recorder.queue[2].name == "Barrier"
assert recorder.queue[2].parameters == []
assert recorder.queue[2].wires == Wires([0])
assert recorder.queue[2].wires == Wires([0, 1, 2, 3])

assert recorder.queue[3].name == "Hadamard"
assert recorder.queue[3].parameters == []
assert recorder.queue[3].wires == Wires([1])
assert recorder.queue[3].wires == Wires([0])

assert recorder.queue[4].name == "Hadamard"
assert recorder.queue[4].parameters == []
assert recorder.queue[4].wires == Wires([2])
assert recorder.queue[4].wires == Wires([1])

assert recorder.queue[5].name == "Hadamard"
assert recorder.queue[5].parameters == []
assert recorder.queue[5].wires == Wires([3])
assert recorder.queue[5].wires == Wires([2])

assert recorder.queue[6].name == "Hadamard"
assert recorder.queue[6].parameters == []
assert recorder.queue[6].wires == Wires([3])

def test_qasm_file_not_found_error(self):
"""Tests that an error is propagated, when a non-existing file is specified for parsing."""
Expand Down Expand Up @@ -1449,7 +1500,7 @@ def ansatz_false():

qml.RZ(0.24, wires=0)
qml.CNOT([0, 1])

qml.Barrier([0, 1, 2])
return [qml.expval(m) for m in [m0, m1, qml.measure(0), qml.measure(1), qml.measure(2)]]

assert loaded_qiskit_circuit() == built_pl_circuit()
Expand Down
13 changes: 13 additions & 0 deletions tests/test_qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,16 @@ def test_num_executions_recorded(self, device):
tapes = [self.tape1, self.tape2]
res = dev.batch_execute(tapes)
assert dev.num_executions == 1

def test_barrier_tape(self, device, tol):
"""Tests that the barriers are accounted for during conversion."""
dev = device(2)

@qml.qnode(dev)
def barrier_func():
qml.Barrier([0, 1])
return qml.state()

res = barrier_func()
assert barrier_func.tape.operations[0] == qml.Barrier([0, 1])
assert np.allclose(res, dev.batch_execute([barrier_func.tape]), atol=0)

0 comments on commit 40121d0

Please sign in to comment.