Implementing your own gates in PennyLane does not require deep manipulation of the code base but is as easy as listing some core properties of the operation to be added. Even easier, you can construct your own templates to create structured special purpose circuits and simplify your workflow.

## Adding a custom gate

We will build up an example of a custom gate, beginning with a minimal callable Python object and successively adding information to it.

### The bare minimum

Let’s start by looking at a minimal callable implementation of a one-parameter two-qubit gate:

```
from pennylane.operation import Operation
class IncognitoGate(Operation):
num_params = 1
num_wires = 2
par_domain = "R"
```

As we can see, from a programming perspective, we don’t need much to define a new custom gate in PennyLane (although it is not particularly useful at this stage).
The minimal information required to form a valid, callable subclass of `Operation` is the following:

- How many (variational) parameters does the gate take as input? (
`num_params`) - On how many wires does the gate act? (
`num_wires`) - From which domain are the parameters of the gate chosen? (
`par_domain`)

The available domains are:- “N”: natural numbers and \(0\).
- “R”: real numbers, i.e. floating point numbers.
- “A”: arrays of real or complex values.
- “L”: lists of arrays of real or complex values.
`None`: there are no parameters. Note that`par_domain`has to be provided explicitly in this case (as it is an abstract method).

### A working example

While it forms a well-defined gate, the `IncognitoGate` does not do anything yet and in addition it will of course not be supported on any PennyLane device.
Let us therefore promote it to a meaningful operation, for example a Pauli rotation generated by the Pauli word \(X\otimes X\), which can be implemented via `PauliRot`:

```
import pennylane as qml
from pennylane.operation import Operation
from pennylane import numpy as np
class RXX(Operation):
num_params = 1
num_wires = 2
par_domain = "R"
@staticmethod
def decomposition(theta, wires):
return [qml.PauliRot(theta, 'XX', wires=wires)]
```

We add the method `decomposition` to relate our new gate to the gates that PennyLane already knows. This suffices to make the gate useful and usable in a quantum function:

```
dev = qml.device('default.qubit', wires=2, shots=100)
@qml.qnode(dev)
def circuit(theta):
RXX(theta, wires=[0, 1])
qml.Hadamard(1)
return qml.expval(qml.PauliZ(0) @ qml.PauliY(1))
```

```
>>> circuit(0.3)
tensor(-0.12, requires_grad=True)
```

Notice how we did not have to implement the action of the gate in detail. The support for our brand-new gate `RXX` was simply inherited and all devices supporting `qml.PauliRot` - either directly or by decomposing it - will also support `RXX`.

### Adding details

Now that we know the bare minimum required to create a useful gate, let’s consider some optional information we may want to provide about our gate:

#### Gradient details

As our new gate is parametrized by a real-valued parameter, we may want to tell PennyLane that there is a parameter-shift rule to compute its gradient analytically. This can be done by providing the subclass with the method `grad_method`. Available methods for differentiation are:

- “A”: Analytic differentiation via the parameter shift rule. The particular rule is stored in the
`grad_recipe`of the gate and defaults to the two-term parameter shift rule. - “F”: Numeric differentiation via finite difference. The order of the rule (
`1`for forward finite difference or`2`for central finite difference) and the shift size`h`can be passed to`qml.qnode`. `None`: No differentiation of the operation is possible/available.

If we set `grad_method = "A"` but the applicable parameter shift rule is not the default two-term rule, provide a `grad_recipe` to the gate explicitly. An example would be the four-term rule available as `four_term_grad_recipe` in PennyLane. Take a look at one of the gotchas regarding which gradient rule is used.

#### Matrix representation details

In addition to an implicit definition via `decomposition` it is often useful to provide the matrix of the gate explicitly. We do this via the class method `_matrix` which generates the class attribute `matrix` whenever we instantiate the new gate. Unless we register a new gate as supported by a specific device, the `matrix` will not be used internally, but it can be very useful in other contexts. For example, below we actually make use of the `matrix` attribute of another operator to define the generator property. (Also see a gotcha about only providing `_matrix`.)

Instead of `_matrix`, we may define the instance attribute `matrix` directly, using the property `self.parameters` (and `self.wires` if needed). For our rotation gate, this would take the following form:

