final first commit

This commit is contained in:
2026-05-19 17:19:36 +08:00
commit b199e2105e
114 changed files with 6844 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
final/

View 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
View 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.

View 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
```

View 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)

View 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)

View 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

View 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}.")

View 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

View 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)

View 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()

View 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

View 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

View 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

View 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)

View 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

View 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)

View 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

View 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])

View File

@@ -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}]}

View 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}]}

View 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)

View 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

View 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

View 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

View 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)

View File

@@ -0,0 +1 @@
{"MPI_enabled": false, "MPS_enabled": false, "NCCL_enabled": false, "expectation_enabled": true}

View File

@@ -0,0 +1 @@
{"MPI_enabled": true, "MPS_enabled": false, "NCCL_enabled": false, "expectation_enabled": true}

View File

@@ -0,0 +1 @@
{"MPI_enabled": false, "MPS_enabled": false, "NCCL_enabled": true, "expectation_enabled": true}

View 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

View 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)

View 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()

View 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)

View 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

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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}]}

View 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)

View 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)

View 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}

View File

View 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()

View 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()

View 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()

View 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()

View 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()

View 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
}

View 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()

View 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

View 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``)

View File

@@ -0,0 +1 @@
{"MPI_enabled": false, "MPS_enabled": false, "NCCL_enabled": false, "expectation_enabled": true}

View File

@@ -0,0 +1 @@
{"MPI_enabled": true, "MPS_enabled": false, "NCCL_enabled": false, "expectation_enabled": true}

View File

@@ -0,0 +1 @@
{"MPI_enabled": false, "MPS_enabled": false, "NCCL_enabled": true, "expectation_enabled": true}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1 @@
{"expectation_enabled": false, "use_mps": false}

View File

@@ -0,0 +1 @@
{"expectation_enabled": false, "use_mps": true, "max_bond_dimension": 20, "svd_cutoff": 1e-10}

View File

@@ -0,0 +1 @@
{"expectation_enabled": true, "use_mps": false, "pauli_pattern": "XIIII"}

View File

@@ -0,0 +1,6 @@
{
"expectation_enabled": true,
"use_mps": false,
"pauli_pattern_auto": true,
"pauli_pattern_style": "mixed"
}

View File

@@ -0,0 +1 @@
{"expectation_enabled": true, "use_mps": true, "max_bond_dimension": 20, "svd_cutoff": 1e-10, "pauli_pattern": "XIIII"}

View 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

Some files were not shown because too many files have changed in this diff Show More