Skip to content

Update documentation on qudits #5196

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

Merged
merged 12 commits into from
May 10, 2022
200 changes: 170 additions & 30 deletions docs/qudits.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,14 @@
"id": "9c07f9b01c71"
Copy link
Collaborator

@mpharrigan mpharrigan Apr 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of the class cirq.Qid class.

too much class

>unitary evolution on three qudits, a qubit, a qutrit, and another qutrit,

so six things? 3 + 1 + 1+ 1. Maybe a colon is supposed to be here?


Reply via ReviewNB

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, think also that we decided that cirq.Class could be used for first instance and then not repeated. For notebooks this doesn't seem to matter as it doesn't deep link.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

},
"source": [
"Most of the time in quantum computation, we work with qubits, which are 2-level quantum systems. A qu-*d*-it is a generalization of a qubit to a d-level or d-dimension system.\n",
"Most of the time in quantum computation, we work with qubits, which are 2-level quantum systems. But 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$.\n",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a complete sentence as written. Perhaps change to "However, it is possible..."

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

"\n",
"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.\n",
"In Cirq, qudits work exactly like qubits except they have a `dimension` attribute other than 2, and they can only be used with gates specific to that dimension.\n",
"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.\n",
"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` class. \n",
"\n",
"Both qubits and qudits are represented by a `Qid` object.\n",
"To apply a gate to some qudits, the dimensions of the qudits must match the dimensions it works on. For example, if a gate represents a unitary evolution on three qudits, a qubit, a qutrit, and another qutrit, 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.\n",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a colon or something here (or parentheses) to show that the qubit, qutrit and qutrit are the 3 qudits.

For example, if a gate represents a unitary evolution on three qudits**:** a qubit, a qutrit, and another qutrit, then the gate's ...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rewrote as three sentences to make clearer.

"\n",
"To apply a gate to some qudits, the dimensions of the qudits must match the dimensions it works on. For example, if a gate represents a unitary evolution on three qudits, a qubit, a qutrit, and another qutrit, 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. \n",
"\n",
"This is an example single qutrit gate used in a circuit:\n"
"This is an example single qutrit gate acting on a single qutrit in a simple quantum circuit:\n"
]
},
{
Expand All @@ -113,23 +111,42 @@
],
"source": [
"import cirq\n",
"import numpy as np\n",
"\n",
"class QutritPlusGate(cirq.SingleQubitGate):\n",
"class QutritPlusGate(cirq.Gate):\n",
" \"\"\"A gate that adds one in the computational basis of a qutrit.\n",
" \n",
" This gate acts on three-level systems. In the computational basis of\n",
" this system it enacts the transformation U|x> = |x + 1 mod 3>, or\n",
" in other words U|0> = |1>, U|1> = |2>, and U|2> = |0>.\n",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional. Consider using right angle bracket "〉" rather than ">".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

" \"\"\"\n",
" \n",
" def _qid_shape_(self):\n",
" # By implementing this method this gate implements the\n",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: Can this be a docstring with triple quotes rather than line comments? Same below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

" # cirq.qid_shape protocol and will return the tuple (3,)\n",
" # when cirq.qid_shape acts on an instance of this class.\n",
" # This indicates that the gate acts on a single qutrit.\n",
" return (3,)\n",
"\n",
" def _unitary_(self):\n",
" # Since the gate acts on three level systems it has a unitary \n",
" # effect which is a three by three unitary matrix.\n",
" return np.array([[0, 0, 1],\n",
" [1, 0, 0],\n",
" [0, 1, 0]])\n",
"\n",
" def _circuit_diagram_info_(self, args):\n",
" return '[+1]'\n",
"\n",
"# Here we create a qutrit for the gate to act on. \n",
"q0 = cirq.LineQid(0, dimension=3)\n",
"\n",
"# We can now enact the gate on this qutrit.\n",
"circuit = cirq.Circuit(\n",
" QutritPlusGate().on(q0)\n",
")\n",
"\n",
"# When we print this out we see that the qutrit is labeled by its dimension.\n",
"print(circuit)"
]
},
Expand All @@ -139,15 +156,22 @@
"id": "vWUhanx-fofU"
},
"source": [
"## Qids\n",
"## cirq.Qid\n",
"\n",
"`Qid` is the type that represents both qubits and qudits. By default, a qid like `cirq.NamedQubit('a')` is a qubit.\n",
"`cirq.Qid` is the type that represents both qubits and qudits.\n",
"\n",
"While Cirq has the built-in qubit types, it also provides the corresponding Qid types: \n",
"Cirq has the built-in qubit types, `cirq.NamedQubit`, `cirq.GridQubit`, and `cirq.LineQubit`, and it also provides corresponding `cirq.Qid` types: \n",
"\n",
"- `cirq.NamedQid`: To create a qutrit named 'a', specify the dimension with `cirq.NamedQid('a', dimension=3)`.\n",
"- `cirq.GridQid`: To create a 2x2 grid of ququarts, use `cirq.GridQid.rect(2, 2, dimension=4)`.\n",
"- `cirq.LineQid`: In addition, the `LineQid` constructor also supports a dimension argument directly `cirq.LineQid(0, dimension=4)`."
"- `cirq.NamedQid`\n",
" - Example: Create a qutrit named 'a' by specifying the dimension in the constructor: `cirq.NamedQid('a', dimension=3)`.\n",
"- `cirq.GridQid` \n",
" - Example: Create a qutrit at location (2, 0) by specifying the dimension in the constructor: `cirq.GridQid(2, 0, dimension=3)`.\n",
" - 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)`.\n",
"- `cirq.LineQid` \n",
" - Example: Create a qutrit at location 1 on the line by specifying the dimension in the constructor: `cirq.LineQid(0, dimension=3)`.\n",
" - 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)`.\n",
" \n",
"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."
]
},
{
Expand All @@ -156,16 +180,36 @@
"id": "CYYtVX6Ffq0b"
},
"source": [
"### `cirq.qid_shape` and `def _qid_shape_`\n",
"### The `cirq.qid_shape` protocol\n",
"\n",
"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.\n",
"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.\n",
"\n",
"When `Qid`s are used with `Gate`s, `Operation`s, and `Circuit`s, the dimension of each qid must match the corresponding entry in the qid shape. An error is raised otherwise.\n",
"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.\n",
"\n",
"Callers can query the qid shape of an object or a list of `Qid`s by calling `cirq.qid_shape` on it.\n",
"By default, `cirq.qid_shape` will return the equivalent qid shape for qubits if `_qid_shape_` is not defined.\n",
"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)`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ace20c5d8540"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(3,)\n"
]
}
],
"source": [
"# Create an instance of the qutrit gate defined above.\n",
"gate = QutritPlusGate()\n",
"\n",
"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)`."
"# Verify that it acts on a single qutrit.\n",
"print(cirq.qid_shape(gate))"
]
},
{
Expand All @@ -174,14 +218,45 @@
"id": "GFh0hjB4ftMA"
},
"source": [
"### Unitaries, mixtures, and channels\n",
"\n",
"The magic methods `_unitary_`, `_apply_unitary_`, `_mixture_`, and `_kraus_` used to define unitary operations, mixtures, and channels can be used with qudits with one caveat.\n",
"### Unitaries, mixtures, and channels on qudits\n",
"\n",
"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](protocols.md) for how these work.)\n",
"\n",
"The matrix dimensions for qudits will be larger than for qubits based on the values of the qudit dimensions (the object's qid shape). The size of the matrix is determined from the product of the qudit dimensions. \n",
"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. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "d6ea1b23d1c5"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[0 0 1]\n",
" [1 0 0]\n",
" [0 1 0]]\n"
]
}
],
"source": [
"# Create an instance of the qutrit gate defined above. This gate implements _unitary_.\n",
"gate = QutritPlusGate()\n",
"\n",
"For example, a single qubit unitary is a 2x2 matrix, whereas a single qutrit unitary is a 3x3 matrix. A two qutrit unitary is a 9x9 matrix (3 * 3 = 9) and a qubit-ququart unitary is an 8x8 matrix (2 * 4 = 8). The size of the matrices for mixtures and channels follow the same rule."
"# Because it acts on qutrits, its unitary is a 3 by 3 matrix.\n",
"print(cirq.unitary(gate))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "cdd798defc4d"
},
"source": [
"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."
]
},
{
Expand All @@ -190,15 +265,80 @@
"id": "8a68e38cd33a"
},
"source": [
"### Simulators and samplers\n",
"### Simulating qudits\n",
"\n",
"Simulators like `cirq.Simulator` and `cirq.DensityMatrixSimulator` will return simulation results with larger matrices than the same size qubit circuit when simulating qudit circuits.\n",
"Cirq's simulators can be used to simulate or sample from circuits which act on qudits.\n",
"\n",
"The size of the matrix is determined by the product of the dimensions of the qudits being simulated. 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.\n",
"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.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "509a4796a715"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(3,)\n"
]
}
],
"source": [
"# Create a circuit from the gate we defined above.\n",
"q0 = cirq.LineQid(0, dimension=3)\n",
"circuit = cirq.Circuit(QutritPlusGate()(q0))\n",
"\n",
"# Run a simulation of this circuit.\n",
"sim = cirq.Simulator()\n",
"result = sim.simulate(circuit)\n",
"\n",
"# Verify that the returned state is that of a qutrit.\n",
"print(cirq.qid_shape(result))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "67887651a3ca"
},
"source": [
"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$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "f08d7216b7eb"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x=111, 222\n"
]
}
],
"source": [
"# Create a circuit with three qutrit gates.\n",
"q0, q1 = cirq.LineQid.range(2, dimension=3)\n",
"circuit = cirq.Circuit([\n",
" QutritPlusGate()(q0), \n",
" QutritPlusGate()(q1),\n",
" QutritPlusGate()(q1),\n",
" cirq.measure(q0, q1, key=\"x\")\n",
"])\n",
"\n",
"Call `cirq.qid_shape(simulation_result)` to check the qudit dimensions.\n",
"# Sample from this circuit.\n",
"result = cirq.sample(circuit, repetitions=3)\n",
"\n",
"Measurement results from running a qudit circuit are integers in the range `0` to `qid.dimension-1`."
"# See that the results are all integers from 0 to 2.\n",
"print(result)"
]
}
],
Expand Down