```
@property
def matrix(self):
c = np.cos(0.5 * self.parameters[0])
s = np.sin(0.5 * self.parameters[0])
return np.array(
[
[c, 0, 0, -s],
[0, c, -s, 0],
[0, -s, c, 0],
[-s, 0, 0, c]
]
)
```

Furthermore, for some optimizers the generating operator for the gate is used. For our gate, we provide PennyLane with this information via `generator = [(qml.PauliX(0) @ qml.PauliX(1)).matrix, -0.5]`, where the first list entry is the operator and the second entry is the scaling prefactor.
Alternatively, we could pass a NumPy array of the correct size as first entry, in which case we may include the scaling in the generator directly.

#### Other details

For self-adjoint operators or rotation gates like `RXX` we may want to tell PennyLane directly how to obtain the adjoint by implementing the `adjoint` class method.

### The full gate

To sum it up, here is the full definition of our custom rotation gate:

```
import pennylane as qml
from pennylane.operation import Operation
from pennylane import numpy as np
class RXX(Operation):
num_params = 1
num_wires = 2
par_domain = "R"
grad_method = "A"
grad_recipe = None # This is the default but we write it down explicitly here.
generator = [(qml.PauliX(0) @ qml.PauliX(1)).matrix, -0.5]
@staticmethod
def decomposition(theta, wires):
return [qml.PauliRot(theta, 'XX', wires=wires)]
@staticmethod
def _matrix(*params):
theta = params[0]
c = np.cos(0.5 * theta)
s = np.sin(0.5 * theta)
return np.array(
[
[c, 0, 0, -s],
[0, c, -s, 0],
[0, -s, c, 0],
[-s, 0, 0, c]
]
)
def adjoint(self):
return RXX(-self.data[0], wires=self.wires)
```

This new gate now can be used just like any other operation in the PennyLane stack, and will be supported on a wide range of devices.

### Gotchas

Before we move on to creating custom templates in PennyLane, let us look at some gotchas when adding a custom gate:

If a gate has multiple parameters (i.e.

`num_params > 1`), the parameter domain (`par_domain`) and the differentiation method (`grad_method`) of all parameters has to coincide.The

`grad_recipe`method has to be given per parameter. That means for a two-parameter gate that satisfies the two-term (four-term) rule with respect to the first (second) parameter, we would writegrad_recipe = ( [ [0.5, 1, 0.5*np.pi], [-0.5, 1, -0.5*np.pi], ], qml.ops.qubit.four_term_grad_recipe[0], )

As we can see, the structure is a tuple of single gradient recipes, which in turn are lists of entries. Each of these entries finally consists of the prefactor of the term, \(c_i\), a prefactor for the gate parameter, \(a_i\), and the parameter shift, \(s_i\). These are used to compute

\begin{equation*} \frac{\partial}{\partial\phi_k} f = \sum_{i} c_i f(a_i\phi_k + s_i). \end{equation*}Regarding the stored four-term grad recipe, note that we had to extract the first (and only) entry of the tuple

`qml.ops.qubit.four_term_grad_recipe`.One could think about providing a custom gate with

`_matrix`only, fully defining its action on a quantum state, and skip`decomposition`. However, this typically will fail because PennyLane devices do not know how to execute the gate yet. There are two ways to fix this:First, creating a new, special-purpose device would allow us to use the implementation via

`_matrix`directly. Second, adding a custom gate to the*native*gate set of the`default.qubit`device simple and makes the gate available even without a`decomposition`:dev = qml.device('default.qubit', wires=3) dev.operations.add("RXX")

Note that extending the native gate set in general is more complicated or might even be impossible (for example on quantum hardware devices).

If we do not register a custom gate with the device we are using (e.g. by creating a custom device), PennyLane will make use of the decomposition. When computing a derivative with respect to the gate parameter, the decomposition will be done

*first*and therefore`grad_method`and`grad_recipe`of the custom gate will not be used at all.For gates that do not use

`diff_method = "A"`, PennyLane will not allow the definition of a`grad_recipe`to make clear that it would not be used anyways.In the future, the abstract method

