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 qasm measurement #555

Merged
merged 5 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

### Breaking changes 💔

### Deprecations 👋
Expand All @@ -16,6 +19,8 @@

This release contains contributions from (in alphabetical order):

Mashhood Khan

---
# Release 0.36.0

Expand Down
8 changes: 6 additions & 2 deletions pennylane_qiskit/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -584,14 +585,17 @@ 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):
lillian542 marked this conversation as resolved.
Show resolved Hide resolved
"""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 <https://docs.pennylane.ai/en/stable/introduction/measurements.html>`_
that override the terminal measurements that may be present in the input circuit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something here is causing Sphinx build for the docs to fail - I think you need to add a blank line here before the Returns section for the docs to render correctly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, looks like the build succeeds now!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
that override the terminal measurements that may be present in the input circuit
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):
Expand Down
64 changes: 62 additions & 2 deletions tests/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1165,8 +1165,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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is what "breaks" by defaulting to None instead of [] above


assert recorder.queue[0].name == "PauliX"
assert recorder.queue[0].parameters == []
Expand All @@ -1176,6 +1176,66 @@ 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):
obliviateandsurrender marked this conversation as resolved.
Show resolved Hide resolved
"""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];"
lillian542 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we rewrite the string as the one below in test_qasm_mid_circuit_measure? (i.e., one instruction in one line)

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, m0, m1 = loaded_circuit()
lillian542 marked this conversation as resolved.
Show resolved Hide resolved
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])
m0 = qml.measure([0])
lillian542 marked this conversation as resolved.
Show resolved Hide resolved
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):
Expand Down
Loading