Skip to content
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

Copy edits made to Advanced sections and Tutorials #964

Merged
merged 2 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/sphinx/examples/python/tutorials/cost_minimization.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"source": [
"# Cost Minimization\n",
"\n",
"Below we start with a basic example of a hybrid variational algorithm which involves flipping the bloch vector of a qubit from the $\\ket{0}$ to the $\\ket{1}$ state. First we import the relevant packages and set our backend to simulate our workflow on NVIDIA GPUs. \n",
"Below we start with a basic example of a hybrid variational algorithm which involves flipping the Bloch vector of a qubit from the $\\ket{0}$ to the $\\ket{1}$ state. First we import the relevant packages and set our backend to simulate our workflow on NVIDIA GPUs. \n",
"\n",
"\n"
]
Expand Down Expand Up @@ -97,7 +97,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Below we use our built-in optimization suite to minimize the cost function. We will be using the gradient free COBYLA alogrithm. "
"Below we use our built-in optimization suite to minimize the cost function. We will be using the gradient-free COBYLA alogrithm. "
]
},
{
Expand Down
11 changes: 6 additions & 5 deletions docs/sphinx/examples/python/tutorials/executing_circuits.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
"source": [
"# Executing Quantum Circuits \n",
"\n",
"One can excute CUDA Quantum kernels via the `sample` and `observe` function calls. \n",
"In CUDA Quantum, quantum circuits are stored as quantum kernels. For estimating the probability distribution of a measured quantum state in a circuit, we use the `sample` function call, and for computing the expectation value of a quantum state with a given observable, we use the `observe` function call. \n",
"\n",
"Quantum states collapse upon measurement and hence need to be sampled from many times to gather statistics. The CUDA Quantum `sample` call enables this: \n",
"## Sample\n",
"\n",
"## Sample"
"Quantum states collapse upon measurement and hence need to be sampled many times to gather statistics. The CUDA Quantum `sample` call enables this: \n",
"\n"
]
},
{
Expand Down Expand Up @@ -60,11 +61,11 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"In simulation mode, the quantum state is built once and then sampled from $s$ times where $s$ equals the `shots_count` . In hardware execution mode, the quantum state collapses upon measurement and hence needs to be rebuilt over and over again. \n",
"Note that there is a subtle difference between how `sample` is executed with the target device set to a simulator or with the target device set to a QPU. In simulation mode, the quantum state is built once and then sampled $s$ times where $s$ equals the `shots_count`. In hardware execution mode, the quantum state collapses upon measurement and hence needs to be rebuilt over and over again. \n",
"\n",
"## Observe\n",
"\n",
"`observe` allows us to gather qubit statistics and calculate expectation values. We must supply a spin operator in the form of a hamiltonian from which we would like to calculate $\\bra{\\psi}H\\ket{\\psi}$."
"The `observe` function allows us to gather qubit statistics and calculate expectation values. We must supply a spin operator in the form of a Hamiltonian from which we would like to calculate $\\bra{\\psi}H\\ket{\\psi}$."
]
},
{
Expand Down
5 changes: 2 additions & 3 deletions docs/sphinx/using/advanced/cmake_app.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ following top-level :code:`CMakeLists.txt` file to build this file with
find_package(CUDAQ REQUIRED)
add_executable(ghz_exe ghz.cpp)

To configure and build this file, all one needs to do is (from the top level of
the :code:`myCudaQApp` folder)
To configure and build this file, all we need to do is execute the following from the top level of the :code:`myCudaQApp` folder:

.. code:: bash

Expand All @@ -35,6 +34,6 @@ the :code:`myCudaQApp` folder)
ninja
./ghz_exe

If you run the :code:`ninja` command with :code:`--verbose` you'll see that
Notice that by running the :code:`ninja` command with :code:`--verbose`, you'll see that
:code:`nvq++` was used to compile and link the executable.

26 changes: 13 additions & 13 deletions docs/sphinx/using/advanced/cudaq_ir.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Working with the CUDA Quantum IR
********************************
Let's see the output of :code:`nvq++` in verbose mode. Given a simple code like
Let's see the output of :code:`nvq++` in verbose mode. Consider a simple code like the one below, saved to file :code:`simple.cpp`.