`par_domain`will be removed. This would not break our gates defined above but would make the definition of`par_domain`superfluous.

## Adding a custom template

Let’s now look at how to build templates in PennyLane. Templates make our lives much easier by avoiding code duplication and simplifying the structure of our programs. For a guideline and technical details, visit the PennyLane documentation.

### A minimal template

Let’s first look at a very simple template that prepares Bell states on neighbouring qubits.

#### Inheriting from `Operation`

Creating your own custom template in PennyLane and even adding it to the built-in template library can be done easily by implementing a `qml.operation.Operation` subclass, just like we did for the custom gates above.
We will implement the action of the template within the class method `expand`. This method will be used internally by the PennyLane device to obtain a tape with known operations that then can be executed.
This is the recommended way to construct templates which you plan on using often or even adding to the PennyLane library.

```
import pennylane as qml
from pennylane.operation import Operation, AnyWires
class BellPairLayer(Operation):
num_params = 0 # The template does not take any parameters.
num_wires = AnyWires # The template works for any number of wires.
par_domain = None # We will not pass parameters to the template.
def expand(self):
# Create the tape with all operations.
with qml.tape.QuantumTape() as tape:
# Iterate over all qubit pairs
for i in range(len(self.wires)//2):
qml.Hadamard(wires=[self.wires[2*i]])
qml.CNOT(wires=[self.wires[2*i], self.wires[2*i+1]])
return tape
```

We can use the new template like any PennyLane `Operation`. For example, let’s prepare the Bell states and sum over all Pauli Z measurements.

```
from pennylane import numpy as np
num_wires = 8
# Instantiate a qubit device with num_wires=8 wires and 100 shots per expectation value.
dev = qml.device('default.qubit', wires=num_wires, shots=100)
# Simply add an expectation value measurement to the template and create a QNode.
@qml.qnode(dev)
def circuit():
# As we want to call the template on all wires, we may use dev.wires.
BellPairLayer(wires=dev.wires)
return [qml.expval(qml.PauliZ(wire)) for wire in dev.wires]
# Sum the expectation values into a cost function.
def cost():
return np.sum(circuit())
```

```
>>> cost()
tensor(-0.12, requires_grad=False)
```

We see that adding a template to PennyLane does not require much more than spelling out the used operations in the class method `expand`.
The only additional information that is required are the abstract methods

`num_params`, which often will be \(0\) as above or \(1\) because we unpack parameters within`expand`,`num_wires`, which usually will be set to the placeholder`AnyWires`to allow for any number of wires, and`par_domain`, which often will be`"A"`(or`None`for`num_params=0`), but does not play a major role and will be deprecated in future releases.

#### Using the `template` decorator

For simple cases like the Bell pair template above, there is an even easier way of defining the templates, namely as quantum function decorated with `template`:

```
import pennylane as qml
@qml.template
def bell_pair_layer(wires):
# Iterate over all qubit pairs
for i in range(len(wires)//2):
qml.Hadamard(wires=[wires[2*i]])
qml.CNOT(wires=[wires[2*i], wires[2*i+1]])
num_wires = 8
# Instantiate a default qubit device with num_wires=8 wires and 100 shots per expectation value
dev = qml.device('default.qubit', wires=num_wires, shots=100)
# Simply add an expectation value measurement to the template and create a QNode.
@qml.qnode(dev)
def circuit():
# As we want to call the template on all wires, we may use dev.wires.
bell_pair_layer(wires=dev.wires)
return [qml.expval(qml.PauliZ(wire)) for wire in dev.wires]
# Sum the expectation values into a cost function.
def cost():
return np.sum(circuit())
```

As we can see, the quantum function does not construct and return a tape like `BellPairLayer.expand()` did above, but calls the required operations directly.
In addition, the `template` decorator allows us to apply built-ins like `qml.inv()` to the template.

Using the template afterwards is a piece of cake 🍰!

While the decorator method for defining a template is simpler for small circuits like the one shown here, it provides less capabilities and flexibility than the subclassing approach. This makes subclassing the preferred approach to provide templates to others and share it via the PennyLane template library. For more information on contributing your template, please refer to the corresponding technical guideline within the PennyLane documentation.