View on QuantumAI | Run in Google Colab | View source on GitHub | Download notebook |

```
try:
import cirq
except ImportError:
print("installing cirq...")
!pip install --quiet cirq
print("installed cirq.")
```

Most of the time in quantum computation, we work with qubits, which are 2-level quantum systems. However, it is possible to also define quantum computation with higher dimensional systems. A qu-*d*-it is a generalization of a qubit to a d-level or d-dimension system. For example, the state of a single qubit is a superposition of two basis states, \(|\psi\rangle=\alpha|0\rangle+\beta|1\rangle\), whereas the state of a qudit for a three dimensional system is a superposition of three basis states \(|\psi\rangle=\alpha|0\rangle+\beta|1\rangle+\gamma|2\rangle\).

Qudits with known values for d have specific names. A **qubit** has dimension 2, a **qutrit** has dimension 3, a **ququart** has dimension 4, and so on.
In Cirq, qudits work exactly like qubits except they have a `dimension`

attribute different than 2, and they can only be used with gates specific to that dimension. In cirq, both qubits and qudits are subclasses of the class `cirq.Qid`

.

To apply a gate to some qudits, the dimensions of the qudits must match the dimensions it works on. For example, consider gate represents a unitary evolution on three qudits,. Further suppose that there are a qubit, a qutrit, and another qutrit. Then the gate's "qid shape" is `(2, 3, 3)`

and its `on`

method will accept exactly 3 `Qid`

s with dimension 2, 3, and 3, respectively.

This is an example single qutrit gate acting on a single qutrit in a simple quantum circuit:

```
import cirq
import numpy as np
class QutritPlusGate(cirq.Gate):
"""A gate that adds one in the computational basis of a qutrit.
This gate acts on three-level systems. In the computational basis of
this system it enacts the transformation U|x〉 = |x + 1 mod 3〉, or
in other words U|0〉 = |1〉, U|1〉 = |2〉, and U|2> = |0〉.
"""
def _qid_shape_(self):
# By implementing this method this gate implements the
# cirq.qid_shape protocol and will return the tuple (3,)
# when cirq.qid_shape acts on an instance of this class.
# This indicates that the gate acts on a single qutrit.
return (3,)
def _unitary_(self):
# Since the gate acts on three level systems it has a unitary
# effect which is a three by three unitary matrix.
return np.array([[0, 0, 1],
[1, 0, 0],
[0, 1, 0]])
def _circuit_diagram_info_(self, args):
return '[+1]'
# Here we create a qutrit for the gate to act on.
q0 = cirq.LineQid(0, dimension=3)
# We can now enact the gate on this qutrit.
circuit = cirq.Circuit(
QutritPlusGate().on(q0)
)
# When we print this out we see that the qutrit is labeled by its dimension.
print(circuit)
```

0 (d=3): ───[+1]───

## cirq.Qid

`cirq.Qid`

is the type that represents both qubits and qudits.

Cirq has the built-in qubit types, `cirq.NamedQubit`

, `cirq.GridQubit`

, and `cirq.LineQubit`

, and it also provides corresponding `cirq.Qid`

types:

`cirq.NamedQid`

- Example: Create a qutrit named 'a' by specifying the dimension in the constructor:
`cirq.NamedQid('a', dimension=3)`

.

- Example: Create a qutrit named 'a' by specifying the dimension in the constructor:
`cirq.GridQid`

- Example: Create a qutrit at location (2, 0) by specifying the dimension in the constructor:
`cirq.GridQid(2, 0, dimension=3)`

. - Example: You can create regions of
`cirq.GridQid`

s. For example, to create a 2x2 grid of ququarts, use`cirq.GridQid.rect(2, 2, dimension=4)`

.

- Example: Create a qutrit at location (2, 0) by specifying the dimension in the constructor:
`cirq.LineQid`

- Example: Create a qutrit at location 1 on the line by specifying the dimension in the constructor:
`cirq.LineQid(0, dimension=3)`

. - Example: You can create ranges of
`cirq.LineQid`

s. For example, to create qutrits on a line with locations from 0 to 4, use`cirq.LineQid.range(5, dimension=3)`

.

- Example: Create a qutrit at location 1 on the line by specifying the dimension in the constructor:

By default `cirq.Qid`

classes in cirq will default to qubits unless their `dimension`

