# Quantum Circuits¶

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)
```