.. code-block:: cpp
.. code-block:: console

#include <cudaq.h>

Expand All @@ -19,7 +19,7 @@ Let's see the output of :code:`nvq++` in verbose mode. Given a simple code like

int main() { ... }

saved to file :code:`simple.cpp`, we see the following output from :code:`nvq++` verbose mode (up to some absolute paths)
We see the following output from :code:`nvq++` verbose mode (up to some absolute paths).

.. code-block:: console

Expand All @@ -34,13 +34,13 @@ saved to file :code:`simple.cpp`, we see the following output from :code:`nvq++`
clang++ -L/usr/lib/gcc/x86_64-linux-gnu/12 -L/usr/lib64 -L/lib/x86_64-linux-gnu -L/lib64 -L/usr/lib/x86_64-linux-gnu -L/lib -L/usr/lib -L/usr/local/cuda/lib64/stubs -r simple.qke.o simple.classic.o -o simple.o
clang++ -Wl,-rpath,lib -Llib -L/usr/lib/gcc/x86_64-linux-gnu/12 -L/usr/lib64 -L/lib/x86_64-linux-gnu -L/lib64 -L/usr/lib/x86_64-linux-gnu -L/lib -L/usr/lib -L/usr/local/cuda/lib64/stubs simple.o -lcudaq -lcudaq-common -lcudaq-mlir-runtime -lcudaq-builder -lcudaq-ensmallen -lcudaq-nlopt -lcudaq-spin -lcudaq-em-default -lcudaq-platform-default -lnvqir -lnvqir-qpp

The workflow orchestrated above is best visualized in the following figure.
This workflow orchestration is represented in the figure below:

.. image:: ../../_static/nvqpp_workflow.png

We start by mapping CUDA Quantum C++ kernel representations (structs, lambdas, and free functions)
to the Quake dialect. Since we added :code:`--save-temps`,
we can look at the IR code that was produced. :code:`simple.qke` contains
we can look at the IR code that was produced. The base Quake file, :code:`simple.qke`, contains the following:

.. code-block:: console

Expand Down Expand Up @@ -91,17 +91,17 @@ we can look at the IR code that was produced. :code:`simple.qke` contains
}
}

This is the base Quake file, unoptimized and unchanged. It is produced by the
This base Quake file is unoptimized and unchanged. It is produced by the
:code:`cudaq-quake` tool, which also allows us to output the full LLVM IR representation
for the code. This LLVM IR is classical-only, and is directly produced by :code:`clang++`
code-generation. The LLVM IR file :code:`simple.ll` contains the CUDA Quantum kernel
:code:`operator()(Args...)` LLVM function, with a mangled name. Ultimately, we
want to replace this function with our own MLIR-generated function.

Next, the :code:`cudaq-opt` tool is invoked on the :code:`simple.qke` file. This runs an
MLIR pass-pipeline that canonicalizes and optimizes the code. It will also process quantum
MLIR pass pipeline that canonicalizes and optimizes the code. It will also process quantum
lambdas, lift those lambdas to functions, and synthesis adjoint and controlled versions of
CUDA Quantum kernel functions if necessary. The most important pass this this step applies is the
CUDA Quantum kernel functions if necessary. The most important pass that this step applies is the
:code:`kernel-execution` pass, which synthesizes a new entry point LLVM function with the
same name and signature as the original :code:`operator()(Args...)` call function in the
classical :code:`simple.ll` file. We also extract all Quake code representations as strings
Expand All @@ -111,7 +111,7 @@ After :code:`cudaq-opt`, the :code:`cudaq-translate` tool is used to lower the t
Quake representation to an LLVM IR representation, specifically the QIR. We finish by lowering
this representation to object code via standard LLVM tools (e.g. :code:`llc`), and merge all
object files into a single object file, ensuring that our new mangled :code:`operator()(Args...)`
call is injected first, thereby over-writing the original. Finally, based on user compile flags,
call is injected first, thereby overwriting the original. Finally, based on user compile flags,
we configure the link line with specific libraries that implement the :code:`quantum_platform`
(here the :code:`libcudaq-platform-default.so`) and NVQIR circuit simulator backend (the
:code:`libnvqir-qpp.so` Q++ CPU-only simulation backend). These latter libraries are controlled
Expand Down Expand Up @@ -141,8 +141,8 @@ Quake represents an IR closer to the CUDA Quantum source language and models qub
quantum instructions via memory semantics. Quake can be fully dynamic and in
that sense represents a quantum circuit template or generator. With runtime
arguments fully specified, Quake code can be used to generate or synthesize
a fully-known quantum circuit. The value semantics form of Quake can thus be
used as a representation for fully-known
a fully known quantum circuit. The value semantics form of Quake can thus be
used as a representation for fully known
or synthesized quantum circuits. Its utility, therefore, lies in its ability to
optimize quantum code. It departs from the memory semantics model of Quake and
expresses the flow of quantum information explicitly as MLIR values.
Expand All @@ -166,13 +166,13 @@ and produce QIR. Recall the code snippet for the kernel
}
};