parameter is specified in creation. Thus a `cirq.Qid`

like `cirq.NamedQid('a')`

is a qubit.

### The `cirq.qid_shape`

protocol

Quantum gates, operations, and other types that act on a sequence of qudits can specify the dimension of each qudit they act on by implementing the `_qid_shape_`

magic method. This method returns a tuple of integers corresponding to the required dimension of each qudit it operates on, e.g. `(2, 3, 3)`

means an object that acts on a qubit, a qutrit, and another qutrit. When you specify `_qid_shape_`

we say that the object implements the `qid_shape`

protocol.

When `cirq.Qid`

s are used with `cirq.Gate`

s, `cirq.Operation`

s, and `cirq.Circuit`

s, the dimension of each qid must match the corresponding entry in the qid shape. An error is raised otherwise.

Callers can query the qid shape of an object or a list of `Qid`

s by calling `cirq.qid_shape`

on it. By default, `cirq.qid_shape`

will return the equivalent qid shape for qubits if `_qid_shape_`

is not defined. In particular, for a qubit-only gate the qid shape is a tuple of 2s containing one 2 for each qubit e.g. `(2,) * cirq.num_qubits(gate)`

.

```
# Create an instance of the qutrit gate defined above.
gate = QutritPlusGate()
# Verify that it acts on a single qutrit.
print(cirq.qid_shape(gate))
```

(3,)

### Unitaries, mixtures, and channels on qudits

The magic methods `_unitary_`

, `_apply_unitary_`

, `_mixture_`

, and `_kraus_`

can be used to define unitary gates, mixtures, and channels can be used with qudits (see protocols for how these work.)

Because the state space for qudits for \(d>2\) live on larger dimensional spaces, the corresponding objects returned by the magic methods will be of corresponding higher dimension.

```
# Create an instance of the qutrit gate defined above. This gate implements _unitary_.
gate = QutritPlusGate()
# Because it acts on qutrits, its unitary is a 3 by 3 matrix.
print(cirq.unitary(gate))
```

[[0 0 1] [1 0 0] [0 1 0]]

For a single qubit gate, its unitary is a 2x2 matrix, whereas for a single qutrit gate its unitary is a 3x3 matrix. A two qutrit gate will have a unitary that is a 9x9 matrix (3 * 3 = 9) and a qubit-ququart gate will have a unitary that is an 8x8 matrix (2 * 4 = 8). The size of the matrices involved in defining mixtures and channels follow the same pattern.

### Simulating qudits

Cirq's simulators can be used to simulate or sample from circuits which act on qudits.

Simulators like `cirq.Simulator`

and `cirq.DensityMatrixSimulator`

will return simulation results with larger states than the same size qubit circuit when simulating qudit circuits. The size of the state returned is determined by the product of the dimensions of the qudits being simulated. For example, the state vector output of `cirq.Simulator`

after simulating a circuit on a qubit, a qutrit, and a qutrit will have 2 * 3 * 3 = 18 elements. You can call `cirq.qid_shape(simulation_result)`

to check the qudit dimensions.

```
# Create a circuit from the gate we defined above.
q0 = cirq.LineQid(0, dimension=3)
circuit = cirq.Circuit(QutritPlusGate()(q0))
# Run a simulation of this circuit.
sim = cirq.Simulator()
result = sim.simulate(circuit)
# Verify that the returned state is that of a qutrit.
print(cirq.qid_shape(result))
```

(3,)

Circuits on qudits are always assumed to start in the \(|0\rangle\) computational basis state, and all the computational basis states of a qudit are assumed to be \(|0\rangle\), \(|1\rangle\), ..., \(|d-1\rangle\). Correspondingly, measurements of qudits are assumed to be in the computational basis and for each qudit return an integer corresponding to these basis states. Thus measurement results for each qudit are assumed to run from \(0\) to \(d-1\).

```
# Create a circuit with three qutrit gates.
q0, q1 = cirq.LineQid.range(2, dimension=3)
circuit = cirq.Circuit([
QutritPlusGate()(q0),
QutritPlusGate()(q1),
QutritPlusGate()(q1),
cirq.measure(q0, q1, key="x")
])
# Sample from this circuit.
result = cirq.sample(circuit, repetitions=3)
# See that the results are all integers from 0 to 2.
print(result)
```

x=111, 222