diff --git a/.github/workflows/config/spelling_allowlist.txt b/.github/workflows/config/spelling_allowlist.txt index f8a892f403..20bb2315a9 100644 --- a/.github/workflows/config/spelling_allowlist.txt +++ b/.github/workflows/config/spelling_allowlist.txt @@ -63,6 +63,7 @@ MPI MPICH MPS MSB +Mandel Max-Cut MyST NGC @@ -78,10 +79,12 @@ OpenMPI OpenQASM OpenSSL OpenSUSE +Ou POSIX PSIRT Pauli Paulis +Photonics PyPI Pygments QAOA @@ -136,6 +139,7 @@ backends bitcode bitstring bitstrings +bmatrix bool boolean boson @@ -168,6 +172,7 @@ cuQuantum cuTensor cudaq dataflow +ddots deallocate deallocated deallocates @@ -322,6 +327,7 @@ unoptimized upvote variadic variational +vdots verifier vertices waveforms diff --git a/docs/sphinx/api/default_ops.rst b/docs/sphinx/api/default_ops.rst index 214c8f6575..6789518664 100644 --- a/docs/sphinx/api/default_ops.rst +++ b/docs/sphinx/api/default_ops.rst @@ -650,7 +650,7 @@ defined by the qudit level that represents the qumode. If it is applied to a qum where the number of photons is already at the maximum value, the operation has no effect. -:math:`U|0\rangle → |1\rangle, U|1\rangle → |2\rangle, U|2\rangle → |3\rangle, \cdots, U|d\rangle → |d\rangle` +:math:`C|0\rangle → |1\rangle, C|1\rangle → |2\rangle, C|2\rangle → |3\rangle, \cdots, C|d\rangle → |d\rangle` where :math:`d` is the qudit level. .. tab:: Python @@ -674,7 +674,7 @@ This operation reduces the number of photons in a qumode up to a minimum value o 0 representing the vacuum state. If it is applied to a qumode where the number of photons is already at the minimum value 0, the operation has no effect. -:math:`U|0\rangle → |0\rangle, U|1\rangle → |0\rangle, U|2\rangle → |1\rangle, \cdots, U|d\rangle → |d-1\rangle` +:math:`A|0\rangle → |0\rangle, A|1\rangle → |0\rangle, A|2\rangle → |1\rangle, \cdots, A|d\rangle → |d-1\rangle` where :math:`d` is the qudit level. .. tab:: Python diff --git a/docs/sphinx/examples/python/executing_photonic_kernels.ipynb b/docs/sphinx/examples/python/executing_photonic_kernels.ipynb new file mode 100644 index 0000000000..62e8901c98 --- /dev/null +++ b/docs/sphinx/examples/python/executing_photonic_kernels.ipynb @@ -0,0 +1,171 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Executing Quantum Photonic Circuits \n", + "\n", + "In CUDA-Q, there are 2 ways in which one can execute quantum photonic kernels: \n", + "\n", + "1. `sample`: yields measurement counts \n", + "3. `get_state`: yields the quantum statevector of the computation \n", + "\n", + "## Sample\n", + "\n", + "Quantum states collapse upon measurement and hence need to be sampled many times to gather statistics. The CUDA-Q `sample` call enables this: \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import cudaq\n", + "import numpy as np\n", + "\n", + "qumode_count = 2\n", + "\n", + "# Define the simulation target.\n", + "cudaq.set_target(\"orca-photonics\")\n", + "\n", + "# Define a quantum kernel function.\n", + "\n", + "\n", + "@cudaq.kernel\n", + "def kernel(qumode_count: int):\n", + " level = qumode_count + 1\n", + " qumodes = [qudit(level) for _ in range(qumode_count)]\n", + "\n", + " # Apply the create gate to the qumodes.\n", + " for i in range(qumode_count):\n", + " create(qumodes[i]) # |00⟩ -> |11⟩\n", + "\n", + " # Apply the beam_splitter gate to the qumodes.\n", + " beam_splitter(qumodes[0], qumodes[1], np.pi / 6)\n", + "\n", + " # measure all qumodes\n", + " mz(qumodes)\n", + "\n", + "\n", + "result = cudaq.sample(kernel, qumode_count, shots_count=1000)\n", + "\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## Get state\n", + "\n", + "The `get_state` function gives us access to the quantum statevector of the computation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import cudaq\n", + "import numpy as np\n", + "\n", + "qumode_count = 2\n", + "\n", + "# Define the simulation target.\n", + "cudaq.set_target(\"orca-photonics\")\n", + "\n", + "# Define a quantum kernel function.\n", + "\n", + "\n", + "@cudaq.kernel\n", + "def kernel(qumode_count: int):\n", + " level = qumode_count + 1\n", + " qumodes = [qudit(level) for _ in range(qumode_count)]\n", + "\n", + " # Apply the create gate to the qumodes.\n", + " for i in range(qumode_count):\n", + " create(qumodes[i]) # |00⟩ -> |11⟩\n", + "\n", + " # Apply the beam_splitter gate to the qumodes.\n", + " beam_splitter(qumodes[0], qumodes[1], np.pi / 6)\n", + "\n", + " # measure some of all qumodes if need to be measured\n", + " # mz(qumodes)\n", + "\n", + "\n", + "# Compute the statevector of the kernel\n", + "result = cudaq.get_state(kernel, qumode_count)\n", + "\n", + "print(np.array(result))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The statevector generated by the `get_state` command follows little-endian convention for associating numbers with their digit string representations, which places the least significant digit on the right. That is, for the example of a 2-qumode system of level 3 (in which possible states are 0, 1, and 2), we have the following translation between integers and digit string:\n", + "$$\\begin{matrix} \n", + "\\text{Integer} & \\text{digit string representation}\\\\\n", + "& \\text{least significant bit on right}\\\\\n", + "0 = \\textcolor{blue}{0}*3^1 + \\textcolor{red}{0}*3^0 & \\textcolor{blue}{0}\\textcolor{red}{0} \\\\\n", + "1 = \\textcolor{blue}{0}*3^1 + \\textcolor{red}{1}*3^0 & \\textcolor{blue}{0}\\textcolor{red}{1}\\\\\n", + "2 = \\textcolor{blue}{0}*3^1 + \\textcolor{red}{2}*3^0 & \\textcolor{blue}{0}\\textcolor{red}{2}\\\\\n", + "3 = \\textcolor{blue}{1}*3^1 + \\textcolor{red}{0}*3^0 & \\textcolor{blue}{1}\\textcolor{red}{0} \\\\\n", + "4 = \\textcolor{blue}{1}*3^1 + \\textcolor{red}{1}*3^0 & \\textcolor{blue}{1}\\textcolor{red}{1} \\\\\n", + "5 = \\textcolor{blue}{1}*3^1 + \\textcolor{red}{2}*3^0 & \\textcolor{blue}{1}\\textcolor{red}{2} \\\\\n", + "6 = \\textcolor{blue}{2}*3^1 + \\textcolor{red}{0}*3^0 & \\textcolor{blue}{2}\\textcolor{red}{0} \\\\\n", + "7 = \\textcolor{blue}{2}*3^1 + \\textcolor{red}{1}*3^0 & \\textcolor{blue}{2}\\textcolor{red}{1} \\\\\n", + "8 = \\textcolor{blue}{2}*3^1 + \\textcolor{red}{2}*3^0 & \\textcolor{blue}{2}\\textcolor{red}{2} \n", + "\\end{matrix}\n", + "$$\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## Parallelization Techniques\n", + "\n", + "The most intensive task in the computation is the execution of the quantum photonic kernel hence each execution function: `sample`, and `get_state` can be parallelized given access to multiple quantum processing units (multi-QPU). We emulate each QPU with a CPU." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(cudaq.__version__)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/sphinx/snippets/python/using/examples/annihilate_photonic_gate.py b/docs/sphinx/snippets/python/using/examples/annihilate_photonic_gate.py new file mode 100644 index 0000000000..4456264a8f --- /dev/null +++ b/docs/sphinx/snippets/python/using/examples/annihilate_photonic_gate.py @@ -0,0 +1,35 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +#[Begin Docs] +import cudaq + +cudaq.set_target("orca-photonics") + + +@cudaq.kernel +def kernel(): + # A single qumode with 2 levels initialized to the ground / zero state. + level = 2 + qumode = qudit(level) + + # Apply the create gate to the qumode. + create(qumode) # |0⟩ -> |1⟩ + + # Apply the create gate to the qumode. + annihilate(qumode) # |1⟩ -> |0⟩ + + # Measurement operator. + mz(qumode) + + +# Sample the qumode for 1000 shots to gather statistics. +# In this case, the results are deterministic and all return state 0. +result = cudaq.sample(kernel) +print(result) +#[End Docs] diff --git a/docs/sphinx/snippets/python/using/examples/beam_splitter_photonic_gate.py b/docs/sphinx/snippets/python/using/examples/beam_splitter_photonic_gate.py new file mode 100644 index 0000000000..5257c171f2 --- /dev/null +++ b/docs/sphinx/snippets/python/using/examples/beam_splitter_photonic_gate.py @@ -0,0 +1,38 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +#[Begin Docs] +import cudaq +import math + +cudaq.set_target("orca-photonics") + + +@cudaq.kernel +def kernel(): + n_modes = 2 + level = 3 # qudit level + + # Two qumode with 3 levels initialized to the ground / zero state. + qumodes = [qudit(level) for _ in range(n_modes)] + + # Apply the create gate to the qumodes. + for i in range(n_modes): + create(qumodes[i]) # |00⟩ -> |11⟩ + + # Apply the beam_splitter gate to the qumodes. + beam_splitter(qumodes[0], qumodes[1], math.pi / 4) + + # Measurement operator. + mz(qumodes) + + +# Sample the qumode for 1000 shots to gather statistics. +result = cudaq.sample(kernel) +print(result) +#[End Docs] diff --git a/docs/sphinx/snippets/python/using/examples/create_photonic_gate.py b/docs/sphinx/snippets/python/using/examples/create_photonic_gate.py new file mode 100644 index 0000000000..f6d9499156 --- /dev/null +++ b/docs/sphinx/snippets/python/using/examples/create_photonic_gate.py @@ -0,0 +1,32 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +#[Begin Docs] +import cudaq + +cudaq.set_target("orca-photonics") + + +@cudaq.kernel +def kernel(): + # A single qumode with 2 levels initialized to the ground / zero state. + level = 2 + qumode = qudit(level) + + # Apply the create gate to the qumode. + create(qumode) # |0⟩ -> |1⟩ + + # Measurement operator. + mz(qumode) + + +# Sample the qumode for 1000 shots to gather statistics. +# In this case, the results are deterministic and all return state 1. +result = cudaq.sample(kernel) +print(result) +#[End Docs] diff --git a/docs/sphinx/using/backends/simulators.rst b/docs/sphinx/using/backends/simulators.rst index 225aa3c0c0..4e808344d9 100644 --- a/docs/sphinx/using/backends/simulators.rst +++ b/docs/sphinx/using/backends/simulators.rst @@ -578,6 +578,45 @@ To execute a program on the :code:`stim` target, use the following commands: can be slower than executing Stim a single time and generating all the shots from that single execution. + +Photonics Simulators +================================== + +The :code:`orca-photonics` target provides a state vector simulator with +the :code:`Q++` library. + +The :code:`orca-photonics` target supports supports a double precision simulator that can run in multiple CPUs. + +OpenMP CPU-only +++++++++++++++++++++++++++++++++++ + +.. _qpp-cpu-photonics-backend: + +This target provides a state vector simulator based on the CPU-only, OpenMP threaded `Q++ `_ library. + +To execute a program on the :code:`orca-photonics` target, use the following commands: + +.. tab:: Python + + .. code:: bash + + python3 program.py [...] --target orca-photonics + + The target can also be defined in the application code by calling + + .. code:: python + + cudaq.set_target('orca-photonics') + + If a target is set in the application code, this target will override the :code:`--target` command line flag given during program invocation. + +.. tab:: C++ + + .. code:: bash + + nvq++ --target orca-photonics program.cpp [...] -o program.x + + Fermioniq ================================== diff --git a/docs/sphinx/using/examples/examples.rst b/docs/sphinx/using/examples/examples.rst index f62c32afd9..ef3fd40e59 100644 --- a/docs/sphinx/using/examples/examples.rst +++ b/docs/sphinx/using/examples/examples.rst @@ -10,9 +10,11 @@ Examples that illustrate how to use CUDA-Q for application development are avail Introduction Building Kernels Quantum Operations + Photonic Operations Measuring Kernels <../../examples/python/measuring_kernels.ipynb> Visualizing Kernels <../../examples/python/visualization.ipynb> Executing Kernels <../../examples/python/executing_kernels.ipynb> + Executing Photonic Kernels <../../examples/python/executing_photonic_kernels.ipynb> Computing Expectation Values Multi-Control Synthesis Multi-GPU Workflows diff --git a/docs/sphinx/using/examples/photonic_operations.rst b/docs/sphinx/using/examples/photonic_operations.rst new file mode 100644 index 0000000000..18bd1ffbfa --- /dev/null +++ b/docs/sphinx/using/examples/photonic_operations.rst @@ -0,0 +1,158 @@ +Photonics 101 +====================== + +Quantum Photonic States +----------------------------- + +We define a qumode (qudit) to have the states +:math:`\ket{0}`, :math:`\ket{1}`, ... :math:`\ket{d}` in Dirac notation where: + +.. math:: \ket{0} = \begin{bmatrix} 1 & 0 & 0 & \dots & 0 \end{bmatrix} ^ \top + +.. math:: \ket{1} = \begin{bmatrix} 0 & 1 & 0 & \dots & 0 \end{bmatrix}^ \top + +.. math:: \ket{2} = \begin{bmatrix} 0 & 0 & 1 & \dots & 0 \end{bmatrix}^ \top + +.. math:: \vdots + +.. math:: \ket{d} = \begin{bmatrix} 0 & 0 & 0 & \dots & 1 \end{bmatrix}^ \top + +where the linear combinations of states or superpositions are: + +.. math:: \ket{\psi} = \alpha_0\ket{0} + \alpha_1\ket{1} + \alpha_2\ket{2} + \dots + \alpha_d\ket{d} + +where :math:`\alpha_i \in \mathbb{C}`. It is important to note that this is +still the state of one qudit; although we have :math:`d` kets, they represent a +superposition state of one qudit. + +Multiple qudits can be combined and the possible combinations of their states +used to process information. + +A two qudit system, :math:`n=2`, with three levels, :math:`d=3`, has +:math:`d^n=8` computational basis states: +:math:`\ket{00}, \ket{01}, \ket{02}, \ket{10}, \ket{11}, \ket{12}, \ket{20}, \ket{21}, \ket{22}`. + +A photonic quantum state of a :math:`n` qudit system with :math:`d` levels is +written as a sum of :math:`d^n` possible basis states where the coefficients +track the probability of the system collapsing into that state if a measurement +is applied. + +Storing the complex numbers associated with :math:`d^n` amplitudes would not be +feasible using bits and classical computations once :math:`n` and :math:`d` are +relatively large. + + +Quantum Photonics Gates +----------------------- + +We can manipulate the state of a qumode via quantum photonic gates. For +example, the create gate allows us to increase the number of photons in a +qumode up to a maximum given by the qudit level :math:`d`: + +.. math:: C \ket{0} = \ket{1} + +.. math:: \begin{bmatrix} + 0 & 0 & \dots & 0 & 0 & 0 & 0 \\ + 1 & 0 & \dots & 0 & 0 & 0 & 0 \\ + 0 & 1 & \dots & 0 & 0 & 0 & 0 \\ + & & \ddots & & & & \\ + 0 & 0 & \dots & 1 & 0 & 0 & 0 \\ + 0 & 0 & \dots & 0 & 1 & 0 & 0 \\ + 0 & 0 & \dots & 0 & 0 & 1 & 1 + \end{bmatrix} + \begin{bmatrix} 1 \\ 0 \\ 0 \\ \vdots \\ 0 \\ 0 \\ 0 \end{bmatrix} = + \begin{bmatrix} 0 \\ 1 \\ 0 \\ \vdots \\ 0 \\ 0 \\ 0 \end{bmatrix} + +.. literalinclude:: ../../snippets/python/using/examples/create_photonic_gate.py + :language: python + :start-after: [Begin Docs] + :end-before: [End Docs] + +.. parsed-literal:: + + { 1:1000 } + +The annihilate gate allows us to decrease the number of photons in a qumode, if +it is applied to a qumode where the number of photons is already at the minimum +value 0, the operation has no effect: + +.. math:: A \ket{1} = \ket{0} + +.. math:: \begin{bmatrix} + 1 & 1 & 0 & 0 & \dots & 0 & 0 \\ + 0 & 0 & 1 & 0 & \dots & 0 & 0 \\ + 0 & 0 & 0 & 1 & \dots & 0 & 0 \\ + & & & & \ddots & & \\ + 0 & 0 & 0 & 0 & \dots & 1 & 0 \\ + 0 & 0 & 0 & 0 & \dots & 0 & 1 \\ + 0 & 0 & 0 & 0 & \dots & 0 & 0 + \end{bmatrix} + \begin{bmatrix} 0 \\ 1 \\ 0 \\ \vdots \\ 0 \\ 0 \\ 0 \end{bmatrix} = + \begin{bmatrix} 1 \\ 0 \\ 0 \\ \vdots \\ 0 \\ 0 \\ 0 \end{bmatrix} + +.. literalinclude:: ../../snippets/python/using/examples/annihilate_photonic_gate.py + :language: python + :start-after: [Begin Docs] + :end-before: [End Docs] + +.. parsed-literal:: + + { 0:1000 } + +A phase shifter adds a phase :math:`\phi` on a qumode. For the annihilation +(:math:`a_1`) and creation operators (:math:`a_1^\dagger`) of a qumode, the +phase shift operator is defined by + +.. math:: + P(\phi) = \exp\left(i \phi a_1^\dagger a_1 \right) + + +Just like the single-qubit gates above, we can define multi-qudit gates to act +on multiple qumodes. + +Beam splitters act on two qumodes together and are parameterized by a single +angle :math:`\theta`, which is related to the transmission amplitude :math:`t` +by :math:`t=\cos(\theta)`. + +For the annihilation (:math:`a_1` and :math:`a_2`) and creation operators +(:math:`a_1^\dagger` and :math:`a_2^\dagger`) of two qumodes, the beam splitter +operator is defined by + +.. math:: + B(\theta) = \exp\left[i \theta (a_1^\dagger a_2 + a_1 a_2^\dagger) \right] + +As an example, the code below implements a simulation of the Hong-Ou-Mandel +effect, in which two identical photons that interfere on a balanced beam +splitter leave the beam splitter together. + +.. literalinclude:: ../../snippets/python/using/examples/beam_splitter_photonic_gate.py + :language: python + :start-after: [Begin Docs] + :end-before: [End Docs] + +.. parsed-literal:: + + { 02:491 20:509 } + + +For a full list of photonic gates supported in CUDA-Q see +:doc:`../../api/default_ops`. + +Measurements +----------------------------- + +Quantum theory is probabilistic and hence requires statistical inference +to derive observations. Prior to measurement, the state of a qumode is +all possible combinations of :math:`\alpha_0, \alpha_1, \dots, \alpha_d` +and upon measurement, wave function collapse yields either a classical +:math:`0, 1, \dots,` or :math:`d`. + +The mathematical theory devised to explain quantum phenomena tells us +that the probability of observing the qumode in the state +:math:`\ket{0}, \ket{1}, \dots, \ket{d}`, yielding a classical +:math:`0, 1, \dots,` or :math:`d`, is :math:`\lvert \alpha_0 \rvert ^2, \lvert \alpha_1 \rvert ^2, \dots,` +or :math:`\lvert \alpha_d \rvert ^2`, respectively. + +As we see in the example of the `beam_splitter` gate above, states 02 and 20 +are yielded roughly 50% of the times, providing and illustration of the +Hong-Ou-Mandel effect.