PennyLaneAI / pennylane-qiskit

The PennyLane-Qiskit plugin integrates the Qiskit quantum computing framework and IBM Q with PennyLane.
https://docs.pennylane.ai/projects/qiskit
Apache License 2.0
184 stars 66 forks source link

Passing an empty list [] of measurements overrides terminal measurements #469

Closed lillian542 closed 7 months ago

lillian542 commented 7 months ago

We inadvertently changed the behaviour of the QASM conversion without warning when updating the Qiskit conversion, because the QASM conversion works by converting from QASM to Qiskit, and then using the main load function. The change was that in the past, the measurements at the end of a QASM string have not queued. Example at the end for clarity.

The solution in this PR is to slightly modify how the measurements kwarg is handled, so that an empty Iterable is not treated as None, but is instead treated as overriding the terminal measurements with an (empty) list of measurements. We can then pass measurements=[] to load to preserve the old behaviour of ignoring terminal measurements without needing to overwrite them with anything.

The downside is that some users might expect passing [] to do the same thing as passing None, but to be honest I don't think this is less clear than the current implementation. Currently we say:

measurements (None | MeasurementProcess | list[MeasurementProcess]): an optional PennyLane
    measurement or list of PennyLane measurements that overrides any terminal measurements
    that may be present in the input circuit

So the question is: is [] None-like and therefore behaves as None, or is it a list of measurements that overrides the terminal measurements? I think this behaviour is actually slightly more intuitive and allows users to easily recreate the legacy behaviour in existing code by passing an empty list to measurements.

It's in the main load function, so it will also affect Qiskit circuit conversion.

Note: the tests actually did catch this when it changed, we just misunderstood why the test results had changed and updated the tests instead of realizing the test failures meant something was wrong. Oops.

Example:

oq_circuit = (
    """
    OPENQASM 2.0;
    include "qelib1.inc";
    qreg q[2];
    creg c[2];

    h q[0];
    cx q[0], q[1];

    measure q -> c;
    """
)

import pennylane as qml

pl_template_from_oq = qml.from_qasm(oq_circuit)

@qml.qnode(qml.device("default.qubit"))
def pl_circuit_from_oq():
    pl_template_from_oq(wires=[0, 1])
    return qml.expval(qml.Y(0)), qml.var(qml.Z(1))

qml.draw_mpl(pl_circuit_from_oq)()
>>> print(qml.draw(pl_circuit_from_oq)())
0: ──H─╭●──┤↗├─┤  <Y>
1: ────╰X──┤↗├─┤  Var[Z]

Where previously converting would give:

>>> print(qml.draw(pl_circuit_from_oq)())
0: ──H─╭●──┤  <Y>
1: ────╰X──┤  Var[Z]
codecov[bot] commented 7 months ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 100.00%. Comparing base (265039f) to head (fb1abf1).

Additional details and impacted files ```diff @@ Coverage Diff @@ ## master #469 +/- ## ========================================= Coverage 100.00% 100.00% ========================================= Files 7 7 Lines 552 552 ========================================= Hits 552 552 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.