From d59aab4597210ee44c440bdf2ee9646eb8972fc9 Mon Sep 17 00:00:00 2001 From: Gabriel Matos Date: Thu, 2 Nov 2023 14:03:51 +0000 Subject: [PATCH 1/3] Add logo, update documentation - Add formatted list of gates to documentation - Summarise README.md, move some content to documentation * Move examples to documentation * Move "Getting started" to documentation, summarise and replace by "Quick start" - Add logo to README.md - Add logo to documentation - Add custom CSS styles to documentation - Enable MyST parser in documentation `conf.py` - Add MyST parser to documentation requirements --- .github/workflows/docs/requirements.txt | 1 + README.md | 80 ++---------- docs/_static/css/custom.css | 53 ++++++++ docs/conf.py | 20 +++ docs/examples.md | 17 +++ docs/gates.rst | 161 ++++++++++++++++++++++++ docs/getting_started.rst | 96 ++++++++++++++ docs/index.rst | 20 ++- docs/logo.svg | 59 +++++++++ 9 files changed, 433 insertions(+), 74 deletions(-) create mode 100644 docs/_static/css/custom.css create mode 100644 docs/examples.md create mode 100644 docs/gates.rst create mode 100644 docs/getting_started.rst create mode 100644 docs/logo.svg diff --git a/.github/workflows/docs/requirements.txt b/.github/workflows/docs/requirements.txt index 84fd018..b6ad7c2 100644 --- a/.github/workflows/docs/requirements.txt +++ b/.github/workflows/docs/requirements.txt @@ -1,2 +1,3 @@ sphinx ~= 7.2 sphinx_rtd_theme +myst_parser \ No newline at end of file diff --git a/README.md b/README.md index ae0b7a8..799dcf3 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,13 @@ # qujax +
+logo +
+ [![PyPI - Version](https://img.shields.io/pypi/v/qujax)](https://pypi.org/project/qujax/) [![DOI](https://joss.theoj.org/papers/10.21105/joss.05504/status.svg)](https://doi.org/10.21105/joss.05504) -* [Installation](#installation) -* [Quick start](#quick-start) - + [Pure state simulation](#pure-state-simulation) - + [Mixed state simulation](#mixed-state-simulation) -* [Converting from TKET](#converting-from-tket) -* [Examples](#examples) -* [Contributing](#contributing) -* [Citing qujax](#citing-qujax) -* [API Reference](https://cqcl.github.io/qujax/) +[**Documentation**](https://cqcl.github.io/qujax/) | [**Installation**](#installation) | [**Quick start**](#quick-start) | [**Examples**](https://cqcl.github.io/qujax/examples.html) | [**Contributing**](#contributing) | [**Citing qujax**](#citing-qujax) qujax is a [JAX](https://github.com/google/jax)-based Python library for the classical simulation of quantum circuits. It is designed to be *simple*, *fast* and *flexible*. @@ -19,7 +15,7 @@ It follows a functional programming design by translating circuits into pure fun qujax can be used both for pure and for mixed quantum state simulation. It not only supports the standard gate set, but also allows user-defined custom operations, including general quantum channels, enabling the user to e.g. model device noise and errors. -An overview of the core functionalities of qujax can be found in the [Quick start](#quick-start) section. More advanced use-cases, including the training of parameterised quantum circuits, are listed in [Examples](#examples). +A summary of the core functionalities of qujax can be found in the [Quick start](#quick-start) section. More advanced use-cases, including the training of parameterised quantum circuits, can be found in the [Examples](https://cqcl.github.io/qujax/examples.html) section of the documentation. ## Installation @@ -33,9 +29,7 @@ pip install qujax **Important note: qujax circuit parameters are expressed in units of $\pi$ (e.g. in the range $[0,2]$ as opposed to $[0, 2\pi]$)**. -### Pure state simulation - -We start by defining the quantum gates making up the circuit, along with the qubits that they act on and the indices of the parameters for each gate. +Start by defining the quantum gates making up the circuit, the qubits that they act on, and the indices of the parameters for each gate. A list of all gates can be found [here](https://github.com/CQCL/qujax/blob/main/qujax/gates.py) (custom operations can be included by [passing an array or function](https://cqcl.github.io/qujax/statetensor/get_params_to_statetensor_func.html) instead of a string). @@ -56,7 +50,7 @@ qujax.print_circuit(circuit_gates, circuit_qubit_inds, circuit_params_inds); # q1: ---------------------CZ-- ``` -We then translate the circuit to a pure function `param_to_st` that takes a set of parameters and an (optional) initial quantum state as its input. +Translate the circuit to a pure function `param_to_st` that takes a set of parameters and an (optional) initial quantum state as its input. ```python param_to_st = qujax.get_params_to_statetensor_func(circuit_gates, @@ -70,18 +64,7 @@ param_to_st(jnp.array([0.1])) The optional initial state can be passed to `param_to_st` using the `statetensor_in` argument. When it is not provided, the initial state defaults to $\ket{0...0}$. -Note that qujax represents quantum states as _statetensors_. For example, for $N=4$ qubits, the corresponding vector space has $2^4$ dimensions, and a quantum state in this space is represented by an array with shape `(2,2,2,2)`. The usual statevector representation with shape `(16,)` can be obtained by calling `.flatten()` or `.reshape(-1)` or `.reshape(2**N)` on this array. - -In the statetensor representation, the coefficient associated with e.g. basis state $\ket{0101}$ is given by `arr[0,1,0,1]`; each axis corresponds to one qubit. - -```python -param_to_st(jnp.array([0.1])).flatten() -# Array([0.58778524+0.j, 0.+0.j, 0.80901706+0.j, 0.+0.j], dtype=complex64) -``` - -Finally, by defining an observable, we can map the statetensor to an expectation value. A general observable is specified using lists of Pauli matrices, the qubits they act on, and the associated coefficients. - -For example, $Z_1Z_2Z_3Z_4 - 2 X_3$ would be written as `[['Z','Z','Z','Z'], ['X']], [[1,2,3,4], [3]], [1., -2.]`. +Map the state to an expectation value by defining an observable using lists of Pauli matrices, the qubits they act on, and the associated coefficients. ```python st_to_expectation = qujax.get_statetensor_to_expectation_func([['Z']], [[0]], [1.]) @@ -99,52 +82,13 @@ expectation_and_grad(jnp.array([0.1])) # Array([-2.987832], dtype=float32)) ``` -### Mixed state simulation -Mixed state simulations are analogous to the above, but with calls to `get_params_to_densitytensor_func` and `get_densitytensor_to_expectation_func` instead. +Mixed state simulations are analogous to the above, but with calls to [`get_params_to_densitytensor_func`](https://cqcl.github.io/qujax/densitytensor/get_params_to_densitytensor_func.html) and [`get_densitytensor_to_expectation_func`](https://cqcl.github.io/qujax/densitytensor/get_densitytensor_to_expectation_func.html) instead. -```python -param_to_dt = qujax.get_params_to_densitytensor_func(circuit_gates, - circuit_qubit_inds, - circuit_params_inds) -dt = param_to_dt(jnp.array([0.1])) -dt.shape -# (2, 2, 2, 2) - -dt_to_expectation = qujax.get_densitytensor_to_expectation_func([['Z']], [[0]], [1.]) -dt_to_expectation(dt) -# Array(-0.3090171, dtype=float32) -``` - -Similarly to a statetensor, which represents the reshaped $2^N$-dimensional statevector of a pure quantum state, a _densitytensor_ represents the reshaped $2^N \times 2^N$ density matrix of a mixed quantum state. This densitytensor has shape `(2,) * 2 * N`. - -For example, for $N=2$, and a mixed state $\frac{1}{2} (\ket{00}\bra{11} + \ket{11}\bra{00} + \ket{11}\bra{11} + \ket{00}\bra{00})$, the corresponding densitytensor `dt` is such that `dt[0,0,1,1] = dt[1,1,0,0] = dt[1,1,1,1] = dt[0,0,0,0] = 1/2`, and all other entries are zero. - -The equivalent density matrix can be obtained by calling `.reshape(2 ** N, 2 ** N)`. +A more in-depth version of the above can be found in the [Getting started](https://cqcl.github.io/qujax/getting_started.html) section of the documentation. More advanced use-cases, including the training of parameterised quantum circuits, can be found in the [Examples](https://cqcl.github.io/qujax/examples.html) section of the documentation. ## Converting from TKET -One can directly convert a [`pytket`](https://cqcl.github.io/tket/pytket/api/) circuit using the [`tk_to_qujax`](https://cqcl.github.io/pytket-qujax/api/api.html#pytket.extensions.qujax.qujax_convert.tk_to_qujax) and [`tk_to_qujax_symbolic`](https://cqcl.github.io/pytket-qujax/api/api.html#pytket.extensions.qujax.qujax_convert.tk_to_qujax_symbolic) functions in the [**`pytket-qujax`**](https://github.com/CQCL/pytket-qujax) extension. - -An example of this can be found in the [`pytket-qujax_heisenberg_vqe.ipynb`](https://github.com/CQCL/pytket/blob/main/examples/pytket-qujax_heisenberg_vqe.ipynb) notebook. - -## Examples - -Below are some use-case notebooks. These both illustrate the flexibility of qujax and the power of directly interfacing with JAX and its package ecosystem. - -- [`heisenberg_vqe.ipynb`](https://github.com/CQCL/qujax/blob/develop/examples/heisenberg_vqe.ipynb) - an implementation of the variational quantum eigensolver to find the ground state of a quantum Hamiltonian. -- [`maxcut_vqe.ipynb`](https://github.com/CQCL/qujax/blob/develop/examples/maxcut_vqe.ipynb) - an implementation of the variational quantum eigensolver to solve a MaxCut problem. Trains with Adam via [`optax`](https://github.com/deepmind/optax) and uses more realistic stochastic parameter shift gradients. -- [`noise_channel.ipynb`](https://github.com/CQCL/qujax/blob/develop/examples/noise_channel.ipynb) - uses the densitytensor simulator to fit the parameters of a depolarising noise channel. -- [`qaoa.ipynb`](https://github.com/CQCL/qujax/blob/develop/examples/qaoa.ipynb) - uses a problem-inspired QAOA ansatz to find the ground state of a quantum Hamiltonian. Demonstrates how to encode more sophisticated parameters that control multiple gates. -- [`barren_plateaus.ipynb`](https://github.com/CQCL/qujax/blob/develop/examples/barren_plateaus.ipynb) - illustrates how to sample gradients of a cost function to identify the presence of barren plateaus. Uses batched/vectorized evaluation to speed up computation. -- [`reducing_jit_compilation_time.ipynb`](https://github.com/CQCL/qujax/blob/develop/examples/reducing_jit_compilation_time.ipynb) - explains how JAX compilation works and how that can lead to excessive compilation times when executing quantum circuits. Presents a solution for the case of circuits with a repeating structure. -- [`variational_inference.ipynb`](https://github.com/CQCL/qujax/blob/develop/examples/variational_inference.ipynb) - uses a parameterised quantum circuit as a variational distribution to fit to a target probability mass function. Uses Adam via [`optax`](https://github.com/deepmind/optax) to minimise the KL divergence between circuit and target distributions. -- [`classification.ipynb`](https://github.com/CQCL/qujax/blob/develop/examples/classification.ipynb) - train a quantum circuit for binary classification using data re-uploading. -- [`generative_modelling.ipynb`](https://github.com/CQCL/qujax/blob/develop/examples/generative_modelling.ipynb) - uses a parameterised quantum circuit as a generative model for a real life dataset. Trains via stochastic gradient Langevin dynamics on the maximum mean discrepancy between statetensor and dataset. - -The [`pytket`](https://github.com/CQCL/pytket) repository also contains `tk_to_qujax` implementations for some of the above at [`pytket-qujax_classification.ipynb`](https://github.com/CQCL/pytket/blob/main/examples/pytket-qujax-classification.ipynb), -[`pytket-qujax_heisenberg_vqe.ipynb`](https://github.com/CQCL/pytket/blob/main/examples/pytket-qujax_heisenberg_vqe.ipynb) -and [`pytket-qujax_qaoa.ipynb`](https://github.com/CQCL/pytket/blob/main/examples/pytket-qujax_qaoa.ipynb). - +A [`pytket`](https://cqcl.github.io/tket/pytket/api/) circuit can be directly converted using the [`tk_to_qujax`](https://cqcl.github.io/pytket-qujax/api/api.html#pytket.extensions.qujax.qujax_convert.tk_to_qujax) and [`tk_to_qujax_symbolic`](https://cqcl.github.io/pytket-qujax/api/api.html#pytket.extensions.qujax.qujax_convert.tk_to_qujax_symbolic) functions in the [**`pytket-qujax`**](https://github.com/CQCL/pytket-qujax) extension. See [`pytket-qujax_heisenberg_vqe.ipynb`](https://github.com/CQCL/pytket/blob/main/examples/pytket-qujax_heisenberg_vqe.ipynb) for an example. ## Contributing diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css new file mode 100644 index 0000000..3bda74d --- /dev/null +++ b/docs/_static/css/custom.css @@ -0,0 +1,53 @@ + +.wy-nav-top{ + background-color: #203847 +} + +.wy-side-nav-search{ + background-color: white; +} + +h1, h2, h3, h4, h5, h6 { + color: #203847 +} + +.icon.icon-home{ + color: #000000 +} + +.wy-menu-vertical p.caption{ + color: #85cfcb; +} + +.wy-side-nav-search > a{ + color: #000000; +} + +.wy-side-nav-search > div.version{ + color: #000000; +} + +.sig { + background: #85cfcb; +} + +.wy-nav-content { + max-width: 1000px; +} + +html.writer-html4 .rst-content dl:not(.docutils) > dt, html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) > dt { + display:block; + background-color: #ebeef1; +} + +#examples ul, ul.simple { + list-style: none; +} + +#examples ul li, ul.simple li { + margin-bottom: 10px; +} + +div.toctree-wrapper .caption-text{ + color: #203847; +} \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index ab4fdb3..0efc68d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,6 +22,7 @@ "sphinx_rtd_theme", "sphinx.ext.napoleon", "sphinx.ext.mathjax", + "myst_parser", ] templates_path = ["_templates"] @@ -42,3 +43,22 @@ } latex_engine = "pdflatex" + +titles_only = True + +rst_prolog = """ +.. role:: python(code) + :language: python +""" + +html_logo = "logo.svg" + +html_static_path = ["_static"] +html_css_files = [ + "css/custom.css", +] + +html_theme_options = { + "collapse_navigation": False, + "prev_next_buttons_location": "None", +} diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..ea7ac98 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,17 @@ +# Examples + +Below are some use-case notebooks. These both illustrate the flexibility of qujax and the power of directly interfacing with JAX and its package ecosystem. + +- [heisenberg_vqe.ipynb](https://github.com/CQCL/qujax/blob/develop/examples/heisenberg_vqe.ipynb) - an implementation of the variational quantum eigensolver to find the ground state of a quantum Hamiltonian. +- [maxcut_vqe.ipynb](https://github.com/CQCL/qujax/blob/develop/examples/maxcut_vqe.ipynb) - an implementation of the variational quantum eigensolver to solve a MaxCut problem. Trains with Adam via [`optax`](https://github.com/deepmind/optax) and uses more realistic stochastic parameter shift gradients. +- [noise_channel.ipynb](https://github.com/CQCL/qujax/blob/develop/examples/noise_channel.ipynb) - uses the densitytensor simulator to fit the parameters of a depolarising noise channel. +- [qaoa.ipynb](https://github.com/CQCL/qujax/blob/develop/examples/qaoa.ipynb) - uses a problem-inspired QAOA ansatz to find the ground state of a quantum Hamiltonian. Demonstrates how to encode more sophisticated parameters that control multiple gates. +- [barren_plateaus.ipynb](https://github.com/CQCL/qujax/blob/develop/examples/barren_plateaus.ipynb) - illustrates how to sample gradients of a cost function to identify the presence of barren plateaus. Uses batched/vectorized evaluation to speed up computation. +- [reducing_jit_compilation_time.ipynb](https://github.com/CQCL/qujax/blob/develop/examples/reducing_jit_compilation_time.ipynb) - explains how JAX compilation works and how that can lead to excessive compilation times when executing quantum circuits. Presents a solution for the case of circuits with a repeating structure. +- [variational_inference.ipynb](https://github.com/CQCL/qujax/blob/develop/examples/variational_inference.ipynb) - uses a parameterised quantum circuit as a variational distribution to fit to a target probability mass function. Uses Adam via [`optax`](https://github.com/deepmind/optax) to minimise the KL divergence between circuit and target distributions. +- [classification.ipynb](https://github.com/CQCL/qujax/blob/develop/examples/classification.ipynb) - train a quantum circuit for binary classification using data re-uploading. +- [generative_modelling.ipynb](https://github.com/CQCL/qujax/blob/develop/examples/generative_modelling.ipynb) - uses a parameterised quantum circuit as a generative model for a real life dataset. Trains via stochastic gradient Langevin dynamics on the maximum mean discrepancy between statetensor and dataset. + +The [pytket](https://github.com/CQCL/pytket) repository also contains `tk_to_qujax` implementations for some of the above at [pytket-qujax_classification.ipynb](https://github.com/CQCL/pytket/blob/main/examples/pytket-qujax-classification.ipynb), +[pytket-qujax_heisenberg_vqe.ipynb](https://github.com/CQCL/pytket/blob/main/examples/pytket-qujax_heisenberg_vqe.ipynb) +and [pytket-qujax_qaoa.ipynb](https://github.com/CQCL/pytket/blob/main/examples/pytket-qujax_qaoa.ipynb). \ No newline at end of file diff --git a/docs/gates.rst b/docs/gates.rst new file mode 100644 index 0000000..5513380 --- /dev/null +++ b/docs/gates.rst @@ -0,0 +1,161 @@ +Quantum gates +======================= + +This is a list of gates that qujax supports natively. You can also define custom operations by directly passing an array or function instead of a string, as documented in :doc:`statetensor/get_params_to_statetensor_func` and :doc:`densitytensor/get_params_to_densitytensor_func`. + +.. list-table:: + :widths: 25 25 50 + :header-rows: 1 + + * - Name(s) + - String + - Matrix representation + * - Pauli X gate + + NOT gate + + Bit flip gate + - :python:`"X"` + - .. math:: X = NOT = \begin{bmatrix}0 & 1\\ 1 & 0 \end{bmatrix} + * - Pauli Y gate + - :python:`"Y"` + - .. math:: Y = \begin{bmatrix}0 & -i\\ i & 0 \end{bmatrix} + * - Pauli Z gate + + Phase flip gate + - :python:`"Z"` + - .. math:: Z = \begin{bmatrix}1 & 0\\ 0 & -1 \end{bmatrix} + * - Hadamard gate + - :python:`"H"` + - .. math:: H = \frac{1}{\sqrt{2}}\begin{bmatrix}1 & 1\\ 1 & -1 \end{bmatrix} + * - S gate + + P (phase) gate + - :python:`"S"` + - .. math:: S = P = \sqrt{Z} = \begin{bmatrix}1 & 0\\ 0 & i \end{bmatrix} + * - Conjugated S gate + - :python:`"Sdg"` + - .. math:: S^\dagger = \begin{bmatrix}1 & 0\\ 0 & -i \end{bmatrix} + * - T gate + - :python:`"T"` + - .. math:: T = \sqrt[4]{Z} = \begin{bmatrix}1 & 0\\ 0 & \exp(\frac{\pi i}{4}) \end{bmatrix} + * - Conjugated T gate + - :python:`"Tdg"` + - .. math:: T^\dagger = \begin{bmatrix}1 & 0\\ 0 & -\exp(\frac{\pi i}{4}) \end{bmatrix} + * - V gate + - :python:`"V"` + - .. math:: V = \sqrt{X} = \frac{1}{\sqrt{2}}\begin{bmatrix}1 & -i\\ -i & 1 \end{bmatrix} + * - Conjugated V gate + - :python:`"Vdg"` + - .. math:: V^\dagger = \frac{1}{\sqrt{2}}\begin{bmatrix}1 & i\\ i & 1 \end{bmatrix} + * - SX gate + - :python:`"SX"` + - .. math:: SX = \sqrt{X} = \frac{1}{2}\begin{bmatrix}1 + i & 1 - i\\ 1 - i & 1 + i \end{bmatrix} + * - Conjugated SX gate + - :python:`"SXdg"` + - .. math:: SX^\dagger = \frac{1}{2}\begin{bmatrix}1 - i & 1 + i\\ 1 + i & 1 - i \end{bmatrix} + * - CX (Controlled X) gate + + CNOT gate + - :python:`"CX"` + - .. math:: CX = CNOT = \begin{bmatrix}I & 0\\ 0 & X \end{bmatrix} = \begin{bmatrix}1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{bmatrix} + * - CY (Controlled Y) gate + - :python:`"CY"` + - .. math:: CY = \begin{bmatrix}I & 0\\ 0 & Y \end{bmatrix} = \begin{bmatrix}1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & -i \\ 0 & 0 & i & 0 \end{bmatrix} + * - Controlled Z gate + - :python:`"CZ"` + - .. math:: CZ = \begin{bmatrix}I & 0\\ 0 & Z \end{bmatrix} = \begin{bmatrix}1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & -1 \end{bmatrix} + * - Controlled Hadamard gate + - :python:`"CH"` + - .. math:: CH = \begin{bmatrix}I & 0\\ 0 & H \end{bmatrix} = \frac{1}{\sqrt{2}}\begin{bmatrix}1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 1 \\ 0 & 0 & 1 & -1 \end{bmatrix} + * - Controlled V gate + - :python:`"CV"` + - .. math:: CV = \begin{bmatrix}I & 0\\ 0 & V \end{bmatrix} = \frac{1}{\sqrt{2}}\begin{bmatrix}1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & -i \\ 0 & 0 & -i & 1 \end{bmatrix} + * - Conjugated controlled V gate + - :python:`"CVdg"` + - .. math:: CVdg = \begin{bmatrix}I & 0\\ 0 & V^\dagger \end{bmatrix} = \frac{1}{\sqrt{2}}\begin{bmatrix}1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & i \\ 0 & 0 & i & 1 \end{bmatrix} + * - Controlled SX gate + - :python:`"CSX"` + - .. math:: CSX = \begin{bmatrix}I & 0\\ 0 & SX \end{bmatrix} = \frac{1}{2}\begin{bmatrix}1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1+i & 1-i \\ 0 & 0 & 1-i & 1+i \end{bmatrix} + * - Conjugated controlled SX gate + - :python:`"CSXdg"` + - .. math:: CSX^\dagger = \begin{bmatrix}I & 0\\ 0 & SX^\dagger \end{bmatrix} = \frac{1}{2}\begin{bmatrix}1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1-i & 1+i \\ 0 & 0 & 1+i & 1-i \end{bmatrix} + * - Toffoli gate + + CCX + + CCNOT + - :python:`"CCX"` + - .. math:: CCX = \begin{bmatrix}I & 0 & 0 & 0\\ 0 & I & 0 & 0 \\ 0 & 0 & I & 0 \\ 0 & 0 & 0 & X \end{bmatrix} = \begin{bmatrix}1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ \end{bmatrix} + * - Echoed cross-resonance gate + - :python:`"ECR"` + - .. math:: ECR = \begin{bmatrix}0 & V^\dagger \\ V & 0 \end{bmatrix} = \frac{1}{2}\begin{bmatrix}0 & 0 & 1 & i \\ 0 & 0 & i & 1 \\ 1 & -i & 0 & 0 \\ i & 1 & 0 & 0 \end{bmatrix} + * - Swap gate + - :python:`"SWAP"` + - .. math:: SWAP = \begin{bmatrix}1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} + * - Controlled swap gate + - :python:`"CSWAP"` + - .. math:: CSWAP = \begin{bmatrix}I & 0 \\ 0 & SWAP \end{bmatrix} = \begin{bmatrix}1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \end{bmatrix} + * - Rotation around X axis + - :python:`"Rx"` + - .. math:: R_X(\theta) = \exp\left(-i \frac{\pi}{2} \theta X\right) = \begin{bmatrix} \cos( \frac{\pi}{2} \theta) & - \sin( \frac{\pi}{2} \theta) \\ - \sin( \frac{\pi}{2} \theta) & \cos( \frac{\pi}{2} \theta) \end{bmatrix} + * - Rotation around X axis + - :python:`"Ry"` + - .. math:: R_Y(\theta) = \exp\left(-i \frac{\pi}{2} \theta Y\right) = \begin{bmatrix} \cos( \frac{\pi}{2} \theta) & i \sin( \frac{\pi}{2} \theta) \\ - i \sin( \frac{\pi}{2} \theta) & \cos( \frac{\pi}{2} \theta) \end{bmatrix} + * - Rotation around Z axis + - :python:`"Rz"` + - .. math:: R_Z(\theta) = \exp\left(-i \frac{\pi}{2} \theta Z\right) = \begin{bmatrix} \cos( \frac{\pi}{2} \theta) + \sin( \frac{\pi}{2} \theta) & 0 \\ 0 & \cos( \frac{\pi}{2} \theta) - \sin( \frac{\pi}{2} \theta) \end{bmatrix} + * - Controlled rotation around X axis + - :python:`"CRx"` + - .. math:: CR_X(\theta) = \begin{bmatrix}I & 0\\ 0 & RX(\theta) \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \cos( \frac{\pi}{2} \theta) & - \sin( \frac{\pi}{2} \theta) \\ 0 & 0 & - \sin( \frac{\pi}{2} \theta) & \cos( \frac{\pi}{2} \theta) \end{bmatrix} + * - Controlled rotation around Y axis + - :python:`"CRy"` + - .. math:: CR_Y(\theta) = \begin{bmatrix}I & 0\\ 0 & RY(\theta) \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \cos( \frac{\pi}{2} \theta) & i \sin( \frac{\pi}{2} \theta) \\ 0 & 0 & - i \sin( \frac{\pi}{2} \theta) & \cos( \frac{\pi}{2} \theta) \end{bmatrix} + * - Controlled rotation around Z axis + - :python:`"CRz"` + - .. math:: CR_Z(\theta) = \begin{bmatrix}I & 0\\ 0 & RZ(\theta)\end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \cos( \frac{\pi}{2} \theta) + \sin( \frac{\pi}{2} \theta) & 0 \\ 0 & 0 & 0 & \cos( \frac{\pi}{2} \theta) - \sin( \frac{\pi}{2} \theta) \end{bmatrix} + * - U3 + - :python:`"U3"` + - .. math:: U3(\alpha,\beta,\gamma) = \exp((\alpha + \beta) i \frac{\pi}{2}) R_Z(\beta) R_Y(\alpha) R_Z(\gamma) + * - U1 + - :python:`"U1"` + - .. math:: U1(\gamma) = U3(0, 0, \gamma) + * - U2 + - :python:`"U2"` + - .. math:: U2(\beta, \gamma) = U3(0.5, \beta, \gamma) + * - Controlled U3 + - :python:`"CU3"` + - .. math:: CU3(\alpha,\beta,\gamma) = \begin{bmatrix}I & 0\\ 0 & U3(\alpha,\beta,\gamma)\end{bmatrix} + * - Controlled U1 + - :python:`"CU1"` + - .. math:: CU1(\gamma) = \begin{bmatrix}I & 0\\ 0 & U1(\gamma)\end{bmatrix} + * - Controlled U2 + - :python:`"CU2"` + - .. math:: CU2(\beta, \gamma) = \begin{bmatrix}I & 0\\ 0 & U2(\beta, \gamma)\end{bmatrix} + * - Imaginary swap + - :python:`"ISWAP"` + - .. math:: iSWAP(\theta) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos( \frac{\pi}{2} \theta) & i \sin( \frac{\pi}{2} \theta) & 0 \\ 0 & i \sin( \frac{\pi}{2} \theta) & \cos( \frac{\pi}{2} \theta) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} + * - Phased imaginary swap + - :python:`"PhasedISWAP"` + - .. math:: PhasedISWAP(\phi, \theta) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos( \frac{\pi}{2} \theta) & \exp(2i\pi \phi) i \sin( \frac{\pi}{2} \theta) & 0 \\ 0 & \exp(- 2i\pi \phi) i \sin( \frac{\pi}{2} \theta) & \cos( \frac{\pi}{2} \theta) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} + * - XXPhase + + XX interaction + - :python:`"XXPhase"` + - .. math:: R_{XX}(\theta) = \exp\left(\frac{\pi}{2} \theta X\otimes X\right) = \begin{bmatrix} \cos( \frac{\pi}{2} \theta) & 0 & 0 & -i \sin( \frac{\pi}{2} \theta) \\ 0 & \cos( \frac{\pi}{2} \theta) & -i \sin( \frac{\pi}{2} \theta) & 0 \\ 0 & -i \sin( \frac{\pi}{2} \theta) & \cos( \frac{\pi}{2} \theta) & 0 \\ -i \sin( \frac{\pi}{2} \theta) & 0 & 0 & \cos( \frac{\pi}{2} \theta) \end{bmatrix} + * - YYPhase + + YY interaction + - :python:`"YYPhase"` + - .. math:: R_{YY}(\theta) = \exp\left(\frac{\pi}{2} \theta Y\otimes Y\right) = \begin{bmatrix} \cos( \frac{\pi}{2} \theta) & 0 & 0 & i \sin( \frac{\pi}{2} \theta) \\ 0 & \cos( \frac{\pi}{2} \theta) & -i \sin( \frac{\pi}{2} \theta) & 0 \\ 0 & -i \sin( \frac{\pi}{2} \theta) & \cos( \frac{\pi}{2} \theta) & 0 \\ i \sin( \frac{\pi}{2} \theta) & 0 & 0 & \cos( \frac{\pi}{2} \theta) \end{bmatrix} + * - ZZPhase + + ZZ interaction + - :python:`"ZZPhase"` + - .. math:: R_{ZZ}(\theta) = \exp\left(\frac{\pi}{2} \theta Z\otimes Z\right) = \begin{bmatrix} \exp( -i \frac{\pi}{2} \theta) & 0 & 0 & 0 \\ 0 & \exp( i \frac{\pi}{2} \theta) & 0 & 0 \\ 0 & 0 & \exp( i \frac{\pi}{2} \theta) & 0 \\ 0 & 0 & 0 & \exp( -i \frac{\pi}{2} \theta) \end{bmatrix} + * - ZZMax + - :python:`"ZZMax"` + - .. math:: ZZMax = R_{ZZ}(0.5) + * - PhasedX + - :python:`"PhasedX"` + - .. math:: PhasedX(\theta, \phi) = R_Z(\phi)R_X(\theta)R_Z(-\phi) \ No newline at end of file diff --git a/docs/getting_started.rst b/docs/getting_started.rst new file mode 100644 index 0000000..827722e --- /dev/null +++ b/docs/getting_started.rst @@ -0,0 +1,96 @@ +Getting started +################# + +**Important note**: qujax circuit parameters are expressed in units of :math:`\pi` (e.g. in the range :math:`[0,2]` as opposed to :math:`[0, 2\pi]`). + +********************* +Pure state simulation +********************* + +We start by defining the quantum gates making up the circuit, along with the qubits that they act on and the indices of the parameters for each gate. + +A list of all gates can be found in :doc:`gates` (custom operations can be included by passing an array or function instead of a string, as documented in :doc:`statetensor/get_params_to_statetensor_func`). + +.. code-block:: python + + from jax import numpy as jnp + import qujax + + # List of quantum gates + circuit_gates = ['H', 'Ry', 'CZ'] + # Indices of qubits the gates will be applied to + circuit_qubit_inds = [[0], [0], [0, 1]] + # Indices of parameters each parameterised gate will use + circuit_params_inds = [[], [0], []] + + qujax.print_circuit(circuit_gates, circuit_qubit_inds, circuit_params_inds); + # q0: -----H-----Ry[0]-----◯--- + # | + # q1: ---------------------CZ-- + +We then translate the circuit to a pure function :python:`param_to_st` that takes a set of parameters and an (optional) initial quantum state as its input. + +.. code-block:: python + + param_to_st = qujax.get_params_to_statetensor_func(circuit_gates, + circuit_qubit_inds, + circuit_params_inds) + + param_to_st(jnp.array([0.1])) + # Array([[0.58778524+0.j, 0. +0.j], + # [0.80901706+0.j, 0. +0.j]], dtype=complex64) + +The optional initial state can be passed to :python:`param_to_st` using the :python:`statetensor_in` argument. When it is not provided, the initial state defaults to :math:`\ket{0...0}`. + +Note that qujax represents quantum states as *statetensors*. For example, for :math:`N=4` qubits, the corresponding vector space has :math:`2^4` dimensions, and a uantum state in this space is represented by an array with shape :python:`(2,2,2,2)`. The usual statevector representation with shape :python:`(16,)` can be obtained by calling :python:`.flatten()` or :python:`.reshape(-1)` or :python:`.reshape(2**N)` on this array. + +In the statetensor representation, the coefficient associated with e.g. basis state :math:`\ket{0101}` is given by `arr[0,1,0,1]`; each axis corresponds to one qubit. + +.. code-block:: python + + param_to_st(jnp.array([0.1])).flatten() + # Array([0.58778524+0.j, 0.+0.j, 0.80901706+0.j, 0.+0.j], dtype=complex64) + + +Finally, by defining an observable, we can map the statetensor to an expectation value. A general observable is specified using lists of Pauli matrices, the qubits they act on, and the associated coefficients. + +For example, :math:`Z_1Z_2Z_3Z_4 - 2 X_3` would be written as :python:`[['Z','Z','Z','Z'], ['X']], [[1,2,3,4], [3]], [1., -2.]`. + +.. code-block:: python + + st_to_expectation = qujax.get_statetensor_to_expectation_func([['Z']], [[0]], [1.]) + + +Combining :python:`param_to_st` and :python:`st_to_expectation` gives us a parameter to expectation function that can be automatically differentiated using JAX. + +.. code-block:: python + + from jax import value_and_grad + + param_to_expectation = lambda param: st_to_expectation(param_to_st(param)) + expectation_and_grad = value_and_grad(param_to_expectation) + expectation_and_grad(jnp.array([0.1])) + # (Array(-0.3090171, dtype=float32), + # Array([-2.987832], dtype=float32)) + +*********************** +Mixed state simulation +*********************** +Mixed state simulations are analogous to the above, but with calls to :doc:`densitytensor/get_params_to_densitytensor_func` and :doc:`densitytensor/get_densitytensor_to_expectation_func` instead. + +.. code-block:: python + + param_to_dt = qujax.get_params_to_densitytensor_func(circuit_gates, + circuit_qubit_inds, + circuit_params_inds) + dt = param_to_dt(jnp.array([0.1])) + dt.shape + # (2, 2, 2, 2) + + dt_to_expectation = qujax.get_densitytensor_to_expectation_func([['Z']], [[0]], [1.]) + dt_to_expectation(dt) + # Array(-0.3090171, dtype=float32) + +Similarly to a statetensor, which represents the reshaped :math:`2^N`-dimensional statevector of a pure quantum state, a *densitytensor* represents the reshaped :math:`2^N \times 2^N` density matrix of a mixed quantum state. This densitytensor has shape :python:`(2,) * 2 * N`. + +For example, for :math:`N=2`, and a mixed state :math:`\frac{1}{2} (\ket{00}\bra{11} + \ket{11}\bra{00} + \ket{11}\bra{11} + \ket{00}\bra{00})`, the corresponding densitytensor :python:`dt` is such that :python:`dt[0,0,1,1] = dt[1,1,0,0] = dt[1,1,1,1] = dt[0,0,0,0] = 1/2`, and all other entries are zero. \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 7fded40..e3d50bb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,13 +4,14 @@ Welcome to qujax's documentation! ``qujax`` is a `JAX `_-based Python library for the classical simulation of quantum circuits. It is designed to be *simple*, *fast* and *flexible*. -It follows a functional programming design by translating circuits into pure functions. This allows qujax to `seamlessly and directly interface with JAX `_. -Source code can be found on `GitHub `_, including a suite of `example notebooks `_. +It follows a functional programming design by translating circuits into pure functions. This allows qujax to `seamlessly and directly interface with JAX `_, enabling direct access to its powerful automatic differentiation tools, just-in-time compiler, vectorization capabilities, GPU/TPU integration and growing ecosystem of packages. -The `pytket-qujax `_ extension can be used to translate a `tket `_ circuit directly into ``qujax``. +If you are new to the library, we recommend that you head to the :doc:`getting_started` section of the documentation. More advanced use-cases, including the training of parameterised quantum circuits, can be found in :doc:`examples`. -**Note that ``qujax`` assumes parameters are given in units of π (i.e. in [0,2] rather than [0, 2π]).** +The source code can be found on `GitHub `_. The `pytket-qujax `_ extension can be used to translate a `tket `_ circuit directly into ``qujax``. + +**Important note**: qujax circuit parameters are expressed in units of :math:`\pi` (e.g. in the range :math:`[0,2]` as opposed to :math:`[0, 2\pi]`). Install ================================= @@ -42,14 +43,22 @@ If you have used qujax in your code or research, we kindly ask that you cite it. Contents ================================= +.. toctree:: + :caption: Documentation: + :titlesonly: + + Getting started + Examples + List of gates + .. toctree:: :caption: API Reference: :titlesonly: + :maxdepth: 1 Pure state simulation Mixed state simulation Utility functions - List of gates .. toctree:: :caption: Links: @@ -57,6 +66,5 @@ Contents GitHub Paper - Example notebooks PyPI pytket-qujax diff --git a/docs/logo.svg b/docs/logo.svg new file mode 100644 index 0000000..597a250 --- /dev/null +++ b/docs/logo.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + From 956644ba9a4b0252b6fbbe3d11eb2847963743e3 Mon Sep 17 00:00:00 2001 From: Gabriel Matos Date: Thu, 2 Nov 2023 18:41:06 +0000 Subject: [PATCH 2/3] Add version to `myst_parser` documentation requirement --- .github/workflows/docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs/requirements.txt b/.github/workflows/docs/requirements.txt index b6ad7c2..478fc6c 100644 --- a/.github/workflows/docs/requirements.txt +++ b/.github/workflows/docs/requirements.txt @@ -1,3 +1,3 @@ sphinx ~= 7.2 sphinx_rtd_theme -myst_parser \ No newline at end of file +myst-parser ~= 2.0 \ No newline at end of file From e56b6a15830e505dd71d4bfa4d9a3f05dbaacd14 Mon Sep 17 00:00:00 2001 From: Gabriel Matos Date: Thu, 2 Nov 2023 21:06:46 +0000 Subject: [PATCH 3/3] Update logo --- docs/_static/css/custom.css | 8 ++++---- docs/logo.svg | 35 ++++++++++++++--------------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css index 3bda74d..77681d9 100644 --- a/docs/_static/css/custom.css +++ b/docs/_static/css/custom.css @@ -7,10 +7,6 @@ background-color: white; } -h1, h2, h3, h4, h5, h6 { - color: #203847 -} - .icon.icon-home{ color: #000000 } @@ -48,6 +44,10 @@ html.writer-html4 .rst-content dl:not(.docutils) > dt, html.writer-html5 .rst-co margin-bottom: 10px; } +h1, h2, h3, h4, h5, h6 { + color: #203847 +} + div.toctree-wrapper .caption-text{ color: #203847; } \ No newline at end of file diff --git a/docs/logo.svg b/docs/logo.svg index 597a250..d4f4d43 100644 --- a/docs/logo.svg +++ b/docs/logo.svg @@ -9,11 +9,11 @@ id="svg17698" inkscape:version="1.2.2 (732a01da63, 2022-12-09, custom)" sodipodi:docname="logo.svg" + xml:space="preserve" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg"> - - - - - - - - + sodipodi:nodetypes="ccccccc" />