Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the Missing Gates for Conversion #449

Merged
merged 8 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved
}


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)
Loading