Skip to content

Commit

Permalink
Docs preview for PR #1982.
Browse files Browse the repository at this point in the history
  • Loading branch information
cuda-quantum-bot committed Aug 1, 2024
1 parent 1e47fbe commit f970d87
Show file tree
Hide file tree
Showing 108 changed files with 5,702 additions and 691 deletions.
Binary file added pr-1982/_images/Bloch_sphere.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 92 additions & 1 deletion pr-1982/_sources/api/default_ops.rst.txt
Original file line number Diff line number Diff line change
Expand Up @@ -541,4 +541,95 @@ This is a non-linear transformation, and no template overloads are available.
.. code-block:: cpp
cudaq::qubit qubit;
my(qubit);
my(qubit);
User-Defined Custom Operations
==============================

Users can define a custom quantum operation by its unitary matrix. First use
the API to register a custom operation, outside of a CUDA-Q kernel. Then the
operation can be used within a CUDA-Q kernel like any of the built-in operations
defined above.
Custom operations are supported on qubits only (`qudit` with `level = 2`).

.. tab:: Python

The :code:`cudaq.register_operation` API accepts an identifier string for
the custom operation and its unitary matrix. The matrix can be a `list` or
`numpy` array of complex numbers. A 1D matrix is interpreted as row-major.


.. code-block:: python
import cudaq
import numpy as np
cudaq.register_operation("custom_h", 1. / np.sqrt(2.) * np.array([1, 1, 1, -1]))
cudaq.register_operation("custom_x", np.array([0, 1, 1, 0]))
@cudaq.kernel
def bell():
qubits = cudaq.qvector(2)
custom_h(qubits[0])
custom_x.ctrl(qubits[0], qubits[1])
cudaq.sample(bell).dump()
.. tab:: C++

The macro :code:`CUDAQ_REGISTER_OPERATION` accepts a unique name for the
operation, the number of target qubits, the number of rotation parameters
(can be 0), and the unitary matrix as a 1D row-major `std::vector<complex>`
representation.

.. code-block:: cpp
#include <cudaq.h>
CUDAQ_REGISTER_OPERATION(custom_h, 1, 0,
{M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2})
CUDAQ_REGISTER_OPERATION(custom_x, 1, 0, {0, 1, 1, 0})
__qpu__ void bell_pair() {
cudaq::qubit q, r;
custom_h(q);
custom_x<cudaq::ctrl>(q, r);
}
int main() {
auto counts = cudaq::sample(bell_pair);
for (auto &[bits, count] : counts) {
printf("%s\n", bits.data());
}
}
For multi-qubit operations, the matrix is interpreted with MSB qubit ordering,
i.e. big-endian convention. The following example shows two different custom
operations, each operating on 2 qubits.


.. tab:: Python

.. literalinclude:: ../snippets/python/using/examples/two_qubit_custom_op.py
:language: python
:start-after: [Begin Docs]
:end-before: [End Docs]


.. tab:: C++

.. literalinclude:: ../snippets/cpp/using/two_qubit_custom_op.cpp
:language: cpp
:start-after: [Begin Docs]
:end-before: [End Docs]


.. note::

