Quantum Circuits

../_images/qnode.png

In PennyLane, variational quantum circuits are represented as quantum node objects. A quantum node is a combination of a quantum function that composes the circuit, and a device that runs the computation. One can conveniently create quantum nodes using the quantum node decorator.

Each classical interface uses a different version of a quantum node, and we will introduce the standard QNode to use with the NumPy interface here. NumPy-interfacing quantum nodes take NumPy datastructures, such as floats and arrays, and return Numpy data structures. They can be optimized using NumPy-based optimization methods. Quantum nodes for other PennyLane interfaces like PyTorch and TensorFlow’s Eager mode are introduced in the section on interfaces.

Quantum functions

A quantum circuit is constructed as a special Python function, a quantum circuit function, or quantum function in short. For example:

import pennylane as qml

def my_quantum_function(x, y):
    qml.RZ(x, wires=0)
    qml.CNOT(wires=[0,1])
    qml.RY(y, wires=1)
    return qml.expval(qml.PauliZ(1))

Quantum functions are a restricted subset of Python functions, adhering to the following constraints:

  • The body of the function must consist of only supported PennyLane operations or sequences of operations called templates, using one instruction per line.
  • The function must always return either a single or a tuple of measured observable values, by applying a measurement function to a qubit or continuous-value observable.
  • Classical processing of function arguments, either by arithmetic operations or external functions, is not allowed. One current exception is simple scalar multiplication.

Note

The quantum operations cannot be used outside of a quantum circuit function, as all Operations require a QNode in order to perform queuing on initialization.

Note

Measured observables must come after all other operations at the end of the circuit function as part of the return statement, and cannot appear in the middle.

Defining a device

To run - and later optimize - a quantum circuit, one needs to first specify a computational device.

The device is an instance of the Device class, and can represent either a simulator or hardware device. They can be instantiated using the device loader.

dev = qml.device('default.qubit', wires=2)

PennyLane offers some basic devices such as the 'default.qubit' simulator; additional devices can be installed as plugins (see plugins for more details). Note that the choice of a device significantly determines the speed of your computation.

Creating a quantum node

Together, a quantum function and a device are used to create a quantum node or QNode object, which wraps the quantum function and binds it to the device.

A QNode can be explicitly created as follows:

qnode = qml.QNode(my_quantum_function, dev)

The QNode can be used to compute the result of a quantum circuit as if it was a standard Python function. It takes the same arguments as the original quantum function:

>>> qnode(np.pi/4, 0.7)
0.7648421872844883

The QNode decorator

A more convenient - and in fact the recommended - way for creating QNodes is the provided quantum node decorator. This decorator converts a quantum function containing PennyLane quantum operations to a QNode that will run on a quantum device.

Note

The decorator completely replaces the Python-based quantum function with a QNode of the same name - as such, the original function is no longer accessible (but is accessible via the func attribute).

For example:

dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev)
def qfunc(x):
    qml.RZ(x, wires=0)
    qml.CNOT(wires=[0,1])
    qml.RY(x, wires=1)
    return qml.expval(qml.PauliZ(0))

result = qfunc(0.543)