diff --git a/CHANGELOG.md b/CHANGELOG.md index d15906f4..1033f323 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ### Improvements 🛠 +* Updated `load_qasm` to take the optional kwarg `measurements` which get performed at the end of the loaded circuit and `load_qasm` can now detect mid-circuit measurements from `qasm`. +[(#555)](https://github.com/PennyLaneAI/pennylane-qiskit/pull/555) + * Improvements have been made to load circuits with `SwitchCaseOp` gates with default case. [(#514)](https://github.com/PennyLaneAI/pennylane-qiskit/pull/514) @@ -20,6 +23,7 @@ This release contains contributions from (in alphabetical order): Utkarsh Azad +Mashhood Khan --- # Release 0.36.0 diff --git a/pennylane_qiskit/converter.py b/pennylane_qiskit/converter.py index 89c3fa7a..7f7172e0 100644 --- a/pennylane_qiskit/converter.py +++ b/pennylane_qiskit/converter.py @@ -20,6 +20,7 @@ from functools import partial, reduce import numpy as np +import qiskit.qasm2 from qiskit import QuantumCircuit from qiskit.circuit import Parameter, ParameterExpression, ParameterVector from qiskit.circuit import Measure, Barrier, ControlFlowOp, Clbit @@ -584,14 +585,19 @@ def _function(*args, params: dict = None, wires: list = None, **kwargs): return _function -def load_qasm(qasm_string: str): +def load_qasm(qasm_string: str, measurements=None): """Loads a PennyLane template from a QASM string. + Args: qasm_string (str): the name of the QASM string + measurements (None | pennylane.measurements.MeasurementProcess | list[pennylane.measurements.MeasurementProcess]): + the PennyLane `measurements `_ + that override the terminal measurements that may be present in the input circuit + Returns: function: the new PennyLane template """ - return load(QuantumCircuit.from_qasm_str(qasm_string), measurements=[]) + return load(qiskit.qasm2.loads(qasm_string), measurements=measurements) def load_qasm_from_file(file: str): diff --git a/tests/test_converter.py b/tests/test_converter.py index b573e978..6d13f55f 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -1167,8 +1167,8 @@ def test_qasm_(self, recorder): with recorder: quantum_circuit(params={}) - # only X and CNOT queued (not 4 x qml.measure) - assert len(recorder.queue) == 2 + # X and CNOT queued with 4 x qml.measure + assert len(recorder.queue) == 6 assert recorder.queue[0].name == "PauliX" assert recorder.queue[0].parameters == [] @@ -1178,6 +1178,68 @@ def test_qasm_(self, recorder): assert recorder.queue[1].parameters == [] assert recorder.queue[1].wires == Wires([2, 0]) + for i in range(2, 6): + assert recorder.queue[i].name == "MidMeasureMP" + assert recorder.queue[i].wires == Wires([i - 2]) + + def test_qasm_measure(self): + """Tests that measurements specified as an argument are added to the converted circuit.""" + qasm_string = ( + 'include "qelib1.inc";' + "qreg q[2];" + "creg c[2];" + "h q[0];" + "cx q[0], q[1];" + ) + dev = qml.device("default.qubit") + measurements = [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))] + loaded_circuit = load_qasm(qasm_string, measurements=measurements) + + # loaded circuit with measurements + @qml.qnode(dev) + def quantum_circuit1(): + return loaded_circuit() + + # native pennylane measurements + @qml.qnode(dev) + def quantum_circuit2(): + load_qasm(qasm_string)() + return [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))] + + assert quantum_circuit1() == quantum_circuit2() + + def test_qasm_mid_circuit_measure(self): + """Tests that the QASM primitive measure is correctly converted.""" + qasm_string = ( + 'include "qelib1.inc";' + "qreg q[2];" + "creg c[2];" + "h q[0];" + "measure q[0] -> c[0];" + "rz(0.24) q[0];" + "cx q[0], q[1];" + "measure q -> c;" + ) + dev = qml.device("default.qubit") + loaded_circuit = load_qasm(qasm_string) + + # loaded circuit + @qml.qnode(dev) + def quantum_circuit1(): + mid_measure, _, m1 = loaded_circuit() + qml.cond(mid_measure == 0, qml.RX)(np.pi / 2, 0) + return qml.expval(mid_measure), qml.expval(m1) + + # native pennylane circuit + @qml.qnode(dev) + def quantum_circuit2(): + qml.Hadamard(0) + mid_measure = qml.measure(0) + qml.RZ(0.24, 0) + qml.CNOT([0, 1]) + qml.measure([0]) + m1 = qml.measure([1]) + qml.cond(mid_measure == 0, qml.RX)(np.pi / 2, 0) + return qml.expval(mid_measure), qml.expval(m1) + + assert quantum_circuit1() == quantum_circuit2() + class TestConverterIntegration: def test_use_loaded_circuit_in_qnode(self, qubit_device_2_wires):