Custom operations are currently supported only on :doc:`../using/backends/simulators`.
Attempt to use with a hardware backend will result in runtime error.
275 changes: 275 additions & 0 deletions pr-1982/_sources/examples/python/tutorials/visualization.ipynb.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Visualization\n",
"\n",
"## Qubit Visualization\n",
"\n",
"What are the possible states a qubit can be in and how can we build up a visual cue to help us make sense of quantum states and their evolution?\n",
"\n",
"We know our qubit can have two distinct states: $\\ket{0}$ and $\\ket{1}$. Maybe we need a one-dimensional line whose vertices can\n",
"represent each of the states. We also know that qubits can be in an equal superposition states: $\\ket{+}$ and $\\ket{-}$. This now forces us to extend our 1D line to a 2D Cartesian coordinate system. If you dive deeper you will learn about the existence of states like \n",
"$\\ket{+i}$ and $\\ket{-i}$, this calls for a 3D extension.\n",
"\n",
"It turns out that a sphere is able to depict all the possible states of a single qubit. This is called a Bloch sphere. \n",
"\n",
"<img src=\"images/Bloch_sphere.png\" alt=\"Bloch Sphere\" width=\"300\" height=\"300\">\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let us try to showcase the functionality to render such a 3D representation with CUDA-Q. \n",
"First, let us define a single-qubit kernel that returns a different state each time. This kernel uses random rotations.\n",
"\n",
"Note: CUDA-Q uses the [QuTiP](https://qutip.org) library to render Bloch spheres. The following code will throw an error if QuTiP is not installed. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# install `qutip` in the current Python kernel. Skip this if `qutip` is already installed.\n",
"# `matplotlib` is required for all visualization tasks.\n",
"# Make sure to restart your kernel if you execute this!\n",
"# In a Jupyter notebook, go to the menu bar > Kernel > Restart Kernel.\n",
"# In VSCode, click on the Restart button in the Jupyter toolbar.\n",
"\n",
"# The '\\' before the '>' operator is so that the shell does not misunderstand\n",
"# the '>' qualifier for the bash pipe operation.\n",
"\n",
"import sys\n",
"\n",
"try:\n",
" import matplotlib.pyplot as plt\n",
" import qutip\n",
"\n",
"except ImportError:\n",
" print(\"Tools not found, installing. Please restart your kernel after this is done.\")\n",
" !{sys.executable} -m pip install qutip\\>=5 matplotlib\\>=3.5\n",
" print(\"\\nNew libraries have been installed. Please restart your kernel!\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import cudaq\n",
"import numpy as np\n",
"\n",
"## Retry the subsequent cells by setting the target to density matrix simulator.\n",
"# cudaq.set_target(\"density-matrix-cpu\")\n",
"\n",
"\n",
"@cudaq.kernel\n",
"def kernel(angles: np.ndarray):\n",
" qubit = cudaq.qubit()\n",
" rz(angles[0], qubit)\n",
" rx(angles[1], qubit)\n",
" rz(angles[2], qubit)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, we instantiate a random number generator, so we can get random outputs. We then create 4 random single-qubit states by using `cudaq.add_to_bloch_sphere()` on the output state obtained from the random kernel."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"rng = np.random.default_rng(seed=11)\n",
"blochSphereList = []\n",
"for _ in range(4):\n",
" angleList = rng.random(3) * 2 * np.pi\n",
" sph = cudaq.add_to_bloch_sphere(cudaq.get_state(kernel, angleList))\n",
" blochSphereList.append(sph)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can display the spheres with `cudaq.show()`. Show the first sphere:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cudaq.show(blochSphereList[0])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also show multiple Bloch spheres side by side - simply set the `nrows` and `ncols` in the call to `cudaq.show()` accordingly. Make sure to have more spaces than spheres in your list, else it will throw an error! Let us show two spheres in a row:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cudaq.show(blochSphereList[:2], nrows=1, ncols=2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can show them in a column too, if we want! Simply set the `nrows = 2` and `ncols = 1`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cudaq.show(blochSphereList[:2], nrows=2, ncols=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Can we show the entire list of 4 Bloch spheres we created? Absolutely!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cudaq.show(blochSphereList[:], nrows=2, ncols=2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"What if we had to add multiple vectors to a single Bloch sphere? CUDA-Q uses the [QuTiP](https://www.qutip.org) toolbox to construct Bloch spheres. We can then add multiple states to the same Bloch sphere by passing the sphere object as an argument to `cudaq.add_to_bloch_sphere()`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import qutip\n",
"\n",
"rng = np.random.default_rng(seed=47)\n",
"blochSphere = qutip.Bloch()\n",
"for _ in range(10):\n",
" angleList = rng.random(3) * 2 * np.pi\n",
" sph = cudaq.add_to_bloch_sphere(cudaq.get_state(kernel, angleList), blochSphere)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This created a single Bloch sphere with 10 random vectors. Let us see how it looks."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"blochSphere.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Unfortunately, there is no such handy visualization for multi-qubit states. In particular, a multi-qubit state cannot be visualized as multiple Bloch spheres due to the nature of entanglement that makes quantum computing so powerful. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Kernel Visualization\n",
"\n",
"A CUDA-Q kernel can be visualized using the `cudaq.draw` API which returns a string representing the drawing of the execution path, in the specified format. ASCII (default) and LaTeX formats are supported."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"@cudaq.kernel\n",
"def kernel_to_draw():\n",
" q = cudaq.qvector(4)\n",
" h(q)\n",
" x.ctrl(q[0], q[1])\n",
" y.ctrl([q[0], q[1]], q[2])\n",
" z(q[2])\n",
" \n",
" swap(q[0], q[1])\n",
" swap(q[0], q[3])\n",
" swap(q[1], q[2])\n",
"\n",
" r1(3.14159, q[0])\n",
" tdg(q[1])\n",
" s(q[2])\n",
"\n",
"circuit = cudaq.draw(kernel_to_draw)\n",
"print(circuit)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
},
"vscode": {
"interpreter": {
"hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}
2 changes: 1 addition & 1 deletion pr-1982/_sources/using/backends/backends.rst.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ CUDA-Q Backends

.. deprecated:: 0.8
The `nvidia-fp64`, `nvidia-mgpu`, `nvidia-mqpu`, and `nvidia-mqpu-fp64` targets can be
enabled as extensions of the unified `nvidia` target.
enabled as extensions of the unified `nvidia` target (see `nvidia` :ref:`target documentation <nvidia-backend>`).
These target names might be removed in a future release.
Loading

0 comments on commit f970d87

Please sign in to comment.