Using the toolchain, we can lower this to directly to QIR
Using the toolchain, we can lower this directly to QIR,

.. code-block:: console

cudaq-quake simple.cpp | cudaq-opt --canonicalize | cudaq-translate --convert-to=qir

which prints
which prints:

.. code-block:: console

Expand Down
10 changes: 5 additions & 5 deletions docs/sphinx/using/advanced/mlir_pass.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Create your Own CUDA Quantum Compiler Pass
Create your own CUDA Quantum Compiler Pass
******************************************

The CUDA Quantum IR can be transformed, analyzed, or optimized
Expand All @@ -13,17 +13,17 @@ Pass code as part of the existing CUDA Quantum CMake system.
As an example, clone the repository and add the following directory structure
under :code:`lib`, :code:`lib/Plugins/MyCustomPlugin/`. Within this directory create a
:code:`CMakeLists.txt` file and a :code:`MyCustomPlugin.cpp` file. In the CMake file,
add the following
add the following:

.. code:: cmake

add_llvm_pass_plugin(MyCustomPlugin MyCustomPlugin.cpp)

Creating a CUDA Quantum IR pass starts with the implementation of an
:code:`mlir::OperationPass`. A full discussion of the MLIR Pass infrastructure
is beyond the scope of this document, please see
is beyond the scope of this document; please see
`MLIR Passes <https://mlir.llvm.org/docs/PassManagement>`_. To create such
a pass, start with the following template in the :code:`MyCustomPlugin.cpp` file
a pass, start with the following template in the :code:`MyCustomPlugin.cpp` file:

.. code:: cpp

Expand Down Expand Up @@ -89,7 +89,7 @@ and also that there is a :code:`lib/Plugins/CMakeLists.txt` file that adds your
plugin directory with :code:`add_subdirectory`.

Then build CUDA Quantum and you will have a :code:`MyCustomPlugin.so` plugin library
in the install. You can load the plugin and use it with :code:`cudaq-opt` as follows
in the install. You can load the plugin and use it with :code:`cudaq-opt` as follows:

.. code:: bash

Expand Down
25 changes: 12 additions & 13 deletions docs/sphinx/using/advanced/nvqir_simulator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ The primary extension point for NVQIR is the :code:`CircuitSimulator` class. Thi
exposes an API that enables qubit allocation and deallocation, quantum operation
invocation, and measurement and sampling. Subtypes of this class are free to
override these methods to affect simulation of the quantum code in any
simulation strategy specific manner (e.g. state vector, tensor network, etc.). Moreover,
simulation-strategy-specific manner (e.g., state vector, tensor network, etc.). Moreover,
subtypes are free to implement simulators that leverage classical accelerated computing.

In this document, we'll detail this simulator interface and walk through how one
might extend it for new types of simulation.
In this document, we'll detail this simulator interface and walk through how to extend it for new types of simulation.

:code:`CircuitSimulator`
------------------------
Expand All @@ -28,7 +27,7 @@ This templated type handles a lot of the base functionality required for allocat
as well as measurement, sampling, and observation under a number of execution contexts.
This is the type that downstream simulation developers should extend.

