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

Expand documentation for qml.from_qiskit() (abandoned) #5259

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e88583f
some updates to qml.from_qiskit docs
lillian542 Feb 26, 2024
5ef1cee
fix syntax
obliviateandsurrender Feb 26, 2024
af68832
Merge branch 'master' into qiskit_conversion_docs
obliviateandsurrender Feb 26, 2024
d2568ad
Incrementing the version number to `v0.36.0-dev` (#5258)
lillian542 Feb 26, 2024
cb6f1cd
Remove dtype promotion in pauli rep of scalar products with tensorflo…
Qottmann Feb 26, 2024
bcec1aa
only run doc freeze on non-fork PRs (#5270)
timmysilv Feb 28, 2024
d7593a3
Daily rc sync to master (#5274)
github-actions[bot] Feb 28, 2024
ea090dd
Apply @trbromley's suggestions
Mandrenkov Feb 28, 2024
d9b8a89
Merge branch 'master' into qiskit_conversion_docs
Mandrenkov Feb 28, 2024
4faf7f2
Merge branch 'v0.35.0-rc0' into qiskit_conversion_docs
Mandrenkov Feb 28, 2024
3def904
Apply suggestion regarding `measurements` description
Mandrenkov Feb 28, 2024
46e5193
Add links to `Parameter` and `ParameterVector` classes
Mandrenkov Feb 28, 2024
3599c91
Add reference to mid-circuit measurements
Mandrenkov Feb 28, 2024
6bd26ab
Add missing `>` to `ParameterVector` reference
Mandrenkov Feb 28, 2024
e50b824
Replace 'final' with 'terminal'
Mandrenkov Feb 28, 2024
4ed138f
Fix minor reST syntax and grammar issues
Mandrenkov Feb 28, 2024
a84a5b5
Expand section on conditional workflows
Mandrenkov Feb 28, 2024
8683476
Merge branch 'v0.35.0-rc0' into qiskit_conversion_docs
Mandrenkov Feb 28, 2024
7d05c63
Undo changes pulled in from `master`
Mandrenkov Feb 28, 2024
0b1e517
Apply more of @trbromley's suggestions
Mandrenkov Feb 28, 2024
74bacf5
Transfer docstring suggestions to `from_qiskit_op()`
Mandrenkov Feb 28, 2024
bf5c418
Update note about computational basis
Mandrenkov Feb 28, 2024
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
11 changes: 7 additions & 4 deletions doc/introduction/measurements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,9 @@ For example, we could run the previous circuit with ``all_outcomes=True``:
>>> print(result)
{'00': 518, '01': 0, '10': 0, '11': 482}

Note: For complicated Hamiltonians, this can add considerable overhead time (due to the cost of calculating
eigenvalues to determine possible outcomes), and as the number of qubits increases, the length of the output
dictionary showing possible computational basis states grows rapidly.
Note: For complicated Hamiltonians, this can add considerable overhead time (due to the cost of calculating
eigenvalues to determine possible outcomes), and as the number of qubits increases, the length of the output
dictionary showing possible computational basis states grows rapidly.

If counts are obtained along with a measurement function other than :func:`~.pennylane.sample`,
a tuple is returned to provide differentiability for the outputs of QNodes.
Expand All @@ -212,7 +212,7 @@ a tuple is returned to provide differentiability for the outputs of QNodes.
return qml.expval(qml.PauliZ(0)),qml.expval(qml.PauliZ(1)), qml.counts()

>>> circuit()
(-0.036, 0.036, {'01': 482, '10': 518})
(-0.036, 0.036, {'01': 482, '10': 518})

Probability
-----------
Expand Down Expand Up @@ -243,6 +243,9 @@ so corresponds to a :math:`99.75\%` probability of measuring
state :math:`|00\rangle`, and a :math:`0.25\%` probability of
measuring state :math:`|01\rangle`.


.. _mid_circuit_measurements:

Mid-circuit measurements and conditional operations
---------------------------------------------------

Expand Down
266 changes: 236 additions & 30 deletions pennylane/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,39 +95,191 @@ def load(quantum_circuit_object, format: str, **load_kwargs):


def from_qiskit(quantum_circuit, measurements=None):
"""Loads Qiskit `QuantumCircuit <https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.QuantumCircuit>`_
objects by using the converter in the PennyLane-Qiskit plugin.

**Example:**

>>> qc = qiskit.QuantumCircuit(2)
>>> qc.rz(0.543, [0])
>>> qc.cx(0, 1)
>>> my_circuit = qml.from_qiskit(qc)
"""Converts a Qiskit `QuantumCircuit <https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.QuantumCircuit>`_
into a PennyLane :ref:`quantum function <intro_vcirc_qfunc>`.

The ``my_circuit`` template can now be used within QNodes, as a
two-wire quantum template.
.. note::

>>> @qml.qnode(dev)
>>> def circuit(x):
>>> qml.RX(x, wires=1)
>>> my_circuit(wires=(1, 0))
>>> return qml.expval(qml.Z(0))
This function depends upon the PennyLane-Qiskit plugin. Follow the
`installation instructions <https://docs.pennylane.ai/projects/qiskit/en/latest/installation.html>`__
to get up and running. You may need to restart your kernel if you are running in a notebook
environment.

Args:
quantum_circuit (qiskit.QuantumCircuit): a quantum circuit created in Qiskit
measurements (None | MeasurementProcess | list[MeasurementProcess]): the PennyLane
measurements that override the terminal measurements that may be present in the input
circuit
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

Returns:
function: the PennyLane template created based on the ``QuantumCircuit`` object
function: The PennyLane quantum function, created based on the input Qiskit
``QuantumCircuit`` object.

**Example:**

.. code-block:: python

import pennylane as qml
from qiskit import QuantumCircuit

qc = QuantumCircuit(2, 2)
qc.rx(0.785, 0)
qc.ry(1.57, 1)

my_qfunc = qml.from_qiskit(qc)

The ``my_qfunc`` function can now be used within QNodes, as a two-wire quantum
template. We can also pass ``wires`` when calling the returned template to define
which device wires it should operate on. If no wires are passed, it will default
Mandrenkov marked this conversation as resolved.
Show resolved Hide resolved
to sequential wire labels starting at 0.

.. code-block:: python

dev = qml.device("default.qubit")

@qml.qnode(dev)
def circuit():
my_qfunc(wires=["a", "b"])
return qml.expval(qml.Z("a")), qml.var(qml.Z("b"))

>>> circuit()
(tensor(0.70738827, requires_grad=True),
tensor(0.99999937, requires_grad=True))

The measurements can also be passed directly to the function when creating the
quantum function, making it possible to create a PennyLane circuit with
:class:`qml.QNode <~.QNode>`:

>>> measurements = [qml.expval(qml.Z(0)), qml.var(qml.Z(1))]
>>> circuit = qml.QNode(qml.from_qiskit(qc, measurements), dev)
>>> circuit()
(tensor(0.70738827, requires_grad=True),
tensor(0.99999937, requires_grad=True))

.. note::

The ``measurements`` keyword allows one to add a list of PennyLane measurements
that will **override** any terminal measurements present in the ``QuantumCircuit``,
so that they are not performed before the operations specified in ``measurements``.
Converting a ``QuantumCircuit`` with ``measurements`` set will create a quantum function
that does not return terminal or mid-circuit measurement values. See Usage Details below
for more information on how measurements defined on the ``QuantumCircuit`` are returned if
Mandrenkov marked this conversation as resolved.
Show resolved Hide resolved
``measurements=None``.

If an existing ``QuantumCircuit`` already contains measurements, ``from_qiskit``
will return those measurements, which can be used, e.g., for conditioning with
Mandrenkov marked this conversation as resolved.
Show resolved Hide resolved
:func:`qml.cond() <~.cond>`, or simply included directly within the QNode's return:

.. code-block:: python

qc = QuantumCircuit(2, 2)
qc.rx(np.pi, 0)
qc.measure_all()

@qml.qnode(dev)
def circuit():
# Since measurements=None, the measurements present in the QuantumCircuit are returned.
measurements = qml.from_qiskit(qc)()
return [qml.expval(m) for m in measurements]

>>> circuit()
[tensor(1., requires_grad=True), tensor(0., requires_grad=True)]

.. note::

The returned measurements from the ``QuantumCircuit`` are in the measurement basis
and are readout values between 0 (ground) and 1 (excited). Such a measurement on the
``ith`` wire does not match the result of ``qml.expval(qml.PauliZ(i)``, which ranges from
1 (ground) to -1 (excited), indicating a position on the Bloch sphere.

We can alternatively translate the circuit and capture the same information in a different
format as:

>>> measurements = [qml.expval(qml.Z(0)), qml.expval(qml.Z(1))]
>>> circuit = qml.QNode(qml.from_qiskit(qc, measurements), dev)
>>> circuit()
[tensor(-1., requires_grad=True), tensor(1., requires_grad=True)]

See below for more information regarding how to translate more complex circuits from Qiskit to
PennyLane, including handling parameterized Qiskit circuits, mid-circuit measurements, and
classical control flows.

.. details::
:title: Usage Details
:title: Parameterized Quantum Circuits

A Qiskit ``QuantumCircuit`` is parameterized if it contains
`Parameter <https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.Parameter>`__ or
`ParameterVector <https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.ParameterVector>`__
references that need to be given defined values to evaluate the circuit. These can be passed
to the generated quantum function as keyword or positional arguments. If we define a
parameterized circuit:

.. code-block:: python

from qiskit.circuit import QuantumCircuit, Parameter

angle0 = Parameter("x")
angle1 = Parameter("y")

qc = QuantumCircuit(2, 2)
qc.rx(angle0, 0)
qc.ry(angle1, 1)
qc.cx(1, 0)

Then this circuit can be converted into a differentiable circuit in PennyLane and
executed:

.. code-block:: python

import pennylane as qml
from pennylane import numpy as np

dev = qml.device("default.qubit")

qfunc = qml.from_qiskit(qc, measurements=qml.expval(qml.Z(0)))
circuit = qml.QNode(qfunc, dev)

Now, ``circuit`` has a signature of ``(x, y)``. The parameters are ordered alphabetically.

>>> x = np.pi / 4
>>> y = 0
>>> circuit(x, y)
tensor(0.70710678, requires_grad=True)

>>> qml.grad(circuit, argnum=[0, 1])(np.pi/4, np.pi/6)
(array(-0.61237244), array(-0.35355339))

The ``QuantumCircuit`` may also be parameterized with a ``ParameterVector``. These can be
similarly converted:

.. code-block:: python

from qiskit.circuit import ParameterVector

angles = ParameterVector("angles", 2)

qc = QuantumCircuit(2, 2)
qc.rx(angles[0], 0)
qc.ry(angles[1], 1)
qc.cx(1, 0)

@qml.qnode(dev)
def circuit(angles):
qml.from_qiskit(qc)(angles)
return qml.expval(qml.Z(0))

>>> angles = [3.1, 0.45]
>>> circuit(angles)
tensor(-0.89966835, requires_grad=True)

The ``measurement`` keyword allows one to add a list of PennyLane measurements
that will override the terminal measurements present in their ``QuantumCircuit``.

.. details::
:title: Measurements and Classical Control Flows

When ``measurement=None``, all of the measurements performed in the ``QuantumCircuit`` will
be returned by the quantum function in the form of a :ref:`mid-circuit measurement
<mid_circuit_measurements>`. For example, if we define a ``QuantumCircuit`` with
measurements:

.. code-block:: python

Expand All @@ -141,17 +293,71 @@ def from_qiskit(quantum_circuit, measurements=None):
qc.cx(0, 1)
qc.measure_all()

measurements = [qml.expval(qml.Z(0)), qml.vn_entropy([1])]
quantum_circuit = qml.from_qiskit(qc, measurements=measurements)
Then we can create a PennyLane circuit that uses this as a sub-circuit, and performs
additional operations conditional on the results. We can also calculate standard mid-circuit
measurement statistics, like expectation value, on the returned measurements:

.. code-block:: python

@qml.qnode(qml.device("default.qubit"))
def circuit_loaded_qiskit_circuit():
return quantum_circuit()
def circuit():
# apply the QuantumCircuit and retrieve the measurements
mid_measure0, m0, m1 = qml.from_qiskit(qc)()

# conditionally apply an additional operation based on the results
qml.cond(mid_measure0==0, qml.RX)(np.pi/2, 0)

# return the expectation value of one of the mid-circuit measurements, and a terminal measurement
return qml.expval(mid_measure0), qml.expval(m1)

>>> circuit()
(tensor(0.5, requires_grad=True), tensor(0.5, requires_grad=True))

Furthermore, the Qiskit `IfElseOp <https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.IfElseOp>`__,
Mandrenkov marked this conversation as resolved.
Show resolved Hide resolved
`SwitchCaseOp <https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.SwitchCaseOp>`__ and
`c_if <https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.Instruction#c_if>`__
conditional workflows are automatically translated into their PennyLane counterparts during
conversion. For example, if we construct a ``QuantumCircuit`` with these workflows:

.. code-block:: python

qc = QuantumCircuit(4, 1)
qc.h(0)
qc.measure(0, 0)

# Use an `IfElseOp` operation.
noop = QuantumCircuit(1)
flip_x = QuantumCircuit(1)
flip_x.x(0)
qc.if_else((qc.clbits[0], True), flip_x, noop, [1], [])

# Use a `SwitchCaseOp` operation.
with qc.switch(qc.clbits[0]) as case:
with case(0):
qc.y(2)

# Use the `c_if()` function.
qc.z(3).c_if(qc.clbits[0], True)

qc.measure_all()

We can convert the ``QuantumCircuit`` into a PennyLane quantum function using:

.. code-block:: python

dev = qml.device("default.qubit")

measurements = [qml.expval(qml.Z(i)) for i in range(qc.num_qubits)]
cond_circuit = qml.QNode(qml.from_qiskit(qc, measurements=measurements), dev)

>>> print(qml.draw(circuit_loaded_qiskit_circuit)())
0: ──H──┤↗├──RZ(0.24)─╭●─┤ <Z>
1: ───────────────────╰X─┤ vnentropy
The result is:

>>> print(qml.draw(cond_circuit)())
0: ──H──┤↗├──────────╭||─┤ <Z>
1: ──────║───X───────├||─┤ <Z>
2: ──────║───║──Y────├||─┤ <Z>
3: ──────║───║──║──Z─╰||─┤ <Z>
╚═══╩══╩══╝
Mandrenkov marked this conversation as resolved.
Show resolved Hide resolved
"""
try:
return load(quantum_circuit, format="qiskit", measurements=measurements)
Expand Down
Loading