Merge branch 'cuQuantum_cuTensorNet' of https://github.com/qiboteam/qibotn into cuQuantum_cuTensorNet

This commit is contained in:
tankya2
2023-02-22 10:24:24 +08:00
9 changed files with 348 additions and 137 deletions

22
.github/workflows/rules.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
# A single CI script with github workflow
name: Tests
on:
push:
pull_request:
types: [labeled]
jobs:
build:
if: contains(github.event.pull_request.labels.*.name, 'run-workflow') || github.event_name == 'push'
strategy:
matrix:
os: [ubuntu-latest]
python-version: [3.7, 3.8, 3.9, "3.10"]
uses: qiboteam/workflows/.github/workflows/rules.yml@main
with:
os: ${{ matrix.os }}
python-version: ${{ matrix.python-version }}
environment: "qibotn"
pip-extras: "analysis,tests"
secrets: inherit

239
.gitignore vendored
View File

@@ -1,131 +1,162 @@
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
# C extensions # C extensions
*.so *.so
# Distribution / packaging # Distribution / packaging
.Python .Python
build/ build/
develop-eggs/ develop-eggs/
dist/ dist/
downloads/ downloads/
eggs/ eggs/
.eggs/ .eggs/
lib/ lib/
lib64/ lib64/
parts/ parts/
sdist/ sdist/
var/ var/
wheels/ wheels/
pip-wheel-metadata/ share/python-wheels/
share/python-wheels/ *.egg-info/
*.egg-info/ .installed.cfg
.installed.cfg *.egg
*.egg MANIFEST
MANIFEST
# PyInstaller # PyInstaller
# Usually these files are written by a python script from a template # Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it. # before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest *.manifest
*.spec *.spec
# Installer logs # Installer logs
pip-log.txt pip-log.txt
pip-delete-this-directory.txt pip-delete-this-directory.txt
# Unit test / coverage reports # Unit test / coverage reports
htmlcov/ htmlcov/
.tox/ .tox/
.nox/ .nox/
.coverage .coverage
.coverage.* .coverage.*
.cache .cache
nosetests.xml nosetests.xml
coverage.xml coverage.xml
*.cover *.cover
*.py,cover *.py,cover
.hypothesis/ .hypothesis/
.pytest_cache/ .pytest_cache/
cover/
# Translations # Translations
*.mo *.mo
*.pot *.pot
# Django stuff: # Django stuff:
*.log *.log
local_settings.py local_settings.py
db.sqlite3 db.sqlite3
db.sqlite3-journal db.sqlite3-journal
# Flask stuff: # Flask stuff:
instance/ instance/
.webassets-cache .webassets-cache
# Scrapy stuff: # Scrapy stuff:
.scrapy .scrapy
# Sphinx documentation # Sphinx documentation
docs/_build/ docs/_build/
# PyBuilder # PyBuilder
target/ .pybuilder/
target/
# Jupyter Notebook # Jupyter Notebook
.ipynb_checkpoints .ipynb_checkpoints
# IPython # IPython
profile_default/ profile_default/
ipython_config.py ipython_config.py
# pyenv # pyenv
.python-version # For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv # pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies # However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not # having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies. # install all needed dependencies.
#Pipfile.lock #Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow # poetry
__pypackages__/ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# Celery stuff # pdm
celerybeat-schedule # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
celerybeat.pid #pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# SageMath parsed files # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
*.sage.py __pypackages__/
# Environments # Celery stuff
.env celerybeat-schedule
.venv celerybeat.pid
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings # SageMath parsed files
.spyderproject *.sage.py
.spyproject
# Rope project settings # Environments
.ropeproject .env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# mkdocs documentation # Spyder project settings
/site .spyderproject
.spyproject
# mypy # Rope project settings
.mypy_cache/ .ropeproject
.dmypy.json
dmypy.json
# Pyre type checker # mkdocs documentation
.pyre/ /site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
*.pyc *.pyc
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