Actual definition of the quantum state data structure, and its overall evolution are
The actual definition of the quantum state data structure, and its overall evolution are
left as tasks for :code:`CircuitSimulatorBase` subclasses. Examples of simulation subtypes can be found
in :code:`runtime/nvqir/qpp/QppCircuitSimulator.cpp` or :code:`runtime/nvqir/custatevec/CuStateVecCircuitSimulator.cpp`.
The :code:`QppCircuitSimulator` models the state vector using the `Q++ <https://github.com/softwareqinc/qpp>`_ library, which
Expand Down Expand Up @@ -62,17 +61,17 @@ The key methods that need to be overridden by subtypes of :code:`CircuitSimulato
- :code:`qubitIdx : std::size_t -> bool` (returns bit result as bool)
- Measure the qubit, produce a bit result, collapse the state.
* - :code:`sample`
- qubitIdxs : std::vector<std::size_t>, shots : int
- :code`qubitIdxs : std::vector<std::size_t>, shots : int`
- Sample the current multi-qubit state on the provided qubit indices over a certain number of shots
* - :code:`name`
- :code:`void`
- Return the name of this CircuitSimulator, must be the same as the name used in :code:`nvq++ -qpu NAME ...`

The strategy for extending this class is to create a new :code:`cpp` implementation file with the same name as your
To extend a subtype class, you will need to create a new :code:`cpp` implementation file with the same name as your
subtype class name. In this file, you will subclass the :code:`CircuitSimulatorBase<FloatType>` and implement the methods in
the above table. Finally, the subclass must be registered with the NVQIR library so that it
can be picked up and used when a user specifies :code:`nvq++ --target mySimulator ...` from the command line (or :code:`cudaq.set_target('mySimulator')` in Python.)
Type registration can be performed with a provided NVQIR macro
Type registration can be performed with a provided NVQIR macro,

.. code:: cpp

Expand All @@ -99,12 +98,12 @@ Let's see this in action
------------------------

CUDA Quantum provides some CMake utilities to make the creation of your new simulation library
easier. Specifically, but using :code:`find_package(NVQIR)`, you'll get access to a :code:`nvqir_add_backend` function
that will automate a lot of the boilerplate for creating your library and configuration file.
easier. Specifically, by using :code:`find_package(NVQIR)`, you'll get access to a :code:`nvqir_add_backend` function
that will automate much of the boilerplate for creating your library and configuration file.

Let's assume you want a simulation subtype named :code:`MySimulator`. You can create a folder or
repository for this code called :code:`my-simulator` and add :code:`MySimulator.cpp` and
:code:`CMakeLists.txt` files. Fill the CMake file with the following
:code:`CMakeLists.txt` files. Fill the CMake file with the following:

.. code:: cmake

Expand All @@ -113,7 +112,7 @@ repository for this code called :code:`my-simulator` and add :code:`MySimulator.
find_package(NVQIR REQUIRED)
nvqir_add_backend(MySimulator MySimulator.cpp)

and then fill out your :code:`MySimulator.cpp` file with your subtype implementation, something like
and then fill out your :code:`MySimulator.cpp` file with your subtype implementation. For example,

.. code:: cpp

Expand Down Expand Up @@ -156,7 +155,7 @@ and then fill out your :code:`MySimulator.cpp` file with your subtype implementa
/// Register this Simulator with NVQIR.
NVQIR_REGISTER_SIMULATOR(MySimulator)

To build, install, and use this simulation backend, run the following from the top-level of :code:`my-simulator`
To build, install, and use this simulation backend, run the following from the top-level of :code:`my-simulator`:

.. code:: bash

Expand All @@ -165,7 +164,7 @@ To build, install, and use this simulation backend, run the following from the t
cmake .. -G Ninja -DNVQIR_DIR="$CUDA_QUANTUM_PATH/lib/cmake/nvqir"
ninja install

Then given any CUDA Quantum source file, you can compile and target your backend simulator with
Then given any CUDA Quantum source file, you can compile and target your backend simulator with the following:

.. code:: bash

Expand Down
Loading