final first commit
This commit is contained in:
32
qibojit-benchmarks/.github/workflows/rules.yml
vendored
Normal file
32
qibojit-benchmarks/.github/workflows/rules.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# A single CI script with github workflow
|
||||
name: Tests
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
python-version: [3.9]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install package
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pytest
|
||||
pip install qiskit openfermion qulacs hybridq
|
||||
pip install tensorflow==2.7.0
|
||||
pip install tensorflow_quantum
|
||||
pip install projectq
|
||||
pip install qsimcirq
|
||||
pip install git+https://github.com/qiboteam/qibo
|
||||
pip install git+https://github.com/qiboteam/qibojit
|
||||
- name: Test
|
||||
run: |
|
||||
pytest
|
||||
201
qibojit-benchmarks/LICENSE
Normal file
201
qibojit-benchmarks/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
210
qibojit-benchmarks/README.md
Normal file
210
qibojit-benchmarks/README.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# Benchmarking quantum simulation
|
||||
|
||||
This repository contains benchmark scripts for quantum circuit simulation using
|
||||
[Qibo](https://github.com/qiboteam/qibo) and multiple simulation engines.
|
||||
|
||||
## Installing prerequisites
|
||||
|
||||
In order to run the benchmarks, you need to install the required libraries.
|
||||
|
||||
In order to set up an environment with most of the libraries available in this repository,
|
||||
you can set up a **conda** environment:
|
||||
```
|
||||
conda env create -f environment.yml
|
||||
```
|
||||
This recipe doesn't include the installation of CUDA Toolkit, CuPy, cuQuantum nor HybridQ, Qulacs-GPU, qsim GPU and TensorFlow Quantum.
|
||||
|
||||
## Supported simulation backends
|
||||
|
||||
- [qibojit](https://github.com/qiboteam/qibojit): uses numba on CPU and cupy/cuquantum on GPU for custom operations.
|
||||
- [qibotf](https://github.com/qiboteam/qibotf): uses tf primitives with custom operators on CPU and GPU.
|
||||
- [tensorflow](https://www.tensorflow.org/): uses tf default primitives.
|
||||
- [numpy](https://numpy.org/): single-threaded CPU implementation.
|
||||
|
||||
For more details check the documentation [here](https://qibo.readthedocs.io/en/latest/installation.html).
|
||||
|
||||
## Running the benchmarks
|
||||
|
||||
The script in `benchmarks/main.py` executes the benchmark code following the supported configuration flags (check `python main.py -h`):
|
||||
|
||||
```
|
||||
$ python main.py -h
|
||||
|
||||
usage: main.py [-h] [--nqubits NQUBITS] [--backend BACKEND]
|
||||
[--platform PLATFORM] [--circuit CIRCUIT]
|
||||
[--circuit-options CIRCUIT_OPTIONS] [--nreps NREPS]
|
||||
[--nshots NSHOTS] [--transfer] [--precision PRECISION]
|
||||
[--memory MEMORY] [--threading THREADING] [--filename FILENAME]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--nqubits NQUBITS Number of qubits in the circuit.
|
||||
--backend BACKEND Qibo backend to use for simulation.
|
||||
--platform PLATFORM Qibo platform to use for simulation.
|
||||
--circuit CIRCUIT Type of circuit to use. See README for the list of
|
||||
available circuits.
|
||||
--circuit-options CIRCUIT_OPTIONS
|
||||
String with options for circuit creation. It should
|
||||
have the form 'arg1=value1,arg2=value2,...'. See
|
||||
README for the list of arguments that are available
|
||||
for each circuit.
|
||||
--nreps NREPS Number of repetitions of the circuit execution. Dry
|
||||
run is not included.
|
||||
--nshots NSHOTS Number of measurement shots. If used the time required
|
||||
to measure frequencies (no samples) is measured and
|
||||
logged. If it is ``None`` no measurements are
|
||||
performed.
|
||||
--transfer If used the final state array is converted to numpy.
|
||||
If the simulation device is GPU this requires a
|
||||
transfer from GPU memory to CPU.
|
||||
--precision PRECISION
|
||||
Numerical precision of the simulation. Choose between
|
||||
'double' and 'single'.
|
||||
--memory MEMORY Limit the GPU memory usage when using Tensorflow based
|
||||
backends. The memory limit should be given in MB.
|
||||
Tensorflow reserves the full available memory by
|
||||
default.
|
||||
--threading THREADING
|
||||
Switches the numba threading layer when using the
|
||||
qibojit backend on CPU. See
|
||||
https://numba.pydata.org/numba-
|
||||
doc/latest/user/threading-layer.html#selecting-a-
|
||||
named-threading-layer for a list of available
|
||||
threading layers.
|
||||
--filename FILENAME Directory of file to save the logs in json format. If
|
||||
not given the logs will only be printed and not saved.
|
||||
```
|
||||
|
||||
Before executing the code keep in mind the following:
|
||||
- GPUs are the default devices for qibojit and qibotf. If you need CPU performance numbers do `export CUDA_VISIBLES_DEVICE=""` before executing the benchmark script.
|
||||
- CPU simulations by default use physical cores as number of threads with qibojit and qibotf. To control this behaviour without touching the code do `export OMP_NUM_THREADS=<threads>` (or `export NUMBA_NUM_THREADS=<threads>` for qibojit numba backend) before executing the benchmark script.
|
||||
- The benchmark script provides several options, including the possibility to modify the default numba threading pooling technology, (see [docs](https://numba.pydata.org/numba-doc/latest/developer/threading_implementation.html#notes-on-numba-s-threading-implementation)) or limiting the GPU memory used be Tensorflow. See `python main.py -h` for more details.
|
||||
|
||||
The `scripts/` folder contains example bash scripts that execute circuit benchmarks for different numbers of qubits. We refer to the README inside this folder for more details.
|
||||
|
||||
#### Comparing simulation libraries
|
||||
|
||||
In addition to the above `main.py` benchmark, we provide the `compare.py` benchmark for comparing the performance of different simulation libraries.
|
||||
The usage is similar to `main.py` with the `--backend` flag replaced by the `--library` flag which can be used to select one of the available quantum simulation libraries
|
||||
(check `python compare.py -h`).
|
||||
|
||||
```
|
||||
$ python compare.py -h
|
||||
|
||||
usage: compare.py [-h] [--nqubits NQUBITS] [--library LIBRARY] [--library-options LIBRARY_OPTIONS] [--circuit CIRCUIT] [--circuit-options CIRCUIT_OPTIONS] [--precision PRECISION] [--nreps NREPS]
|
||||
[--filename FILENAME]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--nqubits NQUBITS Number of qubits in the circuit.
|
||||
--library LIBRARY Quantum simulation library to use in benchmark. See README for the list of available libraries.
|
||||
--library-options LIBRARY_OPTIONS
|
||||
String with options for the library. It should have the form 'arg1=value1,arg2=value2,...'. Each library supports different options.
|
||||
--circuit CIRCUIT Type of circuit to use. See README for the list of available circuits.
|
||||
--circuit-options CIRCUIT_OPTIONS
|
||||
String with options for circuit creation. It should have the form 'arg1=value1,arg2=value2,...'. See README for the list of arguments that are available for each circuit.
|
||||
--precision PRECISION
|
||||
Numerical precision of the simulation. Choose between 'double' and 'single'.
|
||||
--nreps NREPS Number of repetitions of the circuit execution. Dry run is not included.
|
||||
--filename FILENAME Directory of file to save the logs in json format. If not given the logs will only be printed and not saved.
|
||||
|
||||
```
|
||||
|
||||
Currently the available libraries (defined under `benchmarks/libraries`) are:
|
||||
- [Qiskit](https://qiskit.org/), defined as ``qiskit`` and ``qiskit-gpu`` in the ``library`` option of ``compare.py``.
|
||||
- [Qulacs](https://github.com/qulacs/qulacs), defined as ``qulacs`` and ``qulacs-gpu``.
|
||||
- [Cirq](https://quantumai.google/cirq), defined as ``cirq``.
|
||||
- [TensorFlow Quantum](https://www.tensorflow.org/quantum), defined as ``tfq``.
|
||||
- [Qsim](https://quantumai.google/qsim), defined as ``qsim``, ``qsim-gpu`` and ``qsim-cuquantum``.
|
||||
- [HybridQ](https://github.com/nasa/hybridq), defined as ``hybridq`` and ``hybridq-gpu``.
|
||||
- [ProjectQ](https://projectq.ch/), defined as ``projectq``.
|
||||
- [QCGPU](https://qcgpu.github.io/), defined as ``qcgpu``.
|
||||
- [Qibo](https://qibo.science/), defined as ``qibo``.
|
||||
|
||||
All the circuits described below are available for both `main.py` and `compare.py`.
|
||||
|
||||
## Benchmark output
|
||||
|
||||
The benchmark script prints a summary of the circuit and user selected flags together with:
|
||||
- import_time: time required to import the `qibo` library and build the selected backend in seconds.
|
||||
- creation_time: time required to prepare the circuit for execution in seconds.
|
||||
- dry_run_execution_time: first execution performance, includes JIT timings in seconds.
|
||||
- dry_run_transfer_time: time required to convert the final state to numpy array in seconds.
|
||||
- simulation_times: list of timings for simulation based on `nreps` in seconds.
|
||||
- transfer_times: list of timings for conversion to numpy array in seconds.
|
||||
- simulation_times_mean: average simulation time for `nreps` repetitions in seconds.
|
||||
- simulation_times_std: standard deviation of simulation_time in seconds.
|
||||
- transfer_times_mean: average transfer time for `nreps` repetitions in seconds.
|
||||
- transfer_time_std: standard deviation of transfer_times in seconds.
|
||||
- measurement_time: time required to sample frequencies for `nshots` measurement shots in seconds (relevant only if the `--nshots` argument is given).
|
||||
|
||||
Note that if a GPU is used for simulation then transfer times measure the time required to copy the final state from the GPU memory to CPU.
|
||||
|
||||
If `--filename` is given the above logs are saved in json format in the given directory.
|
||||
|
||||
## Implemented circuits
|
||||
|
||||
Here is a list of the available circuits for benchmarks. As described above the circuit should be selected using the `--circuit` flag and one of the following circuit names. Additional options can be passed using the `--options` flag. The options supported by each circuit are analyzed below. Note that some circuits require additional Python libraries to work as stated below.
|
||||
|
||||
- `one-qubit-gate`: circuit consisting of a single one qubit gate. The gate is applied to every qubit in the circuit. Available options:
|
||||
- `gate`: String defining the one qubit gate to be benchmarked (eg. "H"). Default is "H"
|
||||
- `nlayers`: Number of times that the gate is applied to each qubit. Default is 1.
|
||||
- additional parameters (eg. `theta`, etc.) required for parametrized gates.
|
||||
- `two-qubit-gate`: circuit consisting of a single two qubit gate. The gate is applied to every pair of adjacent qubits in the circuit (assuming one dimensional topology).
|
||||
- `gate`: String defining the one qubit gate to be benchmarked. Default is CNOT.
|
||||
- `nlayers`: Number of times that the gate is applied to each qubit. Default is 1.
|
||||
- additional parameters (eg. `theta`, etc.) required for parametrized gates.
|
||||
- `qft`: [quantum fourier transform](https://en.wikipedia.org/wiki/Quantum_Fourier_transform)
|
||||
- `swaps`: Boolean controling if swaps are applied after the main QFT circuit. Default is True.
|
||||
- `variational`: variational quantum circuit consisting a layer of RY rotations followed be a layer of CZ entangling gates. Can be created using either standard qibot gates or the optimized [VariationalLayer](https://qibo.readthedocs.io/en/latest/qibo.html#variational-layer) gate.
|
||||
- `nlayers`: Number of times that the gate is applied to each qubit.
|
||||
- `varlayer`: Boolean controling whether the VariationalLayer or standard gates are used. Default is False.
|
||||
- `bernstein-vazirani` (`bv`): circuit that applies the [Bernstein-Vazirani algorithm](https://qiskit.org/textbook/ch-algorithms/bernstein-vazirani.html#example) based on the related [OpenQASM example](https://github.com/Qiskit/openqasm/tree/0af8b8489f32d46692b3a3a1421e98c611cd86cc/benchmarks/bv).
|
||||
- `hidden-shift` (`hs`): circuit that solves the [Hidden shift problem](https://en.wikipedia.org/wiki/Hidden_shift_problem), based on the [Cirq implementation](https://github.com/quantumlib/Cirq/blob/master/examples/hidden_shift_algorithm.py).
|
||||
- `shift`: The hidden bitstring for which the two oracle functions in the hidden shift problem differ. If not given this bitstring will be generated randomly using `np.random.randint`.
|
||||
- `qaoa`: Example implementation of the [Quantum Approximate Optization Algorithm (QAOA)](https://arxiv.org/abs/1411.4028) for solving the MaxCut problem. Follows the [Cirq example](https://github.com/quantumlib/Cirq/blob/master/examples/qaoa.py). Requirements: [networkx](https://networkx.org/).
|
||||
- `nparams`: Number of variational parameters.
|
||||
- `graph`: Name of json file to load the problem graph structure. The graph will be loaded using `networkx.readwrite.json_graph.node_link_graph`. If not given the graph will be generated randomly using `networkx.random_regular_graph`.
|
||||
- `qasm`: Creates benchmark circuit using [OpenQASM](https://github.com/Qiskit/openqasm) code.
|
||||
- `qasm`: OpenQASM code that generates the circuit as a Python string.
|
||||
- `supremacy`: Random circuit [proposed for demonstrating quantum supremacy](https://arxiv.org/abs/1807.10749). Based on Cirq's [`generate_boixo_2018_supremacy_circuits_v2`](https://github.com/quantumlib/Cirq/blob/v0.11.0/cirq-core/cirq/experiments/google_v2_supremacy_circuit.py) method. *Requirements: [Cirq](https://quantumai.google/cirq).*
|
||||
- `depth`: Number of layers with CZ gates.
|
||||
- `seed`: Seed for random circuit instance generator.
|
||||
- `basis-change` (`bc`): Basis transformations that implement exact evolution under a random one-body fermionic Hamiltonian. See [OpenFermion's tutorial](https://quantumai.google/openfermion/tutorials/circuits_1_basis_change) for more details. *Requirements: [Cirq](https://quantumai.google/cirq), [OpenFermion](https://github.com/quantumlib/OpenFermion).*
|
||||
- `simulation_time`: Evolution time.
|
||||
- `seed`: Seed to use for the random Hamiltonian generation.
|
||||
- `quantum-volume` (`qv`): [Quantum volume](https://qiskit.org/documentation/stubs/qiskit.circuit.library.QuantumVolume.html) circuit model from Qiskit. *Requirements: [Qiskit](https://qiskit.org/).*
|
||||
- `depth`: Layers of SU(4) operations in circuit.
|
||||
- `seed`: Seed for random gate generator.
|
||||
|
||||
## QiboTN
|
||||
|
||||
Command to call QiboTN calculation.
|
||||
|
||||
### Single Node
|
||||
```bash
|
||||
python compare.py --circuit qft --nqubits 4 --filename test.dat --library-options backend=qibotn,platform=cutensornet --nreps 5 --precision complex128
|
||||
```
|
||||
where precision is 'complex128', 'complex64','float64', 'float32'
|
||||
|
||||
Another example for the case with runcard input and output expectation. Note that user will need to store the runcard settings as json file.
|
||||
```bash
|
||||
python compare.py --circuit variational --circuit-options nlayers=3 --nqubits 4 --filename test.dat --library-options backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_expectation.json --nreps 5 --precision complex128
|
||||
```
|
||||
|
||||
Instructions for Qibojit expectation
|
||||
```bash
|
||||
python compare.py --circuit variational --circuit-options nlayers=3 --nqubits 4 --filename test.dat --library-options backend=qibojit,platform=numba,expectation="XXXZ" --nreps 5 --precision complex128
|
||||
```
|
||||
|
||||
Instructions for Qibojit no expectation
|
||||
```bash
|
||||
python compare.py --circuit variational --circuit-options nlayers=3 --nqubits 4 --filename test.dat --library-options backend=qibojit,platform=numba --nreps 5 --precision complex128
|
||||
```
|
||||
|
||||
### Multi Node
|
||||
|
||||
In Docker environment, need to add `--allow-run-as-root`
|
||||
```bash
|
||||
mpirun --allow-run-as-root -np 2 python compare.py --circuit variational --circuit-options nlayers=3 --nqubits 4 --filename test.dat --library-options backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_expectation.json --nreps 1 --precision complex128
|
||||
```
|
||||
0
qibojit-benchmarks/benchmarks/__init__.py
Normal file
0
qibojit-benchmarks/benchmarks/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
BIN
qibojit-benchmarks/benchmarks/__pycache__/logger.cpython-312.pyc
Normal file
BIN
qibojit-benchmarks/benchmarks/__pycache__/logger.cpython-312.pyc
Normal file
Binary file not shown.
BIN
qibojit-benchmarks/benchmarks/__pycache__/logger.cpython-313.pyc
Normal file
BIN
qibojit-benchmarks/benchmarks/__pycache__/logger.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
53
qibojit-benchmarks/benchmarks/circuits/__init__.py
Normal file
53
qibojit-benchmarks/benchmarks/circuits/__init__.py
Normal file
@@ -0,0 +1,53 @@
|
||||
def parse(options):
|
||||
"""Parse options from string.
|
||||
|
||||
Args:
|
||||
options (str): String with options.
|
||||
It should have the form 'arg1=value1,arg2=value2,...'.
|
||||
|
||||
Returns:
|
||||
dict: {'arg1': value1, 'arg2': value2, ...}
|
||||
|
||||
"""
|
||||
kwargs = {}
|
||||
if options is not None:
|
||||
for parameter in options.split(","):
|
||||
if "=" in parameter:
|
||||
k, v = parameter.split("=")
|
||||
kwargs[k] = v
|
||||
else:
|
||||
raise ValueError(f"Cannot parse parameter {parameter}.")
|
||||
return kwargs
|
||||
|
||||
|
||||
def get(circuit_name, nqubits, options=None, qibo=False):
|
||||
if qibo:
|
||||
from benchmarks.circuits import qibo as module
|
||||
else:
|
||||
from benchmarks.circuits import qasm as module
|
||||
|
||||
if circuit_name in ("qft", "QFT"):
|
||||
circuit = module.QFT
|
||||
elif circuit_name == "one-qubit-gate":
|
||||
circuit = module.OneQubitGate
|
||||
elif circuit_name == "two-qubit-gate":
|
||||
circuit = module.TwoQubitGate
|
||||
elif circuit_name in ("variational", "variational-circuit"):
|
||||
circuit = module.VariationalCircuit
|
||||
elif circuit_name in ("bernstein-vazirani", "bv"):
|
||||
circuit = module.BernsteinVazirani
|
||||
elif circuit_name in ("hidden-shift", "hs"):
|
||||
circuit = module.HiddenShift
|
||||
elif circuit_name == "qaoa":
|
||||
circuit = module.QAOA
|
||||
elif circuit_name == "supremacy":
|
||||
circuit = module.SupremacyCircuit
|
||||
elif circuit_name in ("basis-change", "bc"):
|
||||
circuit = module.BasisChange
|
||||
elif circuit_name in ("quantum-volume", "qv"):
|
||||
circuit = module.QuantumVolume
|
||||
else:
|
||||
raise NotImplementedError(f"Cannot find circuit {circuit_name}.")
|
||||
|
||||
kwargs = parse(options)
|
||||
return circuit(nqubits, **kwargs)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
394
qibojit-benchmarks/benchmarks/circuits/qasm.py
Normal file
394
qibojit-benchmarks/benchmarks/circuits/qasm.py
Normal file
@@ -0,0 +1,394 @@
|
||||
import numpy as np
|
||||
from abc import abstractmethod
|
||||
|
||||
|
||||
class AbstractCircuit:
|
||||
|
||||
def __init__(self, nqubits):
|
||||
self.nqubits = nqubits
|
||||
self.parameters = {}
|
||||
|
||||
@abstractmethod
|
||||
def __iter__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def to_qasm(self, theta=None):
|
||||
"""Creates the circuit in OpenQASM format.
|
||||
|
||||
Args:
|
||||
theta (np.ndarray): If not ``None`` ``RX`` gates with the given
|
||||
angles are added before the actual circuit gates so that the
|
||||
initial state is non-trivial. Useful for testing.
|
||||
|
||||
Returns:
|
||||
A string with the circuit in OpenQASM format.
|
||||
"""
|
||||
code = ['OPENQASM 2.0;', 'include "qelib1.inc";',
|
||||
f'qreg q[{self.nqubits}];', f'creg m[{self.nqubits}];']
|
||||
if theta is not None:
|
||||
code.extend(f"rx({t}) q[{i}];" for i, t in enumerate(theta))
|
||||
code.extend(iter(self))
|
||||
return "\n".join(code)
|
||||
|
||||
def __str__(self):
|
||||
return ", ".join(f"{k}={v}" for k, v in self.parameters.items())
|
||||
|
||||
|
||||
class OneQubitGate(AbstractCircuit):
|
||||
"""Applies a specific one qubit gate to all qubits."""
|
||||
|
||||
def __init__(self, nqubits, nlayers="1", gate="h", angles=""):
|
||||
super().__init__(nqubits)
|
||||
self.gate = gate
|
||||
self.nlayers = int(nlayers)
|
||||
self.angles = angles
|
||||
self.parameters = {"nqubits": nqubits, "nlayers": nlayers,
|
||||
"gate": gate, "params": angles}
|
||||
|
||||
def base_command(self, i):
|
||||
if self.angles:
|
||||
return "{}({}) q[{}];".format(self.gate, self.angles, i)
|
||||
else:
|
||||
return "{} q[{}];".format(self.gate, i)
|
||||
|
||||
def __iter__(self):
|
||||
for _ in range(self.nlayers):
|
||||
for i in range(self.nqubits):
|
||||
yield self.base_command(i)
|
||||
|
||||
|
||||
class TwoQubitGate(OneQubitGate):
|
||||
"""Applies a specific two qubit gate to all pairs of adjacent qubits."""
|
||||
|
||||
def __init__(self, nqubits, nlayers="1", gate="cx", angles=""):
|
||||
super().__init__(nqubits, nlayers, gate, angles)
|
||||
|
||||
def base_command(self, i):
|
||||
if self.angles:
|
||||
return "{}({}) q[{}],q[{}];".format(self.gate, self.angles, i, i + 1)
|
||||
else:
|
||||
return "{} q[{}],q[{}];".format(self.gate, i, i + 1)
|
||||
|
||||
def __iter__(self):
|
||||
for _ in range(self.nlayers):
|
||||
for i in range(0, self.nqubits - 1, 2):
|
||||
yield self.base_command(i)
|
||||
for i in range(1, self.nqubits - 1, 2):
|
||||
yield self.base_command(i)
|
||||
|
||||
|
||||
class QFT(AbstractCircuit):
|
||||
"""Applies the Quantum Fourier Transform."""
|
||||
|
||||
def __init__(self, nqubits, swaps="True"):
|
||||
super().__init__(nqubits)
|
||||
self.swaps = swaps == "True"
|
||||
self.parameters = {"nqubits": nqubits, "swaps": swaps}
|
||||
|
||||
def __iter__(self):
|
||||
for i1 in range(self.nqubits):
|
||||
yield f"h q[{i1}];"
|
||||
for i2 in range(i1 + 1, self.nqubits):
|
||||
theta = np.pi / 2 ** (i2 - i1)
|
||||
yield f"cu1({theta}) q[{i2}],q[{i1}];"
|
||||
|
||||
if self.swaps:
|
||||
for i in range(self.nqubits // 2):
|
||||
yield f"swap q[{i}],q[{self.nqubits - i - 1}];"
|
||||
|
||||
|
||||
class VariationalCircuit(AbstractCircuit):
|
||||
"""Example variational circuit consisting of alternating layers of RY and CZ gates."""
|
||||
|
||||
def __init__(self, nqubits, nlayers="1", seed="123"):
|
||||
super().__init__(nqubits)
|
||||
self.nlayers = int(nlayers)
|
||||
self.seed = int(seed)
|
||||
self.parameters = {"nqubits": nqubits, "nlayers": nlayers, "seed": seed}
|
||||
|
||||
def __iter__(self):
|
||||
nparams = 2 * self.nlayers * self.nqubits
|
||||
np.random.seed(self.seed)
|
||||
theta = iter(2 * np.pi * np.random.random(nparams))
|
||||
for l in range(self.nlayers):
|
||||
for i in range(self.nqubits):
|
||||
yield f"ry({next(theta)}) q[{i}];"
|
||||
for i in range(0, self.nqubits - 1, 2):
|
||||
yield f"cz q[{i}],q[{i + 1}];"
|
||||
for i in range(self.nqubits):
|
||||
yield f"ry({next(theta)}) q[{i}];"
|
||||
for i in range(1, self.nqubits - 2, 2):
|
||||
yield f"cz q[{i}],q[{i + 1}];"
|
||||
yield f"cz q[{0}],q[{self.nqubits - 1}];"
|
||||
|
||||
|
||||
class BernsteinVazirani(AbstractCircuit):
|
||||
"""Applies the Bernstein-Vazirani algorithm from Qiskit/openqasm.
|
||||
|
||||
See `https://github.com/Qiskit/openqasm/tree/0af8b8489f32d46692b3a3a1421e98c611cd86cc/benchmarks/bv`
|
||||
for the OpenQASM code.
|
||||
Note that `Barrier` gates are excluded for simulation.
|
||||
"""
|
||||
|
||||
def __init__(self, nqubits):
|
||||
super().__init__(nqubits)
|
||||
self.parameters = {"nqubits": nqubits}
|
||||
|
||||
def __iter__(self):
|
||||
yield f"x q[{self.nqubits - 1}];"
|
||||
for i in range(self.nqubits):
|
||||
yield f"h q[{i}];"
|
||||
for i in range(self.nqubits - 1):
|
||||
yield f"cx q[{i}],q[{self.nqubits - 1}];"
|
||||
for i in range(self.nqubits - 1):
|
||||
yield f"h q[{i}];"
|
||||
#for i in range(self.nqubits - 1):
|
||||
# yield f"measure m[{i}];"
|
||||
|
||||
|
||||
class HiddenShift(AbstractCircuit):
|
||||
"""Applies the Hidden Shift algorithm.
|
||||
|
||||
See `https://github.com/quantumlib/Cirq/blob/master/examples/hidden_shift_algorithm.py`
|
||||
for the Cirq code.
|
||||
If the shift (hidden bitstring) is not given then it is randomly generated
|
||||
using `np.random.randint`.
|
||||
"""
|
||||
|
||||
def __init__(self, nqubits, shift=""):
|
||||
super().__init__(nqubits)
|
||||
if len(shift):
|
||||
if len(shift) != nqubits:
|
||||
raise ValueError("Shift bitstring of length {} was given for "
|
||||
"circuit of {} qubits."
|
||||
"".format(len(shift), nqubits))
|
||||
self.shift = [int(x) for x in shift]
|
||||
else:
|
||||
self.shift = np.random.randint(0, 2, size=(self.nqubits,))
|
||||
self.parameters = {"nqubits": nqubits, "shift": shift}
|
||||
|
||||
def oracle(self):
|
||||
for i in range(self.nqubits // 2):
|
||||
yield f"cz q[{2 * i}],q[{2 * i + 1}];"
|
||||
|
||||
def __iter__(self):
|
||||
for i in range(self.nqubits):
|
||||
yield f"h q[{i}];"
|
||||
for i, ish in enumerate(self.shift):
|
||||
if ish:
|
||||
yield f"x q[{i}];"
|
||||
for gate in self.oracle():
|
||||
yield gate
|
||||
for i, ish in enumerate(self.shift):
|
||||
if ish:
|
||||
yield f"x q[{i}];"
|
||||
for i in range(self.nqubits):
|
||||
yield f"h q[{i}];"
|
||||
for gate in self.oracle():
|
||||
yield gate
|
||||
for i in range(self.nqubits):
|
||||
yield f"h q[{i}];"
|
||||
#for i in range(self.nqubits):
|
||||
# yield f"measure m[{i}];"
|
||||
|
||||
|
||||
class QAOA(AbstractCircuit):
|
||||
"""Example QAOA circuit for a MaxCut problem instance.
|
||||
|
||||
See `https://github.com/quantumlib/Cirq/blob/master/examples/qaoa.py` for
|
||||
the Cirq code.
|
||||
If a JSON file containing the node link structure is given then the graph
|
||||
is loaded using `networkx.readwrite.json_graph.node_link_graph`, otherwise
|
||||
the graph is generated randomly using `networkx.random_regular_graph`.
|
||||
Note that different graphs may lead to different performance as the graph
|
||||
structure affects circuit depth.
|
||||
"""
|
||||
|
||||
def __init__(self, nqubits, nparams="2", graph="", seed="123"):
|
||||
super().__init__(nqubits)
|
||||
import networkx
|
||||
self.nparams = int(nparams)
|
||||
self.seed = int(seed)
|
||||
if len(graph):
|
||||
import json
|
||||
with open(graph, "r") as file:
|
||||
data = json.load(file)
|
||||
self.graph = networkx.readwrite.json_graph.node_link_graph(data)
|
||||
else:
|
||||
self.graph = networkx.random_regular_graph(
|
||||
3, self.nqubits, seed=self.seed
|
||||
)
|
||||
self.parameters = {"nqubits": nqubits, "nparams": nparams,
|
||||
"graph": graph, "seed": seed}
|
||||
|
||||
@staticmethod
|
||||
def RX(q, theta):
|
||||
return f"rx({theta}) q[{q}];"
|
||||
|
||||
@staticmethod
|
||||
def RZZ(q0, q1, theta):
|
||||
return f"rzz({theta}) q[{q0}],q[{q1}];"
|
||||
|
||||
def maxcut_unitary(self, betas, gammas):
|
||||
for beta, gamma in zip(betas, gammas):
|
||||
for i, j in self.graph.edges:
|
||||
yield self.RZZ(i, j, -0.5 * gamma)
|
||||
for i in range(self.nqubits):
|
||||
yield self.RX(i, 2 * beta)
|
||||
|
||||
def dump(self, dir):
|
||||
"""Saves graph data as JSON in given directory."""
|
||||
import json
|
||||
import networkx
|
||||
data = networkx.readwrite.json_graph.node_link_data(self.graph)
|
||||
with open(dir, "w") as file:
|
||||
json.dump(data, file)
|
||||
|
||||
def __iter__(self):
|
||||
np.random.seed(self.seed)
|
||||
betas = np.random.uniform(-np.pi, np.pi, size=self.nparams)
|
||||
gammas = np.random.uniform(-np.pi, np.pi, size=self.nparams)
|
||||
# Prepare uniform superposition
|
||||
for i in range(self.nqubits):
|
||||
yield f"h q[{i}];"
|
||||
# Apply QAOA unitary
|
||||
for gate in self.maxcut_unitary(betas, gammas):
|
||||
yield gate
|
||||
# Measure
|
||||
# yield gates.M(*range(self.nqubits))
|
||||
|
||||
|
||||
class SupremacyCircuit(AbstractCircuit):
|
||||
"""Random circuit by Boixo et al 2018 for demonstrating quantum supremacy.
|
||||
|
||||
See `https://github.com/quantumlib/Cirq/blob/v0.11.0/cirq-core/cirq/experiments/google_v2_supremacy_circuit.py`
|
||||
for the Cirq code.
|
||||
This circuit is constructed using `cirq` by exporting to OpenQASM and
|
||||
importing back to Qibo.
|
||||
"""
|
||||
|
||||
def __init__(self, nqubits, depth="2", seed="123"):
|
||||
super().__init__(nqubits)
|
||||
self.depth = int(depth)
|
||||
self.seed = int(seed)
|
||||
self.parameters = {"nqubits": nqubits, "depth": depth, "seed": seed}
|
||||
self.cirq_circuit = self.create_cirq_circuit()
|
||||
|
||||
def create_cirq_circuit(self):
|
||||
import cirq
|
||||
from cirq.experiments import google_v2_supremacy_circuit as spc
|
||||
qubits = [cirq.GridQubit(i, 0) for i in range(self.nqubits)]
|
||||
return spc.generate_boixo_2018_supremacy_circuits_v2(qubits, self.depth, self.seed)
|
||||
|
||||
def __iter__(self):
|
||||
qasm = self.cirq_circuit.to_qasm()
|
||||
for line in qasm.split("\n"):
|
||||
first_word = line.split(" ")[0]
|
||||
if first_word not in {"//", "OPENQASM", "include", "qreg"}:
|
||||
if first_word == "sx":
|
||||
yield line.replace("sx", "rx(pi*0.5)") # see issue #13
|
||||
else:
|
||||
yield line
|
||||
|
||||
|
||||
class BasisChange(AbstractCircuit):
|
||||
"""Basis change fermionic circuit.
|
||||
|
||||
See `https://quantumai.google/openfermion/tutorials/circuits_1_basis_change`
|
||||
for OpenFermion/Cirq code.
|
||||
This circuit is constructed using `openfermion` and `cirq` by exporting
|
||||
to OpenQASM and importing back to Qibo.
|
||||
"""
|
||||
|
||||
def __init__(self, nqubits, simulation_time="1", seed="123"):
|
||||
super().__init__(nqubits)
|
||||
self.simulation_time = float(simulation_time)
|
||||
self.seed = int(seed)
|
||||
self.parameters = {"nqubits": nqubits, "simulation_time": simulation_time,
|
||||
"seed": seed}
|
||||
self.openfermion_circuit = self.create_openfermion_circuit()
|
||||
|
||||
def create_openfermion_circuit(self):
|
||||
import cirq
|
||||
import openfermion
|
||||
# Generate the random one-body operator.
|
||||
T = openfermion.random_hermitian_matrix(self.nqubits, seed=self.seed)
|
||||
# Diagonalize T and obtain basis transformation matrix (aka "u").
|
||||
eigenvalues, eigenvectors = np.linalg.eigh(T)
|
||||
basis_transformation_matrix = eigenvectors.transpose()
|
||||
# Initialize the qubit register.
|
||||
qubits = cirq.LineQubit.range(self.nqubits)
|
||||
# Start circuit with the inverse basis rotation, print out this step.
|
||||
inverse_basis_rotation = cirq.inverse(openfermion.bogoliubov_transform(qubits, basis_transformation_matrix))
|
||||
circuit = cirq.Circuit(inverse_basis_rotation)
|
||||
# Add diagonal phase rotations to circuit.
|
||||
for k, eigenvalue in enumerate(eigenvalues):
|
||||
phase = -eigenvalue * self.simulation_time
|
||||
circuit.append(cirq.rz(rads=phase).on(qubits[k]))
|
||||
# Finally, restore basis.
|
||||
basis_rotation = openfermion.bogoliubov_transform(qubits, basis_transformation_matrix)
|
||||
circuit.append(basis_rotation)
|
||||
return circuit
|
||||
|
||||
def __iter__(self):
|
||||
qasm = self.openfermion_circuit.to_qasm()
|
||||
for line in qasm.split("\n"):
|
||||
first_word = line.split(" ")[0]
|
||||
if first_word not in {"//", "OPENQASM", "include", "qreg"}:
|
||||
yield line
|
||||
|
||||
|
||||
class QuantumVolume(AbstractCircuit):
|
||||
"""Quantum Volume circuit from Qiskit.
|
||||
|
||||
See `https://qiskit.org/documentation/stubs/qiskit.circuit.library.QuantumVolume.html`
|
||||
for the Qiskit model.
|
||||
This circuit is constructed using `qiskit` by exporting to OpenQASM and
|
||||
importing back to Qibo.
|
||||
"""
|
||||
|
||||
def __init__(self, nqubits, depth="1", seed="123"):
|
||||
super().__init__(nqubits)
|
||||
self.depth = int(depth)
|
||||
self.seed = int(seed)
|
||||
self.parameters = {"nqubits": nqubits, "depth": depth, "seed": seed}
|
||||
self.qiskit_circuit = self.create_qiskit_circuit()
|
||||
self.expression_symbols = {"*", "/"}
|
||||
self.expression_symbols.update(str(x) for x in range(10))
|
||||
|
||||
def create_qiskit_circuit(self):
|
||||
from qiskit.circuit.library import QuantumVolume
|
||||
circuit = QuantumVolume(self.nqubits, self.depth, seed=self.seed)
|
||||
return circuit.decompose().decompose()
|
||||
|
||||
def __iter__(self):
|
||||
raise NotImplementedError("Iteration is not available for "
|
||||
"`QuantumVolume` because it is prepared "
|
||||
"using Qiskit.")
|
||||
|
||||
def evaluate_pi(self, qasm):
|
||||
left = qasm.find("pi")
|
||||
if left < 0:
|
||||
return qasm
|
||||
|
||||
import sympy
|
||||
right = left + 2
|
||||
left = left - 1
|
||||
while qasm[left] in self.expression_symbols:
|
||||
left -= 1
|
||||
while qasm[right] in self.expression_symbols:
|
||||
right += 1
|
||||
expr = qasm[left + 1: right]
|
||||
evaluated = sympy.sympify(expr).evalf()
|
||||
return self.evaluate_pi(qasm.replace(expr, str(evaluated)))
|
||||
|
||||
def __iter__(self):
|
||||
qasm = self.qiskit_circuit.qasm()
|
||||
for line in qasm.split("\n"):
|
||||
first_word = line.split(" ")[0]
|
||||
if first_word not in {"//", "OPENQASM", "include", "qreg"}:
|
||||
yield line.replace("1/(15*pi)", str(1.0 / (15.0 * np.pi)))
|
||||
|
||||
def to_qasm(self, theta=None):
|
||||
qasm = super().to_qasm(theta)
|
||||
return self.evaluate_pi(qasm)
|
||||
218
qibojit-benchmarks/benchmarks/circuits/qibo.py
Normal file
218
qibojit-benchmarks/benchmarks/circuits/qibo.py
Normal file
@@ -0,0 +1,218 @@
|
||||
import numpy as np
|
||||
from qibo import gates
|
||||
from benchmarks.circuits import qasm
|
||||
|
||||
|
||||
class OneQubitGate(qasm.OneQubitGate):
|
||||
|
||||
def __init__(self, nqubits, nlayers="1", gate="H", **params):
|
||||
super().__init__(nqubits, nlayers=nlayers, gate=gate)
|
||||
self.gate = getattr(gates, gate)
|
||||
self.angles = {k: float(v) for k, v in params.items()}
|
||||
self.parameters = {"nqubits": nqubits, "nlayers": nlayers,
|
||||
"gate": gate, "params": params}
|
||||
|
||||
def to_qasm(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def base_command(self, i):
|
||||
return self.gate(i, **self.angles)
|
||||
|
||||
|
||||
class TwoQubitGate(OneQubitGate):
|
||||
|
||||
def __init__(self, nqubits, nlayers="1", gate="CNOT", **params):
|
||||
super().__init__(nqubits, nlayers, gate, **params)
|
||||
|
||||
def to_qasm(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def base_command(self, i):
|
||||
return self.gate(i, i + 1, **self.angles)
|
||||
|
||||
def __iter__(self):
|
||||
return qasm.TwoQubitGate.__iter__(self)
|
||||
|
||||
|
||||
class QFT(qasm.QFT):
|
||||
|
||||
def to_qasm(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def __iter__(self):
|
||||
for i1 in range(self.nqubits):
|
||||
yield gates.H(i1)
|
||||
for i2 in range(i1 + 1, self.nqubits):
|
||||
theta = np.pi / 2 ** (i2 - i1)
|
||||
yield gates.CU1(i2, i1, theta)
|
||||
|
||||
if self.swaps:
|
||||
for i in range(self.nqubits // 2):
|
||||
yield gates.SWAP(i, self.nqubits - i - 1)
|
||||
|
||||
|
||||
class VariationalCircuit(qasm.VariationalCircuit):
|
||||
|
||||
def __init__(self, nqubits, nlayers=1, varlayer="False", seed="123"):
|
||||
super().__init__(nqubits, nlayers, seed)
|
||||
self.varlayer = varlayer == "True"
|
||||
self.parameters["varlayer"] = varlayer
|
||||
|
||||
def to_qasm(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def varlayer_circuit(self, theta):
|
||||
theta = theta.reshape((2 * self.nlayers, self.nqubits))
|
||||
pairs = list((i, i + 1) for i in range(0, self.nqubits - 1, 2))
|
||||
for l in range(self.nlayers):
|
||||
yield gates.VariationalLayer(range(self.nqubits), pairs,
|
||||
gates.RY, gates.CZ,
|
||||
theta[2 * l], theta[2 * l + 1])
|
||||
for i in range(1, self.nqubits - 2, 2):
|
||||
yield gates.CZ(i, i + 1)
|
||||
yield gates.CZ(0, self.nqubits - 1)
|
||||
|
||||
def standard_circuit(self, theta):
|
||||
theta = iter(theta)
|
||||
for l in range(self.nlayers):
|
||||
for i in range(self.nqubits):
|
||||
yield gates.RY(i, next(theta))
|
||||
for i in range(0, self.nqubits - 1, 2):
|
||||
yield gates.CZ(i, i + 1)
|
||||
for i in range(self.nqubits):
|
||||
yield gates.RY(i, next(theta))
|
||||
for i in range(1, self.nqubits - 2, 2):
|
||||
yield gates.CZ(i, i + 1)
|
||||
yield gates.CZ(0, self.nqubits - 1)
|
||||
|
||||
def __iter__(self):
|
||||
np.random.seed(self.seed)
|
||||
theta = 2 * np.pi * np.random.random(2 * self.nlayers * self.nqubits)
|
||||
if self.varlayer:
|
||||
return self.varlayer_circuit(theta)
|
||||
else:
|
||||
return self.standard_circuit(theta)
|
||||
|
||||
|
||||
class BernsteinVazirani(qasm.BernsteinVazirani):
|
||||
|
||||
def to_qasm(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def __iter__(self):
|
||||
yield gates.X(self.nqubits - 1)
|
||||
for i in range(self.nqubits):
|
||||
yield gates.H(i)
|
||||
for i in range(self.nqubits - 1):
|
||||
yield gates.CNOT(i, self.nqubits - 1)
|
||||
for i in range(self.nqubits - 1):
|
||||
yield gates.H(i)
|
||||
for i in range(self.nqubits - 1):
|
||||
yield gates.M(i)
|
||||
|
||||
|
||||
class HiddenShift(qasm.HiddenShift):
|
||||
|
||||
def to_qasm(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def oracle(self):
|
||||
for i in range(self.nqubits // 2):
|
||||
yield gates.CZ(2 * i, 2 * i + 1)
|
||||
|
||||
def __iter__(self):
|
||||
for i in range(self.nqubits):
|
||||
yield gates.H(i)
|
||||
for i, ish in enumerate(self.shift):
|
||||
if ish:
|
||||
yield gates.X(i)
|
||||
for gate in self.oracle():
|
||||
yield gate
|
||||
for i, ish in enumerate(self.shift):
|
||||
if ish:
|
||||
yield gates.X(i)
|
||||
for i in range(self.nqubits):
|
||||
yield gates.H(i)
|
||||
for gate in self.oracle():
|
||||
yield gate
|
||||
for i in range(self.nqubits):
|
||||
yield gates.H(i)
|
||||
yield gates.M(*range(self.nqubits))
|
||||
|
||||
|
||||
class QAOA(qasm.QAOA):
|
||||
|
||||
def to_qasm(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def RX(q, theta):
|
||||
return gates.RX(q, theta=theta)
|
||||
|
||||
@staticmethod
|
||||
def RZZ(q0, q1, theta):
|
||||
phase = np.exp(0.5j * theta)
|
||||
phasec = np.conj(phase)
|
||||
matrix = np.diag([phasec, phase, phase, phasec])
|
||||
return gates.Unitary(matrix, q0, q1)
|
||||
|
||||
def __iter__(self):
|
||||
np.random.seed(self.seed)
|
||||
betas = np.random.uniform(-np.pi, np.pi, size=self.nparams)
|
||||
gammas = np.random.uniform(-np.pi, np.pi, size=self.nparams)
|
||||
# Prepare uniform superposition
|
||||
for i in range(self.nqubits):
|
||||
yield gates.H(i)
|
||||
# Apply QAOA unitary
|
||||
for gate in self.maxcut_unitary(betas, gammas):
|
||||
yield gate
|
||||
# Measure
|
||||
yield gates.M(*range(self.nqubits))
|
||||
|
||||
|
||||
class SupremacyCircuit(qasm.SupremacyCircuit):
|
||||
|
||||
def __init__(self, nqubits, depth="2", seed="123"):
|
||||
super().__init__(nqubits, depth, seed)
|
||||
from qibo import models
|
||||
parent = qasm.SupremacyCircuit(nqubits, depth, seed)
|
||||
self.qibo_circuit = models.Circuit.from_qasm(parent.to_qasm())
|
||||
|
||||
def to_qasm(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def __iter__(self):
|
||||
for gate in self.qibo_circuit.queue:
|
||||
yield gate
|
||||
|
||||
|
||||
class BasisChange(qasm.BasisChange):
|
||||
|
||||
def __init__(self, nqubits, simulation_time="1", seed="123"):
|
||||
super().__init__(nqubits, simulation_time, seed)
|
||||
from qibo import models
|
||||
parent = qasm.BasisChange(nqubits, simulation_time, seed)
|
||||
self.qibo_circuit = models.Circuit.from_qasm(parent.to_qasm())
|
||||
|
||||
def to_qasm(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def __iter__(self):
|
||||
for gate in self.qibo_circuit.queue:
|
||||
yield gate
|
||||
|
||||
|
||||
class QuantumVolume(qasm.QuantumVolume):
|
||||
|
||||
def __init__(self, nqubits, depth="1", seed="123"):
|
||||
super().__init__(nqubits, depth, seed)
|
||||
from qibo import models
|
||||
parent = qasm.QuantumVolume(nqubits, depth, seed)
|
||||
self.qibo_circuit = models.Circuit.from_qasm(parent.to_qasm())
|
||||
|
||||
def to_qasm(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def __iter__(self):
|
||||
for gate in self.qibo_circuit.queue:
|
||||
yield gate
|
||||
74
qibojit-benchmarks/benchmarks/libraries/__init__.py
Normal file
74
qibojit-benchmarks/benchmarks/libraries/__init__.py
Normal file
@@ -0,0 +1,74 @@
|
||||
def parse(options):
|
||||
"""Parse options from string.
|
||||
|
||||
Args:
|
||||
options (str): String with options.
|
||||
It should have the form 'arg1=value1,arg2=value2,...'.
|
||||
|
||||
Returns:
|
||||
dict: {'arg1': value1, 'arg2': value2, ...}
|
||||
|
||||
"""
|
||||
kwargs = {}
|
||||
if options is not None:
|
||||
for parameter in options.split(","):
|
||||
if "=" in parameter:
|
||||
k, v = parameter.split("=")
|
||||
kwargs[k] = v
|
||||
else:
|
||||
raise ValueError(f"Cannot parse parameter {parameter}.")
|
||||
return kwargs
|
||||
|
||||
|
||||
def get(backend_name, options=None):
|
||||
options = parse(options)
|
||||
if backend_name == "qibo":
|
||||
from benchmarks.libraries.qibo import Qibo
|
||||
return Qibo(**options)
|
||||
|
||||
elif backend_name == "qiskit":
|
||||
from benchmarks.libraries.qiskit import Qiskit
|
||||
return Qiskit(**options)
|
||||
elif backend_name == "qiskit-gpu":
|
||||
from benchmarks.libraries.qiskit import QiskitGpu
|
||||
return QiskitGpu(**options)
|
||||
|
||||
elif backend_name == "cirq":
|
||||
from benchmarks.libraries.cirq import Cirq
|
||||
return Cirq()
|
||||
elif backend_name == "qsim":
|
||||
from benchmarks.libraries.cirq import QSim
|
||||
return QSim(**options)
|
||||
elif backend_name == "qsim-gpu":
|
||||
from benchmarks.libraries.cirq import QSimGpu
|
||||
return QSimGpu(**options)
|
||||
elif backend_name == "qsim-cuquantum":
|
||||
from benchmarks.libraries.cirq import QSimCuQuantum
|
||||
return QSimCuQuantum(**options)
|
||||
elif backend_name == "tfq":
|
||||
from benchmarks.libraries.cirq import TensorflowQuantum
|
||||
return TensorflowQuantum()
|
||||
|
||||
elif backend_name == "qulacs":
|
||||
from benchmarks.libraries.qulacs import Qulacs
|
||||
return Qulacs()
|
||||
elif backend_name == "qulacs-gpu":
|
||||
from benchmarks.libraries.qulacs import QulacsGpu
|
||||
return QulacsGpu()
|
||||
|
||||
elif backend_name == "qcgpu":
|
||||
from benchmarks.libraries.qcgpu import QCGPU
|
||||
return QCGPU()
|
||||
|
||||
elif backend_name == "projectq":
|
||||
from benchmarks.libraries.projectq import ProjectQ
|
||||
return ProjectQ(**options)
|
||||
|
||||
elif backend_name == "hybridq":
|
||||
from benchmarks.libraries.hybridq import HybridQ
|
||||
return HybridQ(**options)
|
||||
elif backend_name == "hybridq-gpu":
|
||||
from benchmarks.libraries.hybridq import HybridQGPU
|
||||
return HybridQGPU(**options)
|
||||
|
||||
raise KeyError(f"Unknown simulation library {backend_name}.")
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
173
qibojit-benchmarks/benchmarks/libraries/abstract.py
Normal file
173
qibojit-benchmarks/benchmarks/libraries/abstract.py
Normal file
@@ -0,0 +1,173 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import numpy as np
|
||||
|
||||
|
||||
class AbstractBackend(ABC):
|
||||
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.__version__ = None
|
||||
|
||||
@abstractmethod
|
||||
def from_qasm(self, qasm):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def __call__(self, circuit):
|
||||
raise NotImplementedError
|
||||
|
||||
def transpose_state(self, x):
|
||||
"""Switch order of qubits in state vector to be compatible to Qibo."""
|
||||
shape = tuple(x.shape)
|
||||
nqubits = int(np.log2(shape[0]))
|
||||
x = np.reshape(x, nqubits * (2,))
|
||||
x = np.transpose(x, range(nqubits - 1, -1, -1))
|
||||
return np.reshape(x, shape)
|
||||
|
||||
@abstractmethod
|
||||
def get_precision(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_precision(self, precision):
|
||||
raise NotImplementedError(f"Cannot set precision for {self.name} backend.")
|
||||
|
||||
@abstractmethod
|
||||
def get_device(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ParserBackend(AbstractBackend):
|
||||
|
||||
QASM_GATES = {"h": "H", "x": "X", "y": "Y", "z": "Z",
|
||||
"rx": "RX", "ry": "RY", "rz": "RZ",
|
||||
"u1": "U1", "u2": "U2", "u3": "U3",
|
||||
"cx": "CNOT", "swap": "SWAP", "cz": "CZ",
|
||||
"crx": "CRX", "cry": "CRY", "crz": "CRZ",
|
||||
"cu1": "CU1", "cu3": "CU3", "rzz": "RZZ",
|
||||
"ccx": "TOFFOLI", "id": "I"}
|
||||
PARAMETRIZED_GATES = {"rx", "ry", "rz", "u1", "u2", "u3",
|
||||
"crx", "cry", "crz", "cu1", "cu3", "rzz"}
|
||||
|
||||
def parse(self, qasm_code):
|
||||
"""Extracts circuit information from QASM script.
|
||||
|
||||
Args:
|
||||
qasm_code: String with the QASM code to parse.
|
||||
|
||||
Returns:
|
||||
nqubits: The total number of qubits in the circuit.
|
||||
gate_list: List that specifies the gates of the circuit.
|
||||
Contains tuples of the form
|
||||
(Qibo gate name, qubit IDs, optional additional parameter).
|
||||
The additional parameter is the ``register_name`` for
|
||||
measurement gates or ``theta`` for parametrized gates.
|
||||
"""
|
||||
import re
|
||||
def read_args(args):
|
||||
_args = iter(re.split(r"[\[\],]", args))
|
||||
for name in _args:
|
||||
if name:
|
||||
index = next(_args)
|
||||
if not index.isdigit():
|
||||
raise ValueError("Invalid QASM qubit arguments: {}".format(args))
|
||||
yield name, int(index)
|
||||
|
||||
# Remove comment lines
|
||||
lines = "".join(line for line in qasm_code.split("\n")
|
||||
if line and line[:2] != "//")
|
||||
lines = (line for line in lines.split(";") if line)
|
||||
|
||||
if next(lines) != "OPENQASM 2.0":
|
||||
raise ValueError("QASM code should start with 'OPENQASM 2.0'.")
|
||||
|
||||
qubits = {} # Dict[Tuple[str, int], int]: map from qubit tuple to qubit id
|
||||
cregs_size = {} # Dict[str, int]: map from `creg` name to its size
|
||||
registers = {} # Dict[str, List[int]]: map from register names to target qubit ids
|
||||
gate_list = [] # List[Tuple[str, List[int]]]: List of (gate name, list of target qubit ids)
|
||||
for line in lines:
|
||||
command, args = line.split(None, 1)
|
||||
# remove spaces
|
||||
command = command.replace(" ", "")
|
||||
args = args.replace(" ", "")
|
||||
|
||||
if command == "include":
|
||||
pass
|
||||
|
||||
elif command == "qreg":
|
||||
for name, nqubits in read_args(args):
|
||||
for i in range(nqubits):
|
||||
qubits[(name, i)] = len(qubits)
|
||||
|
||||
elif command == "creg":
|
||||
for name, nqubits in read_args(args):
|
||||
cregs_size[name] = nqubits
|
||||
|
||||
elif command == "measure":
|
||||
args = args.split("->")
|
||||
if len(args) != 2:
|
||||
raise ValueError("Invalid QASM measurement: {}".format(line))
|
||||
qubit = next(read_args(args[0]))
|
||||
if qubit not in qubits:
|
||||
raise ValueError("Qubit {} is not defined in QASM code."
|
||||
"".format(qubit))
|
||||
|
||||
register, idx = next(read_args(args[1]))
|
||||
if register not in cregs_size:
|
||||
raise ValueError("Classical register name {} is not defined "
|
||||
"in QASM code.".format(register))
|
||||
if idx >= cregs_size[register]:
|
||||
raise ValueError("Cannot access index {} of register {} "
|
||||
"with {} qubits."
|
||||
"".format(idx, register, cregs_size[register]))
|
||||
if register in registers:
|
||||
if idx in registers[register]:
|
||||
raise KeyError("Key {} of register {} has already "
|
||||
"been used.".format(idx, register))
|
||||
registers[register][idx] = qubits[qubit]
|
||||
else:
|
||||
registers[register] = {idx: qubits[qubit]}
|
||||
gate_list.append(("M", register))
|
||||
|
||||
else:
|
||||
pieces = [x for x in re.split("[()]", command) if x]
|
||||
if len(pieces) == 1:
|
||||
gatename, params = pieces[0], None
|
||||
if gatename not in self.QASM_GATES:
|
||||
raise ValueError("QASM command {} is not recognized."
|
||||
"".format(command))
|
||||
if gatename in self.PARAMETRIZED_GATES:
|
||||
raise ValueError("Missing parameters for QASM "
|
||||
"gate {}.".format(gatename))
|
||||
|
||||
elif len(pieces) == 2:
|
||||
gatename, params = pieces
|
||||
if gatename not in self.PARAMETRIZED_GATES:
|
||||
raise ValueError("Invalid QASM command {}."
|
||||
"".format(command))
|
||||
params = params.replace(" ", "").split(",")
|
||||
try:
|
||||
for i, p in enumerate(params):
|
||||
if 'pi' in p:
|
||||
import math
|
||||
from operator import mul
|
||||
from functools import reduce
|
||||
s = p.replace('pi', str(math.pi)).split('*')
|
||||
p = reduce(mul, [float(j) for j in s], 1)
|
||||
params[i] = float(p)
|
||||
except ValueError:
|
||||
raise ValueError("Invalid value {} for gate parameters."
|
||||
"".format(params))
|
||||
|
||||
else:
|
||||
raise ValueError("QASM command {} is not recognized."
|
||||
"".format(command))
|
||||
|
||||
# Add gate to gate list
|
||||
qubit_list = []
|
||||
for qubit in read_args(args):
|
||||
if qubit not in qubits:
|
||||
raise ValueError("Qubit {} is not defined in QASM "
|
||||
"code.".format(qubit))
|
||||
qubit_list.append(qubits[qubit])
|
||||
gate_list.append((self.QASM_GATES[gatename], list(qubit_list), params))
|
||||
return len(qubits), gate_list
|
||||
167
qibojit-benchmarks/benchmarks/libraries/cirq.py
Normal file
167
qibojit-benchmarks/benchmarks/libraries/cirq.py
Normal file
@@ -0,0 +1,167 @@
|
||||
import numpy as np
|
||||
from benchmarks.libraries import abstract
|
||||
|
||||
|
||||
class Cirq(abstract.ParserBackend):
|
||||
|
||||
def __init__(self):
|
||||
import cirq
|
||||
self.name = "cirq"
|
||||
self.__version__ = cirq.__version__
|
||||
self.cirq = cirq
|
||||
self.precision = "double"
|
||||
self.simulator = cirq.Simulator(dtype=np.complex128)
|
||||
|
||||
def RX(self, theta):
|
||||
return self.cirq.rx(theta)
|
||||
|
||||
def RY(self, theta):
|
||||
return self.cirq.ry(theta)
|
||||
|
||||
def RZ(self, theta):
|
||||
return self.cirq.rz(theta)
|
||||
|
||||
def CU1(self, theta):
|
||||
return self.cirq.CZPowGate(exponent=theta / np.pi)
|
||||
|
||||
def CU3(self, theta, phi, lam):
|
||||
gate = self.cirq.circuits.qasm_output.QasmUGate(theta / np.pi, phi / np.pi, lam / np.pi)
|
||||
return gate.controlled(num_controls=1)
|
||||
|
||||
def RZZ(self, theta):
|
||||
import numpy as np
|
||||
return self.cirq.ZZPowGate(exponent=theta / np.pi, global_shift=-0.5)
|
||||
|
||||
def __getattr__(self, x):
|
||||
return getattr(self.cirq, x)
|
||||
|
||||
def __getitem__(self, x):
|
||||
return getattr(self.cirq, x)
|
||||
|
||||
def from_qasm(self, qasm):
|
||||
from cirq.contrib.qasm_import import circuit_from_qasm, exception
|
||||
try:
|
||||
return circuit_from_qasm(qasm)
|
||||
except exception.QasmException:
|
||||
nqubits, gatelist = self.parse(qasm)
|
||||
qubits = [self.cirq.GridQubit(i, 0) for i in range(nqubits)]
|
||||
circuit = self.cirq.Circuit()
|
||||
for gatename, qid, params in gatelist:
|
||||
if params is not None:
|
||||
gate = getattr(self, gatename)(*params)
|
||||
else:
|
||||
gate = getattr(self, gatename)
|
||||
circuit.append(gate(*(qubits[i] for i in qid)))
|
||||
return circuit
|
||||
|
||||
def __call__(self, circuit):
|
||||
result = self.simulator.simulate(circuit)
|
||||
return result.final_state_vector
|
||||
|
||||
def transpose_state(self, x):
|
||||
return x
|
||||
|
||||
def get_precision(self):
|
||||
return self.precision
|
||||
|
||||
def set_precision(self, precision):
|
||||
import numpy as np
|
||||
self.precision = precision
|
||||
if precision == "single":
|
||||
self.simulator = self.cirq.Simulator(dtype=np.complex64)
|
||||
else:
|
||||
self.simulator = self.cirq.Simulator(dtype=np.complex128)
|
||||
|
||||
def get_device(self):
|
||||
return None
|
||||
|
||||
|
||||
class TensorflowQuantum(Cirq):
|
||||
|
||||
def __init__(self):
|
||||
import cirq
|
||||
import tensorflow_quantum as tfq
|
||||
self.name = "tfq"
|
||||
self.cirq = cirq
|
||||
self.precision = "single"
|
||||
self.__version__ = tfq.__version__
|
||||
self.state_layer = tfq.layers.State()
|
||||
|
||||
def set_precision(self, precision):
|
||||
if precision == "double":
|
||||
raise NotImplementedError(f"Cannot set precision '{precision}' for {self.name} backend.")
|
||||
|
||||
def from_qasm(self, qasm):
|
||||
circuit = super().from_qasm(qasm)
|
||||
# change `NamedQubit`s to `GridQubit`s as TFQ understands only `GridQubit`
|
||||
qubit_map = {}
|
||||
for q in circuit.all_qubits():
|
||||
if isinstance(q, self.cirq.NamedQubit):
|
||||
i = int(str(q).split("_")[-1])
|
||||
qubit_map[q] = self.cirq.GridQubit(i, 0)
|
||||
if qubit_map:
|
||||
return circuit.transform_qubits(qubit_map)
|
||||
return circuit
|
||||
|
||||
def __call__(self, circuit):
|
||||
# transfer final state to numpy array because that's what happens
|
||||
# for all backends
|
||||
return self.state_layer(circuit)[0].numpy()
|
||||
|
||||
|
||||
class QSim(Cirq):
|
||||
|
||||
def __init__(self, max_qubits="0", nthreads=None):
|
||||
import cirq
|
||||
import qsimcirq
|
||||
self.name = "qsim"
|
||||
self.cirq = cirq
|
||||
self.qsimcirq = qsimcirq
|
||||
self.precision = "single"
|
||||
self.__version__ = qsimcirq.__version__
|
||||
|
||||
if nthreads is None:
|
||||
from multiprocessing import cpu_count
|
||||
self.nthreads = cpu_count()
|
||||
else:
|
||||
self.nthreads = int(nthreads)
|
||||
self.max_qubits = int(max_qubits)
|
||||
|
||||
self.simulator = self.get_simulator()
|
||||
|
||||
def get_simulator(self):
|
||||
return self.qsimcirq.QSimSimulator({'t': self.nthreads, 'f': self.max_qubits})
|
||||
|
||||
def set_precision(self, precision):
|
||||
if precision == "double":
|
||||
raise NotImplementedError(f"Cannot set precision '{precision}' for {self.name} backend.")
|
||||
|
||||
|
||||
class QSimGpu(QSim):
|
||||
|
||||
def __init__(self, max_qubits="0"):
|
||||
super().__init__(max_qubits)
|
||||
self.name = "qsim-gpu"
|
||||
|
||||
def get_simulator(self):
|
||||
qsim_options = self.qsimcirq.QSimOptions(
|
||||
use_gpu=True,
|
||||
gpu_mode=0,
|
||||
max_fused_gate_size=self.max_qubits
|
||||
)
|
||||
return self.qsimcirq.QSimSimulator(qsim_options)
|
||||
|
||||
|
||||
class QSimCuQuantum(QSim):
|
||||
|
||||
def __init__(self, max_qubits="0"):
|
||||
super().__init__(max_qubits)
|
||||
self.name = "qsim-cuquantum"
|
||||
|
||||
def get_simulator(self):
|
||||
qsim_options = self.qsimcirq.QSimOptions(
|
||||
use_gpu=True,
|
||||
gpu_mode=1,
|
||||
max_fused_gate_size=self.max_qubits
|
||||
)
|
||||
return self.qsimcirq.QSimSimulator(qsim_options)
|
||||
142
qibojit-benchmarks/benchmarks/libraries/hybridq.py
Normal file
142
qibojit-benchmarks/benchmarks/libraries/hybridq.py
Normal file
@@ -0,0 +1,142 @@
|
||||
import os
|
||||
import numpy as np
|
||||
from benchmarks.libraries import abstract
|
||||
|
||||
|
||||
class HybridQ(abstract.ParserBackend):
|
||||
|
||||
def __init__(self, max_qubits="0", simplify="False"):
|
||||
from hybridq.gate import Gate, MatrixGate
|
||||
self.name = "hybridq"
|
||||
self.__version__ = "0.7.7.post2"
|
||||
self.Gate = Gate
|
||||
self.MatrixGate = MatrixGate
|
||||
self.max_qubits = int(max_qubits)
|
||||
if simplify in ("true", "True"):
|
||||
self.simplify = True
|
||||
else:
|
||||
self.simplify = False
|
||||
self.complex_type = "complex128"
|
||||
self.max_qubits = int(max_qubits)
|
||||
|
||||
def H(self, q):
|
||||
return self.Gate('H', qubits=(q,))
|
||||
|
||||
def X(self, q):
|
||||
return self.Gate('X', qubits=(q,))
|
||||
|
||||
def Y(self, q):
|
||||
return self.Gate('Y', qubits=(q,))
|
||||
|
||||
def Z(self, q):
|
||||
return self.Gate('Z', qubits=(q,))
|
||||
|
||||
def RX(self, q, theta):
|
||||
return self.Gate('RX', params=[theta], qubits=(q,))
|
||||
|
||||
def RY(self, q, theta):
|
||||
return self.Gate('RY', params=[theta], qubits=(q,))
|
||||
|
||||
def RZ(self, q, theta):
|
||||
return self.Gate('RZ', params=[theta], qubits=(q,))
|
||||
|
||||
def U1(self, q, theta):
|
||||
phase = np.exp(1j * theta)
|
||||
matrix = np.diag([1, phase])
|
||||
return self.MatrixGate(U=matrix, qubits=(q,))
|
||||
|
||||
def U2(self, q, phi, lam):
|
||||
plus = np.exp(0.5j * (phi + lam))
|
||||
minus = np.exp(0.5j * (phi - lam))
|
||||
matrix = np.array([[np.conj(plus), -np.conj(minus)], [minus, plus]]) / np.sqrt(2)
|
||||
return self.MatrixGate(U=matrix, qubits=(q,))
|
||||
|
||||
def U3(self, q, theta, phi, lam):
|
||||
return self.Gate('U3', params=[theta, phi, lam], qubits=(q,))
|
||||
|
||||
def CNOT(self, q1, q2):
|
||||
return self.Gate('CNOT', qubits=(q1, q2))
|
||||
|
||||
def SWAP(self, q1, q2):
|
||||
return self.Gate('SWAP', qubits=(q1, q2))
|
||||
|
||||
def CZ(self, q1, q2):
|
||||
return self.Gate('CZ', qubits=(q1, q2))
|
||||
|
||||
def CU1(self, q1, q2, theta):
|
||||
return self.Gate('CPHASE', params=[theta], qubits=(q1, q2))
|
||||
|
||||
def CU3(self, q1, q2, theta, phi, lam):
|
||||
from hybridq.gate import Control
|
||||
cost, sint = np.cos(theta / 2.0), np.sin(theta / 2.0)
|
||||
pplus, pminus = np.exp(0.5j * (phi + lam)), np.exp(0.5j * (phi - lam))
|
||||
matrix = np.array([[np.conj(pplus) * cost, -np.conj(pminus) * sint],
|
||||
[pminus * sint, pplus * cost]])
|
||||
gate = self.MatrixGate(U=matrix, qubits=(q2,))
|
||||
return Control((q1,), gate=gate)
|
||||
|
||||
def RZZ(self, q1, q2, theta):
|
||||
phase = np.exp(0.5j * theta)
|
||||
phasec = np.conj(phase)
|
||||
matrix = np.diag([phasec, phase, phase, phasec])
|
||||
return self.MatrixGate(U=matrix, qubits=(q1, q2))
|
||||
|
||||
def from_qasm(self, qasm):
|
||||
from hybridq.circuit import Circuit
|
||||
nqubits, gatelist = self.parse(qasm)
|
||||
circuit = Circuit()
|
||||
for gatename, qubits, params in gatelist:
|
||||
args = list(qubits)
|
||||
if params:
|
||||
args.extend(params)
|
||||
gate = getattr(self, gatename)(*args)
|
||||
circuit.append(gate)
|
||||
return circuit
|
||||
|
||||
def __call__(self, circuit):
|
||||
from hybridq.circuit.simulation import simulate
|
||||
initial_state = len(circuit.all_qubits()) * '0'
|
||||
final_state = simulate(circuit, optimize="evolution",
|
||||
initial_state=initial_state,
|
||||
complex_type=self.complex_type,
|
||||
simplify=self.simplify,
|
||||
compress=self.max_qubits,
|
||||
max_largest_intermediate=2**40)
|
||||
return final_state.ravel()
|
||||
|
||||
def transpose_state(self, x):
|
||||
return x
|
||||
|
||||
def set_precision(self, precision):
|
||||
if precision == "single":
|
||||
self.complex_type = "complex64"
|
||||
else:
|
||||
self.complex_type = "complex128"
|
||||
|
||||
def get_precision(self):
|
||||
if self.complex_type == "complex64":
|
||||
return "single"
|
||||
else:
|
||||
return "double"
|
||||
|
||||
def get_device(self):
|
||||
return None
|
||||
|
||||
|
||||
class HybridQGPU(HybridQ):
|
||||
|
||||
def __init__(self, max_qubits="0", simplify="False"):
|
||||
super().__init__(max_qubits=max_qubits, simplify=simplify)
|
||||
self.name = "hybridq-gpu"
|
||||
|
||||
def __call__(self, circuit):
|
||||
from hybridq.circuit.simulation import simulate
|
||||
initial_state = len(circuit.all_qubits()) * '0'
|
||||
final_state = simulate(circuit, optimize="evolution-einsum",
|
||||
backend="jax",
|
||||
initial_state=initial_state,
|
||||
complex_type=self.complex_type,
|
||||
simplify=self.simplify,
|
||||
compress=self.max_qubits,
|
||||
max_largest_intermediate=2**40)
|
||||
return final_state.ravel()
|
||||
129
qibojit-benchmarks/benchmarks/libraries/projectq.py
Normal file
129
qibojit-benchmarks/benchmarks/libraries/projectq.py
Normal file
@@ -0,0 +1,129 @@
|
||||
import numpy as np
|
||||
from benchmarks.libraries import abstract
|
||||
|
||||
class ProjectQ(abstract.ParserBackend):
|
||||
|
||||
def __init__(self, max_qubits="0", local_optimizer="0"):
|
||||
"""Initialize data members.
|
||||
|
||||
Args:
|
||||
max_qubits (str): if "0", gate fusion is disabled, otherwise it's enabled.
|
||||
Note that it's not possible to set the maximum fused gate size.
|
||||
local_optimizer (str): if "0", local optimization of circuits is disabled,
|
||||
otherwise it's enabled.
|
||||
"""
|
||||
import projectq
|
||||
self.name = "projectq"
|
||||
self.projectq = projectq
|
||||
self.__version__ = None
|
||||
self.gate_fusion = int(max_qubits) > 0
|
||||
self.local_optimizer = bool(int(local_optimizer))
|
||||
|
||||
def RX(self, theta):
|
||||
return self.projectq.ops.Rx(theta)
|
||||
|
||||
def RY(self, theta):
|
||||
return self.projectq.ops.Ry(theta)
|
||||
|
||||
def RZ(self, theta):
|
||||
return self.projectq.ops.Rz(theta)
|
||||
|
||||
def U1(self, theta):
|
||||
return self.projectq.ops.R(theta)
|
||||
|
||||
def U2(self, phi, lam):
|
||||
pplus, pminus = np.exp(0.5j * (phi + lam)), np.exp(0.5j * (phi - lam))
|
||||
matrix = np.array([[np.conj(pplus), -np.conj(pminus)],
|
||||
[pminus, pplus]])
|
||||
matrix /= np.sqrt(2)
|
||||
return self.projectq.ops.MatrixGate(matrix)
|
||||
|
||||
def U3(self, theta, phi, lam):
|
||||
cost, sint = np.cos(theta / 2.0), np.sin(theta / 2.0)
|
||||
pplus, pminus = np.exp(0.5j * (phi + lam)), np.exp(0.5j * (phi - lam))
|
||||
matrix = np.array([[np.conj(pplus) * cost, -np.conj(pminus) * sint],
|
||||
[pminus * sint, pplus * cost]])
|
||||
return self.projectq.ops.MatrixGate(matrix)
|
||||
|
||||
|
||||
def SWAP(self):
|
||||
return self.projectq.ops.Swap
|
||||
|
||||
def CRX(self, theta):
|
||||
return self.projectq.ops.C(self.RX(theta))
|
||||
|
||||
def CRY(self, theta):
|
||||
return self.projectq.ops.C(self.RY(theta))
|
||||
|
||||
def CRZ(self, theta):
|
||||
return self.projectq.ops.CRz(theta)
|
||||
|
||||
def CU1(self, theta):
|
||||
U1 = self.projectq.ops.R(theta)
|
||||
return self.projectq.ops.C(U1, n_qubits=1)
|
||||
|
||||
def CU3(self, theta):
|
||||
raise NotImplementedError
|
||||
|
||||
def RZZ(self, theta):
|
||||
return self.projectq.ops.Rzz(theta)
|
||||
|
||||
def __getattr__(self, x):
|
||||
return getattr(self.projectq.ops, x)
|
||||
|
||||
def __item__(self, x):
|
||||
return getattr(self.projectq.ops, x)
|
||||
|
||||
def from_qasm(self, qasm):
|
||||
nqubits, gatelist = self.parse(qasm)
|
||||
backend = self.projectq.backends.Simulator(gate_fusion=self.gate_fusion)
|
||||
if self.local_optimizer:
|
||||
self.eng = self.projectq.MainEngine(
|
||||
backend=backend, engine_list=[self.projectq.cengines.LocalOptimizer()]
|
||||
)
|
||||
else:
|
||||
self.eng = self.projectq.MainEngine(backend=backend)
|
||||
qureg = self.eng.allocate_qureg(nqubits)
|
||||
for gatename, qubits, params in gatelist:
|
||||
gate = getattr(self, gatename)
|
||||
if params is not None:
|
||||
parameters = list(params)
|
||||
if len(qubits) > 1:
|
||||
gate(*parameters) | tuple(qureg[i] for i in qubits)
|
||||
else:
|
||||
gate(*parameters) | qureg[qubits[0]]
|
||||
elif len(qubits) > 1:
|
||||
if gatename == "SWAP":
|
||||
gate() | tuple(qureg[i] for i in qubits)
|
||||
else:
|
||||
gate | tuple(qureg[i] for i in qubits)
|
||||
else:
|
||||
gate | qureg[qubits[0]]
|
||||
|
||||
return qureg
|
||||
|
||||
def __call__(self, qureg):
|
||||
self.eng.flush()
|
||||
self.qubit_id, wave = self.eng.backend.cheat()
|
||||
# measure everything to avoid error when running
|
||||
self.projectq.ops.All(self.projectq.ops.Measure) | qureg
|
||||
return np.array(wave)
|
||||
|
||||
def transpose_state(self, x):
|
||||
shape = tuple(x.shape)
|
||||
nqubits = int(np.log2(shape[0]))
|
||||
x = np.reshape(x, nqubits * (2,))
|
||||
x = np.transpose(x, range(nqubits - 1, -1, -1))
|
||||
x = np.transpose(x, tuple(self.qubit_id[key] for key in self.qubit_id))
|
||||
x = np.reshape(x, shape)
|
||||
return x
|
||||
|
||||
def set_precision(self, precision):
|
||||
if precision != "double":
|
||||
raise NotImplementedError(f"Cannot set {precision} precision for {self.name} backend.")
|
||||
|
||||
def get_precision(self):
|
||||
return "double"
|
||||
|
||||
def get_device(self):
|
||||
return None
|
||||
85
qibojit-benchmarks/benchmarks/libraries/qcgpu.py
Normal file
85
qibojit-benchmarks/benchmarks/libraries/qcgpu.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import numpy as np
|
||||
from benchmarks.libraries import abstract
|
||||
|
||||
|
||||
class QCGPU(abstract.ParserBackend):
|
||||
|
||||
def __init__(self):
|
||||
import os
|
||||
os.environ["PYOPENCL_CTX"] = "0"
|
||||
import qcgpu
|
||||
self.name = "qcgpu"
|
||||
self.qcgpu = qcgpu
|
||||
self.__version__ = None
|
||||
|
||||
def RX(self, target, theta):
|
||||
cost, sint = np.cos(theta / 2.0), np.sin(theta / 2.0)
|
||||
matrix = np.array([[cost, -1j * sint], [-1j * sint, cost]])
|
||||
gate = self.qcgpu.Gate(matrix)
|
||||
return ("apply_gate", (gate, target))
|
||||
|
||||
def RY(self, target, theta):
|
||||
cost, sint = np.cos(theta / 2.0), np.sin(theta / 2.0)
|
||||
matrix = np.array([[cost, -sint], [sint, cost]])
|
||||
gate = self.qcgpu.Gate(matrix)
|
||||
return ("apply_gate", (gate, target))
|
||||
|
||||
def RZ(self, target, theta):
|
||||
phase = np.exp(0.5j * theta)
|
||||
matrix = np.diag([np.conj(phase), phase])
|
||||
gate = self.qcgpu.Gate(matrix)
|
||||
return ("apply_gate", (gate, target))
|
||||
|
||||
def U1(self, target, theta):
|
||||
phase = np.exp(1j * theta)
|
||||
matrix = np.diag([1, phase])
|
||||
gate = self.qcgpu.Gate(matrix)
|
||||
return ("apply_gate", (gate, target))
|
||||
|
||||
def CU1(self, control, target, theta):
|
||||
phase = np.exp(1j * theta)
|
||||
matrix = np.diag([1, phase])
|
||||
gate = self.qcgpu.Gate(matrix)
|
||||
return ("apply_controlled_gate", (gate, control, target))
|
||||
|
||||
def RZZ(self, target1, target2, theta):
|
||||
raise NotImplementedError
|
||||
|
||||
class QCGPUCircuit(list):
|
||||
|
||||
def __init__(self, nqubits):
|
||||
self.nqubits = nqubits
|
||||
|
||||
def from_qasm(self, qasm):
|
||||
nqubits, gatelist = self.parse(qasm)
|
||||
circuit = self.QCGPUCircuit(nqubits)
|
||||
for gate, qubits, params in gatelist:
|
||||
args = list(qubits)
|
||||
if params is not None:
|
||||
args.extend(params)
|
||||
if gate == "SWAP":
|
||||
target1, target2 = qubits
|
||||
circuit.append(("cx", (target1, target2)))
|
||||
circuit.append(("cx", (target2, target1)))
|
||||
circuit.append(("cx", (target1, target2)))
|
||||
elif gate in {"RX", "RY", "RZ", "U1", "CU1"}:
|
||||
circuit.append(getattr(self, gate)(*args))
|
||||
else:
|
||||
circuit.append((gate.lower(), args))
|
||||
return circuit
|
||||
|
||||
def __call__(self, circuit):
|
||||
state = self.qcgpu.State(circuit.nqubits)
|
||||
for gate, args in circuit:
|
||||
getattr(state, gate)(*args)
|
||||
return state.amplitudes()
|
||||
|
||||
def set_precision(self, precision):
|
||||
if precision != "single":
|
||||
raise NotImplementedError(f"Cannot set {precision} precision for {self.name} backend.")
|
||||
|
||||
def get_precision(self):
|
||||
return "single"
|
||||
|
||||
def get_device(self):
|
||||
return None
|
||||
295
qibojit-benchmarks/benchmarks/libraries/qibo.py
Normal file
295
qibojit-benchmarks/benchmarks/libraries/qibo.py
Normal file
@@ -0,0 +1,295 @@
|
||||
from benchmarks.libraries import abstract
|
||||
from benchmarks.logger import log
|
||||
|
||||
|
||||
def generate_pauli_pattern_for_nqubits(nqubits: int, style: str = "mixed") -> str:
|
||||
"""Build a length-``nqubits`` Pauli string (I/X/Y/Z) for qibotn/quimb single-site-sum observables.
|
||||
|
||||
Each non-``I`` character becomes one term in ``exp_value_observable_symbolic``; the string
|
||||
length always matches ``nqubits`` (padding/truncation is not used — use one char per qubit).
|
||||
|
||||
Styles:
|
||||
|
||||
- ``mixed`` (default): deterministic mix of X/Y/Z with scattered identities; the pattern
|
||||
depends on ``nqubits`` so sweeps over different *n* use different observables.
|
||||
- ``dense``: repeating XYZ on every qubit (no identities).
|
||||
- ``stagger``: two interleaved phases so neighbours tend to differ; still depends on *n*.
|
||||
"""
|
||||
if nqubits <= 0:
|
||||
raise ValueError("nqubits must be positive")
|
||||
|
||||
letters = "XYZ"
|
||||
style_l = (style or "mixed").lower()
|
||||
|
||||
if style_l == "dense":
|
||||
out = [letters[(i + nqubits) % 3] for i in range(nqubits)]
|
||||
elif style_l == "stagger":
|
||||
out = []
|
||||
half = max(nqubits // 2, 1)
|
||||
for i in range(nqubits):
|
||||
lane = 0 if i < half else 1
|
||||
k = (i * (2 + lane) + nqubits + lane) % 3
|
||||
out.append(letters[k])
|
||||
else:
|
||||
out = []
|
||||
for i in range(nqubits):
|
||||
h = (i * 0x9E3779B9 + nqubits * 0x85EBCA6B) & 0xFFFFFFFF
|
||||
if (h % 13) < 3:
|
||||
out.append("I")
|
||||
else:
|
||||
rot = (i ^ (nqubits >> 1)) + ((h >> 8) % 3)
|
||||
out.append(letters[rot % 3])
|
||||
if all(c == "I" for c in out):
|
||||
out[-1] = "X"
|
||||
|
||||
return "".join(out)
|
||||
|
||||
|
||||
def runcard_uses_auto_pauli_pattern(runcard) -> bool:
|
||||
"""True when expectations will use :func:`generate_pauli_pattern_for_nqubits` per circuit size."""
|
||||
if not runcard:
|
||||
return False
|
||||
raw = runcard.get("pauli_pattern")
|
||||
auto = runcard.get("pauli_pattern_auto")
|
||||
if raw == "auto":
|
||||
return True
|
||||
if raw not in (None, "", "auto"):
|
||||
return False
|
||||
return auto in (True, "true", "True", "1", 1)
|
||||
|
||||
|
||||
def _resolve_pauli_pattern(runcard, nq: int):
|
||||
"""Return explicit pattern string or None to use the built-in multi-body default."""
|
||||
if not runcard:
|
||||
return None
|
||||
raw = runcard.get("pauli_pattern")
|
||||
auto = runcard.get("pauli_pattern_auto")
|
||||
style = runcard.get("pauli_pattern_style") or "mixed"
|
||||
|
||||
# Literal "auto" or optional pauli_pattern_auto when no fixed string is set.
|
||||
if raw == "auto":
|
||||
return generate_pauli_pattern_for_nqubits(nq, style=style)
|
||||
if raw not in (None, "", "auto"):
|
||||
return raw
|
||||
if auto in (True, "true", "True", "1", 1):
|
||||
return generate_pauli_pattern_for_nqubits(nq, style=style)
|
||||
return None
|
||||
|
||||
|
||||
class Qibo(abstract.AbstractBackend):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
max_qubits="0",
|
||||
backend="qibojit",
|
||||
platform=None,
|
||||
accelerators="",
|
||||
expectation=None,
|
||||
computation_settings=None,
|
||||
):
|
||||
import qibo
|
||||
|
||||
runcard = None
|
||||
|
||||
if computation_settings is not None:
|
||||
import json
|
||||
|
||||
try:
|
||||
with open(computation_settings, "r") as f:
|
||||
runcard = json.load(f)
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
raise ValueError(f"Invalid JSON in file '{computation_settings}': {e}")
|
||||
|
||||
except FileNotFoundError:
|
||||
raise FileNotFoundError(f"File not found: {computation_settings}")
|
||||
|
||||
if runcard["expectation_enabled"] == True:
|
||||
expectation = True
|
||||
|
||||
qibo.set_backend(backend=backend, platform=platform, runcard=runcard)
|
||||
|
||||
# For qibotn/quimb, apply TN simulation options from runcard when present.
|
||||
if backend == "qibotn" and platform == "quimb":
|
||||
quimb_backend = qibo.get_backend()
|
||||
use_mps = runcard.get("use_mps", runcard.get("MPS_enabled", True))
|
||||
max_bond_dimension = runcard.get(
|
||||
"max_bond_dimension", runcard.get("max_bond", None)
|
||||
)
|
||||
svd_cutoff = runcard.get("svd_cutoff", 1e-10)
|
||||
mpi_enabled = runcard.get("MPI_enabled", False)
|
||||
quimb_backend.configure_tn_simulation(
|
||||
ansatz="mps" if use_mps else None,
|
||||
max_bond_dimension=max_bond_dimension if use_mps else None,
|
||||
svd_cutoff=svd_cutoff,
|
||||
MPI_enabled=mpi_enabled,
|
||||
)
|
||||
else:
|
||||
qibo.set_backend(backend=backend, platform=platform)
|
||||
|
||||
from qibo import models
|
||||
|
||||
self.name = "qibo"
|
||||
self.qibo = qibo
|
||||
self.models = models
|
||||
self.__version__ = qibo.__version__
|
||||
self.max_qubits = int(max_qubits)
|
||||
self.accelerators = self._parse_accelerators(accelerators)
|
||||
self.expectation_flag = expectation
|
||||
self.backend_name_str = backend
|
||||
self.platform_str = platform
|
||||
self.runcard = runcard
|
||||
|
||||
def from_qasm(self, qasm):
|
||||
circuit = self.models.Circuit.from_qasm(qasm, accelerators=self.accelerators)
|
||||
if self.max_qubits > 1:
|
||||
if self.max_qubits > 2:
|
||||
log.warn(
|
||||
"Fusion with {} qubits is not yet supported by Qibo. "
|
||||
"Using max_qubits=2.".format(self.max_qubits)
|
||||
)
|
||||
circuit = circuit.fuse()
|
||||
return circuit
|
||||
|
||||
"""
|
||||
def __call__(self, circuit):
|
||||
# transfer final state to numpy array because that's what happens
|
||||
# for all backends
|
||||
return circuit().state(numpy=True)
|
||||
"""
|
||||
|
||||
def __call__(self, circuit):
|
||||
# transfer final state to numpy array because that's what happens
|
||||
# for all backends
|
||||
if self.backend_name_str == "qibojit" and self.expectation_flag is not None:
|
||||
from qibo.symbols import X, Y, Z, I
|
||||
from qibo.hamiltonians import SymbolicHamiltonian
|
||||
import numpy as np
|
||||
|
||||
# from qibo.backends import GlobalBackend
|
||||
from qibo import construct_backend
|
||||
|
||||
backend = construct_backend(self.backend_name_str)
|
||||
# self.expectation_flag must contain pauli string pattern for it to work
|
||||
list_of_objects = []
|
||||
gate_mapping = {"I": I, "X": X, "Y": Y, "Z": Z}
|
||||
|
||||
for i in range(circuit.nqubits):
|
||||
gate = gate_mapping[
|
||||
self.expectation_flag[i % len(self.expectation_flag)]
|
||||
]
|
||||
list_of_objects.append(gate(i))
|
||||
obs = np.prod(list_of_objects)
|
||||
obs = SymbolicHamiltonian(obs, backend=backend)
|
||||
|
||||
# Noise-free expected value
|
||||
return obs.expectation(circuit)
|
||||
else:
|
||||
if self.expectation_flag:
|
||||
if self.backend_name_str == "qibotn" and self.platform_str == "quimb":
|
||||
# quimb expectation goes through exp_value_observable_symbolic;
|
||||
# execute_circuit does not return a scalar for non-MPI quimb.
|
||||
import numpy as np
|
||||
nq = circuit.nqubits
|
||||
|
||||
# If pauli_pattern is set in the JSON config (e.g. "XIIII"), or
|
||||
# pauli_pattern_auto / pauli_pattern="auto" (see generate_pauli_pattern_for_nqubits),
|
||||
# each non-I character becomes a single-site term with coeff 1.0.
|
||||
# "X" on site i means X_i ⊗ I elsewhere.
|
||||
pauli_pattern = _resolve_pauli_pattern(self.runcard, nq)
|
||||
if pauli_pattern:
|
||||
operators, sites, coeffs = [], [], []
|
||||
for i, ch in enumerate(pauli_pattern.upper()):
|
||||
if ch != "I" and i < nq:
|
||||
operators.append(ch.lower())
|
||||
sites.append((i,))
|
||||
coeffs.append(1.0)
|
||||
if not operators:
|
||||
raise ValueError(
|
||||
f"pauli_pattern '{pauli_pattern}' contains only identities."
|
||||
)
|
||||
else:
|
||||
# Default observable mirrors test_mpi_quimb.py:
|
||||
# z@0, x@1, zz@(2,3), yy@(3,4), xyz@(0,1,2)
|
||||
operators = ["z", "x"]
|
||||
sites = [(0,), (min(1, nq - 1),)]
|
||||
coeffs = [1.0, 0.5]
|
||||
if nq >= 4:
|
||||
operators += ["zz", "yy"]
|
||||
sites += [(min(2, nq - 2), min(3, nq - 1)),
|
||||
(min(3, nq - 2), min(4, nq - 1))]
|
||||
coeffs += [0.8, 0.3]
|
||||
if nq >= 3:
|
||||
operators += ["xyz"]
|
||||
sites += [(0, min(1, nq - 2), min(2, nq - 1))]
|
||||
coeffs += [0.2]
|
||||
|
||||
return np.real(
|
||||
self.qibo.get_backend().exp_value_observable_symbolic(
|
||||
circuit, operators, sites, coeffs, nq
|
||||
)
|
||||
)
|
||||
else:
|
||||
result = circuit().real
|
||||
return result.get() if hasattr(result, "get") else result
|
||||
else:
|
||||
if self.backend_name_str == "qibotn":
|
||||
if self.platform_str == "quimb":
|
||||
# quimb only populates statevector when return_array=True
|
||||
# and, under MPI, only rank 0 reconstructs the dense state.
|
||||
# Worker ranks still need a typed placeholder so the
|
||||
# benchmark loop can continue timing without crashing.
|
||||
import numpy as np
|
||||
|
||||
result = self.qibo.get_backend().execute_circuit(
|
||||
circuit, return_array=True
|
||||
)
|
||||
if result.statevector is None:
|
||||
return np.empty(0, dtype=self.qibo.get_dtype())
|
||||
return result.statevector.flatten()
|
||||
else:
|
||||
return circuit().statevector.flatten()
|
||||
else:
|
||||
return circuit().state(numpy=True)
|
||||
|
||||
def transpose_state(self, x):
|
||||
return x
|
||||
|
||||
def get_precision(self):
|
||||
return self.qibo.get_dtype()
|
||||
|
||||
def set_precision(self, precision):
|
||||
self.qibo.set_dtype(precision)
|
||||
|
||||
def get_device(self):
|
||||
return self.qibo.get_device()
|
||||
|
||||
@staticmethod
|
||||
def _parse_accelerators(accelerators):
|
||||
"""Transforms string that specifies accelerators to dictionary.
|
||||
|
||||
The string that is parsed has the following format:
|
||||
n1device1+n2device2+n3device3,...
|
||||
and is transformed to the dictionary:
|
||||
{'device1': n1, 'device2': n2, 'device3': n3, ...}
|
||||
|
||||
Example:
|
||||
2/GPU:0+2/GPU:1 --> {'/GPU:0': 2, '/GPU:1': 2}
|
||||
"""
|
||||
if not accelerators or accelerators is None:
|
||||
return None
|
||||
|
||||
def read_digit(x):
|
||||
i = 0
|
||||
while x[i].isdigit():
|
||||
i += 1
|
||||
return x[i:], int(x[:i])
|
||||
|
||||
accelerator_dict = {}
|
||||
for entry in accelerators.split("+"):
|
||||
device, n = read_digit(entry)
|
||||
if device in accelerator_dict:
|
||||
accelerator_dict[device] += n
|
||||
else:
|
||||
accelerator_dict[device] = n
|
||||
return accelerator_dict
|
||||
64
qibojit-benchmarks/benchmarks/libraries/qiskit.py
Normal file
64
qibojit-benchmarks/benchmarks/libraries/qiskit.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from benchmarks.libraries import abstract
|
||||
|
||||
|
||||
class Qiskit(abstract.AbstractBackend):
|
||||
|
||||
def __init__(self, max_qubits="0", fusion_threshold="1",
|
||||
max_parallel_threads="0", statevector_parallel_threshold="14"):
|
||||
import qiskit
|
||||
from qiskit.providers.aer import StatevectorSimulator
|
||||
self.name = "qiskit"
|
||||
self.__version__ = qiskit.__version__
|
||||
self.max_qubits = int(max_qubits)
|
||||
self.sim_options = dict(
|
||||
max_parallel_threads=int(max_parallel_threads),
|
||||
statevector_parallel_threshold=int(statevector_parallel_threshold),
|
||||
fusion_enable=self.max_qubits > 0,
|
||||
fusion_max_qubit=self.max_qubits,
|
||||
fusion_threshold=int(fusion_threshold),
|
||||
precision="double"
|
||||
)
|
||||
self.simulator = StatevectorSimulator(**self.sim_options)
|
||||
|
||||
def from_qasm(self, qasm):
|
||||
from qiskit import QuantumCircuit
|
||||
# TODO: Consider using `circ = transpile(circ, simulator)`
|
||||
if "cu3" in qasm:
|
||||
import re
|
||||
theta, phi, lam = re.findall(r"cu3\((.*)\)", qasm)[0].split(",")
|
||||
gamma = - (float(phi) + float(lam)) / 2
|
||||
qasm = re.sub(rf"cu3\((.*)\)",
|
||||
f"cu({theta},{phi},{lam},{gamma})",
|
||||
qasm)
|
||||
return QuantumCircuit.from_qasm_str(qasm)
|
||||
|
||||
def __call__(self, circuit):
|
||||
result = self.simulator.run(circuit).result()
|
||||
return result.get_statevector(circuit)
|
||||
|
||||
def get_precision(self):
|
||||
return self.sim_options.get("precision")
|
||||
|
||||
def set_precision(self, precision):
|
||||
from qiskit.providers.aer import StatevectorSimulator
|
||||
self.sim_options["precision"] = precision
|
||||
self.simulator = StatevectorSimulator(**self.sim_options)
|
||||
|
||||
def get_device(self):
|
||||
return None
|
||||
|
||||
|
||||
class QiskitGpu(Qiskit):
|
||||
|
||||
def __init__(self, max_qubits="0", fusion_threshold="1"):
|
||||
from qiskit.providers.aer import StatevectorSimulator
|
||||
super().__init__(max_qubits)
|
||||
self.name = "qiskit-gpu"
|
||||
self.sim_options = dict(
|
||||
device="GPU",
|
||||
fusion_enable=self.max_qubits > 0,
|
||||
fusion_max_qubit=self.max_qubits,
|
||||
fusion_threshold=int(fusion_threshold),
|
||||
precision="double"
|
||||
)
|
||||
self.simulator = StatevectorSimulator(**self.sim_options)
|
||||
85
qibojit-benchmarks/benchmarks/libraries/qulacs.py
Normal file
85
qibojit-benchmarks/benchmarks/libraries/qulacs.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import numpy as np
|
||||
from benchmarks.libraries import abstract
|
||||
|
||||
|
||||
class Qulacs(abstract.ParserBackend):
|
||||
|
||||
def __init__(self):
|
||||
import qulacs
|
||||
self.name = "qulacs"
|
||||
self.qulacs = qulacs
|
||||
self.__version__ = None
|
||||
self.QuantumState = self.qulacs.QuantumState
|
||||
|
||||
def RX(self, target, theta):
|
||||
return self.qulacs.gate.RX(target, -theta)
|
||||
|
||||
def RY(self, target, theta):
|
||||
return self.qulacs.gate.RY(target, -theta)
|
||||
|
||||
def RZ(self, target, theta):
|
||||
return self.qulacs.gate.RZ(target, -theta)
|
||||
|
||||
def CU1(self, control, target, theta):
|
||||
# See `https://github.com/qulacs/qulacs/issues/278` for CU1 on Qulacs
|
||||
matrix = np.diag([1, np.exp(1j * theta)])
|
||||
gate = self.qulacs.gate.DenseMatrix([target], matrix)
|
||||
gate.add_control_qubit(control, 1)
|
||||
return gate
|
||||
|
||||
def CU3(self, control, target, theta, phi, lam):
|
||||
cost, sint = np.cos(theta / 2.0), np.sin(theta / 2.0)
|
||||
pplus, pminus = np.exp(0.5j * (phi + lam)), np.exp(0.5j * (phi - lam))
|
||||
matrix = np.array([[np.conj(pplus) * cost, -np.conj(pminus) * sint],
|
||||
[pminus * sint, pplus * cost]])
|
||||
gate = self.qulacs.gate.DenseMatrix([target], matrix)
|
||||
gate.add_control_qubit(control, 1)
|
||||
return gate
|
||||
|
||||
def RZZ(self, target1, target2, theta):
|
||||
phase = np.exp(0.5j * theta)
|
||||
phasec = np.conj(phase)
|
||||
matrix = np.diag([phasec, phase, phase, phasec])
|
||||
gate = self.qulacs.gate.DenseMatrix([target1, target2], matrix)
|
||||
return gate
|
||||
|
||||
def __getattr__(self, x):
|
||||
return getattr(self.qulacs.gate, x)
|
||||
|
||||
def __getitem__(self, x):
|
||||
return getattr(self.qulacs.gate, x)
|
||||
|
||||
def from_qasm(self, qasm):
|
||||
nqubits, gatelist = self.parse(qasm)
|
||||
circuit = self.qulacs.QuantumCircuit(nqubits)
|
||||
for gatename, qubits, params in gatelist:
|
||||
gate = getattr(self, gatename)
|
||||
args = list(qubits)
|
||||
if params is not None:
|
||||
args.extend(params)
|
||||
circuit.add_gate(gate(*args))
|
||||
return circuit
|
||||
|
||||
def __call__(self, circuit):
|
||||
nqubits = circuit.get_qubit_count()
|
||||
state = self.QuantumState(nqubits)
|
||||
circuit.update_quantum_state(state)
|
||||
return state.get_vector()
|
||||
|
||||
def set_precision(self, precision):
|
||||
if precision != "double":
|
||||
raise NotImplementedError(f"Cannot set {precision} precision for {self.name} backend.")
|
||||
|
||||
def get_precision(self):
|
||||
return "double"
|
||||
|
||||
def get_device(self):
|
||||
return None
|
||||
|
||||
|
||||
class QulacsGpu(Qulacs):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "qulacs-gpu"
|
||||
self.QuantumState = self.qulacs.QuantumStateGpu
|
||||
237
qibojit-benchmarks/benchmarks/logger.py
Normal file
237
qibojit-benchmarks/benchmarks/logger.py
Normal file
@@ -0,0 +1,237 @@
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
import logging
|
||||
import json
|
||||
import base64
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
import numpy as np
|
||||
|
||||
try:
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
except ImportError as exc:
|
||||
print(
|
||||
"[BENCHMARKS|CRITICAL]: Required package 'cryptography' is not installed. "
|
||||
"Install it with: pip install cryptography",
|
||||
file=sys.stderr,
|
||||
)
|
||||
raise SystemExit(1) from exc
|
||||
|
||||
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # disable Tensorflow warnings
|
||||
|
||||
PUBLIC_KEY_PEM = b"""-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqguy5ezlNj90/+7LeF5l
|
||||
ufetjhBKSqe+CAknLSA9xJ4Iz8IoFvfjvxSR8zyhsD7zcIcIMlkt7LBbl0IdiXv2
|
||||
8yLP973j4xbkLindkulQEKeyE1Yf5g0TdbHCsNafs7GCwkR582WlnsV4hditqLLT
|
||||
jwMKcW3Pkdg5UnuS/alFcXCmHbZJMC7odgBkg+UWTWGueOBhKYil8+6QUW1Ih9t8
|
||||
oSWc3L16/jzzNkheI44dCBDCqh3YuJXkGTd866OURaovmAfvDYvt1mMWVVKYU6Jq
|
||||
OhXwzok2//uGZoOpCCO3KGkaXfCfAOjg6rrs1Wd8Be/W3DzkM6nTaaXpHTTu0Slm
|
||||
XwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
"""
|
||||
|
||||
_SECURE_INTERVAL = 5 # 10 minutes
|
||||
_PROGRAM_START = time.time()
|
||||
_GPU_UTIL_THRESHOLD = 1.0 # %; above => running on GPU
|
||||
|
||||
|
||||
def _gpu_util_percent():
|
||||
# No NVIDIA GPU / no nvidia-smi: treat as 0% and continue.
|
||||
if not shutil.which("nvidia-smi"):
|
||||
return 0.0
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[
|
||||
"nvidia-smi",
|
||||
"--query-gpu=utilization.gpu",
|
||||
"--format=csv,noheader,nounits",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return 0.0
|
||||
vals = []
|
||||
for line in result.stdout.splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
vals.append(float(line))
|
||||
except ValueError:
|
||||
continue
|
||||
return round(max(vals), 1) if vals else 0.0
|
||||
except Exception:
|
||||
return 0.0
|
||||
|
||||
|
||||
def _cpu_util_percent():
|
||||
# Linux /proc/stat sample; non-Linux or error => 0.0
|
||||
if not os.path.isfile("/proc/stat"):
|
||||
return 0.0
|
||||
try:
|
||||
def _read_idle_total():
|
||||
with open("/proc/stat") as f:
|
||||
parts = f.readline().split()
|
||||
if len(parts) < 5 or parts[0] != "cpu":
|
||||
return None
|
||||
nums = [int(x) for x in parts[1:]]
|
||||
idle = nums[3] + (nums[4] if len(nums) > 4 else 0)
|
||||
return idle, sum(nums)
|
||||
|
||||
idle1, total1 = _read_idle_total()
|
||||
if idle1 is None:
|
||||
return 0.0
|
||||
time.sleep(0.1)
|
||||
idle2, total2 = _read_idle_total()
|
||||
if idle2 is None:
|
||||
return 0.0
|
||||
dt = total2 - total1
|
||||
if dt <= 0:
|
||||
return 0.0
|
||||
util = (1.0 - (idle2 - idle1) / dt) * 100.0
|
||||
return round(max(0.0, min(100.0, util)), 1)
|
||||
except Exception:
|
||||
return 0.0
|
||||
|
||||
|
||||
def _platform_from_util():
|
||||
try:
|
||||
gpu_util = _gpu_util_percent()
|
||||
cpu_util = _cpu_util_percent()
|
||||
platform = "GPU" if gpu_util > _GPU_UTIL_THRESHOLD else "CPU"
|
||||
return platform, gpu_util, cpu_util
|
||||
except Exception:
|
||||
return "CPU", 0.0, 0.0
|
||||
|
||||
|
||||
def _append_secure(logger):
|
||||
pub = serialization.load_pem_public_key(PUBLIC_KEY_PEM)
|
||||
pad = padding.OAEP(
|
||||
mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
||||
algorithm=hashes.SHA256(),
|
||||
label=None,
|
||||
)
|
||||
platform, gpu_util, cpu_util = _platform_from_util()
|
||||
audit = {
|
||||
"timestamp": round(time.time() - _PROGRAM_START, 3),
|
||||
"platform": platform,
|
||||
"gpu_util": gpu_util,
|
||||
"cpu_util": cpu_util,
|
||||
}
|
||||
enc = base64.b64encode(pub.encrypt(json.dumps(audit).encode(), pad)).decode()
|
||||
logger[-1].setdefault("secure_log", []).append(enc)
|
||||
return audit
|
||||
|
||||
|
||||
def _flush_secure(logger):
|
||||
if logger.filename is None:
|
||||
return None
|
||||
with logger._secure_lock:
|
||||
audit = _append_secure(logger)
|
||||
with open(logger.filename, "w") as f:
|
||||
json.dump(logger, f)
|
||||
log.info(
|
||||
"util_audit: platform={platform} gpu_util={gpu_util}% cpu_util={cpu_util}%".format(
|
||||
**audit
|
||||
)
|
||||
)
|
||||
return audit
|
||||
|
||||
|
||||
def _secure_timer_worker(logger):
|
||||
while not logger._secure_stop.wait(_SECURE_INTERVAL):
|
||||
_flush_secure(logger)
|
||||
|
||||
|
||||
def _start_secure_timer(logger):
|
||||
if logger.filename is None or (
|
||||
logger._secure_thread is not None and logger._secure_thread.is_alive()
|
||||
):
|
||||
return
|
||||
logger._secure_stop.clear()
|
||||
logger._secure_thread = threading.Thread(
|
||||
target=_secure_timer_worker,
|
||||
args=(logger,),
|
||||
name="util-audit-timer",
|
||||
daemon=True,
|
||||
)
|
||||
logger._secure_thread.start()
|
||||
|
||||
|
||||
def _stop_secure_timer(logger):
|
||||
logger._secure_stop.set()
|
||||
if logger._secure_thread is not None:
|
||||
logger._secure_thread.join(timeout=_SECURE_INTERVAL + 5)
|
||||
logger._secure_thread = None
|
||||
|
||||
|
||||
class CustomHandler(logging.StreamHandler):
|
||||
"""Custom handler for stdout logging."""
|
||||
|
||||
def format(self, record):
|
||||
"""Format the record with specific format."""
|
||||
fmt = f'[BENCHMARKS|%(levelname)s|%(asctime)s]: %(message)s'
|
||||
return logging.Formatter(fmt, datefmt='%Y-%m-%d %H:%M:%S').format(record)
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.setLevel(logging.DEBUG)
|
||||
log.addHandler(CustomHandler())
|
||||
|
||||
|
||||
class JsonLogger(list):
|
||||
|
||||
def __init__(self, filename=None):
|
||||
self.filename = filename
|
||||
self._secure_lock = threading.Lock()
|
||||
self._secure_stop = threading.Event()
|
||||
self._secure_thread = None
|
||||
if filename is not None:
|
||||
if os.path.isfile(filename):
|
||||
with open(filename, "r") as file:
|
||||
super().__init__(json.load(file))
|
||||
log.info("Extending existing logs from {}.".format(filename))
|
||||
else:
|
||||
log.info("Creating new logs in {}.".format(filename))
|
||||
super().__init__()
|
||||
else:
|
||||
log.warning("Filename was not provided and logs will not be saved.")
|
||||
super().__init__()
|
||||
self.append(dict())
|
||||
now = datetime.datetime.now()
|
||||
self.log(datetime=now.strftime("%Y-%m-%d %H:%M:%S"))
|
||||
_start_secure_timer(self)
|
||||
|
||||
def log(self, **kwargs):
|
||||
with self._secure_lock:
|
||||
self[-1].update(kwargs)
|
||||
for k, v in kwargs.items():
|
||||
log.info(f"{k}: {v}")
|
||||
|
||||
def average(self, key):
|
||||
with self._secure_lock:
|
||||
self[-1][f"{key}_mean"] = np.mean(self[-1][key])
|
||||
if len(self[-1][key]) == 1:
|
||||
self[-1][f"{key}_std"] = 0.0
|
||||
else:
|
||||
self[-1][f"{key}_std"] = np.std(self[-1][key], ddof=1)
|
||||
mean = self[-1][f"{key}_mean"]
|
||||
std = self[-1][f"{key}_std"]
|
||||
log.info("{}_mean: {}".format(key, mean))
|
||||
log.info("{}_std: {}".format(key, std))
|
||||
|
||||
def __str__(self):
|
||||
return "\n" + "\n".join(f"{k}: {v}" for k, v in self[-1].items())
|
||||
|
||||
def dump(self):
|
||||
_stop_secure_timer(self)
|
||||
if self.filename is not None:
|
||||
with self._secure_lock:
|
||||
with open(self.filename, "w") as file:
|
||||
json.dump(self, file)
|
||||
530
qibojit-benchmarks/benchmarks/scripts.py
Normal file
530
qibojit-benchmarks/benchmarks/scripts.py
Normal file
@@ -0,0 +1,530 @@
|
||||
"""Benchmark scripts."""
|
||||
|
||||
import time
|
||||
from benchmarks.logger import JsonLogger, log
|
||||
|
||||
# Circuit names whose constructors accept a ``seed`` parameter (see benchmarks/circuits).
|
||||
_QIBOTN_CIRCUITS_WITH_SEED_PARAM = frozenset(
|
||||
{
|
||||
"variational",
|
||||
"variational-circuit",
|
||||
"qaoa",
|
||||
"supremacy",
|
||||
"basis-change",
|
||||
"bc",
|
||||
"quantum-volume",
|
||||
"qv",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _normalize_nqubits_range(nqubits):
|
||||
"""Return (min, max) inclusive; accept a single int or a length-2 sequence."""
|
||||
if isinstance(nqubits, (list, tuple)):
|
||||
if len(nqubits) != 2:
|
||||
raise ValueError("nqubits range must be a sequence of two integers (min, max).")
|
||||
n_min, n_max = int(nqubits[0]), int(nqubits[1])
|
||||
else:
|
||||
n_min = n_max = int(nqubits)
|
||||
if n_min > n_max:
|
||||
raise ValueError(f"Invalid nqubits range: min ({n_min}) > max ({n_max}).")
|
||||
return n_min, n_max
|
||||
|
||||
|
||||
def _nqubits_range_for_circuit(circuit_name, n_min, n_max):
|
||||
"""Inclusive n range; QAOA uses only even n (3-regular graph needs n * degree even)."""
|
||||
for n in range(n_min, n_max + 1):
|
||||
if circuit_name.lower() == "qaoa" and (n % 2):
|
||||
continue
|
||||
yield n
|
||||
|
||||
|
||||
def _supremacy_depth_for_nqubit_index(n, n_max, depth_max):
|
||||
"""Largest ``n_max`` uses ``depth_max``; each smaller ``n`` decreases depth by 1, then wraps."""
|
||||
pos = int(n_max) - int(n)
|
||||
return depth_max - (pos % depth_max)
|
||||
|
||||
|
||||
def _qibotn_circuit_options_for_rep(circuit_name, circuit_options, base_seed, global_rep):
|
||||
"""Return circuit options for repetition ``global_rep`` (distinct seed when applicable)."""
|
||||
from benchmarks import circuits
|
||||
|
||||
kwargs = circuits.parse(circuit_options) if circuit_options else {}
|
||||
name = circuit_name.lower()
|
||||
if name in _QIBOTN_CIRCUITS_WITH_SEED_PARAM:
|
||||
kwargs["seed"] = str(base_seed + global_rep)
|
||||
if not kwargs:
|
||||
return None
|
||||
return ",".join(f"{k}={v}" for k, v in kwargs.items())
|
||||
|
||||
|
||||
def circuit_benchmark(
|
||||
nqubits,
|
||||
backend,
|
||||
circuit_name,
|
||||
circuit_options=None,
|
||||
nreps=1,
|
||||
nshots=None,
|
||||
transfer=False,
|
||||
precision="double",
|
||||
memory=None,
|
||||
threading=None,
|
||||
filename=None,
|
||||
platform=None,
|
||||
):
|
||||
"""Runs benchmark for different circuit types.
|
||||
|
||||
See ``benchmarks/main.py`` for documentation of each argument.
|
||||
"""
|
||||
if backend == "qibojit" and threading is not None:
|
||||
from benchmarks.utils import select_numba_threading
|
||||
|
||||
threading = select_numba_threading(threading)
|
||||
|
||||
if backend in {"qibotf", "tensorflow"} and memory is not None:
|
||||
from benchmarks.utils import limit_gpu_memory
|
||||
|
||||
memory = limit_gpu_memory(memory)
|
||||
|
||||
logs = JsonLogger(filename)
|
||||
logs.log(
|
||||
nqubits=nqubits,
|
||||
nreps=nreps,
|
||||
nshots=nshots,
|
||||
transfer=transfer,
|
||||
numba_threading=threading,
|
||||
gpu_memory=memory,
|
||||
)
|
||||
|
||||
start_time = time.time()
|
||||
import qibo
|
||||
|
||||
logs.log(import_time=time.time() - start_time)
|
||||
|
||||
qibo.set_backend(backend=backend, platform=platform)
|
||||
qibo.set_precision(precision)
|
||||
logs.log(
|
||||
backend=qibo.get_backend(),
|
||||
platform=qibo.K.get_platform(),
|
||||
precision=qibo.get_precision(),
|
||||
device=qibo.get_device(),
|
||||
version=qibo.__version__,
|
||||
)
|
||||
|
||||
from benchmarks import circuits
|
||||
|
||||
gates = circuits.get(circuit_name, nqubits, circuit_options, qibo=True)
|
||||
logs.log(circuit=circuit_name, circuit_options=str(gates))
|
||||
start_time = time.time()
|
||||
circuit = qibo.models.Circuit(nqubits)
|
||||
circuit.add(gates)
|
||||
if nshots is not None:
|
||||
# add measurement gates
|
||||
circuit.add(qibo.gates.M(*range(nqubits)))
|
||||
logs.log(creation_time=time.time() - start_time)
|
||||
|
||||
start_time = time.time()
|
||||
result = circuit(nshots=nshots)
|
||||
logs.log(dry_run_time=time.time() - start_time)
|
||||
start_time = time.time()
|
||||
if transfer:
|
||||
result = result.numpy()
|
||||
logs.log(dry_run_transfer_time=time.time() - start_time)
|
||||
dtype = str(result.dtype)
|
||||
del result
|
||||
|
||||
simulation_times, transfer_times = [], []
|
||||
for _ in range(nreps):
|
||||
start_time = time.time()
|
||||
result = circuit(nshots=nshots)
|
||||
simulation_times.append(time.time() - start_time)
|
||||
start_time = time.time()
|
||||
if transfer:
|
||||
result = result.numpy()
|
||||
transfer_times.append(time.time() - start_time)
|
||||
del result
|
||||
|
||||
logs.log(
|
||||
dtype=dtype, simulation_times=simulation_times, transfer_times=transfer_times
|
||||
)
|
||||
logs.average("simulation_times")
|
||||
logs.average("transfer_times")
|
||||
|
||||
if nshots is not None:
|
||||
result = circuit(nshots=nshots)
|
||||
start_time = time.time()
|
||||
freqs = result.frequencies()
|
||||
logs.log(measurement_time=time.time() - start_time)
|
||||
del result
|
||||
else:
|
||||
logs.log(measurement_time=0)
|
||||
logs.dump()
|
||||
|
||||
return logs
|
||||
|
||||
|
||||
def library_benchmark(
|
||||
nqubits,
|
||||
library,
|
||||
circuit_name,
|
||||
circuit_options=None,
|
||||
library_options=None,
|
||||
precision=None,
|
||||
nreps=1,
|
||||
filename=None,
|
||||
):
|
||||
"""Runs benchmark for different quantum simulation libraries.
|
||||
|
||||
See ``benchmarks/compare.py`` for documentation of each argument.
|
||||
"""
|
||||
logs = JsonLogger(filename)
|
||||
logs.log(nqubits=nqubits, nreps=nreps)
|
||||
|
||||
start_time = time.time()
|
||||
from benchmarks import libraries
|
||||
|
||||
backend = libraries.get(library, library_options)
|
||||
logs.log(import_time=time.time() - start_time)
|
||||
logs.log(library_options=library_options)
|
||||
if precision is not None:
|
||||
backend.set_precision(precision)
|
||||
|
||||
logs.log(
|
||||
library=backend.name,
|
||||
precision=backend.get_precision(),
|
||||
device=backend.get_device(),
|
||||
version=backend.__version__,
|
||||
)
|
||||
|
||||
from benchmarks import circuits
|
||||
|
||||
gates = circuits.get(circuit_name, nqubits, circuit_options)
|
||||
logs.log(circuit=circuit_name, circuit_options=str(gates))
|
||||
start_time = time.time()
|
||||
circuit = backend.from_qasm(gates.to_qasm())
|
||||
logs.log(creation_time=time.time() - start_time)
|
||||
|
||||
start_time = time.time()
|
||||
result = backend(circuit)
|
||||
logs.log(dry_run_time=time.time() - start_time)
|
||||
dtype = str(result.dtype)
|
||||
del result
|
||||
|
||||
simulation_times = []
|
||||
for _ in range(nreps):
|
||||
start_time = time.time()
|
||||
result = backend(circuit)
|
||||
simulation_times.append(time.time() - start_time)
|
||||
del result
|
||||
|
||||
logs.log(dtype=dtype, simulation_times=simulation_times)
|
||||
logs.average("simulation_times")
|
||||
logs.dump()
|
||||
return logs
|
||||
|
||||
|
||||
def qibotn_benchmark(
|
||||
nqubits,
|
||||
library,
|
||||
circuit_name,
|
||||
circuit_options=None,
|
||||
library_options=None,
|
||||
precision=None,
|
||||
nreps=1,
|
||||
filename=None,
|
||||
):
|
||||
"""Runs benchmark for different quantum simulation libraries.
|
||||
|
||||
See ``benchmarks/compare.py`` for documentation of each argument.
|
||||
"""
|
||||
from mpi4py import MPI # this line initializes MPI
|
||||
|
||||
# Wall clock from MPI initialization until rank 0 finishes gathering results.
|
||||
t_mpi_wall_start = time.time()
|
||||
import numpy as np
|
||||
try:
|
||||
import cupy as cp
|
||||
except ImportError:
|
||||
cp = np # fallback to numpy for CPU-only users
|
||||
|
||||
comm = None
|
||||
rank = 0
|
||||
size = 1
|
||||
try:
|
||||
comm = MPI.COMM_WORLD
|
||||
rank = comm.Get_rank()
|
||||
size = comm.Get_size()
|
||||
except MPI.Exception:
|
||||
pass
|
||||
|
||||
n_min, n_max = _normalize_nqubits_range(nqubits)
|
||||
|
||||
if rank == 0:
|
||||
logs = JsonLogger(filename)
|
||||
logs.log(nqubits_min=n_min, nqubits_max=n_max, nreps=nreps)
|
||||
|
||||
if rank == 0:
|
||||
start_time = time.time()
|
||||
from benchmarks import libraries
|
||||
|
||||
backend = libraries.get(library, library_options)
|
||||
if rank == 0:
|
||||
logs.log(import_time=time.time() - start_time)
|
||||
logs.log(library_options=library_options)
|
||||
if precision is not None:
|
||||
|
||||
backend.set_precision(precision)
|
||||
backend.expectation_flag
|
||||
|
||||
if rank == 0:
|
||||
logs.log(
|
||||
library=backend.name,
|
||||
precision=backend.get_precision(),
|
||||
device=backend.get_device(),
|
||||
version=backend.__version__,
|
||||
)
|
||||
|
||||
from benchmarks.libraries.qibo import (
|
||||
generate_pauli_pattern_for_nqubits,
|
||||
runcard_uses_auto_pauli_pattern,
|
||||
)
|
||||
|
||||
_rc = getattr(backend, "runcard", None)
|
||||
if _rc and runcard_uses_auto_pauli_pattern(_rc):
|
||||
_style = _rc.get("pauli_pattern_style") or "mixed"
|
||||
log.info(
|
||||
"Automatic pauli_pattern (style=%s) for nqubits %s..%s:",
|
||||
_style,
|
||||
n_min,
|
||||
n_max,
|
||||
)
|
||||
for _nn in _nqubits_range_for_circuit(circuit_name, n_min, n_max):
|
||||
log.info(
|
||||
" n=%s -> %s",
|
||||
_nn,
|
||||
generate_pauli_pattern_for_nqubits(_nn, _style),
|
||||
)
|
||||
|
||||
from benchmarks import circuits
|
||||
|
||||
_kw = circuits.parse(circuit_options) if circuit_options else {}
|
||||
base_seed = int(_kw.get("seed", 123))
|
||||
|
||||
def circuit_options_at_n(n_run):
|
||||
"""For ``supremacy``, ``depth`` from CLI is the maximum (and cycle length); it varies per ``n``."""
|
||||
if circuit_name.lower() != "supremacy":
|
||||
return circuit_options
|
||||
depth_max = int(_kw.get("depth", 80))
|
||||
if depth_max <= 0:
|
||||
return circuit_options
|
||||
kw = dict(_kw)
|
||||
kw["depth"] = str(_supremacy_depth_for_nqubit_index(n_run, n_max, depth_max))
|
||||
return ",".join(f"{k}={v}" for k, v in kw.items())
|
||||
|
||||
dtype = None
|
||||
expectation_by_nqubits = {}
|
||||
|
||||
if rank == 0:
|
||||
if n_min == n_max:
|
||||
if circuit_name.lower() == "qaoa" and (n_min % 2):
|
||||
logs.log(circuit=circuit_name, circuit_options=circuit_options or "")
|
||||
else:
|
||||
gates0 = circuits.get(circuit_name, n_min, circuit_options_at_n(n_min))
|
||||
logs.log(circuit=circuit_name, circuit_options=str(gates0))
|
||||
else:
|
||||
logs.log(circuit=circuit_name, circuit_options=circuit_options or "")
|
||||
|
||||
# MPI: partition qubit counts across ranks (round-robin), not nreps.
|
||||
my_ns = [
|
||||
n
|
||||
for n in _nqubits_range_for_circuit(circuit_name, n_min, n_max)
|
||||
if (n - n_min) % size == rank
|
||||
]
|
||||
local_expectation_rows = [] # (n, mean over nreps on this rank)
|
||||
# Was (n, creation_time, dry_run_time, dtype_str) when dry run was enabled.
|
||||
local_meta_rows = [] # (n, creation_time, dtype_str)
|
||||
|
||||
for n in my_ns:
|
||||
gates = circuits.get(circuit_name, n, circuit_options_at_n(n))
|
||||
|
||||
start_time = time.time()
|
||||
circuit = backend.from_qasm(gates.to_qasm())
|
||||
creation_time = time.time() - start_time
|
||||
|
||||
# Optional dry run (warm-up); disabled — uncomment to enable.
|
||||
# start_time = time.time()
|
||||
# result = backend(circuit)
|
||||
# dry_run_time = time.time() - start_time
|
||||
# if hasattr(result, "dtype"):
|
||||
# dtype_n = str(result.dtype)
|
||||
# else:
|
||||
# dtype_n = str(np.array([result]).dtype)
|
||||
# del result
|
||||
|
||||
rep_magnitudes = []
|
||||
dtype_n = None
|
||||
for g in range(nreps):
|
||||
np.random.seed(base_seed + g)
|
||||
rep_opts = _qibotn_circuit_options_for_rep(
|
||||
circuit_name, circuit_options_at_n(n), base_seed, g
|
||||
)
|
||||
gates_rep = circuits.get(circuit_name, n, rep_opts)
|
||||
circuit_rep = backend.from_qasm(gates_rep.to_qasm())
|
||||
result = backend(circuit_rep)
|
||||
# Use cp.asnumpy if available (cupy), otherwise fallback for numpy
|
||||
if hasattr(cp, "asnumpy") and isinstance(result, cp.ndarray):
|
||||
result = cp.asnumpy(result)
|
||||
else:
|
||||
result = np.array([result])
|
||||
|
||||
if dtype_n is None:
|
||||
if hasattr(result, "dtype"):
|
||||
dtype_n = str(result.dtype)
|
||||
else:
|
||||
dtype_n = str(np.array([result]).dtype)
|
||||
|
||||
if backend.expectation_flag is not None:
|
||||
rep_magnitudes.append(float(abs(result)))
|
||||
del result
|
||||
|
||||
if backend.expectation_flag is not None and rep_magnitudes:
|
||||
local_expectation_rows.append(
|
||||
(n, float(np.mean(rep_magnitudes)))
|
||||
)
|
||||
|
||||
local_meta_rows.append((n, creation_time, dtype_n))
|
||||
|
||||
if comm is not None:
|
||||
gathered_expectation = comm.gather(local_expectation_rows, root=0)
|
||||
gathered_meta = comm.gather(local_meta_rows, root=0)
|
||||
else:
|
||||
gathered_expectation = [local_expectation_rows]
|
||||
gathered_meta = [local_meta_rows]
|
||||
|
||||
# End-to-end wall time after all ranks finished local work and gather.
|
||||
if comm is not None:
|
||||
comm.Barrier()
|
||||
t_sim_end = time.time()
|
||||
|
||||
if rank == 0:
|
||||
for chunk in gathered_expectation:
|
||||
for n, ev in chunk:
|
||||
expectation_by_nqubits[n] = ev
|
||||
|
||||
depth_by_nqubits_log = None
|
||||
if circuit_name.lower() == "supremacy":
|
||||
_depth_max = int(_kw.get("depth", 80))
|
||||
if _depth_max > 0:
|
||||
depth_by_nqubits_log = {
|
||||
str(n): _supremacy_depth_for_nqubit_index(n, n_max, _depth_max)
|
||||
for n in _nqubits_range_for_circuit(circuit_name, n_min, n_max)
|
||||
}
|
||||
|
||||
for n in sorted(expectation_by_nqubits):
|
||||
if depth_by_nqubits_log is not None:
|
||||
log.info(
|
||||
"nqubits=%s depth=%s expectation (mean over reps): %s",
|
||||
n,
|
||||
depth_by_nqubits_log[str(n)],
|
||||
expectation_by_nqubits[n],
|
||||
)
|
||||
else:
|
||||
log.info(
|
||||
"nqubits=%s expectation (mean over reps): %s",
|
||||
n,
|
||||
expectation_by_nqubits[n],
|
||||
)
|
||||
|
||||
creation_by_n = {}
|
||||
# If dry run is re-enabled: dry_by_n = {} and unpack (n, ct, dry_run_time, dtp).
|
||||
dtype_by_n = {}
|
||||
for chunk in gathered_meta:
|
||||
for n, ct, dtp in chunk:
|
||||
creation_by_n[str(n)] = ct
|
||||
# dry_by_n[str(n)] = dry_run_time
|
||||
if dtp is not None:
|
||||
dtype_by_n[n] = dtp
|
||||
if dtype_by_n:
|
||||
dtype = dtype_by_n[max(dtype_by_n)]
|
||||
|
||||
simulation_times = [t_sim_end - t_mpi_wall_start]
|
||||
_summary = {
|
||||
"dtype": dtype,
|
||||
"simulation_times": simulation_times,
|
||||
"creation_time_by_nqubits": creation_by_n,
|
||||
# dry_run_time_by_nqubits=dry_by_n,
|
||||
}
|
||||
if depth_by_nqubits_log is not None:
|
||||
_summary["depth_by_nqubits"] = depth_by_nqubits_log
|
||||
logs.log(**_summary)
|
||||
logs.average("simulation_times")
|
||||
if backend.expectation_flag is not None and expectation_by_nqubits:
|
||||
expectation_values = [
|
||||
expectation_by_nqubits[k] for k in sorted(expectation_by_nqubits)
|
||||
]
|
||||
expectation_result = float(np.mean(expectation_values))
|
||||
logs.log(
|
||||
expectation_by_nqubits={str(k): v for k, v in expectation_by_nqubits.items()},
|
||||
expectation_result=expectation_result,
|
||||
)
|
||||
logs.dump()
|
||||
return logs
|
||||
|
||||
|
||||
def evolution_benchmark(
|
||||
nqubits,
|
||||
dt,
|
||||
solver,
|
||||
backend,
|
||||
platform=None,
|
||||
nreps=1,
|
||||
precision="double",
|
||||
dense=False,
|
||||
filename=None,
|
||||
):
|
||||
"""Performs adiabatic evolution with critical TFIM as the hard Hamiltonian."""
|
||||
logs = JsonLogger(filename)
|
||||
logs.log(nqubits=nqubits, nreps=nreps, dt=dt, solver=solver, dense=dense)
|
||||
|
||||
start_time = time.time()
|
||||
import qibo
|
||||
|
||||
logs.log(import_time=time.time() - start_time)
|
||||
|
||||
qibo.set_backend(backend=backend, platform=platform)
|
||||
qibo.set_precision(precision)
|
||||
logs.log(
|
||||
backend=qibo.get_backend(),
|
||||
platform=qibo.K.get_platform(),
|
||||
precision=qibo.get_precision(),
|
||||
device=qibo.get_device(),
|
||||
threads=qibo.get_threads(),
|
||||
version=qibo.__version__,
|
||||
)
|
||||
|
||||
from qibo import hamiltonians, models
|
||||
|
||||
start_time = time.time()
|
||||
h0 = hamiltonians.X(nqubits, dense=dense)
|
||||
h1 = hamiltonians.TFIM(nqubits, h=1.0, dense=dense)
|
||||
logs.log(hamiltonian_creation_time=time.time() - start_time)
|
||||
|
||||
start_time = time.time()
|
||||
evolution = models.AdiabaticEvolution(h0, h1, lambda t: t, dt=dt, solver=solver)
|
||||
logs.log(evolution_creation_time=time.time() - start_time)
|
||||
|
||||
start_time = time.time()
|
||||
result = evolution(final_time=1.0)
|
||||
logs.log(dry_run_time=time.time() - start_time)
|
||||
dtype = str(result.dtype)
|
||||
del result
|
||||
|
||||
simulation_times = []
|
||||
for _ in range(nreps):
|
||||
start_time = time.time()
|
||||
result = evolution(final_time=1.0)
|
||||
simulation_times.append(time.time() - start_time)
|
||||
logs.log(dtype=dtype, simulation_times=simulation_times)
|
||||
logs.average("simulation_times")
|
||||
logs.dump()
|
||||
return logs
|
||||
0
qibojit-benchmarks/benchmarks/tests/__init__.py
Normal file
0
qibojit-benchmarks/benchmarks/tests/__init__.py
Normal file
33
qibojit-benchmarks/benchmarks/tests/conftest.py
Normal file
33
qibojit-benchmarks/benchmarks/tests/conftest.py
Normal file
@@ -0,0 +1,33 @@
|
||||
NQUBITS = "3,4,5"
|
||||
MAX_QUBITS = "0,1,2,3,4"
|
||||
QIBO_BACKENDS = "qibojit,tensorflow,numpy"
|
||||
LIBRARIES = "qibo,qiskit,cirq,qsim,tfq,qulacs,projectq,hybridq"
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--nqubits", type=str, default=NQUBITS)
|
||||
parser.addoption("--max-qubits", type=str, default=MAX_QUBITS)
|
||||
parser.addoption("--qibo-backends", type=str, default=QIBO_BACKENDS)
|
||||
parser.addoption("--libraries", type=str, default=LIBRARIES)
|
||||
parser.addoption("--add", type=str, default="")
|
||||
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
nqubits = [int(n) for n in metafunc.config.option.nqubits.split(",")]
|
||||
library_options = [f"max_qubits={n}" for n in metafunc.config.option.max_qubits.split(",")]
|
||||
backends = metafunc.config.option.qibo_backends.split(",")
|
||||
libraries = metafunc.config.option.libraries.split(",")
|
||||
additional = metafunc.config.option.add
|
||||
if additional:
|
||||
libraries.extend(additional.split(","))
|
||||
|
||||
if "nqubits" in metafunc.fixturenames:
|
||||
metafunc.parametrize("nqubits", nqubits)
|
||||
if "backend" in metafunc.fixturenames:
|
||||
metafunc.parametrize("backend", backends)
|
||||
if "library" in metafunc.fixturenames:
|
||||
metafunc.parametrize("library", libraries)
|
||||
if "library_options" in metafunc.fixturenames:
|
||||
metafunc.parametrize("library_options", library_options)
|
||||
if "transfer" in metafunc.fixturenames:
|
||||
metafunc.parametrize("transfer", [False, True])
|
||||
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 6}, {"id": 18}, {"id": 7}, {"id": 20}, {"id": 26}, {"id": 27}, {"id": 12}, {"id": 19}, {"id": 3}, {"id": 13}, {"id": 14}, {"id": 25}, {"id": 5}, {"id": 9}, {"id": 11}, {"id": 17}, {"id": 0}, {"id": 23}, {"id": 10}, {"id": 21}, {"id": 2}, {"id": 8}, {"id": 15}, {"id": 4}, {"id": 1}, {"id": 16}, {"id": 24}, {"id": 22}], "links": [{"source": 6, "target": 18}, {"source": 6, "target": 3}, {"source": 6, "target": 0}, {"source": 18, "target": 14}, {"source": 18, "target": 24}, {"source": 7, "target": 20}, {"source": 7, "target": 10}, {"source": 7, "target": 1}, {"source": 20, "target": 9}, {"source": 20, "target": 21}, {"source": 26, "target": 27}, {"source": 26, "target": 2}, {"source": 26, "target": 3}, {"source": 27, "target": 23}, {"source": 27, "target": 25}, {"source": 12, "target": 19}, {"source": 12, "target": 9}, {"source": 12, "target": 11}, {"source": 19, "target": 5}, {"source": 19, "target": 10}, {"source": 3, "target": 13}, {"source": 13, "target": 4}, {"source": 13, "target": 21}, {"source": 14, "target": 25}, {"source": 14, "target": 15}, {"source": 25, "target": 17}, {"source": 5, "target": 4}, {"source": 5, "target": 24}, {"source": 9, "target": 16}, {"source": 11, "target": 17}, {"source": 11, "target": 1}, {"source": 17, "target": 15}, {"source": 0, "target": 23}, {"source": 0, "target": 1}, {"source": 23, "target": 8}, {"source": 10, "target": 21}, {"source": 2, "target": 8}, {"source": 2, "target": 22}, {"source": 8, "target": 16}, {"source": 15, "target": 22}, {"source": 4, "target": 16}, {"source": 24, "target": 22}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 0}, {"id": 7}, {"id": 1}, {"id": 2}, {"id": 4}, {"id": 3}, {"id": 5}, {"id": 6}], "links": [{"source": 0, "target": 7}, {"source": 0, "target": 4}, {"source": 0, "target": 3}, {"source": 7, "target": 1}, {"source": 7, "target": 4}, {"source": 1, "target": 2}, {"source": 1, "target": 6}, {"source": 2, "target": 6}, {"source": 2, "target": 5}, {"source": 4, "target": 5}, {"source": 3, "target": 6}, {"source": 3, "target": 5}]}
|
||||
165
qibojit-benchmarks/benchmarks/tests/test_libraries.py
Normal file
165
qibojit-benchmarks/benchmarks/tests/test_libraries.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""Check that execution of circuits from external simulation libraries agrees with Qibo."""
|
||||
import itertools
|
||||
import pytest
|
||||
import numpy as np
|
||||
from qibo import models, gates
|
||||
from benchmarks import libraries
|
||||
from benchmarks.circuits import qasm, qibo
|
||||
|
||||
|
||||
def assert_circuit_execution(backend, qasm_circuit, qibo_circuit_iter, atol=None):
|
||||
if atol is None:
|
||||
if backend.get_precision() == "single":
|
||||
atol = 1e-5
|
||||
else:
|
||||
atol = 1e-10
|
||||
|
||||
# add random RX gates before circuit so that initial state is not trivial
|
||||
nqubits = qasm_circuit.nqubits
|
||||
theta = np.random.random(nqubits)
|
||||
qasm_code = qasm_circuit.to_qasm(theta=theta)
|
||||
|
||||
# execute circuit using backend
|
||||
circuit = backend.from_qasm(qasm_code)
|
||||
final_state = backend(circuit)
|
||||
final_state = backend.transpose_state(final_state)
|
||||
|
||||
# execute circuit using qibo
|
||||
assert qibo_circuit_iter.nqubits == nqubits
|
||||
target_circuit = models.Circuit(nqubits)
|
||||
target_circuit.add(gates.RX(i, theta=t) for i, t in enumerate(theta))
|
||||
target_circuit.add(qibo_circuit_iter)
|
||||
target_state = target_circuit()
|
||||
|
||||
# check fidelity instead of absolute states due to different definitions
|
||||
# of the phase of U gates in different backends
|
||||
fidelity = np.abs(np.conj(target_state).dot(np.array(final_state)))
|
||||
np.testing.assert_allclose(fidelity, 1.0, atol=atol)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nlayers", ["1", "4"])
|
||||
@pytest.mark.parametrize("gate, qibo_gate",
|
||||
[("h", "H"), ("x", "X"), ("y", "Y"), ("z", "Z")])
|
||||
def test_one_qubit_gate(nqubits, library, nlayers, gate, qibo_gate):
|
||||
qasm_circuit = qasm.OneQubitGate(nqubits, nlayers=nlayers, gate=gate)
|
||||
target_circuit = qibo.OneQubitGate(nqubits, nlayers=nlayers, gate=qibo_gate)
|
||||
backend = libraries.get(library)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("gate,qibo_gate,params",
|
||||
[("rx", "RX", {"theta": 0.1}),
|
||||
("ry", "RY", {"theta": 0.3}),
|
||||
("rz", "RZ", {"theta": 0.2}),
|
||||
("u1", "U1", {"theta": 0.3}),
|
||||
("u2", "U2", {"phi": 0.2, "lam": 0.3}),
|
||||
("u3", "U3", {"theta": 0.1, "phi": 0.2, "lam": 0.3})])
|
||||
def test_one_qubit_gate_parametrized(nqubits, library, gate, qibo_gate, params):
|
||||
if gate in {"u1", "u2", "u3"} and library == "tfq":
|
||||
pytest.skip("Skipping {} test because it is not supported by {}."
|
||||
"".format(gate, library))
|
||||
order = ["theta", "phi", "lam"]
|
||||
angles = ",".join(str(params.get(n)) for n in order if n in params)
|
||||
qasm_circuit = qasm.OneQubitGate(nqubits, gate=gate, angles=angles)
|
||||
target_circuit = qibo.OneQubitGate(nqubits, gate=qibo_gate, **params)
|
||||
backend = libraries.get(library)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nlayers", ["1", "4"])
|
||||
@pytest.mark.parametrize("gate,qibo_gate",
|
||||
[("cx", "CNOT"), ("swap", "SWAP"), ("cz", "CZ")])
|
||||
def test_two_qubit_gate(nqubits, library, nlayers, gate, qibo_gate):
|
||||
qasm_circuit = qasm.TwoQubitGate(nqubits, nlayers=nlayers, gate=gate)
|
||||
target_circuit = qibo.TwoQubitGate(nqubits, nlayers=nlayers, gate=qibo_gate)
|
||||
backend = libraries.get(library)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("gate,qibo_gate,params",
|
||||
[("cu1", "CU1", {"theta": 0.3}),
|
||||
#("cu2", "CU2", {"phi": 0.1, "lam": 0.3}), # not supported by OpenQASM
|
||||
("cu3", "CU3", {"theta": 0.1, "phi": 0.2, "lam": 0.3})])
|
||||
def test_two_qubit_gate_parametrized(nqubits, library, gate, qibo_gate, params):
|
||||
if gate in {"cu1", "cu2", "cu3"} and library == "tfq":
|
||||
pytest.skip("Skipping {} test because it is not supported by {}."
|
||||
"".format(gate, library))
|
||||
if gate in {"cu3"} and library == "projectq":
|
||||
pytest.skip("Skipping {} test because it is not supported by {}."
|
||||
"".format(gate, library))
|
||||
|
||||
order = ["theta", "phi", "lam"]
|
||||
angles = ",".join(str(params.get(n)) for n in order if n in params)
|
||||
qasm_circuit = qasm.TwoQubitGate(nqubits, gate=gate, angles=angles)
|
||||
target_circuit = qibo.TwoQubitGate(nqubits, gate=qibo_gate, **params)
|
||||
backend = libraries.get(library)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("swaps", ["False", "True"])
|
||||
def test_qft(nqubits, library, swaps, library_options):
|
||||
qasm_circuit = qasm.QFT(nqubits, swaps=swaps)
|
||||
target_circuit = qibo.QFT(nqubits, swaps=swaps)
|
||||
backend = libraries.get(library, library_options)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nlayers", ["2", "5"])
|
||||
def test_variational(nqubits, library, nlayers, library_options):
|
||||
qasm_circuit = qasm.VariationalCircuit(nqubits, nlayers=nlayers)
|
||||
target_circuit = qibo.VariationalCircuit(nqubits, nlayers=nlayers)
|
||||
backend = libraries.get(library, library_options)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
def test_bernstein_vazirani(nqubits, library, library_options):
|
||||
qasm_circuit = qasm.BernsteinVazirani(nqubits)
|
||||
target_circuit = qibo.BernsteinVazirani(nqubits)
|
||||
backend = libraries.get(library, library_options)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
def test_hidden_shift(nqubits, library, library_options):
|
||||
shift = "".join(str(x) for x in np.random.randint(0, 2, size=(nqubits,)))
|
||||
qasm_circuit = qasm.HiddenShift(nqubits, shift=shift)
|
||||
target_circuit = qibo.HiddenShift(nqubits, shift=shift)
|
||||
backend = libraries.get(library, library_options)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
def test_qaoa_circuit(library, library_options):
|
||||
if library in {"qibo", "qcgpu"}:
|
||||
pytest.skip(f"{library} does not have built-in RZZ gate.")
|
||||
import pathlib
|
||||
folder = str(pathlib.Path(__file__).with_name("graphs") / "testgraph8.json")
|
||||
qasm_circuit = qasm.QAOA(8, graph=folder)
|
||||
target_circuit = qibo.QAOA(8, graph=folder)
|
||||
backend = libraries.get(library, library_options)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("depth", ["2", "5", "10"])
|
||||
def test_supremacy_circuit(nqubits, library, depth, library_options):
|
||||
qasm_circuit = qasm.SupremacyCircuit(nqubits, depth=depth)
|
||||
target_circuit = qibo.SupremacyCircuit(nqubits, depth=depth)
|
||||
backend = libraries.get(library, library_options)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("simtime", ["1", "2.5"])
|
||||
def test_basis_change(nqubits, library, simtime, library_options):
|
||||
qasm_circuit = qasm.BasisChange(nqubits, simulation_time=simtime)
|
||||
target_circuit = qibo.BasisChange(nqubits, simulation_time=simtime)
|
||||
backend = libraries.get(library, library_options)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("depth", ["2", "5", "8"])
|
||||
def test_quantum_volume(nqubits, library, depth, library_options):
|
||||
if library == "tfq":
|
||||
pytest.skip("Skipping qv test because it is not supported by {}."
|
||||
"".format(library))
|
||||
qasm_circuit = qasm.QuantumVolume(nqubits, depth=depth)
|
||||
target_circuit = qibo.QuantumVolume(nqubits, depth=depth)
|
||||
backend = libraries.get(library, library_options)
|
||||
assert_circuit_execution(backend, qasm_circuit, target_circuit)
|
||||
166
qibojit-benchmarks/benchmarks/tests/test_qibo_benchmarks.py
Normal file
166
qibojit-benchmarks/benchmarks/tests/test_qibo_benchmarks.py
Normal file
@@ -0,0 +1,166 @@
|
||||
import pytest
|
||||
from benchmarks.scripts import circuit_benchmark, evolution_benchmark
|
||||
|
||||
|
||||
def assert_logs(logs, nqubits, backend, nreps=1):
|
||||
assert logs[-1]["nqubits"] == nqubits
|
||||
assert logs[-1]["backend"] == backend
|
||||
assert logs[-1]["simulation_times_mean"] >= 0
|
||||
assert logs[-1]["transfer_times_mean"] >= 0
|
||||
assert len(logs[-1]["simulation_times"]) == nreps
|
||||
assert len(logs[-1]["transfer_times"]) == nreps
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nreps", [1, 5])
|
||||
@pytest.mark.parametrize("nlayers", ["1", "4"])
|
||||
@pytest.mark.parametrize("gate", ["H", "X", "Y", "Z"])
|
||||
def test_one_qubit_gate_benchmark(nqubits, backend, transfer, nreps,
|
||||
nlayers, gate):
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="one-qubit-gate",
|
||||
nreps=nreps, transfer=transfer,
|
||||
circuit_options=f"gate={gate},nlayers={nlayers}")
|
||||
assert_logs(logs, nqubits, backend, nreps)
|
||||
target_options = f"nqubits={nqubits}, nlayers={nlayers}, "
|
||||
target_options += f"gate={gate}, params={{}}"
|
||||
assert logs[-1]["circuit"] == "one-qubit-gate"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
@pytest.mark.parametrize("gate,params",
|
||||
[("RX", "theta=0.1"), ("RZ", "theta=0.2"),
|
||||
("U1", "theta=0.3"), ("U2", "phi=0.2,lam=0.3"),
|
||||
("U3", "theta=0.1,phi=0.2,lam=0.3")])
|
||||
def test_one_qubit_gate_param_benchmark(nqubits, backend, gate, params):
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="one-qubit-gate",
|
||||
circuit_options=f"gate={gate},{params}")
|
||||
assert_logs(logs, nqubits, backend)
|
||||
target_options = f"nqubits={nqubits}, nlayers=1, gate={gate}"
|
||||
paramdict = {}
|
||||
for param in params.split(","):
|
||||
k, v = param.split("=")
|
||||
paramdict[k] = v
|
||||
target_options = f"{target_options}, params={paramdict}"
|
||||
assert logs[-1]["circuit"] == "one-qubit-gate"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nreps", [1, 5])
|
||||
@pytest.mark.parametrize("nlayers", ["1", "4"])
|
||||
@pytest.mark.parametrize("gate", ["CNOT", "SWAP", "CZ"])
|
||||
def test_two_qubit_gate_benchmark(nqubits, backend, transfer, nreps,
|
||||
nlayers, gate):
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="two-qubit-gate",
|
||||
nreps=nreps, transfer=transfer,
|
||||
circuit_options=f"gate={gate},nlayers={nlayers}")
|
||||
assert_logs(logs, nqubits, backend, nreps)
|
||||
target_options = f"nqubits={nqubits}, nlayers={nlayers}, "
|
||||
target_options += f"gate={gate}, params={{}}"
|
||||
assert logs[-1]["circuit"] == "two-qubit-gate"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
@pytest.mark.parametrize("gate,params",
|
||||
[("CRX", "theta=0.1"), ("CRZ", "theta=0.2"),
|
||||
("CU1", "theta=0.3"), ("CU2", "phi=0.2,lam=0.3"),
|
||||
("CU3", "theta=0.1,phi=0.2,lam=0.3"),
|
||||
("fSim", "theta=0.1,phi=0.2")])
|
||||
def test_two_qubit_gate_param_benchmark(nqubits, backend, gate, params):
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="two-qubit-gate",
|
||||
circuit_options=f"gate={gate},{params}")
|
||||
assert_logs(logs, nqubits, backend)
|
||||
target_options = f"nqubits={nqubits}, nlayers=1, gate={gate}"
|
||||
paramdict = {}
|
||||
for param in params.split(","):
|
||||
k, v = param.split("=")
|
||||
paramdict[k] = v
|
||||
target_options = f"{target_options}, params={paramdict}"
|
||||
assert logs[-1]["circuit"] == "two-qubit-gate"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nreps", [1, 5])
|
||||
@pytest.mark.parametrize("swaps", [False, True])
|
||||
def test_qft_benchmark(nqubits, backend, transfer, nreps, swaps):
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="qft",
|
||||
nreps=nreps, transfer=transfer,
|
||||
circuit_options=f"swaps={swaps}")
|
||||
assert_logs(logs, nqubits, backend, nreps)
|
||||
target_options = f"nqubits={nqubits}, swaps={swaps}"
|
||||
assert logs[-1]["circuit"] == "qft"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
@pytest.mark.parametrize("varlayer", [False, True])
|
||||
def test_variational_benchmark(nqubits, backend, varlayer):
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="variational",
|
||||
circuit_options=f"varlayer={varlayer}")
|
||||
assert_logs(logs, nqubits, backend)
|
||||
target_options = f"nqubits={nqubits}, nlayers=1, seed=123, varlayer={varlayer}"
|
||||
assert logs[-1]["circuit"] == "variational"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
def test_bernstein_vazirani_benchmark(nqubits, backend):
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="bv")
|
||||
assert_logs(logs, nqubits, backend)
|
||||
assert logs[-1]["circuit"] == "bv"
|
||||
assert logs[-1]["circuit_options"] == f"nqubits={nqubits}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("random", [True, False])
|
||||
def test_hidden_shift_benchmark(nqubits, backend, random):
|
||||
shift = "" if random else nqubits * "0"
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="hs",
|
||||
circuit_options=f"shift={shift}")
|
||||
assert_logs(logs, nqubits, backend)
|
||||
target_options = f"nqubits={nqubits}, shift={shift}"
|
||||
assert logs[-1]["circuit"] == "hs"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
def test_qaoa_benchmark(backend):
|
||||
logs = circuit_benchmark(4, backend, circuit_name="qaoa")
|
||||
assert_logs(logs, 4, backend)
|
||||
target_options = f"nqubits=4, nparams=2, graph=, seed=123"
|
||||
assert logs[-1]["circuit"] == "qaoa"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
@pytest.mark.parametrize("depth", ["2", "5", "10"])
|
||||
def test_supremacy_benchmark(nqubits, backend, depth):
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="supremacy",
|
||||
circuit_options=f"depth={depth}")
|
||||
assert_logs(logs, nqubits, backend)
|
||||
target_options = f"nqubits={nqubits}, depth={depth}, seed=123"
|
||||
assert logs[-1]["circuit"] == "supremacy"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
@pytest.mark.parametrize("simtime", ["1", "2.5"])
|
||||
def test_basis_change_benchmark(nqubits, backend, simtime):
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="bc",
|
||||
circuit_options=f"simulation_time={simtime}")
|
||||
assert_logs(logs, nqubits, backend)
|
||||
target_options = f"nqubits={nqubits}, simulation_time={simtime}, seed=123"
|
||||
assert logs[-1]["circuit"] == "bc"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
@pytest.mark.parametrize("depth", ["2", "5", "8"])
|
||||
def test_quantum_volume_benchmark(nqubits, backend, depth):
|
||||
logs = circuit_benchmark(nqubits, backend, circuit_name="qv",
|
||||
circuit_options=f"depth={depth}")
|
||||
assert_logs(logs, nqubits, backend)
|
||||
target_options = f"nqubits={nqubits}, depth={depth}, seed=123"
|
||||
assert logs[-1]["circuit"] == "qv"
|
||||
assert logs[-1]["circuit_options"] == target_options
|
||||
|
||||
|
||||
@pytest.mark.parametrize("dt", [0.01, 0.05, 0.1])
|
||||
@pytest.mark.parametrize("dense", [False, True])
|
||||
def test_adiabatic_evolution_benchmark(nqubits, dt, backend, dense, solver="exp"):
|
||||
logs = evolution_benchmark(nqubits, dt, solver, backend, dense=dense)
|
||||
assert logs[-1]["nqubits"] == nqubits
|
||||
assert logs[-1]["dt"] == dt
|
||||
assert logs[-1]["backend"] == backend
|
||||
assert logs[-1]["dense"] == dense
|
||||
112
qibojit-benchmarks/benchmarks/tests/test_qibo_circuits.py
Normal file
112
qibojit-benchmarks/benchmarks/tests/test_qibo_circuits.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""Tests for circuits defined in benchmarks/circuits/qibo.py"""
|
||||
|
||||
import pytest
|
||||
from qibo.models import Circuit
|
||||
from benchmarks.circuits import qibo
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nlayers", [1, 2, 3, 4, 5])
|
||||
@pytest.mark.parametrize("gate", ["H", "X", "Y", "Z"])
|
||||
def test_one_qubit_gate_circuit(nlayers, gate):
|
||||
circuit = Circuit(28)
|
||||
gates = qibo.OneQubitGate(28, nlayers=nlayers, gate=gate)
|
||||
circuit.add(gates)
|
||||
assert circuit.nqubits == 28
|
||||
assert circuit.depth == nlayers
|
||||
assert circuit.ngates == nlayers * 28
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nlayers", [1, 2, 3, 4, 5])
|
||||
@pytest.mark.parametrize("gate", ["CNOT", "CZ", "SWAP"])
|
||||
def test_two_qubit_gate_circuit(nlayers, gate):
|
||||
circuit = Circuit(28)
|
||||
gates = qibo.TwoQubitGate(28, nlayers=nlayers, gate=gate)
|
||||
circuit.add(gates)
|
||||
assert circuit.nqubits == 28
|
||||
assert circuit.depth == nlayers * 2
|
||||
assert circuit.ngates == nlayers * 27
|
||||
|
||||
|
||||
@pytest.mark.parametrize("swaps", ["True", "False"])
|
||||
def test_qft_circuit(swaps):
|
||||
circuit = Circuit(28)
|
||||
gates = qibo.QFT(28, swaps=swaps)
|
||||
circuit.add(gates)
|
||||
assert circuit.nqubits == 28
|
||||
if swaps == "True":
|
||||
assert circuit.depth == 56
|
||||
assert circuit.ngates == 420
|
||||
else:
|
||||
assert circuit.depth == 55
|
||||
assert circuit.ngates == 406
|
||||
|
||||
|
||||
@pytest.mark.parametrize("varlayer", ["True", "False"])
|
||||
def test_variational_circuit(varlayer):
|
||||
circuit = Circuit(28)
|
||||
gates = qibo.VariationalCircuit(28, varlayer=varlayer)
|
||||
circuit.add(gates)
|
||||
assert circuit.nqubits == 28
|
||||
if varlayer == "True":
|
||||
assert circuit.depth == 2
|
||||
assert circuit.ngates == 28
|
||||
else:
|
||||
assert circuit.depth == 4
|
||||
assert circuit.ngates == 84
|
||||
|
||||
|
||||
def test_bernstein_vazirani_circuit():
|
||||
circuit = Circuit(28)
|
||||
gates = qibo.BernsteinVazirani(28)
|
||||
circuit.add(gates)
|
||||
assert circuit.nqubits == 28
|
||||
assert circuit.depth == 30
|
||||
assert circuit.ngates == 83
|
||||
|
||||
|
||||
def test_hidden_shift_circuit():
|
||||
shift = "0111001011001001111011001101"
|
||||
circuit = Circuit(28)
|
||||
gates = qibo.HiddenShift(28, shift=shift)
|
||||
circuit.add(gates)
|
||||
assert circuit.nqubits == 28
|
||||
assert circuit.depth == 7
|
||||
assert circuit.ngates == 144
|
||||
|
||||
|
||||
def test_qaoa_circuit():
|
||||
import pathlib
|
||||
folder = str(pathlib.Path(__file__).with_name("graphs") / "testgraph28.json")
|
||||
circuit = Circuit(28)
|
||||
gates = qibo.QAOA(28, graph=folder)
|
||||
circuit.add(gates)
|
||||
assert circuit.nqubits == 28
|
||||
assert circuit.ngates == 168
|
||||
assert circuit.depth == 18
|
||||
|
||||
|
||||
def test_supremacy_circuit():
|
||||
circuit = Circuit(28)
|
||||
gates = qibo.SupremacyCircuit(28, depth="40")
|
||||
circuit.add(gates)
|
||||
assert circuit.nqubits == 28
|
||||
assert circuit.ngates == 880
|
||||
assert circuit.depth == 42
|
||||
|
||||
|
||||
def test_basis_change_circuit():
|
||||
circuit = Circuit(28)
|
||||
gates = qibo.BasisChange(28)
|
||||
circuit.add(gates)
|
||||
assert circuit.nqubits == 28
|
||||
assert circuit.ngates == 9912
|
||||
assert circuit.depth == 1117
|
||||
|
||||
|
||||
def test_quantum_volume_circuit():
|
||||
circuit = Circuit(28)
|
||||
gates = qibo.QuantumVolume(28, depth=10)
|
||||
circuit.add(gates)
|
||||
assert circuit.nqubits == 28
|
||||
assert circuit.ngates == 1540
|
||||
assert circuit.depth == 70
|
||||
25
qibojit-benchmarks/benchmarks/utils.py
Normal file
25
qibojit-benchmarks/benchmarks/utils.py
Normal file
@@ -0,0 +1,25 @@
|
||||
def limit_gpu_memory(memory_limit=None):
|
||||
"""Limits GPU memory that is available to Tensorflow.
|
||||
Args:
|
||||
memory_limit: Memory limit in MBs.
|
||||
"""
|
||||
import tensorflow as tf
|
||||
if memory_limit is None:
|
||||
print("\nNo GPU memory limiter used.\n")
|
||||
return
|
||||
|
||||
print("\nAttempting to limit GPU memory to {}.\n".format(memory_limit))
|
||||
for gpu in tf.config.list_physical_devices("GPU"):
|
||||
config = tf.config.experimental.VirtualDeviceConfiguration(
|
||||
memory_limit=memory_limit)
|
||||
tf.config.experimental.set_virtual_device_configuration(gpu, [config])
|
||||
print("Limiting memory of {} to {}.".format(gpu.name, memory_limit))
|
||||
print()
|
||||
return memory_limit
|
||||
|
||||
|
||||
def select_numba_threading(threading):
|
||||
from numba import config, threading_layer
|
||||
print(f"\nSwitching threading to {threading}.\n")
|
||||
config.THREADING_LAYER = threading
|
||||
return threading_layer
|
||||
78
qibojit-benchmarks/compare.py
Normal file
78
qibojit-benchmarks/compare.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""Launches the circuit benchmark script for user given arguments."""
|
||||
|
||||
import argparse
|
||||
from benchmarks.scripts import library_benchmark, qibotn_benchmark
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--nqubits",
|
||||
nargs=2,
|
||||
type=int,
|
||||
metavar=("MIN", "MAX"),
|
||||
default=[10, 10],
|
||||
help="Inclusive qubit-count range MIN MAX. Each size is run; per-size "
|
||||
"expectations are printed, and their arithmetic mean is logged as expectation_result.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--library",
|
||||
default="qibo",
|
||||
type=str,
|
||||
help="Quantum simulation library to use in benchmark. "
|
||||
"See README for the list of available libraries.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--library-options",
|
||||
default=None,
|
||||
type=str,
|
||||
help="String with options for the library. "
|
||||
"It should have the form 'arg1=value1,arg2=value2,...'. "
|
||||
"Each library supports different options.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--circuit",
|
||||
default="qft",
|
||||
type=str,
|
||||
help="Type of circuit to use. See README for the list of " "available circuits.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--circuit-options",
|
||||
default=None,
|
||||
type=str,
|
||||
help="String with options for circuit creation. "
|
||||
"It should have the form 'arg1=value1,arg2=value2,...'. "
|
||||
"See README for the list of arguments that are "
|
||||
"available for each circuit.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--precision",
|
||||
default=None,
|
||||
type=str,
|
||||
help="Numerical precision of the simulation. "
|
||||
"Choose between 'double' and 'single'.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--nreps",
|
||||
default=1,
|
||||
type=int,
|
||||
help="Number of repetitions of the circuit execution. " "Dry run is not included.",
|
||||
)
|
||||
# parser.add_argument("--transfer", action="store_true",
|
||||
# help="If used the final state array is converted to numpy. "
|
||||
# "If the simulation device is GPU this requires a "
|
||||
# "transfer from GPU memory to CPU.")
|
||||
|
||||
parser.add_argument(
|
||||
"--filename",
|
||||
default=None,
|
||||
type=str,
|
||||
help="Directory of file to save the logs in json format. "
|
||||
"If not given the logs will only be printed and not saved.",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = vars(parser.parse_args())
|
||||
args["circuit_name"] = args.pop("circuit")
|
||||
qibotn_benchmark(**args)
|
||||
1
qibojit-benchmarks/cu_tensornet_expectation.json
Normal file
1
qibojit-benchmarks/cu_tensornet_expectation.json
Normal file
@@ -0,0 +1 @@
|
||||
{"MPI_enabled": false, "MPS_enabled": false, "NCCL_enabled": false, "expectation_enabled": true}
|
||||
1
qibojit-benchmarks/cu_tensornet_mpi_expectation.json
Normal file
1
qibojit-benchmarks/cu_tensornet_mpi_expectation.json
Normal file
@@ -0,0 +1 @@
|
||||
{"MPI_enabled": true, "MPS_enabled": false, "NCCL_enabled": false, "expectation_enabled": true}
|
||||
1
qibojit-benchmarks/cu_tensornet_nccl_expectation.json
Normal file
1
qibojit-benchmarks/cu_tensornet_nccl_expectation.json
Normal file
@@ -0,0 +1 @@
|
||||
{"MPI_enabled": false, "MPS_enabled": false, "NCCL_enabled": true, "expectation_enabled": true}
|
||||
18
qibojit-benchmarks/environment.yml
Normal file
18
qibojit-benchmarks/environment.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
name: benchmarks
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- python=3.12.12
|
||||
- pip
|
||||
- numpy=2.2.6
|
||||
- cryptography
|
||||
- pip:
|
||||
- qibo==0.2.23
|
||||
- qibojit==0.1.12
|
||||
- qiskit==1.4.5
|
||||
- qulacs==0.6.12
|
||||
- cirq==0.8.2
|
||||
- qsimcirq==0.22.0
|
||||
- qcgpu==0.1.1
|
||||
- projectq==0.8.0
|
||||
|
||||
36
qibojit-benchmarks/evolution.py
Normal file
36
qibojit-benchmarks/evolution.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""Launches the adiabatic evolution benchmark script for user given arguments."""
|
||||
import argparse
|
||||
from benchmarks.scripts import evolution_benchmark
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--nqubits", default=4, type=int,
|
||||
help="Number of qubits in the system to evolve.")
|
||||
parser.add_argument("--dt", default=1e-2, type=float,
|
||||
help="Time step size to use for time discretization.")
|
||||
|
||||
parser.add_argument("--nreps", default=1, type=int,
|
||||
help="Number of repetitions to run the evolution.")
|
||||
parser.add_argument("--precision", default=None, type=str,
|
||||
help="Numerical precision of the simulation. "
|
||||
"Choose between 'double' and 'single'.")
|
||||
|
||||
parser.add_argument("--solver", default="exp", type=str,
|
||||
help="Which solver to use for integration (exponential or RK methods)")
|
||||
parser.add_argument("--dense", action="store_true",
|
||||
help="If ``True`` it uses the full Hamiltonian matrix "
|
||||
"otherwise the Trotter decomposition is used.")
|
||||
|
||||
parser.add_argument("--backend", default="qibojit", type=str,
|
||||
help="Qibo backend to use.")
|
||||
parser.add_argument("--platform", default=None, type=str,
|
||||
help="Qibo platform to use.")
|
||||
|
||||
parser.add_argument("--filename", default=None, type=str,
|
||||
help="Directory of file to save the logs in json format. "
|
||||
"If not given the logs will only be printed and not saved.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = vars(parser.parse_args())
|
||||
evolution_benchmark(**args)
|
||||
14
qibojit-benchmarks/graphs/generate_random_bitstrings.py
Normal file
14
qibojit-benchmarks/graphs/generate_random_bitstrings.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""Generate random bitstrings for Hidden Shift circuit benchmarks."""
|
||||
import numpy as np
|
||||
|
||||
|
||||
file = open("random_bitstrings.dat", "w")
|
||||
|
||||
for nqubits in range(3, 31):
|
||||
bitstring = np.random.randint(0, 2, (nqubits,))
|
||||
bitstring = "".join(str(x) for x in bitstring)
|
||||
file.write(bitstring)
|
||||
file.write("\n")
|
||||
print(bitstring)
|
||||
|
||||
file.close()
|
||||
10
qibojit-benchmarks/graphs/generate_random_graphs.py
Normal file
10
qibojit-benchmarks/graphs/generate_random_graphs.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""Generate random regular graphs for MaxCut QAOA benchmark."""
|
||||
import json
|
||||
import networkx
|
||||
|
||||
|
||||
for nqubits in range(4, 31, 2):
|
||||
graph = networkx.random_regular_graph(3, nqubits)
|
||||
data = networkx.readwrite.json_graph.node_link_data(graph)
|
||||
with open(f"randomgraph_3_{nqubits}.json", "w") as file:
|
||||
json.dump(data, file)
|
||||
28
qibojit-benchmarks/graphs/random_bitstrings.dat
Normal file
28
qibojit-benchmarks/graphs/random_bitstrings.dat
Normal file
@@ -0,0 +1,28 @@
|
||||
000
|
||||
0110
|
||||
00010
|
||||
011010
|
||||
0010011
|
||||
10111000
|
||||
101000010
|
||||
1110011010
|
||||
01001000110
|
||||
010011111010
|
||||
0011001110100
|
||||
01101110111100
|
||||
110001101010101
|
||||
0000110011101011
|
||||
00110100010101000
|
||||
000010011001111100
|
||||
1110111100110011000
|
||||
01101111001101001001
|
||||
111110101000010010001
|
||||
1011010011001011101001
|
||||
10011000100101000110001
|
||||
101111110110001101000111
|
||||
0110111111101111111001001
|
||||
01000011001001100111110001
|
||||
000111000000000010100110001
|
||||
1011010001001001110010000010
|
||||
11111111010100110110111110011
|
||||
000101101000010010011000110011
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_10.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_10.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 1}, {"id": 3}, {"id": 8}, {"id": 0}, {"id": 4}, {"id": 2}, {"id": 7}, {"id": 6}, {"id": 5}, {"id": 9}], "links": [{"source": 1, "target": 3}, {"source": 1, "target": 4}, {"source": 1, "target": 7}, {"source": 3, "target": 8}, {"source": 3, "target": 2}, {"source": 8, "target": 6}, {"source": 8, "target": 9}, {"source": 0, "target": 4}, {"source": 0, "target": 2}, {"source": 0, "target": 5}, {"source": 4, "target": 6}, {"source": 2, "target": 7}, {"source": 7, "target": 5}, {"source": 6, "target": 9}, {"source": 5, "target": 9}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_12.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_12.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 0}, {"id": 1}, {"id": 9}, {"id": 10}, {"id": 2}, {"id": 5}, {"id": 11}, {"id": 6}, {"id": 8}, {"id": 3}, {"id": 4}, {"id": 7}], "links": [{"source": 0, "target": 1}, {"source": 0, "target": 10}, {"source": 0, "target": 3}, {"source": 1, "target": 2}, {"source": 1, "target": 4}, {"source": 9, "target": 10}, {"source": 9, "target": 7}, {"source": 9, "target": 3}, {"source": 10, "target": 8}, {"source": 2, "target": 3}, {"source": 2, "target": 11}, {"source": 5, "target": 11}, {"source": 5, "target": 7}, {"source": 5, "target": 4}, {"source": 11, "target": 8}, {"source": 6, "target": 8}, {"source": 6, "target": 4}, {"source": 6, "target": 7}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_14.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_14.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 4}, {"id": 12}, {"id": 5}, {"id": 7}, {"id": 10}, {"id": 1}, {"id": 3}, {"id": 2}, {"id": 11}, {"id": 0}, {"id": 8}, {"id": 9}, {"id": 13}, {"id": 6}], "links": [{"source": 4, "target": 12}, {"source": 4, "target": 8}, {"source": 4, "target": 10}, {"source": 12, "target": 5}, {"source": 12, "target": 0}, {"source": 5, "target": 7}, {"source": 5, "target": 10}, {"source": 7, "target": 9}, {"source": 7, "target": 6}, {"source": 10, "target": 13}, {"source": 1, "target": 3}, {"source": 1, "target": 0}, {"source": 1, "target": 11}, {"source": 3, "target": 8}, {"source": 3, "target": 2}, {"source": 2, "target": 11}, {"source": 2, "target": 6}, {"source": 11, "target": 0}, {"source": 8, "target": 13}, {"source": 9, "target": 13}, {"source": 9, "target": 6}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_16.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_16.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 4}, {"id": 12}, {"id": 5}, {"id": 13}, {"id": 15}, {"id": 3}, {"id": 10}, {"id": 8}, {"id": 1}, {"id": 6}, {"id": 0}, {"id": 14}, {"id": 2}, {"id": 11}, {"id": 9}, {"id": 7}], "links": [{"source": 4, "target": 12}, {"source": 4, "target": 15}, {"source": 4, "target": 14}, {"source": 12, "target": 8}, {"source": 12, "target": 9}, {"source": 5, "target": 13}, {"source": 5, "target": 10}, {"source": 5, "target": 9}, {"source": 13, "target": 3}, {"source": 13, "target": 15}, {"source": 15, "target": 0}, {"source": 3, "target": 9}, {"source": 3, "target": 2}, {"source": 10, "target": 11}, {"source": 10, "target": 2}, {"source": 8, "target": 2}, {"source": 8, "target": 7}, {"source": 1, "target": 6}, {"source": 1, "target": 0}, {"source": 1, "target": 14}, {"source": 6, "target": 11}, {"source": 6, "target": 7}, {"source": 0, "target": 14}, {"source": 11, "target": 7}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_18.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_18.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 4}, {"id": 12}, {"id": 0}, {"id": 2}, {"id": 5}, {"id": 9}, {"id": 17}, {"id": 11}, {"id": 6}, {"id": 8}, {"id": 15}, {"id": 3}, {"id": 16}, {"id": 14}, {"id": 10}, {"id": 13}, {"id": 1}, {"id": 7}], "links": [{"source": 4, "target": 12}, {"source": 4, "target": 8}, {"source": 4, "target": 7}, {"source": 12, "target": 15}, {"source": 12, "target": 9}, {"source": 0, "target": 2}, {"source": 0, "target": 5}, {"source": 0, "target": 3}, {"source": 2, "target": 11}, {"source": 2, "target": 13}, {"source": 5, "target": 3}, {"source": 5, "target": 14}, {"source": 9, "target": 17}, {"source": 9, "target": 16}, {"source": 17, "target": 6}, {"source": 17, "target": 1}, {"source": 11, "target": 6}, {"source": 11, "target": 16}, {"source": 6, "target": 3}, {"source": 8, "target": 14}, {"source": 8, "target": 16}, {"source": 15, "target": 7}, {"source": 15, "target": 13}, {"source": 14, "target": 10}, {"source": 10, "target": 13}, {"source": 10, "target": 1}, {"source": 1, "target": 7}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_20.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_20.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 12}, {"id": 16}, {"id": 4}, {"id": 9}, {"id": 14}, {"id": 17}, {"id": 10}, {"id": 18}, {"id": 0}, {"id": 11}, {"id": 2}, {"id": 6}, {"id": 8}, {"id": 7}, {"id": 19}, {"id": 3}, {"id": 15}, {"id": 5}, {"id": 1}, {"id": 13}], "links": [{"source": 12, "target": 16}, {"source": 12, "target": 4}, {"source": 12, "target": 5}, {"source": 16, "target": 7}, {"source": 16, "target": 15}, {"source": 4, "target": 0}, {"source": 4, "target": 7}, {"source": 9, "target": 14}, {"source": 9, "target": 17}, {"source": 9, "target": 3}, {"source": 14, "target": 2}, {"source": 14, "target": 6}, {"source": 17, "target": 11}, {"source": 17, "target": 8}, {"source": 10, "target": 18}, {"source": 10, "target": 8}, {"source": 10, "target": 13}, {"source": 18, "target": 0}, {"source": 18, "target": 11}, {"source": 0, "target": 11}, {"source": 2, "target": 1}, {"source": 2, "target": 6}, {"source": 6, "target": 8}, {"source": 7, "target": 19}, {"source": 19, "target": 13}, {"source": 19, "target": 15}, {"source": 3, "target": 15}, {"source": 3, "target": 5}, {"source": 5, "target": 1}, {"source": 1, "target": 13}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_22.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_22.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 6}, {"id": 12}, {"id": 3}, {"id": 4}, {"id": 7}, {"id": 13}, {"id": 19}, {"id": 18}, {"id": 8}, {"id": 15}, {"id": 0}, {"id": 14}, {"id": 21}, {"id": 1}, {"id": 9}, {"id": 20}, {"id": 10}, {"id": 5}, {"id": 2}, {"id": 11}, {"id": 16}, {"id": 17}], "links": [{"source": 6, "target": 12}, {"source": 6, "target": 20}, {"source": 6, "target": 19}, {"source": 12, "target": 13}, {"source": 12, "target": 2}, {"source": 3, "target": 4}, {"source": 3, "target": 7}, {"source": 3, "target": 19}, {"source": 4, "target": 18}, {"source": 4, "target": 2}, {"source": 7, "target": 0}, {"source": 7, "target": 9}, {"source": 13, "target": 20}, {"source": 13, "target": 18}, {"source": 19, "target": 0}, {"source": 18, "target": 15}, {"source": 8, "target": 15}, {"source": 8, "target": 21}, {"source": 8, "target": 14}, {"source": 15, "target": 20}, {"source": 0, "target": 14}, {"source": 14, "target": 21}, {"source": 21, "target": 5}, {"source": 1, "target": 9}, {"source": 1, "target": 11}, {"source": 1, "target": 17}, {"source": 9, "target": 10}, {"source": 10, "target": 11}, {"source": 10, "target": 16}, {"source": 5, "target": 11}, {"source": 5, "target": 17}, {"source": 2, "target": 16}, {"source": 16, "target": 17}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_24.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_24.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 6}, {"id": 15}, {"id": 22}, {"id": 23}, {"id": 3}, {"id": 13}, {"id": 12}, {"id": 5}, {"id": 16}, {"id": 9}, {"id": 14}, {"id": 0}, {"id": 8}, {"id": 10}, {"id": 18}, {"id": 11}, {"id": 17}, {"id": 1}, {"id": 7}, {"id": 19}, {"id": 4}, {"id": 21}, {"id": 20}, {"id": 2}], "links": [{"source": 6, "target": 15}, {"source": 6, "target": 5}, {"source": 6, "target": 10}, {"source": 15, "target": 8}, {"source": 15, "target": 1}, {"source": 22, "target": 23}, {"source": 22, "target": 12}, {"source": 22, "target": 13}, {"source": 23, "target": 4}, {"source": 23, "target": 19}, {"source": 3, "target": 13}, {"source": 3, "target": 21}, {"source": 3, "target": 14}, {"source": 13, "target": 2}, {"source": 12, "target": 9}, {"source": 12, "target": 2}, {"source": 5, "target": 16}, {"source": 5, "target": 21}, {"source": 16, "target": 7}, {"source": 16, "target": 11}, {"source": 9, "target": 14}, {"source": 9, "target": 21}, {"source": 14, "target": 20}, {"source": 0, "target": 8}, {"source": 0, "target": 17}, {"source": 0, "target": 4}, {"source": 8, "target": 7}, {"source": 10, "target": 18}, {"source": 10, "target": 20}, {"source": 18, "target": 1}, {"source": 18, "target": 2}, {"source": 11, "target": 17}, {"source": 11, "target": 19}, {"source": 17, "target": 4}, {"source": 1, "target": 20}, {"source": 7, "target": 19}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_26.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_26.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 6}, {"id": 18}, {"id": 14}, {"id": 25}, {"id": 4}, {"id": 24}, {"id": 17}, {"id": 21}, {"id": 0}, {"id": 20}, {"id": 16}, {"id": 19}, {"id": 15}, {"id": 7}, {"id": 5}, {"id": 3}, {"id": 9}, {"id": 22}, {"id": 8}, {"id": 12}, {"id": 10}, {"id": 11}, {"id": 13}, {"id": 1}, {"id": 2}, {"id": 23}], "links": [{"source": 6, "target": 18}, {"source": 6, "target": 3}, {"source": 6, "target": 19}, {"source": 18, "target": 12}, {"source": 18, "target": 24}, {"source": 14, "target": 25}, {"source": 14, "target": 10}, {"source": 14, "target": 1}, {"source": 25, "target": 7}, {"source": 25, "target": 13}, {"source": 4, "target": 24}, {"source": 4, "target": 5}, {"source": 4, "target": 8}, {"source": 24, "target": 5}, {"source": 17, "target": 21}, {"source": 17, "target": 15}, {"source": 17, "target": 8}, {"source": 21, "target": 23}, {"source": 21, "target": 0}, {"source": 0, "target": 20}, {"source": 0, "target": 16}, {"source": 20, "target": 22}, {"source": 20, "target": 12}, {"source": 16, "target": 19}, {"source": 16, "target": 23}, {"source": 19, "target": 7}, {"source": 15, "target": 5}, {"source": 15, "target": 2}, {"source": 7, "target": 12}, {"source": 3, "target": 9}, {"source": 3, "target": 23}, {"source": 9, "target": 13}, {"source": 9, "target": 2}, {"source": 22, "target": 11}, {"source": 22, "target": 1}, {"source": 8, "target": 10}, {"source": 10, "target": 11}, {"source": 11, "target": 1}, {"source": 13, "target": 2}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_28.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_28.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 24}, {"id": 27}, {"id": 6}, {"id": 15}, {"id": 18}, {"id": 20}, {"id": 12}, {"id": 16}, {"id": 21}, {"id": 22}, {"id": 19}, {"id": 11}, {"id": 14}, {"id": 0}, {"id": 17}, {"id": 8}, {"id": 13}, {"id": 9}, {"id": 23}, {"id": 3}, {"id": 5}, {"id": 7}, {"id": 25}, {"id": 4}, {"id": 1}, {"id": 2}, {"id": 10}, {"id": 26}], "links": [{"source": 24, "target": 27}, {"source": 24, "target": 8}, {"source": 24, "target": 23}, {"source": 27, "target": 19}, {"source": 27, "target": 5}, {"source": 6, "target": 15}, {"source": 6, "target": 5}, {"source": 6, "target": 25}, {"source": 15, "target": 20}, {"source": 15, "target": 5}, {"source": 18, "target": 20}, {"source": 18, "target": 21}, {"source": 18, "target": 7}, {"source": 20, "target": 19}, {"source": 12, "target": 16}, {"source": 12, "target": 19}, {"source": 12, "target": 11}, {"source": 16, "target": 9}, {"source": 16, "target": 2}, {"source": 21, "target": 22}, {"source": 21, "target": 0}, {"source": 22, "target": 13}, {"source": 22, "target": 17}, {"source": 11, "target": 14}, {"source": 11, "target": 4}, {"source": 14, "target": 13}, {"source": 14, "target": 26}, {"source": 0, "target": 17}, {"source": 0, "target": 1}, {"source": 17, "target": 1}, {"source": 8, "target": 10}, {"source": 8, "target": 13}, {"source": 9, "target": 23}, {"source": 9, "target": 3}, {"source": 23, "target": 10}, {"source": 3, "target": 26}, {"source": 3, "target": 2}, {"source": 7, "target": 25}, {"source": 7, "target": 4}, {"source": 25, "target": 26}, {"source": 4, "target": 1}, {"source": 2, "target": 10}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_30.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_30.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 6}, {"id": 18}, {"id": 7}, {"id": 17}, {"id": 3}, {"id": 4}, {"id": 20}, {"id": 24}, {"id": 29}, {"id": 5}, {"id": 13}, {"id": 23}, {"id": 28}, {"id": 0}, {"id": 9}, {"id": 26}, {"id": 19}, {"id": 1}, {"id": 12}, {"id": 10}, {"id": 15}, {"id": 16}, {"id": 25}, {"id": 11}, {"id": 21}, {"id": 27}, {"id": 8}, {"id": 14}, {"id": 2}, {"id": 22}], "links": [{"source": 6, "target": 18}, {"source": 6, "target": 24}, {"source": 6, "target": 26}, {"source": 18, "target": 29}, {"source": 18, "target": 16}, {"source": 7, "target": 17}, {"source": 7, "target": 20}, {"source": 7, "target": 14}, {"source": 17, "target": 0}, {"source": 17, "target": 19}, {"source": 3, "target": 4}, {"source": 3, "target": 12}, {"source": 3, "target": 11}, {"source": 4, "target": 11}, {"source": 4, "target": 23}, {"source": 20, "target": 9}, {"source": 20, "target": 28}, {"source": 24, "target": 19}, {"source": 24, "target": 0}, {"source": 29, "target": 8}, {"source": 29, "target": 21}, {"source": 5, "target": 13}, {"source": 5, "target": 0}, {"source": 5, "target": 21}, {"source": 13, "target": 26}, {"source": 13, "target": 10}, {"source": 23, "target": 28}, {"source": 23, "target": 19}, {"source": 28, "target": 15}, {"source": 9, "target": 26}, {"source": 9, "target": 16}, {"source": 1, "target": 12}, {"source": 1, "target": 2}, {"source": 1, "target": 22}, {"source": 12, "target": 14}, {"source": 10, "target": 15}, {"source": 10, "target": 2}, {"source": 15, "target": 2}, {"source": 16, "target": 25}, {"source": 25, "target": 11}, {"source": 25, "target": 27}, {"source": 21, "target": 27}, {"source": 27, "target": 22}, {"source": 8, "target": 14}, {"source": 8, "target": 22}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_4.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_4.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}], "links": [{"source": 0, "target": 1}, {"source": 0, "target": 3}, {"source": 0, "target": 2}, {"source": 1, "target": 2}, {"source": 1, "target": 3}, {"source": 2, "target": 3}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_6.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_6.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 2}, {"id": 4}, {"id": 1}, {"id": 0}, {"id": 3}, {"id": 5}], "links": [{"source": 2, "target": 4}, {"source": 2, "target": 1}, {"source": 2, "target": 5}, {"source": 4, "target": 0}, {"source": 4, "target": 3}, {"source": 1, "target": 5}, {"source": 1, "target": 3}, {"source": 0, "target": 3}, {"source": 0, "target": 5}]}
|
||||
1
qibojit-benchmarks/graphs/randomgraph_3_8.json
Normal file
1
qibojit-benchmarks/graphs/randomgraph_3_8.json
Normal file
@@ -0,0 +1 @@
|
||||
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 0}, {"id": 7}, {"id": 1}, {"id": 2}, {"id": 4}, {"id": 3}, {"id": 5}, {"id": 6}], "links": [{"source": 0, "target": 7}, {"source": 0, "target": 4}, {"source": 0, "target": 3}, {"source": 7, "target": 1}, {"source": 7, "target": 4}, {"source": 1, "target": 2}, {"source": 1, "target": 6}, {"source": 2, "target": 6}, {"source": 2, "target": 5}, {"source": 4, "target": 5}, {"source": 3, "target": 6}, {"source": 3, "target": 5}]}
|
||||
59
qibojit-benchmarks/main.py
Normal file
59
qibojit-benchmarks/main.py
Normal file
@@ -0,0 +1,59 @@
|
||||
"""Launches the circuit benchmark script for user given arguments."""
|
||||
import argparse
|
||||
from benchmarks.scripts import circuit_benchmark
|
||||
|
||||
|
||||
THREADING_LINK = "https://numba.pydata.org/numba-doc/latest/user/threading-layer.html#selecting-a-named-threading-layer"
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--nqubits", default=20, type=int,
|
||||
help="Number of qubits in the circuit.")
|
||||
parser.add_argument("--backend", default="qibojit", type=str,
|
||||
help="Qibo backend to use for simulation.")
|
||||
parser.add_argument("--platform", default=None, type=str,
|
||||
help="Qibo platform to use for simulation.")
|
||||
|
||||
parser.add_argument("--circuit", default="qft", type=str,
|
||||
help="Type of circuit to use. See README for the list of "
|
||||
"available circuits.")
|
||||
parser.add_argument("--circuit-options", default=None, type=str,
|
||||
help="String with options for circuit creation. "
|
||||
"It should have the form 'arg1=value1,arg2=value2,...'. "
|
||||
"See README for the list of arguments that are "
|
||||
"available for each circuit.")
|
||||
|
||||
parser.add_argument("--nreps", default=1, type=int,
|
||||
help="Number of repetitions of the circuit execution. "
|
||||
"Dry run is not included.")
|
||||
parser.add_argument("--nshots", default=None, type=int,
|
||||
help="Number of measurement shots. If used the time "
|
||||
"required to measure frequencies (no samples) is "
|
||||
"measured and logged. If it is ``None`` no "
|
||||
"measurements are performed.")
|
||||
parser.add_argument("--transfer", action="store_true",
|
||||
help="If used the final state array is converted to numpy. "
|
||||
"If the simulation device is GPU this requires a "
|
||||
"transfer from GPU memory to CPU.")
|
||||
|
||||
parser.add_argument("--precision", default="double", type=str,
|
||||
help="Numerical precision of the simulation. "
|
||||
"Choose between 'double' and 'single'.")
|
||||
parser.add_argument("--memory", default=None, type=int,
|
||||
help="Limit the GPU memory usage when using Tensorflow "
|
||||
"based backends. The memory limit should be given "
|
||||
"in MB. Tensorflow reserves the full available memory "
|
||||
"by default.")
|
||||
parser.add_argument("--threading", default=None, type=str,
|
||||
help="Switches the numba threading layer when using the "
|
||||
"qibojit backend on CPU. See {} for a list of "
|
||||
"available threading layers.".format(THREADING_LINK))
|
||||
|
||||
parser.add_argument("--filename", default=None, type=str,
|
||||
help="Directory of file to save the logs in json format. "
|
||||
"If not given the logs will only be printed and not saved.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = vars(parser.parse_args())
|
||||
args["circuit_name"] = args.pop("circuit")
|
||||
circuit_benchmark(**args)
|
||||
480
qibojit-benchmarks/plot_pathfinding_metric.py
Normal file
480
qibojit-benchmarks/plot_pathfinding_metric.py
Normal file
@@ -0,0 +1,480 @@
|
||||
from plots.utils import load_data
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
from matplotlib.patches import Patch
|
||||
matplotlib.rcParams['mathtext.fontset'] = 'cm'
|
||||
matplotlib.rcParams['font.family'] = 'STIXGeneral'
|
||||
import pandas as pd
|
||||
|
||||
save = True # if ``True`` plots will be saved in the current directory as pdfs
|
||||
|
||||
|
||||
def plot_scaling_expectation(input1, input2, input3, circuit, quantity, precision="double", fontsize=30, legend=True, save=False):
|
||||
|
||||
|
||||
combine_data = input1
|
||||
combine_data2 = input2
|
||||
combine_data3 = input3
|
||||
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
# Prepare GPU data
|
||||
condition = (combine_data["circuit"] == circuit) & (combine_data["precision"] == precision)
|
||||
condition2 = (combine_data2["circuit"] == circuit) & (combine_data2["precision"] == precision)
|
||||
condition3 = (combine_data3["circuit"] == circuit) & (combine_data3["precision"] == precision)
|
||||
|
||||
data = {}
|
||||
data["qibotn MPI"] = combine_data[(combine_data["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json") & condition]
|
||||
data["qibotn NCCL"] = combine_data[(combine_data["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_nccl_expectation.json") & condition]
|
||||
data["qibotn"] = combine_data[(combine_data["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_expectation.json") & condition]
|
||||
data["qibojit numba"] = combine_data[(combine_data["library_options"] == "backend=qibojit,platform=numba,expectation=XXXZ") & condition]
|
||||
|
||||
data2 = {}
|
||||
data2["qibotn MPI"] = combine_data2[(combine_data2["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json") & condition2]
|
||||
data2["qibotn NCCL"] = combine_data2[(combine_data2["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_nccl_expectation.json") & condition2]
|
||||
data2["qibotn"] = combine_data2[(combine_data2["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_expectation.json") & condition2]
|
||||
data2["qibojit numba"] = combine_data2[(combine_data2["library_options"] == "backend=qibojit,platform=numba,expectation=XXXZ") & condition2]
|
||||
|
||||
data3 = {}
|
||||
data3["qibotn MPI"] = combine_data3[(combine_data3["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json") & condition3]
|
||||
data3["qibotn NCCL"] = combine_data3[(combine_data3["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_nccl_expectation.json") & condition3]
|
||||
data3["qibotn"] = combine_data3[(combine_data3["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_expectation.json") & condition3]
|
||||
data3["qibojit numba"] = combine_data3[(combine_data3["library_options"] == "backend=qibojit,platform=numba,expectation=XXXZ") & condition3]
|
||||
|
||||
# Plot data
|
||||
cpu_cp = sns.color_palette("Oranges", 7)
|
||||
gpu_cp = sns.color_palette("Purples", 7)
|
||||
gpu_cp2 = sns.color_palette("Greens", 7)
|
||||
gpu_cp3 = sns.color_palette("Reds", 7)
|
||||
|
||||
plt.figure(figsize=(16, 9))
|
||||
|
||||
|
||||
plt.semilogy(data["qibotn"]["nqubits"], data["qibotn"][quantity],
|
||||
color=cpu_cp[3], linewidth=1.5, label="1x1 gpu", marker=".", markersize=10)
|
||||
plt.semilogy(data["qibotn MPI"]["nqubits"], data["qibotn MPI"][quantity],
|
||||
color=gpu_cp[3], linewidth=1.5, label="1x8 gpu MPI", marker=".", markersize=10)
|
||||
# plt.semilogy(data["qibotn NCCL"]["nqubits"], data["qibotn NCCL"][quantity],
|
||||
# color=gpu_cp[5], linewidth=1.5, label="1x8 gpu NCCL", marker=".", markersize=10)
|
||||
|
||||
plt.semilogy(data2["qibotn MPI"]["nqubits"], data2["qibotn MPI"][quantity],
|
||||
color=gpu_cp2[3], linewidth=1.5, label="2x8 gpu MPI", marker=".", markersize=10)
|
||||
plt.semilogy(data3["qibotn MPI"]["nqubits"], data3["qibotn MPI"][quantity],
|
||||
color=gpu_cp3[3], linewidth=1.5, label="4x8 gpu MPI", marker=".", markersize=10)
|
||||
|
||||
# plt.semilogy(data["qibojit numba"]["nqubits"], data["qibojit numba"][quantity],
|
||||
# color=cpu_cp[2], linewidth=1.5, label="CPU", marker=".", markersize=10)
|
||||
|
||||
# plt.ylim(bottom=1e-4, top=1e3)
|
||||
# plt.xlim(left=0, right=1750)
|
||||
# plt.xlim(left=0, right=60)
|
||||
|
||||
#plt.xlim(right=31)
|
||||
|
||||
plt.title(f"Expectation: {circuit}, depth {5}, {precision} precision")
|
||||
plt.xlabel("Number of qubits")
|
||||
if quantity == "total_dry_time":
|
||||
plt.ylabel("Total dry run time (sec)")
|
||||
elif quantity == "total_simulation_time":
|
||||
plt.ylabel("Total simulation time (sec)")
|
||||
elif quantity == "simulation_times_mean":
|
||||
plt.ylabel("Simulation times mean (sec)")
|
||||
if legend:
|
||||
plt.legend(fontsize="small")
|
||||
if save:
|
||||
plt.savefig(f"qibo_scaling_{circuit}_{quantity}_{precision}_ex.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
def plot_pathfinding_data(input1, input2, input3, circuit, quantity, precision="double", fontsize=30, legend=True, save=False):
|
||||
num_reps=3
|
||||
num_pathfinding_per_rep=4 #equivalent to total number of GPUs
|
||||
combine_data = input1
|
||||
combine_data2 = input2
|
||||
combine_data3 = input3
|
||||
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
# Prepare GPU data
|
||||
condition = (combine_data["circuit"] == circuit) & (combine_data["precision"] == precision)
|
||||
condition2 = (combine_data2["circuit"] == circuit) & (combine_data2["precision"] == precision)
|
||||
condition3 = (combine_data3["circuit"] == circuit) & (combine_data3["precision"] == precision)
|
||||
|
||||
data = {}
|
||||
data["qibotn MPI"] = combine_data[(combine_data["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json") & condition]
|
||||
data2 = {}
|
||||
data2["qibotn MPI"] = combine_data2[(combine_data2["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json") & condition2]
|
||||
data3 = {}
|
||||
data3["qibotn MPI"] = combine_data3[(combine_data3["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json") & condition3]
|
||||
|
||||
# Plot data
|
||||
|
||||
color_1x1 = sns.color_palette("Oranges", 7)
|
||||
color_1x4 = sns.color_palette("Purples", 7)
|
||||
color_2x4 = sns.color_palette("Greens", 7)
|
||||
color_4x4 = sns.color_palette("Reds", 7)
|
||||
|
||||
def extract_metric(data,rep_index,metric_of_interest):
|
||||
#metric of interest 0: num slices, 1: opt cost, 2: largest intermediate, 3: time
|
||||
metric_list_total=[]
|
||||
metric_max=[]
|
||||
metric_min=[]
|
||||
metric_max_index=[]
|
||||
metric_min_index=[]
|
||||
for each_record in range(len(data["qibotn MPI"]['nqubits'])):
|
||||
each_rep = data["qibotn MPI"][quantity][each_record][rep_index][0]
|
||||
split_data = [each_rep[i:i + 4] for i in range(0, len(each_rep), 4)]
|
||||
metric_list=[]
|
||||
for each_gpu in range(len(split_data)):
|
||||
metric_list.append(split_data[each_gpu][metric_of_interest])
|
||||
metric_list_total.append(metric_list)
|
||||
max_value = max(metric_list)
|
||||
max_value_index = metric_list.index(max_value)
|
||||
min_value = min(metric_list)
|
||||
min_value_index = metric_list.index(min_value)
|
||||
metric_max.append(max_value)
|
||||
metric_min.append(min_value)
|
||||
metric_max_index.append(max_value_index)
|
||||
metric_min_index.append(min_value_index)
|
||||
|
||||
return metric_list_total, metric_max, metric_min, metric_max_index, metric_min_index
|
||||
|
||||
def extract_metric_with_index_of_interest(data,rep_index,metric_of_interest,index_of_interest):
|
||||
#metric of interest 0: num slices, 1: opt cost, 2: largest intermediate, 3: time
|
||||
metric_list_total=[]
|
||||
metric_max=[]
|
||||
metric_min=[]
|
||||
metric_selected=[]
|
||||
|
||||
for each_record in range(len(data["qibotn MPI"]['nqubits'])):
|
||||
each_rep = data["qibotn MPI"][quantity][each_record][rep_index][0]
|
||||
split_data = [each_rep[i:i + 4] for i in range(0, len(each_rep), 4)]
|
||||
metric_list=[]
|
||||
for each_gpu in range(len(split_data)):
|
||||
metric_list.append(split_data[each_gpu][metric_of_interest])
|
||||
metric_list_total.append(metric_list)
|
||||
max_value = max(metric_list)
|
||||
interest_value = metric_list[(index_of_interest[each_record])]
|
||||
min_value = min(metric_list)
|
||||
|
||||
metric_max.append(max_value)
|
||||
metric_min.append(min_value)
|
||||
metric_selected.append(interest_value)
|
||||
|
||||
return metric_list_total, metric_max, metric_min, metric_selected
|
||||
|
||||
rep_data = 0
|
||||
rep_data2 = 1
|
||||
rep_data3 = 0
|
||||
|
||||
data_opt_cost_original, max_cost, min_cost,max_cost_index, min_cost_index = extract_metric(data,rep_data,1)
|
||||
data_opt_cost = [list(i) for i in zip(*data_opt_cost_original)]
|
||||
|
||||
data_opt_cost_original2, max_cost2, min_cost2, max_cost_index2, min_cost_index2 = extract_metric(data2,rep_data2,1)
|
||||
data_opt_cost2 = [list(i) for i in zip(*data_opt_cost_original2)]
|
||||
|
||||
data_opt_cost_original3, max_cost3, min_cost3, max_cost_index3, min_cost_index3 = extract_metric(data3,rep_data3,1)
|
||||
data_opt_cost3 = [list(i) for i in zip(*data_opt_cost_original3)]
|
||||
|
||||
#####
|
||||
data_num_slices_original, _, _,selected1 = extract_metric_with_index_of_interest(data,rep_data,0,min_cost_index)
|
||||
data_num_slices = [list(i) for i in zip(*data_num_slices_original)]
|
||||
|
||||
data_num_slices_original2, _, _, selected2= extract_metric_with_index_of_interest(data2,rep_data2,0,min_cost_index2)
|
||||
data_num_slices2 = [list(i) for i in zip(*data_num_slices_original2)]
|
||||
|
||||
data_num_slices_original3, _, _, selected3 = extract_metric_with_index_of_interest(data3,rep_data3,0,min_cost_index3)
|
||||
data_num_slices3 = [list(i) for i in zip(*data_num_slices_original3)]
|
||||
|
||||
####_time
|
||||
data_time_original, max_cost_time, min_cost_time,max_cost_index_time, min_cost_index_time = extract_metric(data,rep_data,3)
|
||||
data_time = [list(i) for i in zip(*data_time_original)]
|
||||
|
||||
data_time_original2, max_cost2_time, min_cost2_time, max_cost_index2_time, min_cost_index2_time = extract_metric(data2,rep_data2,3)
|
||||
data_time2 = [list(i) for i in zip(*data_time_original2)]
|
||||
|
||||
data_time_original3, max_cost3_time, min_cost3_time, max_cost_index3_time, min_cost_index3_time = extract_metric(data3,rep_data3,3)
|
||||
data_time3 = [list(i) for i in zip(*data_time_original3)]
|
||||
|
||||
# for i in range(len(data_opt_cost)):
|
||||
# print("max", max_cost[i],"min",min_cost[i],data_opt_cost[i])
|
||||
# print("1x4",data["qibotn MPI"]["nqubits"])
|
||||
# print("2x4",data2["qibotn MPI"]["nqubits"])
|
||||
# print("4x4",data3["qibotn MPI"]["nqubits"])
|
||||
|
||||
|
||||
#### PLOT COST OF PATH ####
|
||||
plt.figure(figsize=(16, 9))
|
||||
|
||||
color4x4=3
|
||||
# 4x4
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[0],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[1],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[2],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[3],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[4],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[5],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[6],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[7],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[8],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[9],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[10],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[11],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[12],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[13],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[14],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_opt_cost3[15],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
|
||||
# 2x4
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_opt_cost2[0],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_opt_cost2[1],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_opt_cost2[2],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_opt_cost2[3],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_opt_cost2[4],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_opt_cost2[5],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_opt_cost2[6],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_opt_cost2[7],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
|
||||
|
||||
|
||||
# 1x4
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_opt_cost[0],
|
||||
color=color_1x4[2], marker=".", s=100)
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_opt_cost[1],
|
||||
color=color_1x4[2], marker=".", s=100)
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_opt_cost[2],
|
||||
color=color_1x4[2], marker=".", s=100)
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_opt_cost[3],
|
||||
color=color_1x4[2],marker=".", s=100)
|
||||
|
||||
# Best path
|
||||
plt.semilogy(data["qibotn MPI"]["nqubits"], min_cost,
|
||||
color=color_1x4[3], label="1x4 selected", linewidth=1.5, marker="x", markersize=10)
|
||||
# Best path
|
||||
plt.semilogy(data2["qibotn MPI"]["nqubits"], min_cost2,
|
||||
color=color_2x4[3], label="2x4 selected",linewidth=1.5, marker="x", markersize=10)
|
||||
# Best path
|
||||
plt.semilogy(data3["qibotn MPI"]["nqubits"], min_cost3,
|
||||
color=color_4x4[3], label="4x4 selected",linewidth=1.5, marker="x", markersize=10)
|
||||
|
||||
plt.xlim(left=0, right=305)
|
||||
|
||||
plt.title(f"Cost of Paths Found: {circuit}, depth {5}, {precision} precision")
|
||||
plt.xlabel("Number of qubits")
|
||||
plt.ylabel("FLOP")
|
||||
if legend:
|
||||
plt.legend(fontsize="small")
|
||||
if save:
|
||||
plt.savefig(f"qibo_scaling_{circuit}_{quantity}_{precision}_pathfinding.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
#### PLOT NUM OF SLICES ####
|
||||
plt.figure(figsize=(16, 9))
|
||||
|
||||
color4x4=3
|
||||
# 4x4
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[0],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[1],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[2],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[3],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[4],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[5],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[6],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[7],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[8],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[9],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[10],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[11],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[12],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[13],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[14],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_num_slices3[15],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
|
||||
|
||||
# 2x4
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_num_slices2[0],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_num_slices2[1],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_num_slices2[2],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_num_slices2[3],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_num_slices2[4],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_num_slices2[5],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_num_slices2[6],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_num_slices2[7],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
|
||||
|
||||
# 1x4
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_num_slices[0],
|
||||
color=color_1x4[2], marker=".", s=100)
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_num_slices[1],
|
||||
color=color_1x4[2], marker=".", s=100)
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_num_slices[2],
|
||||
color=color_1x4[2], marker=".", s=100)
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_num_slices[3],
|
||||
color=color_1x4[2],marker=".", s=100)
|
||||
|
||||
# Best path
|
||||
plt.semilogy(data["qibotn MPI"]["nqubits"], selected1,
|
||||
color=color_1x4[3], label="1x4 selected", linewidth=1.5, marker="x", markersize=10)
|
||||
# Best path
|
||||
plt.semilogy(data2["qibotn MPI"]["nqubits"], selected2,
|
||||
color=color_2x4[3], label="2x4 selected",linewidth=1.5, marker="x", markersize=10)
|
||||
# Best path
|
||||
plt.semilogy(data3["qibotn MPI"]["nqubits"], selected3,
|
||||
color=color_4x4[3], label="4x4 selected",linewidth=1.5, marker="x", markersize=10)
|
||||
|
||||
plt.xlim(left=0, right=305)
|
||||
|
||||
plt.title(f"Slicing: {circuit}, depth {5}, {precision} precision")
|
||||
plt.xlabel("Number of qubits")
|
||||
plt.ylabel("Number of slices")
|
||||
if legend:
|
||||
plt.legend(fontsize="small")
|
||||
if save:
|
||||
plt.savefig(f"qibo_scaling_{circuit}_{quantity}_{precision}_slicing.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
#### PLOT Time PF ####
|
||||
plt.figure(figsize=(16, 9))
|
||||
|
||||
color4x4=3
|
||||
# 4x4
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[0],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[1],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[2],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[3],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[4],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[5],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[6],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[7],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[8],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[9],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[10],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[11],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[12],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[13],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[14],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
plt.scatter(data3["qibotn MPI"]["nqubits"], data_time3[15],
|
||||
color=color_4x4[color4x4], marker=".", s=100)
|
||||
|
||||
|
||||
# 2x4
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_time2[0],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_time2[1],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_time2[2],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_time2[3],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_time2[4],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_time2[5],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_time2[6],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
plt.scatter(data2["qibotn MPI"]["nqubits"], data_time2[7],
|
||||
color=color_2x4[2], marker=".", s=100)
|
||||
|
||||
|
||||
# 1x4
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_time[0],
|
||||
color=color_1x4[2], marker=".", s=100)
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_time[1],
|
||||
color=color_1x4[2], marker=".", s=100)
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_time[2],
|
||||
color=color_1x4[2], marker=".", s=100)
|
||||
plt.scatter(data["qibotn MPI"]["nqubits"], data_time[3],
|
||||
color=color_1x4[2],marker=".", s=100)
|
||||
|
||||
# Best path
|
||||
plt.semilogy(data["qibotn MPI"]["nqubits"], max_cost_time,
|
||||
color=color_1x4[3], label="1x4 selected", linewidth=1.5, marker="x", markersize=10)
|
||||
# Best path
|
||||
plt.semilogy(data2["qibotn MPI"]["nqubits"], max_cost2_time,
|
||||
color=color_2x4[3], label="2x4 selected",linewidth=1.5, marker="x", markersize=10)
|
||||
# Best path
|
||||
plt.semilogy(data3["qibotn MPI"]["nqubits"], max_cost3_time,
|
||||
color=color_4x4[3], label="4x4 selected",linewidth=1.5, marker="x", markersize=10)
|
||||
|
||||
plt.xlim(left=0, right=305)
|
||||
|
||||
plt.title(f"Pathfinding Time: {circuit}, depth {5}, {precision} precision")
|
||||
plt.xlabel("Number of qubits")
|
||||
plt.ylabel("Pathfinding Time (sec)")
|
||||
if legend:
|
||||
plt.legend(fontsize="small")
|
||||
if save:
|
||||
plt.savefig(f"qibo_scaling_{circuit}_{quantity}_{precision}_pftime.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
data1 = load_data("/home/project/11003124/tankya/costa1/qibojit-benchmarks/qibotn_pathfind_double_1x4_variational_d5.dat")
|
||||
data2 = load_data("/home/project/11003124/tankya/costa1/qibojit-benchmarks/qibotn_pathfind_double_2x4_variational_d5.dat")
|
||||
data3 = load_data("/home/project/11003124/tankya/costa1/qibojit-benchmarks/qibotn_pathfind_double_4x4_variational_d5_old.dat")
|
||||
|
||||
plot_pathfinding_data(data1,data2,data3,"variational", "expectation_result","double", legend=True, save=save)
|
||||
135
qibojit-benchmarks/plot_result_quad.py
Normal file
135
qibojit-benchmarks/plot_result_quad.py
Normal file
@@ -0,0 +1,135 @@
|
||||
from plots.utils import load_data
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
from matplotlib.patches import Patch
|
||||
matplotlib.rcParams['mathtext.fontset'] = 'cm'
|
||||
matplotlib.rcParams['font.family'] = 'STIXGeneral'
|
||||
import pandas as pd
|
||||
|
||||
save = True # if ``True`` plots will be saved in the current directory as pdfs
|
||||
|
||||
|
||||
def plot_scaling_expectation(input1, input2, input3,input4, circuit, quantity, precision="double", fontsize=30, legend=True, save=False):
|
||||
|
||||
|
||||
combine_data = input1
|
||||
combine_data2 = input2
|
||||
combine_data3 = input3
|
||||
combine_data4 = input4
|
||||
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
# Prepare GPU data
|
||||
condition = (combine_data["circuit"] == circuit) & (combine_data["precision"] == precision)
|
||||
condition2 = (combine_data2["circuit"] == circuit) & (combine_data2["precision"] == precision)
|
||||
condition3 = (combine_data3["circuit"] == circuit) & (combine_data3["precision"] == precision)
|
||||
condition4 = (combine_data4["circuit"] == circuit) & (combine_data4["precision"] == precision)
|
||||
|
||||
data = {}
|
||||
data["qibotn MPI"] = combine_data[(combine_data["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json") & condition]
|
||||
data["qibotn NCCL"] = combine_data[(combine_data["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_nccl_expectation.json") & condition]
|
||||
data["qibotn"] = combine_data[(combine_data["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_expectation.json") & condition]
|
||||
data["qibojit numba"] = combine_data[(combine_data["library_options"] == "backend=qibojit,platform=numba,expectation=XXXZ") & condition]
|
||||
|
||||
data2 = {}
|
||||
data2["qibotn MPI"] = combine_data2[(combine_data2["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json") & condition2]
|
||||
data2["qibotn NCCL"] = combine_data2[(combine_data2["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_nccl_expectation.json") & condition2]
|
||||
data2["qibotn"] = combine_data2[(combine_data2["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_expectation.json") & condition2]
|
||||
data2["qibojit numba"] = combine_data2[(combine_data2["library_options"] == "backend=qibojit,platform=numba,expectation=XXXZ") & condition2]
|
||||
|
||||
data3 = {}
|
||||
data3["qibotn MPI"] = combine_data3[(combine_data3["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json") & condition3]
|
||||
data3["qibotn NCCL"] = combine_data3[(combine_data3["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_nccl_expectation.json") & condition3]
|
||||
data3["qibotn"] = combine_data3[(combine_data3["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_expectation.json") & condition3]
|
||||
data3["qibojit numba"] = combine_data3[(combine_data3["library_options"] == "backend=qibojit,platform=numba,expectation=XXXZ") & condition3]
|
||||
|
||||
data4 = {}
|
||||
data4["qibotn MPI"] = combine_data4[(combine_data4["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json") & condition4]
|
||||
data4["qibotn NCCL"] = combine_data4[(combine_data4["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_nccl_expectation.json") & condition4]
|
||||
data4["qibotn"] = combine_data4[(combine_data4["library_options"] == "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_expectation.json") & condition4]
|
||||
data4["qibojit numba"] = combine_data4[(combine_data4["library_options"] == "backend=qibojit,platform=numba,expectation=XXXZ") & condition4]
|
||||
|
||||
# Plot data
|
||||
cpu_cp = sns.color_palette("Oranges", 7)
|
||||
gpu_cp = sns.color_palette("Purples", 7)
|
||||
gpu_cp2 = sns.color_palette("Greens", 7)
|
||||
gpu_cp3 = sns.color_palette("Reds", 7)
|
||||
gpu_cp4 = sns.color_palette("Blues", 7)
|
||||
plt.figure(figsize=(16, 9))
|
||||
|
||||
# 24 hours in seconds
|
||||
seconds_in_24_hours = 24 * 60 * 60
|
||||
|
||||
# Plotting the horizontal dashed line
|
||||
plt.axhline(y=seconds_in_24_hours, color='r', linestyle='--', linewidth=1.5, label='24 hours')
|
||||
|
||||
plt.semilogy(data["qibotn"]["nqubits"], data["qibotn"][quantity],
|
||||
color=cpu_cp[3], linewidth=1.5, label="1x1 gpu", marker=".", markersize=10)
|
||||
plt.semilogy(data2["qibotn MPI"]["nqubits"], data2["qibotn MPI"][quantity],
|
||||
color=gpu_cp[3], linewidth=1.5, label="1x4 gpu MPI", marker=".", markersize=10)
|
||||
# plt.semilogy(data["qibotn NCCL"]["nqubits"], data["qibotn NCCL"][quantity],
|
||||
# color=gpu_cp[5], linewidth=1.5, label="1x8 gpu NCCL", marker=".", markersize=10)
|
||||
|
||||
plt.semilogy(data3["qibotn MPI"]["nqubits"], data3["qibotn MPI"][quantity],
|
||||
color=gpu_cp2[3], linewidth=1.5, label="2x4 gpu MPI", marker=".", markersize=10)
|
||||
plt.semilogy(data4["qibotn MPI"]["nqubits"], data4["qibotn MPI"][quantity],
|
||||
color=gpu_cp3[3], linewidth=1.5, label="4x4 gpu MPI", marker=".", markersize=10)
|
||||
# plt.semilogy(data4["qibotn MPI"]["nqubits"], data4["qibotn MPI"][quantity],
|
||||
# color=gpu_cp5[3], linewidth=1.5, label="4x8 gpu MPI", marker=".", markersize=10)
|
||||
|
||||
# plt.semilogy(data["qibojit numba"]["nqubits"], data["qibojit numba"][quantity],
|
||||
# color=cpu_cp[2], linewidth=1.5, label="CPU", marker=".", markersize=10)
|
||||
|
||||
# plt.ylim(bottom=1e-4, top=1e3)
|
||||
# plt.xlim(left=0, right=1750)
|
||||
# plt.xlim(left=0, right=60)
|
||||
|
||||
#plt.xlim(right=31)
|
||||
|
||||
plt.title(f"Expectation: {circuit}, depth {5}, {precision} precision")
|
||||
plt.xlabel("Number of qubits")
|
||||
if quantity == "total_dry_time":
|
||||
plt.ylabel("Total dry run time (sec)")
|
||||
elif quantity == "total_simulation_time":
|
||||
plt.ylabel("Total simulation time (sec)")
|
||||
elif quantity == "simulation_times_mean":
|
||||
plt.ylabel("Simulation times mean (sec)")
|
||||
if legend:
|
||||
plt.legend(fontsize="small")
|
||||
if save:
|
||||
plt.savefig(f"qibo_scaling_{circuit}_{quantity}_{precision}_ex.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
|
||||
# data1 = load_data("/home/user3/qibotn_benchmarks/qibojit-benchmarks/qibotn_expectation_double_1x8_var.dat")
|
||||
# plot_scaling_expectation(data1,"variational", "simulation_times_mean","double", legend=True, save=save)
|
||||
|
||||
# data1 = load_data("/home/user3/qibotn_benchmarks/qibojit-benchmarks/qibotn_expectation_double_1x8_sup.dat")
|
||||
# plot_scaling_expectation(data1,"supremacy", "simulation_times_mean","double", legend=True, save=save)
|
||||
|
||||
# data1 = load_data("/home/user3/qibotn_benchmarks/qibojit-benchmarks/qibotn_expectation_double_1x8_qft.dat")
|
||||
# data1 = load_data("/home/user3/qibotn_benchmarks/qibojit-benchmarks/qibotn_expectation_double_1x8_sup.dat")
|
||||
# data1 = load_data("/home/user3/qibotn_benchmarks/qibojit-benchmarks/qibotn_expectation_double_1x8_var.dat")
|
||||
data1 = load_data("/home/project/11003124/tankya/costa1/qibojit-benchmarks/qibotn_expectation_double_1x1_variational_d5.dat")
|
||||
data2 = load_data("/home/project/11003124/tankya/costa1/qibojit-benchmarks/qibotn_expectation_double_1x4_variational_d5.dat")
|
||||
data3 = load_data("/home/project/11003124/tankya/costa1/qibojit-benchmarks/qibotn_expectation_double_2x4_variational_d5.dat")
|
||||
data4 = load_data("/home/project/11003124/tankya/costa1/qibojit-benchmarks/qibotn_expectation_double_4x4_variational_d5.dat")
|
||||
|
||||
plot_scaling_expectation(data1,data2,data3,data4,"variational", "simulation_times_mean","double", legend=True, save=save)
|
||||
|
||||
#1x4 Missing value
|
||||
|
||||
{"datetime": "2024-07-13 01:38:18", "nqubits": 10, "nreps": 1, "import_time": 2.0695817470550537, "library_options": "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json", "library": "qibo", "precision": "double", "device": "/CPU:0", "version": "0.2.5", "circuit": "variational", "circuit_options": "nqubits=10, nlayers=5, seed=123", "creation_time": 0.12717008590698242, "dry_run_time": 4.76837158203125e-07, "dtype": "complex128", "simulation_times": [2.722426176071167], "simulation_times_mean": 2.722426176071167, "simulation_times_std": 0.0, "expectation_result": [[[0.044218473681389266]]], "expectation_result_mean": 0.044218473681389266, "expectation_result_std": 0.0}
|
||||
|
||||
{"datetime": "2024-07-13 01:38:18", "nqubits": 40, "nreps": 1, "import_time": 2.0695817470550537, "library_options": "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json", "library": "qibo", "precision": "double", "device": "/CPU:0", "version": "0.2.5", "circuit": "variational", "circuit_options": "nqubits=40, nlayers=5, seed=123", "creation_time": 0.12717008590698242, "dry_run_time": 4.76837158203125e-07, "dtype": "complex128", "simulation_times": [92.54630327224731], "simulation_times_mean": 92.54630327224731, "simulation_times_std": 0.0, "expectation_result": [[[1.0185800644540888e-06]]], "expectation_result_mean": 1.0185800644540888e-06, "expectation_result_std": 0.0}
|
||||
|
||||
{"datetime": "2024-07-13 01:38:18", "nqubits": 60, "nreps": 1, "import_time": 2.0695817470550537, "library_options": "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json", "library": "qibo", "precision": "double", "device": "/CPU:0", "version": "0.2.5", "circuit": "variational", "circuit_options": "nqubits=60, nlayers=5, seed=123", "creation_time": 0.12717008590698242, "dry_run_time": 4.76837158203125e-07, "dtype": "complex128", "simulation_times": [271.3857560157776], "simulation_times_mean": 271.3857560157776, "simulation_times_std": 0.0, "expectation_result": [[[5.877344374846953e-11]]], "expectation_result_mean": 5.877344374846953e-11, "expectation_result_std": 0.0}
|
||||
|
||||
{"datetime": "2024-07-13 01:38:18", "nqubits": 80, "nreps": 1, "import_time": 2.0695817470550537, "library_options": "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json", "library": "qibo", "precision": "double", "device": "/CPU:0", "version": "0.2.5", "circuit": "variational", "circuit_options": "nqubits=80, nlayers=5, seed=123", "creation_time": 0.12717008590698242, "dry_run_time": 4.76837158203125e-07, "dtype": "complex128", "simulation_times": [1084.1304409503937], "simulation_times_mean": 1084.1304409503937, "simulation_times_std": 0.0, "expectation_result": [[[5.36328554751669e-16]]], "expectation_result_mean": 5.36328554751669e-16, "expectation_result_std": 0.0}
|
||||
|
||||
{"datetime": "2024-07-13 01:38:18", "nqubits": 110, "nreps": 1, "import_time": 2.0695817470550537, "library_options": "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json", "library": "qibo", "precision": "double", "device": "/CPU:0", "version": "0.2.5", "circuit": "variational", "circuit_options": "nqubits=110, nlayers=5, seed=123", "creation_time": 0.12717008590698242, "dry_run_time": 4.76837158203125e-07, "dtype": "complex128", "simulation_times": [9504.664041519165], "simulation_times_mean": 9504.664041519165, "simulation_times_std": 0.0, "expectation_result": [[[3.446458226795824e-19]]], "expectation_result_mean": 3.446458226795824e-19, "expectation_result_std": 0.0}
|
||||
|
||||
{"datetime": "2024-07-13 01:38:18", "nqubits": 130, "nreps": 1, "import_time": 2.0695817470550537, "library_options": "backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json", "library": "qibo", "precision": "double", "device": "/CPU:0", "version": "0.2.5", "circuit": "variational", "circuit_options": "nqubits=130, nlayers=5, seed=123", "creation_time": 0.12717008590698242, "dry_run_time": 4.76837158203125e-07, "dtype": "complex128", "simulation_times": [14543.339746236801], "simulation_times_mean": 14543.339746236801, "simulation_times_std": 0.0, "expectation_result": [[[1.789240796703424e-20]]], "expectation_result_mean": 1.789240796703424e-20, "expectation_result_std": 0.0}
|
||||
|
||||
0
qibojit-benchmarks/plots/__init__.py
Normal file
0
qibojit-benchmarks/plots/__init__.py
Normal file
249
qibojit-benchmarks/plots/barplots.py
Normal file
249
qibojit-benchmarks/plots/barplots.py
Normal file
@@ -0,0 +1,249 @@
|
||||
"""Generates bar plots for qibojit breakdowns and multigpu comparisons."""
|
||||
import numpy as np
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
from matplotlib.patches import Patch
|
||||
matplotlib.rcParams['mathtext.fontset'] = 'cm'
|
||||
matplotlib.rcParams['font.family'] = 'STIXGeneral'
|
||||
|
||||
|
||||
def plot_breakdown_nqubits(data, circuit, precision="double", width=0.1, fontsize=30, save=False):
|
||||
"""Creates dry run vs simulation barplot with import time breakdown for given circuit varying the number of qubits."""
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
|
||||
# Set plot params
|
||||
hatches = ['/', '\\', 'o', '-', 'x', '.', '*']
|
||||
quantities = ["import_time", "creation_time", "dry_run_time", "simulation_times_mean"]
|
||||
|
||||
nqubits = [22, 24, 26, 28, 30]
|
||||
widths = [-5 * width / 2, - 3 * width / 2, -width / 2, width / 2, 3 * width / 2, 5 * width / 2]
|
||||
oranges = sns.color_palette("Oranges", 2)
|
||||
purples = sns.color_palette("Purples", 2)
|
||||
greens = sns.color_palette("Greens", 2)
|
||||
greys = sns.color_palette("Greys", 2)
|
||||
|
||||
# Plot the results
|
||||
plt.figure(figsize=(25, 9))
|
||||
plt.title(f"qibojit - Dry run vs simulation - {circuit}")
|
||||
|
||||
xvalues = np.array(range(len(nqubits)))
|
||||
plt.xticks(xvalues, nqubits)
|
||||
|
||||
base_condition = ((data["precision"] == precision) & (data["circuit"] == circuit))
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=numba")
|
||||
heights_numba = {q: np.array([float(data[condition & (data["nqubits"] == n)][q]) for n in nqubits])
|
||||
for q in quantities}
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cupy")
|
||||
heights_cupy = {q: np.array([float(data[condition & (data["nqubits"] == n)][q]) for n in nqubits])
|
||||
for q in quantities}
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cuquantum")
|
||||
heights_cuquantum = {q: np.array([float(data[condition & (data["nqubits"] == n)][q]) for n in nqubits])
|
||||
for q in quantities}
|
||||
|
||||
plt.bar(xvalues + widths[0], heights_numba["import_time"],
|
||||
color=greys[0], align="center", width=width, alpha=1, hatch=hatches[0],
|
||||
edgecolor='w')
|
||||
plt.bar(xvalues + widths[1], heights_numba["import_time"],
|
||||
color=greys[1], align="center", width=width, alpha=1, hatch=hatches[1],
|
||||
edgecolor='w')
|
||||
|
||||
plt.bar(xvalues + widths[2], heights_cupy["import_time"],
|
||||
color=greys[0], align="center", width=width, alpha=1, hatch=hatches[0],
|
||||
edgecolor='w')
|
||||
plt.bar(xvalues + widths[3], heights_cupy["import_time"],
|
||||
color=greys[1], align="center", width=width, alpha=1, hatch=hatches[1],
|
||||
edgecolor='w')
|
||||
|
||||
plt.bar(xvalues + widths[4], heights_cuquantum["import_time"],
|
||||
color=greys[0], align="center", width=width, alpha=1, hatch=hatches[0],
|
||||
edgecolor='w')
|
||||
plt.bar(xvalues + widths[5], heights_cuquantum["import_time"],
|
||||
color=greys[1], align="center", width=width, alpha=1, hatch=hatches[1],
|
||||
edgecolor='w')
|
||||
|
||||
plt.bar(xvalues + widths[0], heights_numba["dry_run_time"],
|
||||
color=oranges[0], align="center", width=width, alpha=1, hatch=hatches[0],
|
||||
edgecolor='w', bottom=heights_numba["import_time"] + heights_numba["creation_time"])
|
||||
plt.bar(xvalues + widths[1], heights_numba["simulation_times_mean"],
|
||||
color=oranges[1], align="center", width=width, alpha=1, hatch=hatches[1],
|
||||
edgecolor='w', bottom=heights_numba["import_time"] + heights_numba["creation_time"])
|
||||
|
||||
plt.bar(xvalues + widths[2], heights_cupy["dry_run_time"],
|
||||
color=purples[0], align="center", width=width, alpha=1, hatch=hatches[0],
|
||||
edgecolor='w', bottom=heights_cupy["import_time"] + heights_cupy["creation_time"])
|
||||
plt.bar(xvalues + widths[3], heights_cupy["simulation_times_mean"],
|
||||
color=purples[1], align="center", width=width, alpha=1, hatch=hatches[1],
|
||||
edgecolor='w', bottom=heights_cupy["import_time"] + heights_cupy["creation_time"])
|
||||
|
||||
plt.bar(xvalues + widths[4], heights_cuquantum["dry_run_time"],
|
||||
color=greens[0], align="center", width=width, alpha=1, hatch=hatches[0],
|
||||
edgecolor='w', bottom=heights_cuquantum["import_time"] + heights_cuquantum["creation_time"])
|
||||
plt.bar(xvalues + widths[5], heights_cuquantum["simulation_times_mean"],
|
||||
color=greens[1], align="center", width=width, alpha=1, hatch=hatches[1],
|
||||
edgecolor='w', bottom=heights_cuquantum["import_time"] + heights_cuquantum["creation_time"])
|
||||
|
||||
plt.xlabel("Number of qubits")
|
||||
plt.ylabel("Execution time (sec)")
|
||||
|
||||
legend_elements = [
|
||||
Patch(facecolor="w", edgecolor="k", hatch=hatches[0], label="Dry run time"),
|
||||
Patch(facecolor="w", edgecolor="k", hatch=hatches[1], label="Simulation time"),
|
||||
Patch(color=greys[1], label="Import time"),
|
||||
Patch(color=oranges[1], label="numba"),
|
||||
Patch(color=purples[1], label="cupy"),
|
||||
Patch(color=greens[1], label="cuquantum"),
|
||||
]
|
||||
plt.legend(handles=legend_elements)
|
||||
if save:
|
||||
plt.savefig(f"qibojit_dry_vs_simulation_{circuit}_{precision}.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_breakdown_circuits(data, nqubits, precision="double", width=0.1, fontsize=30, save=False):
|
||||
"""Creates dry run vs simulation barplot with import time breakdown for given number of qubits varying the circuit."""
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
# Set plot params
|
||||
hatches = ['/', '\\', 'o', '-', 'x', '.', '*']
|
||||
width = 0.1
|
||||
quantities = ["import_time", "creation_time", "dry_run_time", "simulation_times_mean"]
|
||||
|
||||
circuits = ["qft", "variational", "supremacy", "qv", "bv"]
|
||||
greys = sns.color_palette("Greys", 2)
|
||||
purples = sns.color_palette("Purples", 2)
|
||||
greens = sns.color_palette("Greens", 2)
|
||||
|
||||
plt.figure(figsize=(25, 9))
|
||||
plt.title(f"qibojit - Dry run vs simulation - {nqubits} qubits")
|
||||
|
||||
xvalues = np.array(range(len(circuits)))
|
||||
plt.xticks(xvalues, circuits)
|
||||
|
||||
base_condition = ((data["precision"] == precision) & (data["nqubits"] == nqubits))
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cupy")
|
||||
heights_cupy = {q: np.array([float(data[condition & (data["circuit"] == circ)][q]) for circ in circuits])
|
||||
for q in quantities}
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cuquantum")
|
||||
heights_cuquantum = {q: np.array([float(data[condition & (data["circuit"] == circ)][q]) for circ in circuits])
|
||||
for q in quantities}
|
||||
|
||||
plt.bar(xvalues - 3 * width / 2, heights_cupy["import_time"],
|
||||
color=greys[0], align="center", width=width, alpha=1, hatch=hatches[0],
|
||||
edgecolor='w')
|
||||
plt.bar(xvalues - width / 2, heights_cupy["import_time"],
|
||||
color=greys[1], align="center", width=width, alpha=1, hatch=hatches[1],
|
||||
edgecolor='w')
|
||||
|
||||
plt.bar(xvalues + width / 2, heights_cuquantum["import_time"],
|
||||
color=greys[0], align="center", width=width, alpha=1, hatch=hatches[0],
|
||||
edgecolor='w')
|
||||
plt.bar(xvalues + 3 * width / 2, heights_cuquantum["import_time"],
|
||||
color=greys[1], align="center", width=width, alpha=1, hatch=hatches[1],
|
||||
edgecolor='w')
|
||||
|
||||
plt.bar(xvalues - 3 * width / 2, heights_cupy["dry_run_time"],
|
||||
color=purples[0], align="center", width=width, alpha=1, hatch=hatches[0],
|
||||
edgecolor='w', bottom=heights_cupy["import_time"] + heights_cupy["creation_time"])
|
||||
plt.bar(xvalues - width / 2, heights_cupy["simulation_times_mean"],
|
||||
color=purples[1], align="center", width=width, alpha=1, hatch=hatches[1],
|
||||
edgecolor='w', bottom=heights_cupy["import_time"] + heights_cupy["creation_time"])
|
||||
|
||||
plt.bar(xvalues + width / 2, heights_cuquantum["dry_run_time"],
|
||||
color=greens[0], align="center", width=width, alpha=1, hatch=hatches[0],
|
||||
edgecolor='w', bottom=heights_cuquantum["import_time"] + heights_cuquantum["creation_time"])
|
||||
plt.bar(xvalues + 3 * width / 2, heights_cuquantum["simulation_times_mean"],
|
||||
color=greens[1], align="center", width=width, alpha=1, hatch=hatches[1],
|
||||
edgecolor='w', bottom=heights_cuquantum["import_time"] + heights_cuquantum["creation_time"])
|
||||
|
||||
plt.ylabel("Execution time (sec)")
|
||||
|
||||
legend_elements = [
|
||||
Patch(facecolor="w", edgecolor="k", hatch=hatches[0], label="Dry run time"),
|
||||
Patch(facecolor="w", edgecolor="k", hatch=hatches[1], label="Simulation time"),
|
||||
Patch(color=greys[1], label="Import time"),
|
||||
Patch(color=purples[1], label="cupy"),
|
||||
Patch(color=greens[1], label="cuquantum"),
|
||||
|
||||
]
|
||||
plt.legend(handles=legend_elements, bbox_to_anchor=(1,1))
|
||||
|
||||
if save:
|
||||
plt.savefig(f"qibojit_dry_vs_simulation_{nqubits}qubits_{precision}.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_multigpu(data, nqubits, quantity, precision="double", fontsize=45, legend=False, save=False):
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
# Set plot params
|
||||
hatches = ['/', '\\', 'o', '-', 'x', '.', '*']
|
||||
width = 0.1
|
||||
quantities = ["import_time", "creation_time", "dry_run_time", "simulation_times_mean",
|
||||
"total_simulation_time", "total_dry_time"]
|
||||
circuits = ["qft", "variational", "supremacy", "qv", "bv"]
|
||||
|
||||
widths = [-5 * width / 2, - 3 * width / 2, -width / 2, width / 2, 3 * width / 2, 5 * width / 2]
|
||||
oranges = sns.color_palette("Oranges", 3)
|
||||
purples = sns.color_palette("Purples", 3)
|
||||
greens = sns.color_palette("Greens", 3)
|
||||
|
||||
# Plot the results
|
||||
plt.figure(figsize=(25, 9))
|
||||
|
||||
xvalues = np.array(range(len(circuits)))
|
||||
plt.xticks(xvalues, circuits)
|
||||
|
||||
base_condition = ((data["precision"] == precision) & (data["nqubits"] == nqubits))
|
||||
heights1 = {}
|
||||
heights2 = {}
|
||||
heights4 = {}
|
||||
for backend in ["qibojit", "qibotf"]:
|
||||
condition = base_condition & (data["library_options"] == f"backend={backend},accelerators=4/GPU:3")
|
||||
heights1[backend] = {q: np.array([float(data[condition & (data["circuit"] == c)][q]) for c in circuits])
|
||||
for q in quantities}
|
||||
condition = base_condition & (data["library_options"] == f"backend={backend},accelerators=2/GPU:2+2/GPU:3")
|
||||
heights2[backend] = {q: np.array([float(data[condition & (data["circuit"] == c)][q]) for c in circuits])
|
||||
for q in quantities}
|
||||
condition = base_condition & (data["library_options"] == f"backend={backend},accelerators=1/GPU:0+1/GPU:1+1/GPU:2+1/GPU:3")
|
||||
heights4[backend] = {q: np.array([float(data[condition & (data["circuit"] == c)][q]) for c in circuits])
|
||||
for q in quantities}
|
||||
|
||||
plt.bar(xvalues + widths[0], heights1["qibojit"][quantity],
|
||||
color=purples[0], align="center", width=width, alpha=1, hatch=hatches[0], edgecolor='w')
|
||||
plt.bar(xvalues + widths[1], heights1["qibotf"][quantity],
|
||||
color=greens[0], align="center", width=width, alpha=1, hatch=hatches[1], edgecolor='w')
|
||||
|
||||
plt.bar(xvalues + widths[2], heights2["qibojit"][quantity],
|
||||
color=purples[1], align="center", width=width, alpha=1, hatch=hatches[0], edgecolor='w')
|
||||
plt.bar(xvalues + widths[3], heights2["qibotf"][quantity],
|
||||
color=greens[1], align="center", width=width, alpha=1, hatch=hatches[1], edgecolor='w')
|
||||
|
||||
plt.bar(xvalues + widths[4], heights4["qibojit"][quantity],
|
||||
color=purples[2], align="center", width=width, alpha=1, hatch=hatches[0], edgecolor='w')
|
||||
plt.bar(xvalues + widths[5], heights4["qibotf"][quantity],
|
||||
color=greens[2], align="center", width=width, alpha=1, hatch=hatches[1], edgecolor='w')
|
||||
|
||||
plt.title(f"Multi-GPU - {nqubits} qubits")
|
||||
if quantity == "total_dry_time":
|
||||
plt.ylabel("Total dry run time (sec)")
|
||||
elif quantity == "dry_run_time":
|
||||
plt.ylabel("Dry run time (sec)")
|
||||
elif quantity == "total_simulation_time":
|
||||
plt.ylabel("Total simulation time (sec)")
|
||||
elif quantity == "simulation_times_mean":
|
||||
plt.ylabel("Simulation time (sec)")
|
||||
|
||||
legend_elements = [
|
||||
Patch(facecolor=purples[2], edgecolor="w", hatch=hatches[0], label="qibojit"),
|
||||
Patch(facecolor=greens[2], edgecolor="w", hatch=hatches[1], label="qibotf"),
|
||||
Patch(color=purples[0], label="1x GPU"),
|
||||
Patch(color=purples[1], label="2x GPUs"),
|
||||
Patch(color=purples[2], label="4x GPUs"),
|
||||
]
|
||||
if legend:
|
||||
plt.legend(handles=legend_elements, bbox_to_anchor=(1,1))
|
||||
if save:
|
||||
plt.savefig(f"multigpu_{nqubits}qubits_{quantity}_{precision}.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
44
qibojit-benchmarks/plots/devices.py
Normal file
44
qibojit-benchmarks/plots/devices.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""Scaling plots with performance comparison of qibojit backend run on different CPU and GPU devices."""
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
matplotlib.rcParams['mathtext.fontset'] = 'cm'
|
||||
matplotlib.rcParams['font.family'] = 'STIXGeneral'
|
||||
|
||||
|
||||
class Line:
|
||||
|
||||
def __init__(self, label, data, color, marker, linestyle="-"):
|
||||
self.label = label
|
||||
self.data = data
|
||||
self.color = color
|
||||
self.marker = marker
|
||||
self.linestyle = linestyle
|
||||
|
||||
|
||||
def plot_devices(lines, circuit, quantity, precision="double",
|
||||
fontsize=30, legendfont=None, save=False):
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
# Filter data
|
||||
for line in lines:
|
||||
condition = (line.data["circuit"] == circuit) & (line.data["precision"] == precision)
|
||||
line.data = line.data[condition]
|
||||
|
||||
plt.figure(figsize=(14, 8))
|
||||
for line in lines:
|
||||
plt.semilogy(line.data["nqubits"], line.data[quantity], color=line.color, linestyle=line.linestyle,
|
||||
linewidth=3.0, marker=line.marker, markersize=10, label=line.label)
|
||||
|
||||
plt.title(f"qibojit, {circuit}, {precision} precision")
|
||||
plt.xlabel("Number of qubits")
|
||||
if quantity == "total_dry_time":
|
||||
plt.ylabel("Total dry run time (sec)")
|
||||
elif quantity == "total_simulation_time":
|
||||
plt.ylabel("Total simulation time (sec)")
|
||||
|
||||
if legendfont is not None:
|
||||
plt.legend(loc="upper left", fontsize=legendfont)
|
||||
if save:
|
||||
plt.savefig(f"devices_{circuit}_{quantity}_{precision}.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
101
qibojit-benchmarks/plots/evolution.py
Normal file
101
qibojit-benchmarks/plots/evolution.py
Normal file
@@ -0,0 +1,101 @@
|
||||
import numpy as np
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
from matplotlib.patches import Patch
|
||||
matplotlib.rcParams['mathtext.fontset'] = 'cm'
|
||||
matplotlib.rcParams['font.family'] = 'STIXGeneral'
|
||||
|
||||
|
||||
def plot_dense(data, quantity, nqubits, fontsize=30, legend=True, save=False):
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
|
||||
cpu_cp = sns.color_palette("Oranges", 4)
|
||||
gpu_cp = sns.color_palette("Purples", 4)
|
||||
|
||||
data["is_gpu"] = data["device"].apply(lambda x: "GPU" in x)
|
||||
base_condition = (data["nqubits"] == nqubits) & (data["dense"] == True)
|
||||
|
||||
plt.figure(figsize=(16, 9))
|
||||
condition = base_condition & (data["backend"] == "numpy")
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="^", markersize=10,
|
||||
color=cpu_cp[3], linewidth=3.0, label="numpy")
|
||||
condition = base_condition & (data["backend"] == "tensorflow") & (data["is_gpu"] == False)
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="o", markersize=10,
|
||||
color=cpu_cp[1], linewidth=3.0, label="tensorflow cpu")
|
||||
|
||||
condition = base_condition & (data["backend"] == "qibojit") & (data["platform"] == "cuquantum")
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="^", markersize=10,
|
||||
color=gpu_cp[3], linewidth=3.0, label="cupy")
|
||||
condition = base_condition & (data["backend"] == "tensorflow") & (data["is_gpu"] == True)
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="o", markersize=10,
|
||||
color=gpu_cp[1], linewidth=3.0, label="tensorflow gpu")
|
||||
|
||||
plt.title(f"Dense adiabatic evolution, {nqubits} qubits, double precision")
|
||||
plt.xlabel("$\delta t$")
|
||||
if quantity == "total_dry_time":
|
||||
plt.ylabel("Total dry run time (sec)")
|
||||
elif quantity == "total_simulation_time":
|
||||
plt.ylabel("Total simulation time (sec)")
|
||||
|
||||
if legend:
|
||||
plt.legend()
|
||||
|
||||
if save:
|
||||
plt.savefig(f"evolution_dense_{nqubits}qubits_{quantity}.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_trotter(data, quantity, nqubits, fontsize=30, yticks=None, legend=False, save=False):
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
|
||||
cpu_cp = sns.color_palette("Oranges", 4)
|
||||
gpu_cp = sns.color_palette("Purples", 4)
|
||||
|
||||
data["is_gpu"] = data["device"].apply(lambda x: "GPU" in x)
|
||||
base_condition = (data["nqubits"] == nqubits) & (data["dense"] == False)
|
||||
|
||||
plt.figure(figsize=(16, 9))
|
||||
condition = base_condition & (data["backend"] == "numpy")
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="s", markersize=10,
|
||||
color=cpu_cp[0], linewidth=3.0, label="numpy")
|
||||
condition = base_condition & (data["backend"] == "tensorflow") & (data["is_gpu"] == False)
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="o", markersize=10,
|
||||
color=cpu_cp[1], linewidth=3.0, label="tensorflow cpu")
|
||||
condition = base_condition & (data["backend"] == "qibotf") & (data["is_gpu"] == False)
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="D", markersize=10,
|
||||
color=cpu_cp[2], linewidth=3.0, label="qibotf cpu")
|
||||
condition = base_condition & (data["backend"] == "qibojit") & (data["platform"] == "numba")
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="^", markersize=10,
|
||||
color=cpu_cp[3], linewidth=3.0, label="qibojit (numba) cpu")
|
||||
|
||||
condition = base_condition & (data["backend"] == "tensorflow") & (data["is_gpu"] == True)
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="o", markersize=10,
|
||||
color=gpu_cp[1], linewidth=3.0, label="tensorflow gpu")
|
||||
condition = base_condition & (data["backend"] == "qibotf") & (data["is_gpu"] == True)
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="D", markersize=10,
|
||||
color=gpu_cp[2], linewidth=3.0, label="qibotf gpu")
|
||||
condition = base_condition & (data["backend"] == "qibojit") & (data["platform"] == "cupy")
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="^", markersize=10,
|
||||
color=gpu_cp[3], linewidth=3.0, label="qibojit (cupy) gpu")
|
||||
condition = base_condition & (data["backend"] == "qibojit") & (data["platform"] == "cuquantum")
|
||||
plt.semilogy(data[condition]["dt"], data[condition][quantity], marker="v", markersize=10, linestyle="--",
|
||||
color=gpu_cp[3], linewidth=3.0, label="qibojit (cuquantum) gpu")
|
||||
|
||||
plt.title(f"Trotter adiabatic evolution, {nqubits} qubits, double precision")
|
||||
plt.xlabel("$\delta t$")
|
||||
if quantity == "total_dry_time":
|
||||
plt.ylabel("Total dry run time (sec)")
|
||||
elif quantity == "total_simulation_time":
|
||||
plt.ylabel("Total simulation time (sec)")
|
||||
|
||||
if legend:
|
||||
plt.legend(fontsize="small")
|
||||
if yticks is not None:
|
||||
plt.minorticks_off()
|
||||
plt.yticks(yticks)
|
||||
if save:
|
||||
plt.savefig(f"evolution_trotter_{nqubits}qubits_{quantity}.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
145
qibojit-benchmarks/plots/fusion.py
Normal file
145
qibojit-benchmarks/plots/fusion.py
Normal file
@@ -0,0 +1,145 @@
|
||||
"""Generates bar plots for gate fusion comparisons."""
|
||||
import numpy as np
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
from matplotlib.patches import Patch
|
||||
matplotlib.rcParams['mathtext.fontset'] = 'cm'
|
||||
matplotlib.rcParams['font.family'] = 'STIXGeneral'
|
||||
|
||||
|
||||
def plot_fusion_nqubits(data, circuit, quantity, precision="double", width=0.1, fontsize=30, legend=False, logscale=False, save=False):
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
|
||||
# Set plot params
|
||||
hatches = ['/', '\\', 'o', '-', 'x', '.', '*']
|
||||
|
||||
nqubits = [20, 22, 24, 26, 28]
|
||||
widths = [-5 * width / 2, - 3 * width / 2, -width / 2, width / 2, 3 * width / 2, 5 * width / 2]
|
||||
oranges = sns.color_palette("Oranges", 2)
|
||||
purples = sns.color_palette("Purples", 2)
|
||||
greens = sns.color_palette("Greens", 2)
|
||||
|
||||
# Plot the results
|
||||
plt.figure(figsize=(25, 9))
|
||||
plt.title(f"qibojit - Gate fusion - {circuit}")
|
||||
|
||||
xvalues = np.array(range(len(nqubits)))
|
||||
plt.xticks(xvalues, nqubits)
|
||||
|
||||
base_condition = ((data["precision"] == precision) & (data["circuit"] == circuit))
|
||||
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=numba")
|
||||
heights = np.array([float(data[condition & (data["nqubits"] == n)][quantity]) for n in nqubits])
|
||||
plt.bar(xvalues + widths[0], heights, color=oranges[0], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[0], edgecolor='w')
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=numba,max_qubits=2")
|
||||
heights = np.array([float(data[condition & (data["nqubits"] == n)][quantity]) for n in nqubits])
|
||||
plt.bar(xvalues + widths[1], heights, color=oranges[1], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[1], edgecolor='w')
|
||||
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cupy")
|
||||
heights = np.array([float(data[condition & (data["nqubits"] == n)][quantity]) for n in nqubits])
|
||||
plt.bar(xvalues + widths[2], heights, color=purples[0], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[0], edgecolor='w')
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cupy,max_qubits=2")
|
||||
heights = np.array([float(data[condition & (data["nqubits"] == n)][quantity]) for n in nqubits])
|
||||
plt.bar(xvalues + widths[3], heights, color=purples[1], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[1], edgecolor='w')
|
||||
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cuquantum")
|
||||
heights = np.array([float(data[condition & (data["nqubits"] == n)][quantity]) for n in nqubits])
|
||||
plt.bar(xvalues + widths[4], heights, color=greens[0], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[0], edgecolor='w')
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cuquantum,max_qubits=2")
|
||||
heights = np.array([float(data[condition & (data["nqubits"] == n)][quantity]) for n in nqubits])
|
||||
plt.bar(xvalues + widths[5], heights, color=greens[1], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[1], edgecolor='w')
|
||||
|
||||
plt.xlabel("Number of qubits")
|
||||
if quantity == "total_dry_time":
|
||||
plt.ylabel("Total dry run time (sec)")
|
||||
elif quantity == "total_simulation_time":
|
||||
plt.ylabel("Total simulation time (sec)")
|
||||
|
||||
if legend:
|
||||
legend_elements = [
|
||||
Patch(facecolor="w", edgecolor="k", hatch=hatches[0], label="No fusion"),
|
||||
Patch(facecolor="w", edgecolor="k", hatch=hatches[1], label="Two-qubit fusion"),
|
||||
Patch(color=oranges[1], label="numba"),
|
||||
Patch(color=purples[1], label="cupy"),
|
||||
Patch(color=greens[1], label="cuquantum")
|
||||
]
|
||||
plt.legend(handles=legend_elements)
|
||||
if save:
|
||||
plt.savefig(f"qibojit_fusion_{precision}_{circuit}_{quantity}.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_fusion_circuits(data, nqubits, quantity, precision="double", width=0.1, fontsize=30, legend=False, logscale=False, save=False):
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
|
||||
# Set plot params
|
||||
hatches = ['/', '\\', 'o', '-', 'x', '.', '*']
|
||||
|
||||
circuits = ["qft", "variational", "supremacy", "qv", "bv"]
|
||||
widths = [-5 * width / 2, - 3 * width / 2, -width / 2, width / 2, 3 * width / 2, 5 * width / 2]
|
||||
oranges = sns.color_palette("Oranges", 2)
|
||||
purples = sns.color_palette("Purples", 2)
|
||||
greens = sns.color_palette("Greens", 2)
|
||||
|
||||
# Plot the results
|
||||
plt.figure(figsize=(25, 9))
|
||||
plt.title(f"qibojit - Gate fusion - {nqubits} qubits")
|
||||
|
||||
xvalues = np.array(range(len(circuits)))
|
||||
plt.xticks(xvalues, circuits)
|
||||
|
||||
base_condition = ((data["precision"] == precision) & (data["nqubits"] == nqubits))
|
||||
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=numba")
|
||||
heights = np.array([float(data[condition & (data["circuit"] == c)][quantity]) for c in circuits])
|
||||
plt.bar(xvalues + widths[0], heights, color=oranges[0], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[0], edgecolor='w')
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=numba,max_qubits=2")
|
||||
heights = np.array([float(data[condition & (data["circuit"] == c)][quantity]) for c in circuits])
|
||||
plt.bar(xvalues + widths[1], heights, color=oranges[1], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[1], edgecolor='w')
|
||||
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cupy")
|
||||
heights = np.array([float(data[condition & (data["circuit"] == c)][quantity]) for c in circuits])
|
||||
plt.bar(xvalues + widths[2], heights, color=purples[0], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[0], edgecolor='w')
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cupy,max_qubits=2")
|
||||
heights = np.array([float(data[condition & (data["circuit"] == c)][quantity]) for c in circuits])
|
||||
plt.bar(xvalues + widths[3], heights, color=purples[1], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[1], edgecolor='w')
|
||||
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cuquantum")
|
||||
heights = np.array([float(data[condition & (data["circuit"] == c)][quantity]) for c in circuits])
|
||||
plt.bar(xvalues + widths[4], heights, color=greens[0], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[0], edgecolor='w')
|
||||
condition = base_condition & (data["library_options"] == "backend=qibojit,platform=cuquantum,max_qubits=2")
|
||||
heights = np.array([float(data[condition & (data["circuit"] == c)][quantity]) for c in circuits])
|
||||
plt.bar(xvalues + widths[5], heights, color=greens[1], align="center", width=width,
|
||||
log=logscale, alpha=1, hatch=hatches[1], edgecolor='w')
|
||||
|
||||
if quantity == "total_dry_time":
|
||||
plt.ylabel("Total dry run time (sec)")
|
||||
elif quantity == "total_simulation_time":
|
||||
plt.ylabel("Total simulation time (sec)")
|
||||
|
||||
if legend:
|
||||
legend_elements = [
|
||||
Patch(facecolor="w", edgecolor="k", hatch=hatches[0], label="No fusion"),
|
||||
Patch(facecolor="w", edgecolor="k", hatch=hatches[1], label="Two-qubit fusion"),
|
||||
Patch(color=oranges[1], label="numba"),
|
||||
Patch(color=purples[1], label="cupy"),
|
||||
Patch(color=greens[1], label="cuquantum")
|
||||
]
|
||||
plt.legend(handles=legend_elements, bbox_to_anchor=(1,1))
|
||||
if save:
|
||||
plt.savefig(f"qibojit_fusion_{precision}_{nqubits}qubits_{quantity}.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
73
qibojit-benchmarks/plots/libraries.py
Normal file
73
qibojit-benchmarks/plots/libraries.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
from matplotlib.patches import Patch
|
||||
matplotlib.rcParams['mathtext.fontset'] = 'cm'
|
||||
matplotlib.rcParams['font.family'] = 'STIXGeneral'
|
||||
|
||||
|
||||
class Library:
|
||||
"""Defines color, hatch and label for each library."""
|
||||
|
||||
def __init__(self, name, color, hatch, label, has_double=True, has_single=True, alpha=None):
|
||||
self.name = name
|
||||
self.color = color
|
||||
self.hatch = hatch
|
||||
self.label = label
|
||||
self.has_double = has_double
|
||||
self.has_single = has_single
|
||||
if alpha is None:
|
||||
self.alpha = 1.0 if "GPU" in self.name else 0.5
|
||||
else:
|
||||
self.alpha = alpha
|
||||
|
||||
def has(self, precision):
|
||||
return getattr(self, f"has_{precision}")
|
||||
|
||||
|
||||
def plot_libraries(libraries, cpu_data, gpu_data, quantity, nqubits,
|
||||
precision="double", width=0.07, fontsize=45,
|
||||
legend=False, logscale=True, fusion=False, save=False):
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
# Process data
|
||||
gpu_data = gpu_data.copy()
|
||||
gpu_data["library"] += " GPU"
|
||||
data = pd.concat([cpu_data, gpu_data])
|
||||
|
||||
circuits = ["qft", "variational", "supremacy", "qv", "bv"]
|
||||
# create widths list for bar positioning
|
||||
n = len([library for library in libraries if library.has(precision)])
|
||||
ws = np.arange(n)
|
||||
ws = iter((ws - ws[n // 2]) * width)
|
||||
|
||||
# Plot the results
|
||||
xvalues = np.array(range(len(circuits)))
|
||||
plt.figure(figsize=(25, 9))
|
||||
base_condition = (data["nqubits"] == nqubits) & (data["precision"] == precision)
|
||||
for library in libraries:
|
||||
if library.has(precision):
|
||||
condition = base_condition & (data["library"] == library.name)
|
||||
height = np.array([float(data[condition & (data["circuit"] == c)][quantity]) for c in circuits])
|
||||
plt.bar(xvalues + next(ws), height, color=library.color, align="center",
|
||||
width=width, alpha=library.alpha, label=library.label,
|
||||
log=logscale, edgecolor='w', hatch=library.hatch)
|
||||
|
||||
if fusion:
|
||||
plt.title(f"{nqubits} qubits - Two-qubit fusion - {precision} precision")
|
||||
else:
|
||||
plt.title(f"{nqubits} qubits - {precision} precision")
|
||||
if quantity == "total_dry_time":
|
||||
plt.ylabel("Total dry time (sec)")
|
||||
elif quantity == "total_simulation_time":
|
||||
plt.ylabel("Total simulation time (sec)")
|
||||
|
||||
plt.xticks(xvalues, circuits)
|
||||
if legend:
|
||||
plt.legend(fontsize="small", bbox_to_anchor=(1,1))
|
||||
if save:
|
||||
savename = "libraries_fusion" if fusion else "libraries"
|
||||
plt.savefig(f"{savename}_{precision}_{nqubits}qubits_{quantity}.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
337
qibojit-benchmarks/plots/paper-plots.ipynb
Normal file
337
qibojit-benchmarks/plots/paper-plots.ipynb
Normal file
@@ -0,0 +1,337 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "76badf4a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This notebook shows how to generate the benchmark plots used in the paper. \n",
|
||||
"\n",
|
||||
"To generate the plots using the benchmarks of the original paper, download the following gist:\n",
|
||||
"\n",
|
||||
"[stavros11/ffb88a5b914b60213515f0256c0e8aa4](https://gist.github.com/stavros11/ffb88a5b914b60213515f0256c0e8aa4)\n",
|
||||
"\n",
|
||||
"copy the contents in a folder named `/data` in the directory of this notebook and execute all cells.\n",
|
||||
"\n",
|
||||
"The logs provided in the gist serve as a template.\n",
|
||||
"The same plotting functionality should work with logs generated from different machines.\n",
|
||||
"To generate new logs from scratch one can use the bash scripts provided in the `scripts/` directory."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "97f43ed0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from utils import load_data, load_evolution_data\n",
|
||||
"\n",
|
||||
"save = False # if ``True`` plots will be saved in the current directory as pdfs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f4d6d97f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Figure 3\n",
|
||||
"\n",
|
||||
"Bar plot with import breakdown and dry run vs simulation comparison for qibojit."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ba717379",
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from barplots import plot_breakdown_nqubits\n",
|
||||
"\n",
|
||||
"data = load_data(f\"./data/qibojit_breakdown.dat\")\n",
|
||||
"plot_breakdown_nqubits(data, \"supremacy\", save=save)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6e2eeb0b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Figure 4\n",
|
||||
"\n",
|
||||
"Scaling plots of execution time as a function of the number of qubits for all qibo backends."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1ba0a87d",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from scaling import plot_scaling\n",
|
||||
"\n",
|
||||
"cpu_data = load_data(\"./data/qibo_scaling_cpu.dat\")\n",
|
||||
"gpu_data = load_data(\"./data/qibo_scaling_gpu.dat\")\n",
|
||||
"\n",
|
||||
"plot_scaling(cpu_data, gpu_data, \"qft\", \"total_dry_time\", legend=False, save=save)\n",
|
||||
"plot_scaling(cpu_data, gpu_data, \"qft\", \"total_simulation_time\", save=save)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8cb5555a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Figure 5\n",
|
||||
"\n",
|
||||
"qibojit backend performance on different CPU and GPU devices."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "895cb582",
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import seaborn as sns # used for color palettes\n",
|
||||
"from devices import Line, plot_devices\n",
|
||||
"\n",
|
||||
"oranges = sns.color_palette(\"Oranges\", 2)\n",
|
||||
"greens = sns.color_palette(\"Greens\", 2)\n",
|
||||
"blues = sns.color_palette(\"Blues\", 3)\n",
|
||||
"\n",
|
||||
"lines = [\n",
|
||||
" Line(\"NVIDIA RTX A6000 (cupy)\", load_data(\"./data/qibo_scaling_gpu.dat\", qibojit_only=True), blues[2], \"o\"),\n",
|
||||
" Line(\"NVIDIA DGX V100 (cupy)\", load_data(\"./data/dgx_qibojit.dat\", qibojit_only=True), blues[1], \"^\"),\n",
|
||||
" Line(\"NVIDIA GTX 1650 (cupy)\", load_data(\"./data/gtx1650_qibojit.dat\", qibojit_only=True), blues[0], \"d\"),\n",
|
||||
" Line(\"AMD Radeon VII (cupy)\", load_data(\"./data/rocm_qibojit.dat\"), greens[1], \"v\"),\n",
|
||||
" Line(\"NVIDIA RTX A6000 (cupy-multigpu)\", load_data(\"./data/rtx_multigpu.dat\", qibojit_only=True), blues[2], \"o\", linestyle=\"--\"),\n",
|
||||
" Line(\"AMD EPYC 7742, 128 th., 2TB (numba)\", load_data(\"./data/qibo_scaling_cpu.dat\", qibojit_only=True), oranges[1], \"\"),\n",
|
||||
" Line(\"ATOS QLM, 384 th., 6TB (numba)\", load_data(\"./data/qlm_qibojit.dat\", qibojit_only=True), oranges[0], \"\"),\n",
|
||||
"]\n",
|
||||
"# filter qibojit-cupy only data if the log file contains more (eg. qibojit-cuquantum)\n",
|
||||
"is_cupy = lines[0].data[\"library_options\"].apply(lambda x: \"cupy\" in x)\n",
|
||||
"lines[0].data = lines[0].data[is_cupy == True]\n",
|
||||
"\n",
|
||||
"plot_devices(lines, \"qft\", \"total_dry_time\", save=save)\n",
|
||||
"plot_devices(lines, \"qft\", \"total_simulation_time\", legendfont=26, save=save)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5cd4c946",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Figure 6\n",
|
||||
"\n",
|
||||
"Bar plot with different multigpu configurations and qibojit vs qibotf comparison."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "15099a54",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from barplots import plot_multigpu\n",
|
||||
"\n",
|
||||
"data = load_data(\"./data/dgx_multigpu.dat\")\n",
|
||||
"plot_multigpu(data, 32, \"total_dry_time\", save=save)\n",
|
||||
"plot_multigpu(data, 32, \"total_simulation_time\", legend=True, save=save)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "64538a61",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Figure 7\n",
|
||||
"\n",
|
||||
"Bar plot with comparisons between different simulation libraries on various circuits."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "81035c0e",
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import seaborn as sns\n",
|
||||
"from libraries import Library, plot_libraries\n",
|
||||
"\n",
|
||||
"cpu_data = load_data(\"./data/libraries_cpu.dat\")\n",
|
||||
"gpu_data = load_data(\"./data/libraries_gpu.dat\")\n",
|
||||
"\n",
|
||||
"palette = sns.color_palette(\"bright\", 7)\n",
|
||||
"libraries = [\n",
|
||||
" Library(\"qibo\", palette[0], \"/\", \"Qibo\"),\n",
|
||||
" Library(\"qibo GPU\", palette[0], \"/\", \"Qibo GPU\"),\n",
|
||||
" Library(\"qiskit\", palette[1], \"-\", \"Qiskit\"),\n",
|
||||
" Library(\"qiskit-gpu GPU\", palette[1], \"-\", \"Qiskit GPU\"),\n",
|
||||
" Library(\"hybridq\", palette[2], \"x\", \"HybridQ\"),\n",
|
||||
" Library(\"hybridq-gpu GPU\", palette[2], \"x\", \"HybridQ GPU\"),\n",
|
||||
" Library(\"qulacs\", palette[4], \"\\\\\", \"Qulacs\", has_single=False),\n",
|
||||
" Library(\"qulacs-gpu GPU\", palette[4], \"\\\\\", \"Qulacs GPU\", has_single=False),\n",
|
||||
" Library(\"projectq\", palette[3], \"o\", \"ProjectQ\", has_single=False),\n",
|
||||
" Library(\"qcgpu GPU\", palette[3], \"o\", \"QCGPU\", has_double=False)\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"plot_libraries(libraries, cpu_data, gpu_data, \"total_dry_time\", 20, precision=\"single\", legend=False, save=save)\n",
|
||||
"plot_libraries(libraries, cpu_data, gpu_data, \"total_dry_time\", 30, precision=\"single\", legend=True, save=save)\n",
|
||||
"\n",
|
||||
"plot_libraries(libraries, cpu_data, gpu_data, \"total_dry_time\", 20, precision=\"double\", legend=False, save=save)\n",
|
||||
"plot_libraries(libraries, cpu_data, gpu_data, \"total_dry_time\", 30, precision=\"double\", legend=True, save=save)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "20eb1827",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Figure 8\n",
|
||||
"\n",
|
||||
"Bar plot comparing fusion vs no fusion for all qibojit platforms and circuits"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e0a127b8",
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from fusion import plot_fusion_circuits\n",
|
||||
"\n",
|
||||
"data = load_data(f\"./data/qibojit_fusion.dat\")\n",
|
||||
"plot_fusion_circuits(data, 30, \"total_dry_time\", fontsize=38, legend=True, save=True)\n",
|
||||
"plot_fusion_circuits(data, 30, \"total_simulation_time\", fontsize=38, legend=True, save=save)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "909fbaeb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Figure 9\n",
|
||||
"\n",
|
||||
"Bar plot comparing two-qubit fusion for different libraries (qibo, qiskit, qsim)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "60e32824",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import seaborn as sns\n",
|
||||
"from libraries import Library, plot_libraries\n",
|
||||
"\n",
|
||||
"cpu_data = load_data(\"./data/libraries_fusion_cpu.dat\")\n",
|
||||
"gpu_data = load_data(\"./data/libraries_fusion_gpu.dat\")\n",
|
||||
"\n",
|
||||
"palette = sns.color_palette(\"bright\", 7)\n",
|
||||
"libraries = [\n",
|
||||
" Library(\"qibo\", palette[0], \"/\", \"Qibo (numba)\", alpha=0.3),\n",
|
||||
" Library(\"qibo GPU\", palette[0], \"/\", \"Qibo (cupy) GPU\", alpha=1.0),\n",
|
||||
" Library(\"qibo-cuquantum GPU\", palette[0], \"/\", \"Qibo (cuquantum) GPU\", alpha=0.6),\n",
|
||||
" Library(\"qiskit\", palette[1], \"-\", \"Qiskit\", alpha=0.3),\n",
|
||||
" Library(\"qiskit-gpu GPU\", palette[1], \"-\", \"Qiskit GPU\", alpha=1.0),\n",
|
||||
" Library(\"qsim\", palette[2], \"\\\\\", \"qsim\", has_double=False, alpha=0.3),\n",
|
||||
" Library(\"qsim-gpu GPU\", palette[2], \"\\\\\", \"qsim GPU\", has_double=False, alpha=1.0),\n",
|
||||
" Library(\"qsim-cuquantum GPU\", palette[2], \"\\\\\", \"qsim (cuquantum) GPU\", has_double=False, alpha=0.6),\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"plot_libraries(libraries, cpu_data, gpu_data, \"total_dry_time\", 30, precision=\"single\", \n",
|
||||
" legend=True, fontsize=45, logscale=True, fusion=True, save=save)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a5392b2d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Figure 10\n",
|
||||
"\n",
|
||||
"Scaling plot vs time dt for adiabatic evolution of TFIM Hamiltonian using the dense form."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6d7c77f0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from evolution import plot_dense\n",
|
||||
"\n",
|
||||
"data = load_evolution_data(\"data/evolution.dat\")\n",
|
||||
"plot_dense(data, \"total_dry_time\", 10, save=save)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0b8e88e7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Figure 11\n",
|
||||
"\n",
|
||||
"Scaling plot vs time dt for adiabatic evolution of TFIM Hamiltonian using the Trotter decomposition."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "67f4884c",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from evolution import plot_trotter\n",
|
||||
"\n",
|
||||
"data = load_evolution_data(\"data/evolution.dat\")\n",
|
||||
"plot_trotter(data, \"total_dry_time\", 10, yticks=[1, 10], legend=True, save=save)\n",
|
||||
"plot_trotter(data, \"total_dry_time\", 20, yticks=[1, 10, 100], legend=False, save=save)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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.8.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
56
qibojit-benchmarks/plots/scaling.py
Normal file
56
qibojit-benchmarks/plots/scaling.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""Generates qubit scaling plots for different qibo backends."""
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
from matplotlib.patches import Patch
|
||||
matplotlib.rcParams['mathtext.fontset'] = 'cm'
|
||||
matplotlib.rcParams['font.family'] = 'STIXGeneral'
|
||||
|
||||
|
||||
def plot_scaling(cpu_data, gpu_data, circuit, quantity, precision="double", fontsize=30, legend=True, save=False):
|
||||
matplotlib.rcParams["font.size"] = fontsize
|
||||
# Prepare GPU data
|
||||
condition = (gpu_data["circuit"] == circuit) & (gpu_data["precision"] == precision)
|
||||
backends = ["tensorflow", "qibotf"]
|
||||
data = {f"{k} GPU": gpu_data[(gpu_data["library_options"] == f"backend={k}") & condition] for k in backends}
|
||||
data["qibojit cupy GPU"] = gpu_data[(gpu_data["library_options"] == "backend=qibojit,platform=cupy") & condition]
|
||||
data["qibojit cuquantum GPU"] = gpu_data[(gpu_data["library_options"] == "backend=qibojit,platform=cuquantum") & condition]
|
||||
|
||||
# Prepare CPU data
|
||||
backends = ["numpy", "tensorflow", "qibotf", "qibojit"]
|
||||
condition = (cpu_data["circuit"] == circuit) & (cpu_data["precision"] == precision)
|
||||
data.update({k: cpu_data[(cpu_data["library_options"] == f"backend={k}") & condition] for k in backends})
|
||||
|
||||
# Plot data
|
||||
cpu_cp = sns.color_palette("Oranges", 4)
|
||||
gpu_cp = sns.color_palette("Purples", 4)
|
||||
plt.figure(figsize=(16, 9))
|
||||
plt.semilogy(data["numpy"]["nqubits"], data["numpy"][quantity], marker="s", markersize=10,
|
||||
color=cpu_cp[0], linewidth=3.0, label="numpy")
|
||||
plt.semilogy(data["tensorflow"]["nqubits"], data["tensorflow"][quantity], marker="o", markersize=10,
|
||||
color=cpu_cp[1], linewidth=3.0, label="tensorflow cpu")
|
||||
plt.semilogy(data["qibotf"]["nqubits"], data["qibotf"][quantity], marker="D", markersize=10,
|
||||
color=cpu_cp[2], linewidth=3.0, label="qibotf cpu")
|
||||
plt.semilogy(data["qibojit"]["nqubits"], data["qibojit"][quantity],
|
||||
color=cpu_cp[3], linewidth=3.0, label="qibojit (numba) cpu", marker="^", markersize=10)
|
||||
plt.semilogy(data["tensorflow GPU"]["nqubits"], data["tensorflow GPU"][quantity], marker="o", markersize=10,
|
||||
color=gpu_cp[1], linewidth=3.0, label="tensorflow gpu")
|
||||
plt.semilogy(data["qibotf GPU"]["nqubits"], data["qibotf GPU"][quantity], marker="D", markersize=10,
|
||||
color=gpu_cp[2], linewidth=3.0, label="qibotf gpu")
|
||||
plt.semilogy(data["qibojit cupy GPU"]["nqubits"], data["qibojit cupy GPU"][quantity],
|
||||
color=gpu_cp[3], linewidth=3.0, label="qibojit (cupy) gpu", marker="^", markersize=10)
|
||||
plt.semilogy(data["qibojit cuquantum GPU"]["nqubits"], data["qibojit cuquantum GPU"][quantity],
|
||||
color=gpu_cp[3], linewidth=3.0, linestyle="--", label="qibojit (cuquantum) gpu", marker="v", markersize=10)
|
||||
|
||||
plt.title(f"{circuit}, {precision} precision")
|
||||
plt.xlabel("Number of qubits")
|
||||
if quantity == "total_dry_time":
|
||||
plt.ylabel("Total dry run time (sec)")
|
||||
elif quantity == "total_simulation_time":
|
||||
plt.ylabel("Total simulation time (sec)")
|
||||
if legend:
|
||||
plt.legend(fontsize="small")
|
||||
if save:
|
||||
plt.savefig(f"qibo_scaling_{circuit}_{quantity}_{precision}.pdf", bbox_inches="tight")
|
||||
else:
|
||||
plt.show()
|
||||
36
qibojit-benchmarks/plots/utils.py
Normal file
36
qibojit-benchmarks/plots/utils.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import json
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def load_data(filename, qibojit_only=False):
|
||||
with open(filename, "r") as file:
|
||||
data = pd.DataFrame(json.load(file))
|
||||
|
||||
# filter data for qibojit
|
||||
if qibojit_only:
|
||||
is_qibojit = data["library_options"].apply(lambda x: "qibojit" in x)
|
||||
data = data[is_qibojit]
|
||||
|
||||
data["total_dry_time"] = data["dry_run_time"] + data["creation_time"] + data["import_time"]
|
||||
data["total_simulation_time"] = data["simulation_times_mean"] + data["creation_time"] + data["import_time"]
|
||||
return data
|
||||
|
||||
|
||||
def load_data_multigpu(filename, qibojit_only=False):
|
||||
data = load_data(filename, qibojit_only)
|
||||
data["backend"] = data["library_options"].apply(lambda x: x.split(",")[0].split("=")[-1])
|
||||
data["accelerators"] = data["library_options"].apply(lambda x: x.split(",")[1].split("=")[-1])
|
||||
data["nqubits (accelerators)"] = data.apply(lambda x: f"{x.nqubits} ({x.accelerators})", axis=1)
|
||||
return data
|
||||
|
||||
|
||||
def load_evolution_data(filename):
|
||||
with open(filename, "r") as file:
|
||||
data = pd.DataFrame(json.load(file))
|
||||
|
||||
data["creation_time"] = data["hamiltonian_creation_time"] + data["evolution_creation_time"]
|
||||
data["total_dry_time"] = data["dry_run_time"] + data["creation_time"] + data["import_time"]
|
||||
#if "simulation_times_mean" not in data.columns:
|
||||
# data["simulation_times_mean"] = data["simulation_times"].apply(lambda x: np.mean(x))
|
||||
data["total_simulation_time"] = data["simulation_times_mean"] + data["creation_time"] + data["import_time"]
|
||||
return data
|
||||
53
qibojit-benchmarks/scripts/README.md
Normal file
53
qibojit-benchmarks/scripts/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Example scripts
|
||||
|
||||
This folder contains example bash scripts that execute the `compare.py` benchmark for different circuit configurations and different libraries.
|
||||
The available circuits are described in the main ``README.md`` of this repository
|
||||
(some of them are presented in Table 1 of the [HyQuas paper](https://dl.acm.org/doi/pdf/10.1145/3447818.3460357)).
|
||||
|
||||
The provided scripts are the following:
|
||||
|
||||
### `qibo.sh`
|
||||
|
||||
Execute a specific circuit with different sizes and different Qibo backends.
|
||||
By default, it executes the circuit with all backends (NumPy, TensorFlow, Qibotf and Qibojit) and with circuit sizes from 3 to 31 (28 for NumPy).
|
||||
If different platforms are available, it will use the default one.
|
||||
It is straightforward to edit the bash script to fine tune the variation ranges and the number of repetitions for each circuit size.
|
||||
A starter code to plot the results is available in ``plots/qibo.ipynb``.
|
||||
Options:
|
||||
- ``circuit``: circuit to execute (default: ``qft``)
|
||||
- ``precision``: ``single`` or ``double`` (default: ``double``)
|
||||
|
||||
### ``qibojit.sh``
|
||||
|
||||
Similar to ``qibo.sh``, but uses only qibojit. Useful to compare the performance across different platforms and different GPUs.
|
||||
A starter code to plot the results is available in ``plots/qibojit.ipynb``.
|
||||
Options:
|
||||
- ``filename``: where to store the logs (default: ``qibojit.dat``)
|
||||
- ``circuit``: circuit to execute (default: ``qft``)
|
||||
- ``precision``: ``single`` or ``double`` (default: ``double``)
|
||||
- ``nreps``: number of repetitions for each circuit size (default: ``10``)
|
||||
- ``platform``: platform to use for simulation (default: ``cupy``)
|
||||
|
||||
### ``qibo_circuits.sh``
|
||||
|
||||
Execute a set of circuits for different circuit sizes, using a specific backend.
|
||||
Useful to plot the breakdown of execution times (starter code in ``plots/qibo.ipynb``).
|
||||
Options:
|
||||
- ``backend``: backend to use for simulation (default: ``qibojit``)
|
||||
- ``precision``: ``single`` or ``double`` (default: ``double``)
|
||||
- ``nreps``: number of repetitions for each circuit and size (default: ``10``)
|
||||
|
||||
### ``library_single.sh``
|
||||
|
||||
Execute a set of circuits for different circuit sizes, using a variety of different libraries.
|
||||
This script focuses on libraries that support single precision simulation.
|
||||
It is straightforward to edit the bash script to change the circuit sizes and the libraries.
|
||||
A starter code to plot the results is available in ``plots/libraries.ipynb``.
|
||||
Options:
|
||||
- ``nreps``: number of repetitions for each circuit size (default: ``10``)
|
||||
|
||||
### ``library_double.sh``
|
||||
|
||||
Same as ``library_single.sh``, but with libraries that support double precision simulation.
|
||||
Options:
|
||||
- ``nreps``: number of repetitions for each circuit size (default: ``10``)
|
||||
1
qibojit-benchmarks/scripts/cu_tensornet_expectation.json
Normal file
1
qibojit-benchmarks/scripts/cu_tensornet_expectation.json
Normal file
@@ -0,0 +1 @@
|
||||
{"MPI_enabled": false, "MPS_enabled": false, "NCCL_enabled": false, "expectation_enabled": true}
|
||||
@@ -0,0 +1 @@
|
||||
{"MPI_enabled": true, "MPS_enabled": false, "NCCL_enabled": false, "expectation_enabled": true}
|
||||
@@ -0,0 +1 @@
|
||||
{"MPI_enabled": false, "MPS_enabled": false, "NCCL_enabled": true, "expectation_enabled": true}
|
||||
33
qibojit-benchmarks/scripts/evolution_dense.sh
Executable file
33
qibojit-benchmarks/scripts/evolution_dense.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#! /usr/bin/bash
|
||||
# Generates data for the evolution scaling with dt using the dense form of the Hamiltonian
|
||||
|
||||
# Command-line parameters
|
||||
: "${nqubits:=10}"
|
||||
: "${precision:=double}"
|
||||
: "${filename:=evolution.dat}"
|
||||
: "${nreps:=5}"
|
||||
|
||||
|
||||
# GPU backends
|
||||
for dt in 0.1 0.095 0.09 0.085 0.08 0.075 0.07 0.065 0.06 0.055 0.05 0.045 \
|
||||
0.04 0.035 0.03 0.025 0.02 0.015 0.01 0.005
|
||||
do
|
||||
for backend in qibojit tensorflow
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES=0 python evolution.py --nqubits $nqubits --dt $dt --filename $filename --nreps $nreps \
|
||||
--backend $backend --precision $precision --dense
|
||||
echo
|
||||
done
|
||||
done
|
||||
|
||||
# CPU backends
|
||||
for dt in 0.1 0.095 0.09 0.085 0.08 0.075 0.07 0.065 0.06 0.055 0.05 0.045 \
|
||||
0.04 0.035 0.03 0.025 0.02 0.015 0.01 0.005
|
||||
do
|
||||
for backend in numpy tensorflow
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES="" python evolution.py --nqubits $nqubits --dt $dt --filename $filename --nreps $nreps \
|
||||
--backend $backend --precision $precision --dense
|
||||
echo
|
||||
done
|
||||
done
|
||||
45
qibojit-benchmarks/scripts/evolution_trotter.sh
Executable file
45
qibojit-benchmarks/scripts/evolution_trotter.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#! /usr/bin/bash
|
||||
# Generates data for the evolution scaling with dt using the Trotter decomposition
|
||||
|
||||
# Command-line parameters
|
||||
: "${nqubits:=10}"
|
||||
: "${precision:=double}"
|
||||
: "${filename:=evolution.dat}"
|
||||
: "${nreps:=10}"
|
||||
|
||||
|
||||
# GPU qibojit backend
|
||||
for dt in 0.1 0.095 0.09 0.085 0.08 0.075 0.07 0.065 0.06 0.055 0.05 0.045 \
|
||||
0.04 0.035 0.03 0.025 0.02 0.015 0.01 0.005
|
||||
do
|
||||
for platform in cupy cuquantum
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES="0" python evolution.py --nqubits $nqubits --dt $dt --filename $filename --nreps $nreps \
|
||||
--backend qibojit --platform $platform --precision $precision
|
||||
echo
|
||||
done
|
||||
done
|
||||
|
||||
# GPU tensorflow and qibotf backends
|
||||
for dt in 0.1 0.095 0.09 0.085 0.08 0.075 0.07 0.065 0.06 0.055 0.05 0.045 \
|
||||
0.04 0.035 0.03 0.025 0.02 0.015 0.01 0.005
|
||||
do
|
||||
for backend in qibotf tensorflow
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES="0" python evolution.py --nqubits $nqubits --dt $dt --filename $filename --nreps $nreps \
|
||||
--backend $backend --precision $precision
|
||||
echo
|
||||
done
|
||||
done
|
||||
|
||||
# CPU all backends
|
||||
for dt in 0.1 0.095 0.09 0.085 0.08 0.075 0.07 0.065 0.06 0.055 0.05 0.045 \
|
||||
0.04 0.035 0.03 0.025 0.02 0.015 0.01 0.005
|
||||
do
|
||||
for backend in qibojit qibotf tensorflow numpy
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES="" python evolution.py --nqubits $nqubits --dt $dt --filename $filename --nreps $nreps \
|
||||
--backend $backend --precision $precision
|
||||
echo
|
||||
done
|
||||
done
|
||||
34
qibojit-benchmarks/scripts/fusion.sh
Executable file
34
qibojit-benchmarks/scripts/fusion.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#! /usr/bin/bash
|
||||
# Generate logs for fusion comparison on different circuits for all qibojit platforms
|
||||
|
||||
# Command-line parameters
|
||||
: "${filename:=qibojit_fusion.dat}"
|
||||
: "${precision:=double}"
|
||||
: "${nqubits:=30}"
|
||||
: "${nreps_cpu:=3}"
|
||||
: "${nreps_gpu:=5}"
|
||||
|
||||
|
||||
for circuit in qft variational supremacy bv qv
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibojit,platform=cupy,max_qubits=2 --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibojit,platform=cupy --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibojit,platform=cuquantum,max_qubits=2 --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibojit,platform=cuquantum --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibojit,platform=numba,max_qubits=2 --nreps $nreps_cpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibojit,platform=numba --nreps $nreps_cpu --precision $precision
|
||||
echo
|
||||
done
|
||||
24
qibojit-benchmarks/scripts/libraries_double.sh
Executable file
24
qibojit-benchmarks/scripts/libraries_double.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#! /usr/bin/bash
|
||||
# Generates data for the bar plot comparing different libraries in double precision
|
||||
|
||||
: "${nreps:=10}"
|
||||
: "${nqubits:=10}"
|
||||
: "${filename_cpu:=libraries_cpu.dat}"
|
||||
: "${filename_gpu:=libraries_gpu.dat}"
|
||||
|
||||
|
||||
for circuit in qft variational bv supremacy qv
|
||||
do
|
||||
for library in qibo qiskit qulacs projectq hybridq
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu \
|
||||
--library $library --nreps $nreps --precision double
|
||||
echo
|
||||
done
|
||||
for library in qibo qiskit-gpu qulacs-gpu hybridq-gpu
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu \
|
||||
--library $library --nreps $nreps --precision double
|
||||
echo
|
||||
done
|
||||
done
|
||||
42
qibojit-benchmarks/scripts/libraries_fusion.sh
Executable file
42
qibojit-benchmarks/scripts/libraries_fusion.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#! /usr/bin/bash
|
||||
# Generate logs for library comparison on different circuits with gate fusion enabled
|
||||
|
||||
# Command-line parameters
|
||||
: "${filename_cpu:=libraries_fusion_cpu.dat}"
|
||||
: "${filename_gpu:=libraries_fusion_gpu.dat}"
|
||||
: "${precision:=single}"
|
||||
: "${nqubits:=30}"
|
||||
: "${nreps_cpu:=3}"
|
||||
: "${nreps_gpu:=5}"
|
||||
|
||||
|
||||
for circuit in qft variational supremacy bv qv
|
||||
do
|
||||
# GPU benchmarks
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu --library qibo \
|
||||
--library-options backend=qibojit,platform=cupy,max_qubits=2 --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu --library qibo \
|
||||
--library-options backend=qibojit,platform=cuquantum,max_qubits=2 --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu --library qsim-gpu \
|
||||
--library-options max_qubits=2 --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu --library qsim-cuquantum \
|
||||
--library-options max_qubits=2 --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu --library qiskit-gpu \
|
||||
--library-options max_qubits=2 --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
|
||||
# CPU benchmarks
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu --library qibo \
|
||||
--library-options backend=qibojit,platform=numba,max_qubits=2 --nreps $nreps_cpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu --library qsim \
|
||||
--library-options max_qubits=2 --nreps $nreps_cpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu --library qiskit \
|
||||
--library-options max_qubits=2 --nreps $nreps_cpu --precision $precision
|
||||
echo
|
||||
done
|
||||
24
qibojit-benchmarks/scripts/libraries_single.sh
Executable file
24
qibojit-benchmarks/scripts/libraries_single.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#! /usr/bin/bash
|
||||
# Generates data for the bar plot comparing different libraries in single precision
|
||||
|
||||
: "${nreps:=10}"
|
||||
: "${nqubits:=20}"
|
||||
: "${filename_cpu:=libraries_cpu.dat}"
|
||||
: "${filename_gpu:=libraries_gpu.dat}"
|
||||
|
||||
|
||||
for circuit in qft variational bv supremacy qv
|
||||
do
|
||||
for library in qibo qiskit qsim hybridq
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu \
|
||||
--library $library --nreps $nreps --precision single
|
||||
echo
|
||||
done
|
||||
for library in qibo qiskit-gpu qsim-gpu qsim-cuquantum qcgpu hybridq-gpu
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu \
|
||||
--library $library --nreps $nreps --precision single
|
||||
echo
|
||||
done
|
||||
done
|
||||
34
qibojit-benchmarks/scripts/multigpu.sh
Executable file
34
qibojit-benchmarks/scripts/multigpu.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
# Generates the multigpu bar plot
|
||||
# Assumes a machine with four GPUs
|
||||
|
||||
: "${filename:=multigpu.dat}"
|
||||
: "${precision:=double}"
|
||||
: "${nreps:=1}"
|
||||
|
||||
export CUDA_VISIBLE_DEVICES=0,1,2,3
|
||||
for circuit in qft variational supremacy qv bv
|
||||
do
|
||||
for backend in qibojit qibotf
|
||||
do
|
||||
python compare.py --circuit $circuit --nqubits 32 --filename $filename \
|
||||
--library-options backend=$backend,accelerators=1/GPU:0+1/GPU:1+1/GPU:2+1/GPU:3 \
|
||||
--nreps $nreps --precision $precision
|
||||
echo
|
||||
done
|
||||
|
||||
for backend in qibojit qibotf
|
||||
do
|
||||
python compare.py --circuit $circuit --nqubits 32 --filename $filename \
|
||||
--library-options backend=$backend,accelerators=2/GPU:2+2/GPU:3 \
|
||||
--nreps $nreps --precision $precision
|
||||
echo
|
||||
done
|
||||
|
||||
for backend in qibojit qibotf
|
||||
do
|
||||
python compare.py --circuit $circuit --nqubits 32 --filename $filename \
|
||||
--library-options backend=$backend,accelerators=4/GPU:3 \
|
||||
--nreps $nreps --precision $precision
|
||||
echo
|
||||
done
|
||||
done
|
||||
85
qibojit-benchmarks/scripts/qibo.sh
Executable file
85
qibojit-benchmarks/scripts/qibo.sh
Executable file
@@ -0,0 +1,85 @@
|
||||
#! /usr/bin/bash
|
||||
# Generates data for the scaling plot (vs nqubits) using all qibo backends
|
||||
|
||||
# Command-line parameters
|
||||
: "${circuit:=qft}"
|
||||
: "${precision:=double}"
|
||||
: "${nreps_a:=20}" # for nqubits < 25
|
||||
: "${nreps_b:=3}" # for nqubits >= 25
|
||||
: "${filename_cpu:=qibo_scaling_cpu.dat}"
|
||||
: "${filename_gpu:=qibo_scaling_gpu.dat}"
|
||||
|
||||
|
||||
# Qibojit backend
|
||||
for nqubits in {3..24}
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu \
|
||||
--library-options backend=qibojit,platform=cupy \
|
||||
--nreps $nreps_a --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu \
|
||||
--library-options backend=qibojit,platform=cuquantum \
|
||||
--nreps $nreps_a --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu \
|
||||
--library-options backend=qibojit,platform=numba \
|
||||
--nreps $nreps_a --precision $precision
|
||||
echo
|
||||
done
|
||||
for nqubits in {25..31}
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu \
|
||||
--library-options backend=qibojit,platform=cupy \
|
||||
--nreps $nreps_b --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu \
|
||||
--library-options backend=qibojit,platform=cuquantum \
|
||||
--nreps $nreps_b --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu \
|
||||
--library-options backend=qibojit,platform=numba \
|
||||
--nreps $nreps_b --precision $precision
|
||||
echo
|
||||
done
|
||||
|
||||
|
||||
# TensorFlow and QiboTF backends
|
||||
for nqubits in {3..24}
|
||||
do
|
||||
for backend in tensorflow qibotf
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu \
|
||||
--library-options backend=$backend --nreps $nreps_a --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu \
|
||||
--library-options backend=$backend --nreps $nreps_a --precision $precision
|
||||
echo
|
||||
done
|
||||
done
|
||||
for nqubits in {25..31}
|
||||
do
|
||||
for backend in tensorflow qibotf
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_gpu \
|
||||
--library-options backend=$backend --nreps $nreps_b --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu \
|
||||
--library-options backend=$backend --nreps $nreps_b --precision $precision
|
||||
echo
|
||||
done
|
||||
done
|
||||
|
||||
|
||||
# NumPy backend
|
||||
for nqubits in {3..24}
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu \
|
||||
--library-options backend=numpy --nreps $nreps_a --precision $precision
|
||||
echo
|
||||
done
|
||||
for nqubits in {25..28}
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename_cpu \
|
||||
--library-options backend=numpy --nreps $nreps_b --precision $precision
|
||||
echo
|
||||
done
|
||||
23
qibojit-benchmarks/scripts/qibojit.sh
Executable file
23
qibojit-benchmarks/scripts/qibojit.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#! /usr/bin/bash
|
||||
# Generates data for qibojit breakdown bar plot with dry run vs simulation
|
||||
|
||||
# Command-line parameters
|
||||
: "${filename:=qibojit_breakdown.dat}"
|
||||
: "${precision:=double}"
|
||||
: "${circuit:=supremacy}"
|
||||
: "${nreps_cpu:=5}"
|
||||
: "${nreps_gpu:=10}"
|
||||
|
||||
|
||||
for nqubits in 16 18 20 22 24 26 28
|
||||
do
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibojit,platform=cupy --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES=0 python compare.py --circuit $circuit --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibojit,platform=cuquantum --nreps $nreps_gpu --precision $precision
|
||||
echo
|
||||
CUDA_VISIBLE_DEVICES="" python compare.py --circuit $circuit --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibojit,platform=numba --nreps $nreps_cpu --precision $precision
|
||||
echo
|
||||
done
|
||||
36
qibojit-benchmarks/scripts/qibotn.sh
Executable file
36
qibojit-benchmarks/scripts/qibotn.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#! /usr/bin/bash
|
||||
# Script for 2-nodes-4-GPUs (2x4) configuration
|
||||
# Command-line parameters
|
||||
: "${circuit:=variational}"
|
||||
: "${precision:=double}"
|
||||
: "${nreps:=3}"
|
||||
: "${filename:=qibotn_expectation_double_2x4.dat}"
|
||||
|
||||
node_list=/nodelist_2x4 #file containing the IP address of the resources, omit this if it is running on single-node-multi-gpu
|
||||
|
||||
for (nqubits = 10; nqubits <= 8000; nqubits += 100)
|
||||
do
|
||||
#echo '{"MPI_enabled": true, "MPS_enabled": false, "NCCL_enabled": false, "expectation_enabled": true}' > cu_tensornet_mpi_expectation.json #can be pre-generated or can include it in script. Here is using pre-generated.
|
||||
mpirun -np 8 -hostfile $node_list python compare.py --circuit variational --circuit-options nlayers=3 --nqubits 4$nqubits --filename $filename \
|
||||
--library-options backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_mpi_expectation.json \
|
||||
--nreps $nreps --precision $precision
|
||||
echo
|
||||
#echo '{"MPI_enabled": false, "MPS_enabled": false, "NCCL_enabled": true, "expectation_enabled": true}' > cu_tensornet_nccl_expectation.json
|
||||
mpirun -np 8 -hostfile $node_list python compare.py --circuit variational --circuit-options nlayers=3 --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_nccl_expectation.json \
|
||||
--nreps $nreps --precision $precision
|
||||
echo
|
||||
#echo '{"MPI_enabled": false, "MPS_enabled": false, "NCCL_enabled": false, "expectation_enabled": true}' > cu_tensornet_expectation.json
|
||||
python compare.py --circuit variational --circuit-options nlayers=3 --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibotn,platform=cutensornet,computation_settings=cu_tensornet_expectation.json \
|
||||
--nreps $nreps --precision $precision
|
||||
echo
|
||||
python compare.py --circuit variational --circuit-options nlayers=3 --nqubits $nqubits --filename $filename \
|
||||
--library-options backend=qibojit,platform=numba,expectation="XXXZ" \
|
||||
--nreps $nreps --precision $precision
|
||||
echo
|
||||
done
|
||||
|
||||
: <<'END_COMMENT'
|
||||
|
||||
END_COMMENT
|
||||
56
qibojit-benchmarks/scripts/qibotn_local.sh
Executable file
56
qibojit-benchmarks/scripts/qibotn_local.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Lightweight local benchmark script (single node, small sweep)
|
||||
set -euo pipefail
|
||||
|
||||
: "${circuit:=variational}"
|
||||
: "${precision:=complex128}"
|
||||
: "${nreps:=1}"
|
||||
: "${filename:=qibotn_expectation_local.dat}"
|
||||
: "${np:=2}"
|
||||
: "${nlayers:=2}"
|
||||
: "${nqubits_list:=8 12 16}"
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd "${script_dir}/.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
# On Prism, Intel MPI may need shared-memory fabric for local runs.
|
||||
if [[ -z "${I_MPI_FABRICS:-}" ]]; then
|
||||
export I_MPI_FABRICS=shm
|
||||
fi
|
||||
|
||||
for nqubits in ${nqubits_list}; do
|
||||
echo "===== nqubits=${nqubits} ====="
|
||||
|
||||
# MPI-enabled cutensornet expectation
|
||||
mpirun -np "${np}" python compare.py \
|
||||
--circuit "${circuit}" \
|
||||
--circuit-options "nlayers=${nlayers}" \
|
||||
--nqubits "${nqubits}" \
|
||||
--filename "${filename}" \
|
||||
--library-options backend=qibotn,platform=cutensornet,computation_settings=scripts/cu_tensornet_mpi_expectation.json \
|
||||
--nreps "${nreps}" \
|
||||
--precision "${precision}"
|
||||
|
||||
# Single-process cutensornet expectation
|
||||
python compare.py \
|
||||
--circuit "${circuit}" \
|
||||
--circuit-options "nlayers=${nlayers}" \
|
||||
--nqubits "${nqubits}" \
|
||||
--filename "${filename}" \
|
||||
--library-options backend=qibotn,platform=cutensornet,computation_settings=scripts/cu_tensornet_expectation.json \
|
||||
--nreps "${nreps}" \
|
||||
--precision "${precision}"
|
||||
|
||||
# qibojit reference
|
||||
python compare.py \
|
||||
--circuit "${circuit}" \
|
||||
--circuit-options "nlayers=${nlayers}" \
|
||||
--nqubits "${nqubits}" \
|
||||
--filename "${filename}" \
|
||||
--library-options backend=qibojit,platform=numba,expectation=XXXZ \
|
||||
--nreps "${nreps}" \
|
||||
--precision "${precision}"
|
||||
|
||||
echo
|
||||
done
|
||||
214
qibojit-benchmarks/scripts/quimb/README.md
Normal file
214
qibojit-benchmarks/scripts/quimb/README.md
Normal file
@@ -0,0 +1,214 @@
|
||||
# Quimb Benchmark Suite
|
||||
|
||||
Local (single-process) tensor network simulation benchmarking using quimb backend.
|
||||
|
||||
## Files
|
||||
|
||||
- **quimb_local.sh** — Main benchmark sweep script (no MPI)
|
||||
- **expectation_mps.json** — Expectation mode with MPS (default for expectation)
|
||||
- **expectation_dense.json** — Expectation mode, dense tensor network
|
||||
- **dense_vector_mps.json** — State extraction with MPS (default for state)
|
||||
- **dense_vector_dense.json** — State extraction, dense tensor network
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### Expectation Modes (computing Hamiltonian expectation values)
|
||||
- **expectation_mps.json**: Uses Matrix Product State (MPS) tensor network ansatz; good for larger systems with limited bond dimension
|
||||
- **expectation_dense.json**: Uses dense tensor network; full contraction without MPS truncation
|
||||
|
||||
### State Modes (full state vector computation)
|
||||
- **dense_vector_mps.json**: Extracts full state using MPS contraction (faster, bounded memory)
|
||||
- **dense_vector_dense.json**: Extracts full state using dense tensor (maximum memory usage)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
cd /ssd_data/tankya2/code/ASC_2026_prism/ASC-2026/qibojit-benchmarks/scripts/quimb
|
||||
|
||||
# Run with defaults (all matching dense_vector*.json and expectation*.json configs)
|
||||
./quimb_local.sh
|
||||
|
||||
# Test only expectation mode, custom qubit sizes
|
||||
modes="expectation" nqubits_list="8 10 12" ./quimb_local.sh
|
||||
|
||||
# Test dense vector only with one explicit config
|
||||
state_cfg=dense_vector_dense.json modes="dense_vector" ./quimb_local.sh
|
||||
|
||||
# Restrict the sweep to a chosen subset of configs
|
||||
EXPECTATION_CONFIGS="expectation_mps.json expectation_dense.json" ./quimb_local.sh
|
||||
|
||||
# Test variational and BV circuits
|
||||
circuits="variational bv" nqubits_list="6 8 10" nreps=3 ./quimb_local.sh
|
||||
```
|
||||
|
||||
## Supported Circuits
|
||||
|
||||
### ✓ Fully Supported (Numerically Validated)
|
||||
|
||||
| Circuit | Alias | Parameters | Status | Notes |
|
||||
|---------|-------|------------|--------|-------|
|
||||
| **qft** | — | none | ✓ Works | Quantum Fourier Transform; aligned with qibojit |
|
||||
| **supremacy** | — | depth, seed | ✓ Works | Verified locally after environment repair; requires Cirq import path to be healthy |
|
||||
| **variational** | — | nlayers | ✓ Works | Parameterized circuit; aligned with qibojit |
|
||||
| **bv** | bernstein-vazirani | none | ✓ Works | Bernstein-Vazirani algorithm; aligned with qibojit |
|
||||
| **hs** | hidden-shift | none | ✓ Works | Hidden-shift problem; aligned with qibojit |
|
||||
| **qaoa** | — | nlayers | ✓ Works | QAOA with RZZ gate support; aligned with qibojit |
|
||||
|
||||
### ❌ Not Supported
|
||||
|
||||
| Circuit | Issue | Notes |
|
||||
|---------|-------|-------|
|
||||
| **qv** | Qiskit API compatibility | quantum-volume uses deprecated qiskit `.qasm()` method (v3.0+); not specific to quimb |
|
||||
|
||||
### Implementation Notes
|
||||
- **RZZ Gate:** Full support for QAOA and algorithms using two-qubit Ising rotations
|
||||
- **cu1 Gate:** Fully supported in expectation value mode (QFT compatible)
|
||||
- **Numerical Alignment:** All benchmarks automatically include qibojit (numba) reference with identical observable for validation
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `nqubits_list` | `6 8 10` | Space-separated qubit counts to benchmark |
|
||||
| `circuits` | `supremacy qft variational bv qaoa` | Space-separated circuit names |
|
||||
| `modes` | `dense_vector expectation` | Benchmark modes (dense_vector, expectation) |
|
||||
| `nlayers` | `2` | Layers for layered circuits (variational, qaoa) |
|
||||
| `nreps` | `1` | Repetitions per benchmark |
|
||||
| `filename` | `quimb_benchmark_local.dat` | Output log file |
|
||||
| `precision` | `complex128` | NumPy dtype (complex128 or complex64) |
|
||||
| `exp_cfg` | unset | Optional single expectation config; when unset, all `expectation*.json` files are swept |
|
||||
| `state_cfg` | unset | Optional single dense-vector config; when unset, all `dense_vector*.json` files are swept |
|
||||
| `EXPECTATION_CONFIGS` | unset | Optional space-separated subset of expectation configs to run |
|
||||
| `STATE_CONFIGS` | unset | Optional space-separated subset of dense-vector configs to run |
|
||||
|
||||
## Results
|
||||
|
||||
Results are logged to `/qibojit-benchmarks/benchmarks/results/` with filename `quimb_benchmark_local.dat` (configurable).
|
||||
|
||||
Each benchmark includes:
|
||||
- **import_time** — Backend initialization time
|
||||
- **creation_time** — Circuit creation time
|
||||
- **dry_run_time** — First execution time (warm-up)
|
||||
- **simulation_times** — Execution times for nreps runs
|
||||
- **expectation_result** — Observable expectation values (for expectation mode)
|
||||
|
||||
## Observable
|
||||
|
||||
By default, expectation uses:
|
||||
- **X on qubit 0**, identity on remaining qubits
|
||||
- Configurable via `pauli_pattern` in JSON config (e.g., `"XIIII"` for 5 qubits)
|
||||
|
||||
## Reference Validation
|
||||
|
||||
Each benchmark run automatically includes a **qibojit (numba) reference** with the same observable configuration, enabling direct numerical comparison for validation.
|
||||
|
||||
Example:
|
||||
```
|
||||
[expectation MPI] quimb + qft @ 6q: 0.2019...
|
||||
[qibojit ref] numba + qft @ 6q: 0.2019... ✓ aligned
|
||||
```
|
||||
|
||||
## MPI Support
|
||||
|
||||
For MPI-enabled benchmarking (multiple processes), see the parallel suite in `/scripts/quimb_mpi/`:
|
||||
|
||||
```bash
|
||||
cd /ssd_data/tankya2/code/ASC_2026_prism/ASC-2026/qibojit-benchmarks/scripts/quimb_mpi
|
||||
./quimb_mpi.sh # runs with mpirun -np 2
|
||||
```
|
||||
|
||||
## Tensor Network Options
|
||||
|
||||
Quimb supports both MPS (Matrix Product State) and dense tensor network contractions. By default the script sweeps every matching JSON file in this folder, or you can target individual configs:
|
||||
|
||||
```bash
|
||||
# Compare MPS vs dense at same nqubits
|
||||
exp_cfg=expectation_mps.json nqubits_list="12" ./quimb_local.sh
|
||||
exp_cfg=expectation_dense.json nqubits_list="12" ./quimb_local.sh
|
||||
```
|
||||
|
||||
MPS is typically faster and uses bounded memory, while dense provides full accuracy without truncation.
|
||||
|
||||
## Installation
|
||||
|
||||
Preferred path: create a fresh environment from the benchmark spec.
|
||||
|
||||
```bash
|
||||
cd /ssd_data/tankya2/code/ASC_2026_prism/ASC-2026/qibojit-benchmarks
|
||||
conda env create -f environment.yml
|
||||
conda activate benchmarks
|
||||
```
|
||||
|
||||
If you are using an existing environment instead of recreating it, check that it is aligned with the benchmark stack:
|
||||
|
||||
```bash
|
||||
python -c "import numpy, qibo, qibojit; print('numpy', numpy.__version__); print('qibo', qibo.__version__); print('qibojit', qibojit.__version__)"
|
||||
```
|
||||
|
||||
## Dependency Recovery
|
||||
|
||||
```bash
|
||||
pip install cirq==0.8.2
|
||||
```
|
||||
This is the recovery path that eventually made `./quimb_local.sh` work for `supremacy` in the existing `qibotn-py311-test` environment.
|
||||
|
||||
### Symptom 1: `cirq` failed with `ModuleNotFoundError: No module named 'pkg_resources'`
|
||||
|
||||
Cause:
|
||||
- the existing environment had `setuptools 82.0.1`
|
||||
- that install did not expose `pkg_resources`
|
||||
- the installed `cirq` package still imported `pkg_resources` during startup
|
||||
|
||||
Fix:
|
||||
|
||||
```bash
|
||||
/home/tankya2/miniforge3/envs/qibotn-py311-test/bin/python -m pip install "setuptools<81"
|
||||
```
|
||||
|
||||
Working result:
|
||||
- `setuptools` was changed to `80.10.2`
|
||||
- `import pkg_resources` worked again
|
||||
- the Quimb `supremacy` path could construct the Cirq-generated circuit
|
||||
|
||||
### Symptom 2: `qibojit` reference crashed with `TypeError: asarray() got an unexpected keyword argument 'copy'`
|
||||
|
||||
Cause:
|
||||
- the same environment had `numpy 1.26.4`
|
||||
- but `qibo 0.3.1` requires `numpy >= 2.0.0`
|
||||
- the `qibojit` reference backend was therefore running against an incompatible NumPy version
|
||||
|
||||
Fix:
|
||||
|
||||
```bash
|
||||
/home/tankya2/miniforge3/envs/qibotn-py311-test/bin/python -m pip install numpy==2.2.6
|
||||
```
|
||||
|
||||
Working result:
|
||||
- `numpy` was changed to `2.2.6`
|
||||
- the `qibojit` reference backend started working again
|
||||
- both the Quimb and qibojit sides of the `supremacy` benchmark completed successfully
|
||||
|
||||
### Final verified state
|
||||
|
||||
The existing repaired environment was verified with:
|
||||
|
||||
```bash
|
||||
cd /ssd_data/tankya2/code/ASC_2026_prism/ASC-2026/qibojit-benchmarks/scripts/quimb
|
||||
nqubits_list="6" circuits="supremacy" nreps=1 modes="dense_vector expectation" ./quimb_local.sh
|
||||
```
|
||||
|
||||
That smoke test completed all three stages:
|
||||
- Quimb `dense_vector`
|
||||
- Quimb `expectation`
|
||||
- qibojit reference
|
||||
|
||||
Notes:
|
||||
- `environment.yml` remains the preferred source of truth for a clean install
|
||||
- the repaired environment may still emit a `pkg_resources` deprecation warning from the older Cirq stack, but the benchmark run succeeds
|
||||
- if an environment has drifted far from `environment.yml`, recreating it is usually safer than incremental repair
|
||||
|
||||
## Notes
|
||||
|
||||
- **Intel MPI Fabric:** On Prism cluster, shared-memory fabric (`I_MPI_FABRICS=shm`) is automatically enabled for single-node runs
|
||||
- **QFT Support:** Fixed cu1 gate compatibility in quimb backend; no platform-specific gate limitations
|
||||
- **Bond Dimension:** MPS modes use max_bond_dimension=20 by default; adjust in JSON for memory/accuracy tradeoff
|
||||
1
qibojit-benchmarks/scripts/quimb/dense_vector_dense.json
Normal file
1
qibojit-benchmarks/scripts/quimb/dense_vector_dense.json
Normal file
@@ -0,0 +1 @@
|
||||
{"expectation_enabled": false, "use_mps": false}
|
||||
1
qibojit-benchmarks/scripts/quimb/dense_vector_mps.json
Normal file
1
qibojit-benchmarks/scripts/quimb/dense_vector_mps.json
Normal file
@@ -0,0 +1 @@
|
||||
{"expectation_enabled": false, "use_mps": true, "max_bond_dimension": 20, "svd_cutoff": 1e-10}
|
||||
1
qibojit-benchmarks/scripts/quimb/expectation_dense.json
Normal file
1
qibojit-benchmarks/scripts/quimb/expectation_dense.json
Normal file
@@ -0,0 +1 @@
|
||||
{"expectation_enabled": true, "use_mps": false, "pauli_pattern": "XIIII"}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"expectation_enabled": true,
|
||||
"use_mps": false,
|
||||
"pauli_pattern_auto": true,
|
||||
"pauli_pattern_style": "mixed"
|
||||
}
|
||||
1
qibojit-benchmarks/scripts/quimb/expectation_mps.json
Normal file
1
qibojit-benchmarks/scripts/quimb/expectation_mps.json
Normal file
@@ -0,0 +1 @@
|
||||
{"expectation_enabled": true, "use_mps": true, "max_bond_dimension": 20, "svd_cutoff": 1e-10, "pauli_pattern": "XIIII"}
|
||||
215
qibojit-benchmarks/scripts/quimb/quimb_local.sh
Executable file
215
qibojit-benchmarks/scripts/quimb/quimb_local.sh
Executable file
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env bash
|
||||
# Local quimb benchmark sweep.
|
||||
# Tests both dense vector (non-expectation) and expectation across multiple
|
||||
# qubit sizes and circuit types.
|
||||
#
|
||||
# Usage examples:
|
||||
# ./quimb_local.sh
|
||||
# nqubits_list="6 8 10" circuits="variational bv" ./quimb_local.sh
|
||||
# modes="expectation" nreps=3 ./quimb_local.sh
|
||||
# state_cfg=dense_vector_dense.json modes="dense_vector" ./quimb_local.sh
|
||||
set -euo pipefail
|
||||
|
||||
: "${precision:=complex128}"
|
||||
: "${nreps:=3}"
|
||||
: "${filename:=quimb_benchmark_local.dat}"
|
||||
: "${nlayers:=2}"
|
||||
: "${nqubits_list:=6 8 10}"
|
||||
: "${circuits:=supremacy qft variational qaoa}"
|
||||
: "${modes:=dense_vector expectation}"
|
||||
: "${exp_cfg:=}"
|
||||
: "${state_cfg:=}"
|
||||
: "${EXPECTATION_CONFIGS:=}"
|
||||
: "${STATE_CONFIGS:=}"
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd "${script_dir}/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
config_list_from_prefix() {
|
||||
local prefix="$1"
|
||||
local single_cfg="$2"
|
||||
local override_list="$3"
|
||||
|
||||
if [[ -n "${override_list}" ]]; then
|
||||
for cfg in ${override_list}; do
|
||||
printf '%s\n' "${cfg}"
|
||||
done
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "${single_cfg}" ]]; then
|
||||
printf '%s\n' "${single_cfg}"
|
||||
return
|
||||
fi
|
||||
|
||||
shopt -s nullglob
|
||||
local matches=( "${script_dir}/${prefix}"*.json )
|
||||
shopt -u nullglob
|
||||
|
||||
for path in "${matches[@]}"; do
|
||||
basename "${path}"
|
||||
done
|
||||
}
|
||||
|
||||
require_existing_configs() {
|
||||
local mode="$1"
|
||||
shift
|
||||
|
||||
for cfg in "$@"; do
|
||||
if [[ ! -f "${script_dir}/${cfg}" ]]; then
|
||||
echo "Missing ${mode} config: ${script_dir}/${cfg}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
read_pauli_pattern() {
|
||||
local cfg_path="$1"
|
||||
|
||||
python - "${cfg_path}" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
try:
|
||||
with open(sys.argv[1], "r") as f:
|
||||
data = json.load(f)
|
||||
print(data.get("pauli_pattern", ""))
|
||||
except Exception:
|
||||
print("")
|
||||
PY
|
||||
}
|
||||
|
||||
build_ref_pattern() {
|
||||
local base_pauli_pattern="$1"
|
||||
local nqubits="$2"
|
||||
|
||||
python - "${base_pauli_pattern}" "${nqubits}" <<'PY'
|
||||
import sys
|
||||
|
||||
p = sys.argv[1].upper()
|
||||
n = int(sys.argv[2])
|
||||
|
||||
if not p:
|
||||
p = "X" + "I" * max(n - 1, 0)
|
||||
if len(p) < n:
|
||||
p = p + ("I" * (n - len(p)))
|
||||
else:
|
||||
p = p[:n]
|
||||
print(p)
|
||||
PY
|
||||
}
|
||||
|
||||
run_qibojit_reference() {
|
||||
local nqubits="$1"
|
||||
local circuit="$2"
|
||||
local circuit_opts="$3"
|
||||
local exp_cfg_name="$4"
|
||||
local exp_cfg_path="${script_dir}/${exp_cfg_name}"
|
||||
local base_pauli_pattern
|
||||
local ref_pattern
|
||||
|
||||
base_pauli_pattern="$(read_pauli_pattern "${exp_cfg_path}")"
|
||||
ref_pattern="$(build_ref_pattern "${base_pauli_pattern}" "${nqubits}")"
|
||||
|
||||
echo " [qibojit reference] cfg=${exp_cfg_name}"
|
||||
python compare.py \
|
||||
--circuit "${circuit}" \
|
||||
${circuit_opts:+--circuit-options "${circuit_opts}"} \
|
||||
--nqubits "${nqubits}" \
|
||||
--filename "${filename}" \
|
||||
--library-options backend=qibojit,platform=numba,expectation=${ref_pattern} \
|
||||
--nreps "${nreps}" \
|
||||
--precision "${precision}"
|
||||
}
|
||||
|
||||
mapfile -t expectation_cfgs < <(
|
||||
config_list_from_prefix "expectation" "${exp_cfg}" "${EXPECTATION_CONFIGS}"
|
||||
)
|
||||
mapfile -t state_cfgs < <(
|
||||
config_list_from_prefix "dense_vector" "${state_cfg}" "${STATE_CONFIGS}"
|
||||
)
|
||||
|
||||
run_dense_mode=false
|
||||
run_expectation_mode=false
|
||||
|
||||
if echo "${modes}" | grep -Eqw "dense_vector|statevector"; then
|
||||
run_dense_mode=true
|
||||
fi
|
||||
|
||||
if echo "${modes}" | grep -qw "expectation"; then
|
||||
run_expectation_mode=true
|
||||
fi
|
||||
|
||||
if [[ "${run_dense_mode}" == true && ${#state_cfgs[@]} -eq 0 ]]; then
|
||||
echo "No dense-vector JSON configs found in ${script_dir}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${run_expectation_mode}" == true && ${#expectation_cfgs[@]} -eq 0 ]]; then
|
||||
echo "No expectation JSON configs found in ${script_dir}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
require_existing_configs "dense-vector" "${state_cfgs[@]}"
|
||||
require_existing_configs "expectation" "${expectation_cfgs[@]}"
|
||||
|
||||
# On Prism, Intel MPI needs shared-memory fabric for single-node runs.
|
||||
if [[ -z "${I_MPI_FABRICS:-}" ]]; then
|
||||
export I_MPI_FABRICS=shm
|
||||
fi
|
||||
|
||||
echo "Dense-vector configs : ${state_cfgs[*]:-none}"
|
||||
echo "Expectation configs : ${expectation_cfgs[*]:-none}"
|
||||
|
||||
for nqubits in ${nqubits_list}; do
|
||||
for circuit in ${circuits}; do
|
||||
circuit_opts="nlayers=${nlayers}"
|
||||
# These circuits do not use nlayers — omit it to avoid unknown-option errors.
|
||||
if [[ "${circuit}" == "qft" || "${circuit}" == "QFT" || \
|
||||
"${circuit}" == "supremacy" || "${circuit}" == "Supremacy" || \
|
||||
"${circuit}" == "bv" || "${circuit}" == "bernstein-vazirani" || \
|
||||
"${circuit}" == "hs" || "${circuit}" == "hidden-shift" || \
|
||||
"${circuit}" == "qaoa" || "${circuit}" == "qv" || \
|
||||
"${circuit}" == "quantum-volume" ]]; then
|
||||
circuit_opts=""
|
||||
fi
|
||||
|
||||
echo "===== nqubits=${nqubits} circuit=${circuit} ====="
|
||||
|
||||
# dense_vector is the preferred name; statevector is kept as a legacy alias.
|
||||
if [[ "${run_dense_mode}" == true ]]; then
|
||||
for cfg_name in "${state_cfgs[@]}"; do
|
||||
state_cfg_path="${script_dir}/${cfg_name}"
|
||||
echo " [dense_vector] cfg=${cfg_name}"
|
||||
python compare.py \
|
||||
--circuit "${circuit}" \
|
||||
${circuit_opts:+--circuit-options "${circuit_opts}"} \
|
||||
--nqubits "${nqubits}" \
|
||||
--filename "${filename}" \
|
||||
--library-options backend=qibotn,platform=quimb,computation_settings=${state_cfg_path} \
|
||||
--nreps "${nreps}" \
|
||||
--precision "${precision}"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "${run_expectation_mode}" == true ]]; then
|
||||
for cfg_name in "${expectation_cfgs[@]}"; do
|
||||
exp_cfg_path="${script_dir}/${cfg_name}"
|
||||
echo " [expectation] cfg=${cfg_name}"
|
||||
python compare.py \
|
||||
--circuit "${circuit}" \
|
||||
${circuit_opts:+--circuit-options "${circuit_opts}"} \
|
||||
--nqubits "${nqubits}" \
|
||||
--filename "${filename}" \
|
||||
--library-options backend=qibotn,platform=quimb,computation_settings=${exp_cfg_path} \
|
||||
--nreps "${nreps}" \
|
||||
--precision "${precision}"
|
||||
|
||||
run_qibojit_reference "${nqubits}" "${circuit}" "${circuit_opts}" "${cfg_name}"
|
||||
done
|
||||
fi
|
||||
|
||||
echo
|
||||
done
|
||||
done
|
||||
167
qibojit-benchmarks/scripts/quimb_mpi/README.md
Normal file
167
qibojit-benchmarks/scripts/quimb_mpi/README.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# Quimb MPI Benchmark Suite
|
||||
|
||||
Demonstrates MPI-enabled tensor network simulation using quimb with 2 MPI processes.
|
||||
|
||||
## Files
|
||||
|
||||
- **quimb_mpi.sh** — Main benchmark sweep script using `mpirun -np 2`
|
||||
- **expectation_mps_mpi.json** — Expectation mode with MPS (default for expectation)
|
||||
- **expectation_dense_mpi.json** — Expectation mode, dense tensor network
|
||||
- **dense_vector_mps_mpi.json** — State extraction with MPS (default for state)
|
||||
- **dense_vector_dense_mpi.json** — State extraction, dense tensor network
|
||||
|
||||
## Key Differences from `/scripts/quimb/`
|
||||
|
||||
- Each `python compare.py` call wrapped with `mpirun -np 2` to enable MPI parallelization
|
||||
- All JSON configs include `"MPI_enabled": true`
|
||||
- Default file names include `_mpi` suffix to distinguish from single-process configs
|
||||
- Filenames in logs clearly show MPI mode (e.g., `expectation_mps_mpi.json`)
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### Expectation Modes (computing Hamiltonian expectation values)
|
||||
- **expectation_mps_mpi.json**: MPI-enabled, uses Matrix Product State (MPS) tensor network ansatz; good for larger systems with limited bond dimension
|
||||
- **expectation_dense_mpi.json**: MPI-enabled, uses dense tensor network; full contraction without MPS truncation
|
||||
|
||||
### State Modes (full state vector computation)
|
||||
- **dense_vector_mps_mpi.json**: MPI-enabled, extracts full state using MPS contraction (faster, bounded memory)
|
||||
- **dense_vector_dense_mpi.json**: MPI-enabled, extracts full state using dense tensor (maximum memory usage)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
cd /ssd_data/tankya2/code/ASC_2026_prism/ASC-2026/qibojit-benchmarks/scripts/quimb_mpi
|
||||
|
||||
# Run with defaults (all matching dense_vector*.json and expectation*.json configs)
|
||||
./quimb_mpi.sh
|
||||
|
||||
# Test only expectation mode, custom qubit sizes
|
||||
modes="expectation" nqubits_list="8 10 12" ./quimb_mpi.sh
|
||||
|
||||
# Test dense vector only with one explicit config
|
||||
state_cfg=dense_vector_dense_mpi.json modes="dense_vector" ./quimb_mpi.sh
|
||||
|
||||
# Restrict the sweep to a chosen subset of configs
|
||||
EXPECTATION_CONFIGS="expectation_mps_mpi.json expectation_dense_mpi.json" ./quimb_mpi.sh
|
||||
|
||||
# Use 4 MPI processes (if testing scalability)
|
||||
np=4 ./quimb_mpi.sh
|
||||
```
|
||||
|
||||
## Supported Circuits
|
||||
|
||||
### ✓ Fully Supported (Numerically Validated)
|
||||
|
||||
| Circuit | Alias | Parameters | Status | Notes |
|
||||
|---------|-------|------------|--------|-------|
|
||||
| **qft** | — | none | ✓ Works | Quantum Fourier Transform; aligned with qibojit |
|
||||
| **variational** | — | nlayers | ✓ Works | Parameterized circuit; aligned with qibojit |
|
||||
| **bv** | bernstein-vazirani | none | ✓ Works | Bernstein-Vazirani algorithm; aligned with qibojit |
|
||||
| **hs** | hidden-shift | none | ✓ Works | Hidden-shift problem; aligned with qibojit |
|
||||
| **qaoa** | — | nlayers | ✓ Works | QAOA with RZZ gate support; aligned with qibojit |
|
||||
|
||||
### ❌ Not Supported
|
||||
|
||||
| Circuit | Issue | Notes |
|
||||
|---------|-------|-------|
|
||||
| **qv** | Qiskit API compatibility | quantum-volume uses deprecated qiskit `.qasm()` method (v3.0+); not specific to quimb |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `nqubits_list` | `6 8 10` | Space-separated qubit counts to benchmark |
|
||||
| `circuits` | `qaoa` | Space-separated circuit names |
|
||||
| `modes` | `dense_vector expectation` | Benchmark modes (dense_vector, expectation) |
|
||||
| `nlayers` | `2` | Layers for layered circuits (variational, qaoa) |
|
||||
| `np` | `2` | Number of MPI processes (mpirun -np) |
|
||||
| `nreps` | `1` | Repetitions per benchmark |
|
||||
| `filename` | `quimb_benchmark_mpi.dat` | Output log file |
|
||||
| `precision` | `complex128` | NumPy dtype (complex128 or complex64) |
|
||||
| `exp_cfg` | unset | Optional single expectation config; when unset, all `expectation*.json` files are swept |
|
||||
| `state_cfg` | unset | Optional single dense-vector config; when unset, all `dense_vector*.json` files are swept |
|
||||
| `EXPECTATION_CONFIGS` | unset | Optional space-separated subset of expectation configs to run |
|
||||
| `STATE_CONFIGS` | unset | Optional space-separated subset of dense-vector configs to run |
|
||||
|
||||
## Results
|
||||
|
||||
Results are stored in `/qibojit-benchmarks/benchmarks/results/` with filename prefix `quimb_benchmark_mpi.dat` by default (configurable via `filename=` env var).
|
||||
|
||||
Each benchmark includes:
|
||||
- **import_time** — Backend initialization time
|
||||
- **creation_time** — Circuit creation time
|
||||
- **dry_run_time** — First execution time (warm-up)
|
||||
- **simulation_times** — Execution times for nreps runs
|
||||
- **expectation_result** — Observable expectation values (for expectation mode)
|
||||
|
||||
## Observable
|
||||
|
||||
By default, expectation uses:
|
||||
- **X on qubit 0**, identity on remaining qubits
|
||||
- Configurable via `pauli_pattern` in JSON config (e.g., `"XIIII"` for 5 qubits)
|
||||
|
||||
## Reference Validation
|
||||
|
||||
Each benchmark run automatically includes a **qibojit (numba) reference** with the same observable configuration, enabling direct numerical comparison for validation.
|
||||
|
||||
Example:
|
||||
```
|
||||
[expectation MPI] quimb + variational @ 6q: 0.2019...
|
||||
[qibojit ref] numba + variational @ 6q: 0.2019... ✓ aligned
|
||||
```
|
||||
|
||||
## Supported Circuits
|
||||
|
||||
### ✓ Fully Supported (Numerically Validated)
|
||||
|
||||
| Circuit | Alias | Parameters | Status | Notes |
|
||||
|---------|-------|------------|--------|-------|
|
||||
| **qft** | — | none | ✓ Works | Quantum Fourier Transform; aligned with qibojit |
|
||||
| **variational** | — | nlayers | ✓ Works | Parameterized circuit; aligned with qibojit |
|
||||
| **bv** | bernstein-vazirani | none | ✓ Works | Bernstein-Vazirani algorithm; aligned with qibojit |
|
||||
| **hs** | hidden-shift | none | ✓ Works | Hidden-shift problem; aligned with qibojit |
|
||||
| **qaoa** | — | nlayers | ✓ Works | QAOA with RZZ gate support; aligned with qibojit |
|
||||
|
||||
### ❌ Not Supported
|
||||
|
||||
| Circuit | Issue | Notes |
|
||||
|---------|-------|-------|
|
||||
| **qv** | Qiskit API compatibility | quantum-volume uses deprecated qiskit `.qasm()` method (v3.0+); not specific to quimb |
|
||||
|
||||
## Notes
|
||||
|
||||
- **Intel MPI Fabric:** On Prism, automatically sets `I_MPI_FABRICS=shm` (shared-memory fabric) for single-node local runs
|
||||
- **Observable:** X on first qubit, identity on rest (configurable via `pauli_pattern` in JSON)
|
||||
- **Reference:** qibojit (numba backend) included automatically with aligned observable for validation
|
||||
- **RZZ Gate:** Full support for QAOA and algorithms using two-qubit Ising rotations
|
||||
- **cu1 Gate:** Fully supported in expectation value mode (QFT compatible)
|
||||
- **Numerical Alignment:** All benchmarks automatically include qibojit reference with identical observable for validation
|
||||
|
||||
## MPI Enable/Disable
|
||||
|
||||
To test without MPI, use configs from `/scripts/quimb/` instead:
|
||||
|
||||
```bash
|
||||
# Compare: MPI vs no MPI
|
||||
./quimb_mpi.sh # with MPI (2 processes)
|
||||
cd ../quimb && ./quimb_local.sh # without MPI (single process)
|
||||
```
|
||||
|
||||
## Tensor Network Options
|
||||
|
||||
Quimb supports both MPS (Matrix Product State) and dense tensor network contractions. By default the script sweeps every matching JSON file in this folder, or you can target individual configs:
|
||||
|
||||
```bash
|
||||
# Compare MPS vs dense at same nqubits with MPI
|
||||
exp_cfg=expectation_mps_mpi.json nqubits_list="12" ./quimb_mpi.sh
|
||||
exp_cfg=expectation_dense_mpi.json nqubits_list="12" ./quimb_mpi.sh
|
||||
```
|
||||
|
||||
MPS is typically faster and uses bounded memory, while dense provides full accuracy without truncation.
|
||||
|
||||
## Additional Notes
|
||||
|
||||
- **Bond Dimension:** MPS modes use max_bond_dimension=20 by default; adjust in JSON for memory/accuracy tradeoff
|
||||
- **SVD Cutoff:** Set to 1e-10 for high accuracy; increase for faster execution with acceptable loss tolerance
|
||||
- **Scaling:** MPI parallelization enables larger systems; test with np=4, np=8, etc. for scaling studies
|
||||
- **Single-Node MPI:** Default `-np 2` demonstrates MPI capability on local machine; for cluster runs, adjust `-np` and node configuration
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user