25
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,25 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-toml
- id: check-merge-conflict
- id: debug-statements
- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
args: ["--profile", "black"]
- repo: https://github.com/asottile/pyupgrade
rev: v3.3.1
hooks:
- id: pyupgrade

7
pyproject.toml Normal file
View File

@@ -0,0 +1,7 @@
[tool.pylint.reports]
output-format = "colorized"
[tool.pytest.ini_options]
testpaths = ["tests/"]
addopts = ["--cov=qibotn", "--cov-report=xml"]
env = ["D:NUMBA_DISABLE_JIT=1"]

View File

@@ -1,33 +1,30 @@
# Installation script for python
from setuptools import setup, find_packages from setuptools import setup, find_packages
import os
import re import re
import pathlib
HERE = pathlib.Path(__file__).parent.absolute()
PACKAGE = "qibotn" PACKAGE = "qibotn"
# Returns the qibotn version # Returns the qibotn version
def get_version(): def version():
""" Gets the version from the package's __init__ file """Gets the version from the package's __init__ file
if there is some problem, let it happily fail """ if there is some problem, let it happily fail"""
VERSIONFILE = os.path.join("src", PACKAGE, "__init__.py") version_file = HERE / "src" / PACKAGE / "__init__.py"
initfile_lines = open(VERSIONFILE, "rt").readlines() version_regex = r"^__version__ = ['\"]([^'\"]*)['\"]"
VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
for line in initfile_lines: initfile = version_file.read_text(encoding="utf-8")
mo = re.search(VSRE, line, re.M) matched = re.search(version_regex, initfile, re.M)
if mo:
return mo.group(1) if matched is not None:
return matched.group(1)
return "0.0.0"
# load long description from README # load long description from README
this_directory = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(this_directory, "README.md"), encoding="utf-8") as f:
long_description = f.read()
setup( setup(
name="qibotn", name="qibotn",
version=get_version(), version=version(),
description="A tensor-network translation module for quantum computing", description="A tensor-network translation module for quantum computing",
author="The Qibo team", author="The Qibo team",
author_email="", author_email="",
@@ -42,19 +39,22 @@ setup(
"Topic :: Scientific/Engineering :: Physics", "Topic :: Scientific/Engineering :: Physics",
], ],
install_requires=[ install_requires=[
"networkx>=2.3", "qibo>=0.1.10",
"opt_einsum>=3.2", "qibojit>=0.0.7",
"autoray>=0.2.0", "quimb[tensor]>=1.4.0",
"diskcache>=3.0",
"randomgen>=1.18",
"quimb",
"qibo"
], ],
extras_require={ extras_require={
"docs": ["sphinx", "sphinx_rtd_theme", "recommonmark", "sphinxcontrib-bibtex", "sphinx_markdown_tables", "nbsphinx", "IPython", "doc2dash>=2.4.1", ], "docs": [],
"tests": ["pytest", "cirq", "ply", "sklearn", "dill", "coverage", "pytest-cov"], "tests": [
"pytest>=7.2.0",
"pytest-cov>=4.0.0",
"pytest-env>=0.8.1",
],
"analysis": [
"pylint>=2.16.0",
],
}, },
python_requires=">=3.7.0", python_requires=">=3.7.0",
long_description=long_description, long_description=(HERE / "README.md").read_text(encoding="utf-8"),
long_description_content_type='text/markdown', long_description_content_type="text/markdown",
) )

View File

@@ -1,13 +1,28 @@
import argparse import argparse
from timeit import default_timer as timer
from qibotn import quimb as qiboquimb
from QiboCircuitConvertor import QiboCircuitToEinsum from QiboCircuitConvertor import QiboCircuitToEinsum
from cuquantum import contract from cuquantum import contract
import cupy as cp import cupy as cp
from qibo.models import * from qibo.models import QFT
from timeit import default_timer as timer
def parser(): def parser():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument(
"--nqubits", default=10, type=int, help="Number of quibits in the circuits."
)
return parser.parse_args()
def main(args: argparse.Namespace):
print("Testing for %d nqubits" % (args.nqubits))
qiboquimb.eval(args.nqubits, args.qasm_circ, args.init_state)
def parser_cuquantum():
parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
"--nqubits", default=10, type=int, help="Number of quibits in the circuits." "--nqubits", default=10, type=int, help="Number of quibits in the circuits."
@@ -42,7 +57,7 @@ def run_bench(task, label):
return result return result
def main(args: argparse.Namespace): def main_cuquantum(args: argparse.Namespace):
print("Testing for %d nqubits" % (args.nqubits)) print("Testing for %d nqubits" % (args.nqubits))
nqubits = args.nqubits nqubits = args.nqubits
circuit_name = args.circuit circuit_name = args.circuit

39
src/qibotn/quimb.py Normal file
View File

@@ -0,0 +1,39 @@
import numpy as np
import quimb.tensor as qtn
from qibo.models import Circuit as QiboCircuit
def from_qibo(circuit: QiboCircuit, psi0=None):
nqubits = circuit.nqubits
tncirc = qtn.Circuit(nqubits, psi0=psi0)
for gate in circuit.queue:
tncirc.apply_gate(
gate.name,
*gate.parameters,
*gate.qubits,
parametrize=len(gate.parameters) > 0
)
return tncirc
def init_state_tn(nqubits, init_state_sv):
dims = tuple(2 * np.ones(nqubits, dtype=int))
return qtn.tensor_1d.MatrixProductState.from_dense(init_state_sv, dims)
def eval(qasm: str, init_state, backend="numpy"):
"""Evaluate QASM with Quimb
backend (quimb): numpy, cupy, jax. Passed to ``opt_einsum``.
"""
circuit = QiboCircuit.from_qasm(qasm)
init_state_mps = init_state_tn(circuit.nqubits, init_state)
circ_quimb = from_qibo(circuit, psi0=init_state_mps)
interim = circ_quimb.psi.full_simplify(seq="DRC")
amplitudes = interim.to_dense(backend=backend).flatten()
return amplitudes

12
tests/config.py Normal file
View File

@@ -0,0 +1,12 @@
from dataclasses import dataclass
from typing import Optional
@dataclass
class Executor:
backend: str
platform: Optional[str] = None
qibo = Executor(backend="qibojit", platform="numpy")
quimb = Executor(backend="numpy")

View File

@@ -0,0 +1,60 @@
import copy
import os
from timeit import default_timer as timer
import config
import numpy as np
import pytest
import qibo
from qibo.models import QFT
def create_init_state(nqubits):
init_state = np.random.random(2**nqubits) + 1j * np.random.random(2**nqubits)
init_state = init_state / np.sqrt((np.abs(init_state) ** 2).sum())
return init_state
def qibo_qft(nqubits, init_state, swaps):
circ_qibo = QFT(nqubits, swaps)
state_vec = np.array(circ_qibo(init_state))
return circ_qibo, state_vec
def time(func):
start = timer()
res = func()
end = timer()
time = end - start
return time, res
@pytest.mark.parametrize("nqubits", [1, 2, 5, 10])
def test_eval(nqubits: int):
# hack quimb to use the correct number of processes
# TODO: remove completely, or at least delegate to the backend
# implementation
os.environ["QUIMB_NUM_PROCS"] = str(os.cpu_count())
import qibotn.quimb
init_state = create_init_state(nqubits=nqubits)
init_state_tn = copy.deepcopy(init_state)
# Test qibo
qibo.set_backend(backend=config.qibo.backend, platform=config.qibo.platform)
qibo_time, (qibo_circ, result_sv) = time(
lambda: qibo_qft(nqubits, init_state, swaps=True)
)
# Convert to qasm for other backends
qasm_circ = qibo_circ.to_qasm()
# Test quimb
quimb_time, result_tn = time(
lambda: qibotn.quimb.eval(
qasm_circ, init_state_tn, backend=config.quimb.backend
)
)
assert 1e-2 * qibo_time < quimb_time < 1e2 * qibo_time
assert np.allclose(result_sv, result_tn), "Resulting dense vectors do not match"