diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index cd0bbea7b0..3627b4bd94 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,3 +1,47 @@ +# Release 0.23.0-dev + +### New features since last release + +* Add Lightning GBenchmark Suite. +[(#249)](https://github.com/PennyLaneAI/pennylane-lightning/pull/249) + +* Add run-time and compile information in binary. +[(#253)](https://github.com/PennyLaneAI/pennylane-lightning/pull/253) + +### Improvements + +* Add `ENABLE_BLAS` build to CI checks. +[(#249)](https://github.com/PennyLaneAI/pennylane-lightning/pull/249) + +* Add more `clang-tidy` checks and kernel tests. +[(#253)](https://github.com/PennyLaneAI/pennylane-lightning/pull/253) + +* Skip over identity operations in `"lightning.qubit"`. +[(#268)](https://github.com/PennyLaneAI/pennylane-lightning/pull/268) + +### Documentation + +### Bug fixes + +* Update tests to remove `JacobianTape`. +[(#260)](https://github.com/PennyLaneAI/pennylane-lightning/pull/260) + +* Fix tests for MSVC. +[(#264)](https://github.com/PennyLaneAI/pennylane-lightning/pull/264) + +* Fix `#include ` for PPC and AArch64 in Linux. +[(#266)](https://github.com/PennyLaneAI/pennylane-lightning/pull/266) + +* Remove deprecated tape execution methods. +[(#270)](https://github.com/PennyLaneAI/pennylane-lightning/pull/270) + +### Contributors + +This release contains contributions from (in alphabetical order): +Ali Asadi, Chae-Yeun Park, and Lee James O'Riordan + +--- + # Release 0.22.1 ### Bug fixes diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 347f111cae..b4a91f94a5 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -45,7 +45,7 @@ jobs: python-version: 3.7 - name: Install dependencies - run: sudo apt update && sudo apt -y install clang-tidy-12 cmake g++ + run: sudo apt update && sudo apt -y install clang-tidy-12 cmake g++ libomp-12-dev env: DEBIAN_FRONTEND: noninteractive diff --git a/.github/workflows/tests.yml b/.github/workflows/tests_linux.yml similarity index 51% rename from .github/workflows/tests.yml rename to .github/workflows/tests_linux.yml index 2d1983bd3c..94b57f4d75 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests_linux.yml @@ -1,4 +1,4 @@ -name: Testing +name: Testing (Linux) on: push: branches: @@ -13,7 +13,7 @@ env: jobs: cpptests: - name: C++ tests + name: C++ tests (Linux) runs-on: ${{ matrix.os }} strategy: matrix: @@ -30,13 +30,75 @@ jobs: python-version: '3.7' - uses: actions/checkout@v2 + with: + fetch-depth: 2 # for codecov - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc g++ + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc g++ gcovr lcov + + - name: Build and run unit tests + run: | + cmake . -BBuild -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON + cmake --build ./Build + mkdir -p ./Build/tests/results + ./Build/pennylane_lightning/src/tests/runner --order lex --reporter junit --out ./Build/tests/results/report.xml + + - name: Upload test results + uses: actions/upload-artifact@v2 + if: always() + with: + name: ubuntu-test-report + path: Build/tests/results/report.xml + + - name: Publish test results + uses: EnricoMi/publish-unit-test-result-action@v1 + if: always() + with: + check_name: Test Report (C++) on Ubuntu + files: Build/tests/results/report.xml + + - name: Build and run unit tests for code Coverage + run: | + cmake pennylane_lightning/src -BBuildCov -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON -DENABLE_COVERAGE=ON + cmake --build ./BuildCov + cd ./BuildCov + ./tests/runner + lcov --directory . -b ../pennylane_lightning/src --capture --output-file coverage.info + lcov --remove coverage.info '/usr/*' --output-file coverage.info + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v2.1.0 + with: + file: ./BuildCov/coverage.info + fail_ci_if_error: true + + cpptestswithblas: + name: C++ tests (Linux, OpenBLAS) + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-20.04] + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.4.1 + with: + access_token: ${{ github.token }} + + - uses: actions/setup-python@v2 + name: Install Python + with: + python-version: '3.7' + + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Install dependencies + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc g++ libopenblas-dev gcovr lcov - name: Build and run unit tests run: | - cmake . -BBuild -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=1 + cmake . -BBuild -DCMAKE_BUILD_TYPE=Release -DENABLE_BLAS=ON -DBUILD_TESTS=1 cmake --build ./Build mkdir -p ./Build/tests/results ./Build/pennylane_lightning/src/tests/runner --order lex --reporter junit --out ./Build/tests/results/report.xml @@ -55,6 +117,21 @@ jobs: check_name: Test Report (C++) on Ubuntu files: Build/tests/results/report.xml + - name: Build and run unit tests for code coverage + run: | + cmake pennylane_lightning/src -BBuildCov -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON -DENABLE_COVERAGE=ON -DENABLE_BLAS=ON + cmake --build ./BuildCov + cd ./BuildCov + ./tests/runner + lcov --directory . -b ../pennylane_lightning/src --capture --output-file coverage.info + lcov --remove coverage.info '/usr/*' --output-file coverage.info + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v2.0.1 + with: + file: ./BuildCov/coverage.info + fail_ci_if_error: true + pythontests: name: Python tests runs-on: ${{ matrix.os }} @@ -67,6 +144,7 @@ jobs: uses: actions/checkout@v2 with: path: main + fetch-depth: 2 - uses: actions/setup-python@v2 name: Install Python @@ -103,6 +181,7 @@ jobs: pl-device-test --device lightning.qubit --shots=None --skip-ops $COVERAGE_FLAGS --cov-append - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1.0.12 + uses: codecov/codecov-action@v2.0.1 with: file: coverage.xml + fail_ci_if_error: true diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml new file mode 100644 index 0000000000..caccc79351 --- /dev/null +++ b/.github/workflows/tests_windows.yml @@ -0,0 +1,53 @@ +name: Testing (Windows) +on: + push: + branches: + - master + pull_request: + +jobs: + cpptests: + name: C++ tests (Windows) + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest] + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.4.1 + with: + access_token: ${{ github.token }} + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Configure MSVC for amd64 # Use cl.exe as a default compiler + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: amd64 + + - name: Setup OpenCppCoverage and add to PATH + run: | + choco install OpenCppCoverage -y + echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH + + - name: Build and run unit tests for code coverage + run: | + cmake ./pennylane_lightning/src -BBuild -DBUILD_TESTS=ON -DENABLE_OPENMP=OFF -DENABLE_WARNINGS=OFF + cmake --build ./Build --config Debug + mkdir -p ./Build/tests/results + .\Build\tests\Debug\runner.exe --order lex --reporter junit --out .\Build\tests\results\report.xml + OpenCppCoverage --sources pennylane_lightning\src --export_type cobertura:coverage.xml Build\tests\Debug\runner.exe + + - name: Upload test results + uses: actions/upload-artifact@v2 + if: always() + with: + name: windows-test-report + path: .\Build\tests\results\report.xml + + - name: Upload Report to Codecov + uses: codecov/codecov-action@v2.0.1 + with: + files: ./coverage.xml + fail_ci_if_error: true diff --git a/.github/workflows/tests_without_binary.yml b/.github/workflows/tests_without_binary.yml index 9a770dd968..b1009dceb1 100644 --- a/.github/workflows/tests_without_binary.yml +++ b/.github/workflows/tests_without_binary.yml @@ -55,6 +55,7 @@ jobs: pl-device-test --device lightning.qubit --shots=None --skip-ops $COVERAGE_FLAGS --cov-append - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1.0.12 + uses: codecov/codecov-action@v2.0.1 with: file: coverage.xml + fail_ci_if_error: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 36e5d3c6e5..31e94a659e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.14) -set(LOGO " +set(LOGO [=[ ░█░░░▀█▀░█▀▀░█░█░▀█▀░█▀█░▀█▀░█▀█░█▀▀░░░░▄▀▄░█░█░█▀▄░▀█▀░▀█▀ ░█░░░░█░░█░█░█▀█░░█░░█░█░░█░░█░█░█░█░░░░█\█░█░█░█▀▄░░█░░░█░ ░▀▀▀░▀▀▀░▀▀▀░▀░▀░░▀░░▀░▀░▀▀▀░▀░▀░▀▀▀░▀░░░▀\░▀▀▀░▀▀░░▀▀▀░░▀░ -") +]=]) message(${LOGO}) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment version") @@ -12,7 +12,8 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment ve project(pennylane_lightning) project(pennylane_lightning - DESCRIPTION "C++ stavector simulator bindings for PennyLane. " + + DESCRIPTION "C++ state-vector simulator bindings for PennyLane. " LANGUAGES CXX ) @@ -44,6 +45,7 @@ endif() option(ENABLE_CLANG_TIDY "Enable clang-tidy build checks" OFF) # Compile options +option(ENABLE_COVERAGE "Enable code coverage" OFF) option(ENABLE_WARNINGS "Enable warnings" ON) option(ENABLE_NATIVE "Enable native CPU build tuning" OFF) option(ENABLE_AVX "Enable AVX support" OFF) @@ -53,7 +55,7 @@ option(ENABLE_BLAS "Enable BLAS" OFF) # Other build options option(BUILD_TESTS "Build cpp tests" OFF) option(BUILD_EXAMPLES "Build cpp examples" OFF) - +option(BUILD_BENCHMARKS "Enable cpp benchmarks" OFF) # Process compile options include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/process_options.cmake") @@ -67,6 +69,7 @@ FetchContent_Declare(pybind11 ) FetchContent_MakeAvailable(pybind11) + # All CMakeLists.txt in subdirectories use pennylane_lightning_compile_options and pennylane_lightning_external_libs add_subdirectory(pennylane_lightning/src) diff --git a/Makefile b/Makefile index 02556dc3e0..0cf651043d 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ clean: find . -type d -name '__pycache__' -exec rm -r {} \+ rm -rf dist rm -rf build - rm -rf BuildTests BuildBench + rm -rf BuildTests BuildBench BuildGBench rm -rf .coverage coverage_html_report/ rm -rf tmp rm -rf *.dat @@ -120,3 +120,9 @@ check-tidy: rm -rf ./Build cmake . -BBuild -DENABLE_CLANG_TIDY=ON -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON cmake --build ./Build + +.PHONY: gbenchmark +gbenchmark: + rm -rf ./BuildGBench + cmake $(LIGHTNING_CPP_DIR) -BBuildGBench -DBUILD_BENCHMARKS=ON -DENABLE_OPENMP=ON -DENABLE_BLAS=ON -DCMAKE_BUILD_TYPE=Release + cmake --build ./BuildGBench --target utils apply_operations apply_multirz \ No newline at end of file diff --git a/README.rst b/README.rst index fadd387da8..c71298df72 100644 --- a/README.rst +++ b/README.rst @@ -97,7 +97,7 @@ and install the compilied library with GPU support ----------- -For GPU support, `PennyLane-Lightning-GPU `_ can be be installed by providing the optional ``[gpu]`` tag: +For GPU support, `PennyLane-Lightning-GPU `_ can be installed by providing the optional ``[gpu]`` tag: .. code-block:: console diff --git a/bin/cpp-files b/bin/cpp-files index b09cc88cf1..7ccd202783 100755 --- a/bin/cpp-files +++ b/bin/cpp-files @@ -14,6 +14,9 @@ if __name__ == '__main__': parser = argparse.ArgumentParser( description="Output C/C++ files in json list" ) + parser.add_argument( + "--header-only", action='store_true', dest='header_only', help="whether only include header files" + ) parser.add_argument( "paths", nargs="+", metavar="DIR", help="paths to the root source directories" ) @@ -23,9 +26,9 @@ if __name__ == '__main__': args = parser.parse_args() - files = set(get_cpp_files(args.paths)) + files = set(get_cpp_files(args.paths, header_only = args.header_only)) if args.exclude_dirs: - files_excludes = set(get_cpp_files(args.exclude_dirs)) + files_excludes = set(get_cpp_files(args.exclude_dirs, header_only = args.header_only)) files -= files_excludes json.dump(list(files), sys.stdout) diff --git a/bin/utils.py b/bin/utils.py index 90d1693031..6834078976 100644 --- a/bin/utils.py +++ b/bin/utils.py @@ -1,24 +1,30 @@ from pathlib import Path -import re -import fnmatch +from re import compile as re_compile +from fnmatch import fnmatch -SRCFILE_EXT = ("c", "cc", "cpp", "cxx", "h", "hh", "hpp", "hxx", "cu", "cuh") +SRCFILE_EXT = ["c", "cc", "cpp", "cxx", "cu"] +HEADERFILE_EXT = ["h", "hh", "hpp", "hxx", "cuh"] LIGHTNING_SOURCE_DIR = Path(__file__).resolve().parent.parent -rgx_gitignore_comment = re.compile("#.*$") +rgx_gitignore_comment = re_compile("#.*$") -def get_cpp_files_from_path(path, ignore_patterns = None, use_gitignore = True): +def get_cpp_files_from_path(path, ignore_patterns = None, use_gitignore = True, header_only = False): """return set of C++ source files from a path Args: paths (pathlib.Path or str): a path to process ignore_patterns: patterns to ignore use_gitignore: find ignore patterns from .gitignore + header_only: find only header files when true """ path = Path(path) files_rel = set() # file paths relative to path - for ext in SRCFILE_EXT: + + exts = HEADERFILE_EXT + if not header_only: + exts += SRCFILE_EXT + for ext in exts: for file_path in path.rglob(f"*.{ext}"): files_rel.add(file_path.relative_to(path)) @@ -39,22 +45,23 @@ def get_cpp_files_from_path(path, ignore_patterns = None, use_gitignore = True): files_to_remove = set() for ignore_pattern in ignore_patterns: for f in files_rel: - if fnmatch.fnmatch(str(f), ignore_pattern): + if fnmatch(str(f), ignore_pattern): files_to_remove.add(f) files_rel -= files_to_remove return set(str(path.joinpath(f)) for f in files_rel) -def get_cpp_files(paths, ignore_patterns = None, use_gitignore = True): +def get_cpp_files(paths, ignore_patterns = None, use_gitignore = True, header_only = False): """return list of C++ source files from paths. Args: paths (list): list of all paths to process ignore_patterns: patterns to ignore use_gitignore: find ignore patterns from .gitignore + header_only: find only header files when true """ files = set() for path in paths: - files |= get_cpp_files_from_path(path, ignore_patterns, use_gitignore) + files |= get_cpp_files_from_path(path, ignore_patterns, use_gitignore, header_only) return list(files) diff --git a/cmake/process_options.cmake b/cmake/process_options.cmake index d3ecccd3f9..e030c93ed9 100644 --- a/cmake/process_options.cmake +++ b/cmake/process_options.cmake @@ -39,6 +39,13 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") $<$:-fwrapv;-fno-plt;-pipe>) endif() +if(ENABLE_COVERAGE) + message(STATUS "ENABLE_COVERAGE is ON.") + target_compile_options(lightning_compile_options INTERFACE + $<$:-fprofile-arcs;-ftest-coverage>) + target_link_libraries(lightning_external_libs INTERFACE gcov) +endif() + if(ENABLE_WARNINGS) message(STATUS "ENABLE_WARNINGS is ON.") if(MSVC) @@ -47,7 +54,7 @@ if(ENABLE_WARNINGS) target_compile_options(lightning_compile_options INTERFACE $<$:-Wall;-Wextra;-Werror>) endif() else() - message(STATUS "ENABLE_WARNINGS is OFF") + message(STATUS "ENABLE_WARNINGS is OFF.") endif() if(ENABLE_NATIVE) @@ -59,7 +66,21 @@ if(ENABLE_AVX) message(STATUS "ENABLE_AVX is ON.") target_compile_options(lightning_compile_options INTERFACE -mavx) else() - message(STATUS "ENABLE_AVX is OFF") + message(STATUS "ENABLE_AVX is OFF.") +endif() + +if(ENABLE_AVX2) + message(STATUS "ENABLE_AVX2 is ON.") + target_compile_options(lightning_compile_options INTERFACE -mavx2) +else() + message(STATUS "ENABLE_AVX2 is OFF") +endif() + +if(ENABLE_AVX512) + message(STATUS "ENABLE_AVX512 is ON.") + target_compile_options(lightning_compile_options INTERFACE -mavx512f) # Now we only use avx512f +else() + message(STATUS "ENABLE_AVX512 is OFF") endif() if(ENABLE_OPENMP) @@ -73,7 +94,7 @@ if(ENABLE_OPENMP) target_link_libraries(lightning_external_libs INTERFACE OpenMP::OpenMP_CXX) else() - message(STATUS "ENABLE_OPENMP is OFF") + message(STATUS "ENABLE_OPENMP is OFF.") endif() if(ENABLE_BLAS) @@ -94,5 +115,5 @@ if(ENABLE_BLAS) target_link_options(lightning_external_libs INTERFACE "${BLAS_LINKER_FLAGS}") target_compile_options(lightning_compile_options INTERFACE "-D_ENABLE_BLAS=1") else() - message(STATUS "ENABLE_BLAS is OFF") + message(STATUS "ENABLE_BLAS is OFF.") endif() diff --git a/codecov.yml b/codecov.yml index 17396d47d7..5033324b14 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,12 @@ fixes: - "main/::" + +ignore: + - "pennylane_lightning/src/tests" + +codecov: + notify: + after_n_builds: 5 + +comment: + after_n_builds: 5 diff --git a/doc/_ext/edit_on_github.py b/doc/_ext/edit_on_github.py index b69348d97d..954ed00ab4 100644 --- a/doc/_ext/edit_on_github.py +++ b/doc/_ext/edit_on_github.py @@ -8,19 +8,20 @@ import warnings -__licence__ = 'BSD (3 clause)' +__licence__ = "BSD (3 clause)" def get_github_url(app, view, path): - return 'https://github.com/{project}/{view}/{branch}/{path}'.format( + return "https://github.com/{project}/{view}/{branch}/{path}".format( project=app.config.edit_on_github_project, view=view, branch=app.config.edit_on_github_branch, - path=path) + path=path, + ) def html_page_context(app, pagename, templatename, context, doctree): - if templatename != 'page.html': + if templatename != "page.html": return if not app.config.edit_on_github_project: @@ -29,16 +30,16 @@ def html_page_context(app, pagename, templatename, context, doctree): if not doctree: return - - path = os.path.relpath(doctree.get('source'), app.builder.srcdir) - show_url = get_github_url(app, 'blob', path) - edit_url = get_github_url(app, 'edit', path) - context['show_on_github_url'] = show_url - context['edit_on_github_url'] = edit_url + path = os.path.relpath(doctree.get("source"), app.builder.srcdir) + show_url = get_github_url(app, "blob", path) + edit_url = get_github_url(app, "edit", path) + + context["show_on_github_url"] = show_url + context["edit_on_github_url"] = edit_url def setup(app): - app.add_config_value('edit_on_github_project', '', True) - app.add_config_value('edit_on_github_branch', 'master', True) - app.connect('html-page-context', html_page_context) \ No newline at end of file + app.add_config_value("edit_on_github_project", "", True) + app.add_config_value("edit_on_github_branch", "master", True) + app.connect("html-page-context", html_page_context) diff --git a/doc/conf.py b/doc/conf.py index 00cbacb9f3..1660c8443d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -20,46 +20,50 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('')) -sys.path.insert(0, os.path.abspath('_ext')) -sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath('doc')), 'doc')) +sys.path.insert(0, os.path.abspath("")) +sys.path.insert(0, os.path.abspath("_ext")) +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath("doc")), "doc")) # For obtaining all relevant C++ source files -currdir = Path(__file__).resolve().parent # PROJECT_SOURCE_DIR/docs +currdir = Path(__file__).resolve().parent # PROJECT_SOURCE_DIR/docs PROJECT_SOURCE_DIR = currdir.parent -CPP_SOURCE_DIR = PROJECT_SOURCE_DIR.joinpath('pennylane_lightning/src') -CPP_EXCLUDE_DIRS = ['examples', 'tests'] # relative to CPP_SOURCE_DIR +CPP_SOURCE_DIR = PROJECT_SOURCE_DIR.joinpath("pennylane_lightning/src") +CPP_EXCLUDE_DIRS = ["examples", "tests", "benchmarks"] # relative to CPP_SOURCE_DIR + def obtain_cpp_files(): - script_path = PROJECT_SOURCE_DIR.joinpath('bin/cpp-files') + script_path = PROJECT_SOURCE_DIR.joinpath("bin/cpp-files") if not script_path.exists(): - print('The project directory structure is corrupted.') + print("The project directory structure is corrupted.") sys.exit(1) exclude_dirs = [CPP_SOURCE_DIR.joinpath(exclude_dir) for exclude_dir in CPP_EXCLUDE_DIRS] - p = subprocess.run([str(script_path), CPP_SOURCE_DIR, '--exclude-dirs', *exclude_dirs], capture_output = True) + p = subprocess.run( + [str(script_path), "--header-only", CPP_SOURCE_DIR, "--exclude-dirs", *exclude_dirs], + capture_output=True, + ) file_list = json.loads(p.stdout) - file_list = ['../' + str(Path(f).relative_to(PROJECT_SOURCE_DIR)) for f in file_list] + file_list = ["../" + str(Path(f).relative_to(PROJECT_SOURCE_DIR)) for f in file_list] return file_list + CPP_FILES = obtain_cpp_files() print(CPP_FILES) - class Mock(MagicMock): - __name__ = 'foo' + __name__ = "foo" @classmethod def __getattr__(cls, name): return MagicMock() -MOCK_MODULES = ['pennylane_lightning.lightning_qubit_ops'] +MOCK_MODULES = ["pennylane_lightning.lightning_qubit_ops"] mock = Mock() for mod_name in MOCK_MODULES: @@ -68,7 +72,7 @@ def __getattr__(cls, name): # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.6' +needs_sphinx = "3.3" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -85,9 +89,9 @@ def __getattr__(cls, name): "sphinx.ext.mathjax", "sphinx.ext.napoleon", "sphinx.ext.todo", - 'sphinx.ext.viewcode', + "sphinx.ext.viewcode", "sphinx_automodapi.automodapi", - 'sphinx_automodapi.smart_resolver' + "sphinx_automodapi.smart_resolver", ] intersphinx_mapping = {"https://pennylane.readthedocs.io/en/stable/": None} @@ -116,10 +120,7 @@ def __getattr__(cls, name): # TIP: if using the sphinx-bootstrap-theme, you need # "treeViewIsBootstrap": True, "exhaleExecutesDoxygen": True, - "exhaleDoxygenStdin": ( - "INPUT = " + ' '.join(CPP_FILES) + ' ' - "EXCLUDE_SYMBOLS = std::* " - ), + "exhaleDoxygenStdin": ("INPUT = " + " ".join(CPP_FILES) + " " "EXCLUDE_SYMBOLS = std::* "), "afterTitleDescription": inspect.cleandoc( """ The Pennylane Lightning C++ API is intended to be called from Python through Pybind11. Direct use of the C++ API is currently unsupported and is provided for reference only. @@ -128,21 +129,21 @@ def __getattr__(cls, name): } # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates', 'xanadu_theme'] +templates_path = ["_templates", "xanadu_theme"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'PennyLane-Lightning' +project = "PennyLane-Lightning" copyright = "Copyright 2021" -author = 'Xanadu Inc.' +author = "Xanadu Inc." add_module_names = False @@ -151,11 +152,12 @@ def __getattr__(cls, name): # built documents. import pennylane_lightning + # The full version, including alpha/beta/rc tags. release = pennylane_lightning.__version__ # The short X.Y version. -version = re.match(r'^(\d+\.\d+)', release).expand(r'\1') +version = re.match(r"^(\d+\.\d+)", release).expand(r"\1") # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -165,19 +167,19 @@ def __getattr__(cls, name): language = None # today_fmt is used as the format for a strftime call. -today_fmt = '%Y-%m-%d' +today_fmt = "%Y-%m-%d" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. show_authors = True # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True @@ -188,12 +190,12 @@ def __getattr__(cls, name): # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -html_favicon = '_static/favicon.ico' +html_favicon = "_static/favicon.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -201,26 +203,24 @@ def __getattr__(cls, name): # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars html_sidebars = { - '**' : [ - 'logo-text.html', - 'searchbox.html', - 'globaltoc.html', + "**": [ + "logo-text.html", + "searchbox.html", + "globaltoc.html", ] } # -- Xanadu theme --------------------------------------------------------- -html_theme = 'xanadu_theme' -html_theme_path = ['.'] +html_theme = "xanadu_theme" +html_theme_path = ["."] # xanadu theme options (see theme.conf for more information) html_theme_options = { # Set the name of the project to appear in the left sidebar. "project_nav_name": "PennyLane-Lightning", - # Path to a touch icon "touch_icon": "logo_new.png", - "large_toc": True, "navigation_button": "#19b37b", "navigation_button_hover": "#0e714d", @@ -231,22 +231,22 @@ def __getattr__(cls, name): "download_button": "#19b37b", } -edit_on_github_project = 'XanaduAI/pennylane-lightning' -edit_on_github_branch = 'master/doc' +edit_on_github_project = "PennyLaneAI/pennylane-lightning" +edit_on_github_branch = "master/doc" -#============================================================ +# ============================================================ # the order in which autodoc lists the documented members -autodoc_member_order = 'bysource' +autodoc_member_order = "bysource" # inheritance_diagram graphviz attributes -inheritance_node_attrs = dict(color='lightskyblue1', style='filled') +inheritance_node_attrs = dict(color="lightskyblue1", style="filled") -#autodoc_default_flags = ['members'] +# autodoc_default_flags = ['members'] autosummary_generate = True from directives import CustomDeviceGalleryItemDirective -def setup(app): - app.add_directive('devicegalleryitem', CustomDeviceGalleryItemDirective) +def setup(app): + app.add_directive("devicegalleryitem", CustomDeviceGalleryItemDirective) diff --git a/doc/directives.py b/doc/directives.py index 953c5d38ba..3dfe1cc5d7 100644 --- a/doc/directives.py +++ b/doc/directives.py @@ -49,25 +49,27 @@ class CustomDeviceGalleryItemDirective(Directive): required_arguments = 0 optional_arguments = 4 final_argument_whitespace = True - option_spec = {'name': directives.unchanged, - 'description': directives.unchanged, - 'link': directives.unchanged} + option_spec = { + "name": directives.unchanged, + "description": directives.unchanged, + "link": directives.unchanged, + } has_content = False add_index = False def run(self): try: - if 'name' in self.options: - name = self.options['name'] + if "name" in self.options: + name = self.options["name"] - if 'description' in self.options: - description = self.options['description'] + if "description" in self.options: + description = self.options["description"] else: - raise ValueError('description not found') + raise ValueError("description not found") - if 'link' in self.options: - link = self.options['link'] + if "link" in self.options: + link = self.options["link"] else: link = "code/qml_templates" @@ -79,10 +81,8 @@ def run(self): raise return [] - thumbnail_rst = GALLERY_TEMPLATE.format(name=name, - description=description, - link=link) - thumbnail = StringList(thumbnail_rst.split('\n')) + thumbnail_rst = GALLERY_TEMPLATE.format(name=name, description=description, link=link) + thumbnail = StringList(thumbnail_rst.split("\n")) thumb = nodes.paragraph() self.state.nested_parse(thumbnail, self.content_offset, thumb) return [thumb] diff --git a/pennylane_lightning/_version.py b/pennylane_lightning/_version.py index 8ea7d90e55..fc586eba69 100644 --- a/pennylane_lightning/_version.py +++ b/pennylane_lightning/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.22.1" +__version__ = "0.23.0-dev13" diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 14ff819478..c50f167bfd 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -34,7 +34,8 @@ DeviceError, ) from pennylane.devices import DefaultQubit -from pennylane.operation import Expectation, Tensor +from pennylane.operation import Tensor +from pennylane.measurements import Expectation from pennylane.wires import Wires # Remove after the next release of PL @@ -202,7 +203,13 @@ def apply_lightning(self, state, operations, dtype=np.complex128): else: raise TypeError(f"Unsupported complex Type: {dtype}") + # Skip over identity operations instead of performing + # matrix multiplication with the identity. + skipped_ops = ["Identity"] + for o in operations: + if o.base_name in skipped_ops: + continue name = o.name.split(".")[0] # The split is because inverse gates have .inv appended if _is_lightning_gate(name): kernel = self._kernel_for_ops[name] @@ -575,6 +582,33 @@ def probability(self, wires=None, shot_range=None, bin_size=None): return M.probs(device_wires) + def generate_samples(self): + """Generate samples + + Returns: + array[int]: array of samples in binary representation with shape ``(dev.shots, dev.num_wires)`` + """ + + # To support np.complex64 based on the type of self._state + dtype = self._state.dtype + if dtype == np.complex64: + use_csingle = True + elif dtype == np.complex128: + use_csingle = False + else: + raise TypeError(f"Unsupported complex Type: {dtype}") + + # Initialization of state + ket = np.ravel(self._state) + + if use_csingle: + ket = ket.astype(np.complex64) + + state_vector = StateVectorC64(ket) if use_csingle else StateVectorC128(ket) + M = MeasuresC64(state_vector) if use_csingle else MeasuresC128(state_vector) + + return M.generate_samples(len(self.wires), self.shots).astype(int) + def expval(self, observable, shot_range=None, bin_size=None): """Expectation value of the supplied observable. @@ -687,6 +721,7 @@ class LightningQubit(DefaultQubit): # pragma: no cover version = __version__ author = "Xanadu Inc." _CPP_BINARY_AVAILABLE = False + operations = _remove_snapshot_from_operations(DefaultQubit.operations) def __init__(self, *args, **kwargs): warn( diff --git a/pennylane_lightning/src/.clang-tidy b/pennylane_lightning/src/.clang-tidy index f015b16a1d..50b924d24b 100644 --- a/pennylane_lightning/src/.clang-tidy +++ b/pennylane_lightning/src/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,-llvmlibc-*,modernize-*,-modernize-use-trailing-return-type,clang-analyzer-cplusplus*,openmp-*,performance-*,portability-*,readability-*,hicpp-*,-hicpp-no-array-decay,bugprone-suspicious-*,llvm-namespace-comment,' +Checks: '-*,clang-diagnostic-*,clang-analyzer-*,modernize-*,-modernize-use-trailing-return-type,clang-analyzer-cplusplus*,openmp-*,performance-*,portability-*,readability-*,hicpp-*,-hicpp-avoid-c-arrays,-hicpp-no-array-decay,bugprone-suspicious-*,llvm-namespace-comment,cppcoreguidelines-avoid-non-const-global-variables,cppcoreguidelines-slicing,cppcoreguidelines-special-member-functions' WarningsAsErrors: '*' HeaderFilterRegex: '.*' AnalyzeTemporaryDtors: false @@ -25,8 +25,6 @@ CheckOptions: value: 'false' - key: readability-magic-numbers.IgnoredIntegerValues value: '1;2;3;4;' - - key: readability-magic-numbers.IgnorePowersOf2IntegerValues - value: true - key: modernize-use-default-member-init.UseAssignment value: 'false' - key: readability-function-size.NestingThreshold @@ -218,7 +216,7 @@ CheckOptions: - key: modernize-use-auto.RemoveStars value: 'false' - key: readability-magic-numbers.IgnorePowersOf2IntegerValues - value: 'false' + value: 'true' - key: portability-simd-intrinsics.Std value: '' - key: readability-redundant-member-init.IgnoreBaseInCopyConstructors diff --git a/pennylane_lightning/src/CMakeLists.txt b/pennylane_lightning/src/CMakeLists.txt index b6776ac992..b58b5125ac 100644 --- a/pennylane_lightning/src/CMakeLists.txt +++ b/pennylane_lightning/src/CMakeLists.txt @@ -11,8 +11,9 @@ if(ENABLE_CLANG_TIDY) if(NOT DEFINED CLANG_TIDY_BINARY) set(CLANG_TIDY_BINARY clang-tidy) endif() + message(STATUS "Using CLANG_TIDY_BINARY=${CLANG_TIDY_BINARY}") set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_BINARY}; - -extra-arg=-std=c++17; + -extra-arg=-std=c++17; ) endif() @@ -44,3 +45,7 @@ if (BUILD_EXAMPLES) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") add_subdirectory("examples" "examples") endif() + +if (BUILD_BENCHMARKS) + add_subdirectory("benchmarks" "benchmarks") +endif() diff --git a/pennylane_lightning/src/algorithms/AdjointDiff.hpp b/pennylane_lightning/src/algorithms/AdjointDiff.hpp index 1d84d139b6..7eb32e44c3 100644 --- a/pennylane_lightning/src/algorithms/AdjointDiff.hpp +++ b/pennylane_lightning/src/algorithms/AdjointDiff.hpp @@ -142,7 +142,7 @@ template class AdjointJacobian { } }, observable.getObsParams()[j]); - } else { // Offloat to SV dispatcher if no parameters provided + } else { // Offload to SV dispatcher if no parameters provided state.applyOperation(observable.getObsName()[j], observable.getObsWires()[j], false); } @@ -193,7 +193,7 @@ template class AdjointJacobian { } #endif if (ex) { - std::rethrow_exception(ex); + std::rethrow_exception(ex); //LCOV_EXCL_LINE } // clang-format on } @@ -242,7 +242,7 @@ template class AdjointJacobian { } #endif if (ex) { - std::rethrow_exception(ex); + std::rethrow_exception(ex); //LCOV_EXCL_LINE } // clang-format on } @@ -340,8 +340,8 @@ template class AdjointJacobian { applyOperations(lambda, ops); } - const auto tp_begin = tp.begin(); - auto tp_it = tp.end(); + auto tp_it = tp.rbegin(); + const auto tp_rend = tp.rend(); // Create observable-applied state-vectors std::vector> H_lambda( @@ -355,49 +355,51 @@ template class AdjointJacobian { PL_ABORT_IF(ops.getOpsParams()[op_idx].size() > 1, "The operation is not supported using the adjoint " "differentiation method"); - if ((ops_name[op_idx] != "QubitStateVector") && - (ops_name[op_idx] != "BasisState")) { - mu.updateData(lambda.getDataVector()); - applyOperationAdj(lambda, ops, op_idx); - - if (ops.hasParams(op_idx)) { - if ((current_param_idx == *(std::prev(tp_it))) || - std::find(tp_begin, tp_it, current_param_idx) != - tp_it) { - const T scalingFactor = - applyGenerator(mu, ops_name[op_idx], - ops.getOpsWires()[op_idx], - !ops.getOpsInverses()[op_idx]) * - (ops.getOpsInverses()[op_idx] ? -1 : 1); - - const size_t mat_row_idx = - trainableParamNumber * num_observables; - - // clang-format off - - #if defined(_OPENMP) - #pragma omp parallel for default(none) \ - shared(H_lambda, jac, mu, scalingFactor, \ - mat_row_idx, \ - num_observables) - #endif - - // clang-format on - for (size_t obs_idx = 0; obs_idx < num_observables; - obs_idx++) { - jac[mat_row_idx + obs_idx] = - -2 * scalingFactor * - std::imag(innerProdC( - H_lambda[obs_idx].getDataVector(), - mu.getDataVector())); - } - trainableParamNumber--; - std::advance(tp_it, -1); + if ((ops_name[op_idx] == "QubitStateVector") || + (ops_name[op_idx] == "BasisState")) { + continue; + } + if (tp_it == tp_rend) { + break; // All done + } + mu.updateData(lambda.getDataVector()); + applyOperationAdj(lambda, ops, op_idx); + + if (ops.hasParams(op_idx)) { + if (current_param_idx == *tp_it) { + const T scalingFactor = + applyGenerator(mu, ops_name[op_idx], + ops.getOpsWires()[op_idx], + !ops.getOpsInverses()[op_idx]) * + (ops.getOpsInverses()[op_idx] ? -1 : 1); + + const size_t mat_row_idx = + trainableParamNumber * num_observables; + + // clang-format off + + #if defined(_OPENMP) + #pragma omp parallel for default(none) \ + shared(H_lambda, jac, mu, scalingFactor, \ + mat_row_idx, \ + num_observables) + #endif + + // clang-format on + for (size_t obs_idx = 0; obs_idx < num_observables; + obs_idx++) { + jac[mat_row_idx + obs_idx] = + -2 * scalingFactor * + std::imag( + innerProdC(H_lambda[obs_idx].getDataVector(), + mu.getDataVector())); } - current_param_idx--; + trainableParamNumber--; + ++tp_it; } - applyOperationsAdj(H_lambda, ops, static_cast(op_idx)); + current_param_idx--; } + applyOperationsAdj(H_lambda, ops, static_cast(op_idx)); } jac = Transpose(jac, jd.getNumParams(), num_observables); } diff --git a/pennylane_lightning/src/algorithms/JacobianTape.hpp b/pennylane_lightning/src/algorithms/JacobianTape.hpp index ca7d0ac6f7..26ff1e2951 100644 --- a/pennylane_lightning/src/algorithms/JacobianTape.hpp +++ b/pennylane_lightning/src/algorithms/JacobianTape.hpp @@ -275,7 +275,7 @@ template class JacobianData { * @param obs Observables for which to calculate Jacobian. * @param ops Operations used to create given state. * @param trainP List of parameters participating in Jacobian - * calculation. + * calculation. This must be sorted. */ JacobianData(size_t num_params, size_t num_elem, std::complex *ps, std::vector> obs, OpsData ops, diff --git a/pennylane_lightning/src/benchmarks/Bench_ApplyMultiRZ.cpp b/pennylane_lightning/src/benchmarks/Bench_ApplyMultiRZ.cpp new file mode 100644 index 0000000000..cbdfc92fd2 --- /dev/null +++ b/pennylane_lightning/src/benchmarks/Bench_ApplyMultiRZ.cpp @@ -0,0 +1,126 @@ +// Copyright 2022 Xanadu Quantum Technologies Inc. + +// 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. +#include +#include +#include + +#include "Constant.hpp" +#include "StateVectorManaged.hpp" + +#include "Bench_Utils.hpp" + +using Kernel = Pennylane::Gates::KernelType; + +/** + * @brief Generate distinct wires. + * + * @tparam RandomEngine Random number generator engine. + * @param num_qubits Number of qubits. + * @param num_wires Number of wires. + * + * @return std::vector + */ +template +static inline auto generateDistinctWires(RandomEngine &eng, size_t num_qubits, + size_t num_wires) + -> std::vector { + std::vector v(num_qubits, 0); + std::iota(v.begin(), v.end(), 0); + shuffle(v.begin(), v.end(), eng); + return {v.begin(), v.begin() + num_wires}; +} + +//***********************************************************************// +// applyOperation +//***********************************************************************// + +/** + * @brief Benchmark applyOperation for "MultiRZ" in PennyLane-Lightning. + * + * @tparam T Floating point precision type. + * @param kernel Pennylane::Gates::KernelType. + */ +template +static void applyOperation_MultiRZ(benchmark::State &state, Kernel kernel) { + const size_t num_gates = state.range(0); + const size_t num_qubits = state.range(1); + const size_t num_wires = state.range(2); + + if (!num_gates) { + state.SkipWithError("Invalid number of gates."); + } + + if (!num_qubits) { + state.SkipWithError("Invalid number of qubits."); + } + + if (!num_wires) { + state.SkipWithError("Invalid number of wires."); + } + + std::random_device rd; + std::mt19937_64 eng(rd()); + + std::uniform_real_distribution param_distr(-M_PI, M_PI); + + std::vector> wires; + std::vector params; + + wires.reserve(num_gates); + params.reserve(num_gates); + + for (size_t i = 0; i < num_gates; i++) { + wires.emplace_back(generateDistinctWires(eng, num_qubits, num_wires)); + params.emplace_back(param_distr(eng)); + } + + for (auto _ : state) { + Pennylane::StateVectorManaged sv{num_qubits}; + + for (size_t g = 0; g < num_gates; g++) { + sv.applyOperation(kernel, "MultiRZ", wires[g], false, {params[g]}); + } + + benchmark::DoNotOptimize(sv.getDataVector()[0]); + benchmark::DoNotOptimize(sv.getDataVector()[(1 << num_qubits) - 1]); + } +} + +BENCHMARK_APPLYOPS(applyOperation_MultiRZ, float, LM, Kernel::LM) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + benchmark::CreateDenseRange(2, 4, /*step=*/2), // num_wires + }); + +BENCHMARK_APPLYOPS(applyOperation_MultiRZ, float, PI, Kernel::PI) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + benchmark::CreateDenseRange(2, 4, /*step=*/2), // num_wires + }); + +BENCHMARK_APPLYOPS(applyOperation_MultiRZ, double, LM, Kernel::LM) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + benchmark::CreateDenseRange(2, 4, /*step=*/2), // num_wires + }); + +BENCHMARK_APPLYOPS(applyOperation_MultiRZ, double, PI, Kernel::PI) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + benchmark::CreateDenseRange(2, 4, /*step=*/2), // num_wires + }); diff --git a/pennylane_lightning/src/benchmarks/Bench_ApplyOperations.cpp b/pennylane_lightning/src/benchmarks/Bench_ApplyOperations.cpp new file mode 100644 index 0000000000..4f2768a89b --- /dev/null +++ b/pennylane_lightning/src/benchmarks/Bench_ApplyOperations.cpp @@ -0,0 +1,307 @@ +// Copyright 2022 Xanadu Quantum Technologies Inc. + +// 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. +#include +#include +#include +#include +#include +#include +#include + +#include "Constant.hpp" +#include "StateVectorManaged.hpp" + +#include "Bench_Utils.hpp" + +using Kernel = Pennylane::Gates::KernelType; + +using OpName = std::string; +using NumWires = size_t; +using NumParams = size_t; +using SerializedOp = std::tuple; + +/** + * @brief Get all gates except multi-qubit gates. + * + * @return std::unordered_map> + */ +static inline auto getLightningGates() + -> std::unordered_map> { + namespace Constant = Pennylane::Gates::Constant; + + std::unordered_map> all_gates; + for (const auto &[gate_op, gate_name] : Constant::gate_names) { + if (!Pennylane::Util::array_has_elt(Constant::multi_qubit_gates, + gate_op)) { + all_gates.emplace( + gate_name, + std::make_pair(static_cast(Pennylane::Util::lookup( + Constant::gate_wires, gate_op)), + static_cast(Pennylane::Util::lookup( + Constant::gate_num_params, gate_op)))); + } + } + return all_gates; +} + +/** + * @brief Benchmark getLightningGates. + */ +static void availableLightningGates(benchmark::State &state) { + for (auto _ : state) { + auto res = getLightningGates(); + benchmark::DoNotOptimize(res.size()); + } +} +BENCHMARK(availableLightningGates); + +/** + * @brief Serialize a list of operations. + * + * @return std::vector> + */ +static inline auto +serializeOperationsFromStrings(benchmark::State &state, + const std::vector op_names) { + std::vector ops; + const auto all_gates = getLightningGates(); + for (auto &name : op_names) { + auto gate_info_pair = all_gates.find(name); + if (gate_info_pair == all_gates.end()) { + state.SkipWithError("Invalid operation name."); + } + ops.emplace_back(std::make_tuple(std::move(name), + (*gate_info_pair).second.first, + (*gate_info_pair).second.second)); + } + return ops; +} + +/** + * @brief Benchmark serializeOperationsFromStrings. + * + * @param op_names List of operations' names. + */ +static void serializeOps(benchmark::State &state, + const std::vector op_names) { + for (auto _ : state) { + const auto res = serializeOperationsFromStrings(state, op_names); + benchmark::DoNotOptimize(res.size()); + } +} +BENCHMARK_CAPTURE(serializeOps, ops_RXYZ, {"RX", "RY", "RZ"}); +BENCHMARK_CAPTURE(serializeOps, ops_CRXYZ, {"CRX", "CRY", "CRZ"}); +BENCHMARK_CAPTURE(serializeOps, ops_PauliXYZ, {"PauliX", "PauliY", "PauliZ"}); +BENCHMARK_CAPTURE(serializeOps, ops_all, {"PauliX", "PauliY", + "PauliZ", "Hadamard", + "S", "T", + "RX", "RY", + "RZ", "Rot", + "PhaseShift", "CNOT", + "SWAP", "ControlledPhaseShift", + "CRX", "CRY", + "CRZ", "CRot", + "Toffoli", "CSWAP"}); + +/** + * @brief Generate neighboring wires from a start index + * + * @param start_idx Start index. + * @param num_qubits Number of qubits. + * @param num_wires Number of wires to be considered. + * + * @return std::vector + */ +static inline auto generateNeighboringWires(size_t start_idx, size_t num_qubits, + size_t num_wires) + -> std::vector { + std::vector v; + v.reserve(num_wires); + for (size_t k = 0; k < num_wires; k++) { + v.emplace_back((start_idx + k) % num_qubits); + } + return v; +} + +//***********************************************************************// +// applyOperation +//***********************************************************************// + +/** + * @brief Benchmark applyOperation in PennyLane-Lightning . + * + * @tparam T Floating point precision type. + * @param kernel Pennylane::Gates::KernelType. + * @param op_names List of operations' names. + */ +template +static void applyOperations_RandOps(benchmark::State &state, + const Kernel kernel, + const std::vector op_names) { + if (op_names.empty()) { + state.SkipWithError("Invalid list of operations."); + } + + const size_t num_gates = state.range(0); + const size_t num_qubits = state.range(1); + + if (!num_gates) { + state.SkipWithError("Invalid number of gates."); + } + + if (!num_qubits) { + state.SkipWithError("Invalid number of qubits."); + } + + const auto ops = serializeOperationsFromStrings(state, op_names); + + std::random_device rd; + std::mt19937_64 eng(rd()); + + std::uniform_int_distribution gate_distr(0, ops.size() - 1); + std::uniform_int_distribution inv_distr(0, 1); + std::uniform_real_distribution param_distr(0.0, 2 * M_PI); + std::uniform_int_distribution wire_distr(0, num_qubits - 1); + + auto param_generator = [¶m_distr, &eng]() { return param_distr(eng); }; + + std::vector rand_gate_names; + std::vector> rand_gate_wires; + std::vector> rand_gate_params; + + std::vector rand_inverses; + + for (size_t i = 0; i < num_gates; i++) { + size_t wire_start_idx = wire_distr(eng); + const auto &[op_name, n_wires, n_params] = ops[gate_distr(eng)]; + + rand_gate_names.emplace_back(op_name); + rand_gate_wires.emplace_back( + generateNeighboringWires(wire_start_idx, num_qubits, n_wires)); + + std::vector params(n_params); + std::generate(params.begin(), params.end(), param_generator); + rand_gate_params.emplace_back(std::move(params)); + + rand_inverses.emplace_back(static_cast(inv_distr(eng))); + } + + for (auto _ : state) { + Pennylane::StateVectorManaged sv{num_qubits}; + + for (size_t g = 0; g < num_gates; g++) { + sv.applyOperation(kernel, OpName(rand_gate_names[g]), + rand_gate_wires[g], rand_inverses[g], + rand_gate_params[g]); + } + + benchmark::DoNotOptimize(sv.getDataVector()[0]); + benchmark::DoNotOptimize(sv.getDataVector()[(1 << num_qubits) - 1]); + } +} + +/* RXYZ */ +BENCHMARK_APPLYOPS(applyOperations_RandOps, float, LM_RXYZ, Kernel::LM, + {"RX", "RY", "RZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +BENCHMARK_APPLYOPS(applyOperations_RandOps, float, PI_RXYZ, Kernel::PI, + {"RX", "RY", "RZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +BENCHMARK_APPLYOPS(applyOperations_RandOps, double, LM_RXYZ, Kernel::LM, + {"RX", "RY", "RZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +BENCHMARK_APPLYOPS(applyOperations_RandOps, double, PI_RXYZ, Kernel::PI, + {"RX", "RY", "RZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +/* PauliXYZ */ +BENCHMARK_APPLYOPS(applyOperations_RandOps, double, LM_PauliXYZ, Kernel::LM, + {"PauliX", "PauliY", "PauliZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +BENCHMARK_APPLYOPS(applyOperations_RandOps, double, PI_PauliXYZ, Kernel::PI, + {"PauliX", "PauliY", "PauliZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +BENCHMARK_APPLYOPS(applyOperations_RandOps, float, LM_PauliXYZ, Kernel::LM, + {"PauliX", "PauliY", "PauliZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +BENCHMARK_APPLYOPS(applyOperations_RandOps, float, PI_PauliXYZ, Kernel::PI, + {"PauliX", "PauliY", "PauliZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +/* From All Gates */ +BENCHMARK_APPLYOPS(applyOperations_RandOps, float, LM_all, Kernel::LM, + {"PauliX", "PauliY", "PauliZ", "Hadamard", "S", "T", "RX", + "RY", "RZ", "Rot", "PhaseShift", "CNOT", "SWAP", + "ControlledPhaseShift", "CRX", "CRY", "CRZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +BENCHMARK_APPLYOPS(applyOperations_RandOps, float, PI_all, Kernel::PI, + {"PauliX", "PauliY", "PauliZ", "Hadamard", "S", "T", "RX", + "RY", "RZ", "Rot", "PhaseShift", "CNOT", "SWAP", + "ControlledPhaseShift", "CRX", "CRY", "CRZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +BENCHMARK_APPLYOPS(applyOperations_RandOps, double, LM_all, Kernel::LM, + {"PauliX", "PauliY", "PauliZ", "Hadamard", "S", "T", "RX", + "RY", "RZ", "Rot", "PhaseShift", "CNOT", "SWAP", + "ControlledPhaseShift", "CRX", "CRY", "CRZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); + +BENCHMARK_APPLYOPS(applyOperations_RandOps, double, PI_all, Kernel::PI, + {"PauliX", "PauliY", "PauliZ", "Hadamard", "S", "T", "RX", + "RY", "RZ", "Rot", "PhaseShift", "CNOT", "SWAP", + "ControlledPhaseShift", "CRX", "CRY", "CRZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 64, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 24, /*step=*/2), // num_qubits + }); diff --git a/pennylane_lightning/src/benchmarks/Bench_BitUtil.cpp b/pennylane_lightning/src/benchmarks/Bench_BitUtil.cpp new file mode 100644 index 0000000000..6762083693 --- /dev/null +++ b/pennylane_lightning/src/benchmarks/Bench_BitUtil.cpp @@ -0,0 +1,169 @@ +// Copyright 2022 Xanadu Quantum Technologies Inc. + +// 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. +#include +#include + +#include + +#include + +/** + * @brief Benchmark generating an uniform random integer. + */ +static void generate_uniform_random_number(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_int_distribution distr; + for (auto _ : state) { + benchmark::DoNotOptimize(distr(eng)); + } +} +BENCHMARK(generate_uniform_random_number); + +/** + * @brief Benchmark naive popcount. + */ +static void naive_popcount(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_int_distribution distr; + for (auto _ : state) { + auto val = static_cast(distr(eng)); + benchmark::DoNotOptimize(Pennylane::Util::Internal::countBit1(val)); + } +} +BENCHMARK(naive_popcount); + +#if defined(__GNUC__) || defined(__clang__) +/** + * @brief Benchmark builtin popcount. + */ +static void builtin_popcount(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_int_distribution distr; + for (auto _ : state) { + auto val = static_cast(distr(eng)); + benchmark::DoNotOptimize(__builtin_popcountl(val)); + } +} +BENCHMARK(builtin_popcount); +; +#endif + +#if defined(_MSC_VER) +/** + * @brief Benchmark builtin popcount. + */ +static void msv_builtin_popcount(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_int_distribution distr; + for (auto _ : state) { + auto val = static_cast(distr(eng)); + benchmark::DoNotOptimize(__popcnt64(val)); + } +} +BENCHMARK(msv_builtin_popcount); +#endif + +/** + * @brief Benchmark naive log2PerfectPower using + * Pennylane::Util::Internal::countTrailing0. + */ +static void naive_log2PerfectPower(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_int_distribution distr; + for (auto _ : state) { + auto val = static_cast(distr(eng)); + benchmark::DoNotOptimize( + Pennylane::Util::Internal::countTrailing0(val)); + } +} +BENCHMARK(naive_log2PerfectPower); + +#if defined(__GNUC__) || defined(__clang__) +/** + * @brief Benchmark builtin log2PerfectPower using __builtin_ctzl. + */ +static void builtin_log2PerfectPower(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_int_distribution distr; + for (auto _ : state) { + auto val = static_cast(distr(eng)); + benchmark::DoNotOptimize(__builtin_ctzl(val)); + } +} +BENCHMARK(builtin_log2PerfectPower); +#endif + +#if defined(_MSC_VER) +/** + * @brief Benchmark builtin log2PerfectPower using __lzcnt64. + */ +static void msv_builtin_log2PerfectPower(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_int_distribution distr; + for (auto _ : state) { + auto val = static_cast(distr(eng)); + benchmark::DoNotOptimize(63 - __lzcnt64(val)); + } +} +BENCHMARK(msv_builtin_log2PerfectPower); +#endif + +/** + * @brief Benchmark log2PerfectPower in PennyLane-Lightning. + */ +static void lightning_isPerfectPowerOf2(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_int_distribution distr; + for (auto _ : state) { + auto val = static_cast(distr(eng)); + benchmark::DoNotOptimize(Pennylane::Util::isPerfectPowerOf2(val)); + } +} +BENCHMARK(lightning_isPerfectPowerOf2); + +/** + * @brief Benchmark fillTrailingOnes in PennyLane-Lightning. + */ +static void lightning_fillTrailingOnes(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_int_distribution distr; + for (auto _ : state) { + auto val = static_cast(distr(eng)); + benchmark::DoNotOptimize(Pennylane::Util::fillTrailingOnes(val)); + } +} +BENCHMARK(lightning_fillTrailingOnes); + +/** + * @brief Benchmark fillLeadingOnes in PennyLane-Lightning. + */ +static void lightning_fillLeadingOnes(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_int_distribution distr; + for (auto _ : state) { + auto val = static_cast(distr(eng)); + benchmark::DoNotOptimize(Pennylane::Util::fillLeadingOnes(val)); + } +} +BENCHMARK(lightning_fillLeadingOnes); diff --git a/pennylane_lightning/src/benchmarks/Bench_LinearAlgebra.cpp b/pennylane_lightning/src/benchmarks/Bench_LinearAlgebra.cpp new file mode 100644 index 0000000000..f57af90575 --- /dev/null +++ b/pennylane_lightning/src/benchmarks/Bench_LinearAlgebra.cpp @@ -0,0 +1,431 @@ +// Copyright 2022 Xanadu Quantum Technologies Inc. + +// 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. +#include +#include + +#include + +#include + +/** + * @brief Benchmark generating a vector of random complex numbers. + * + * @tparam T Floating point precision type. + */ +template +static void create_random_cmplx_vector(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_real_distribution distr; + const auto sz = static_cast(state.range(0)); + + for (auto _ : state) { + std::vector> vec; + + for (size_t i = 0; i < sz; i++) { + vec.push_back({distr(eng), distr(eng)}); + } + benchmark::DoNotOptimize(vec.size()); + } +} +BENCHMARK(create_random_cmplx_vector) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +BENCHMARK(create_random_cmplx_vector) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +//***********************************************************************// +// Inner Product +//***********************************************************************// + +/** + * @brief Benchmark std::inner_product for two vectors of complex numbers. + * + * @tparam T Floating point precision type. + */ +template static void std_innerProd_cmplx(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_real_distribution distr; + const auto sz = static_cast(state.range(0)); + + std::vector> vec1; + for (size_t i = 0; i < sz; i++) + vec1.push_back({distr(eng), distr(eng)}); + + std::vector> vec2; + for (size_t i = 0; i < sz; i++) + vec2.push_back({distr(eng), distr(eng)}); + + for (auto _ : state) { + std::complex res = std::inner_product( + vec1.data(), vec1.data() + sz, vec2.data(), std::complex(), + Pennylane::Util::ConstSum, + static_cast (*)(std::complex, std::complex)>( + &Pennylane::Util::ConstMult)); + benchmark::DoNotOptimize(res); + } +} +BENCHMARK(std_innerProd_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +BENCHMARK(std_innerProd_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +/** + * @brief Benchmark Pennylane::Util::omp_innerProd for two vectors of complex + * numbers. + * + * @tparam T Floating point precision type. + */ +template static void omp_innerProd_cmplx(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_real_distribution distr; + const auto sz = static_cast(state.range(0)); + + std::vector> vec1; + for (size_t i = 0; i < sz; i++) + vec1.push_back({distr(eng), distr(eng)}); + + std::vector> vec2; + for (size_t i = 0; i < sz; i++) + vec2.push_back({distr(eng), distr(eng)}); + + for (auto _ : state) { + std::complex res(.0, .0); + + Pennylane::Util::omp_innerProd(vec1.data(), vec2.data(), res, sz); + benchmark::DoNotOptimize(res); + } +} +BENCHMARK(omp_innerProd_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +BENCHMARK(omp_innerProd_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +#if __has_include() && defined _ENABLE_BLAS +/** + * @brief Benchmark cblas_cdotc_sub and cblas_zdotc_sub for two vectors of + * complex numbers. + * + * @tparam T Floating point precision type. + */ +template static void blas_innerProd_cmplx(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_real_distribution distr; + const auto sz = static_cast(state.range(0)); + + std::vector> vec1; + for (size_t i = 0; i < sz; i++) + vec1.push_back({distr(eng), distr(eng)}); + + std::vector> vec2; + for (size_t i = 0; i < sz; i++) + vec2.push_back({distr(eng), distr(eng)}); + + for (auto _ : state) { + std::complex res(.0, .0); + + if constexpr (std::is_same_v) { + cblas_cdotc_sub(sz, vec1.data(), 1, vec2.data(), 1, &res); + } else if constexpr (std::is_same_v) { + cblas_zdotc_sub(sz, vec1.data(), 1, vec2.data(), 1, &res); + } + + benchmark::DoNotOptimize(res); + } +} +BENCHMARK(blas_innerProd_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +BENCHMARK(blas_innerProd_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); +#endif + +//***********************************************************************// +// Matrix Transpose +//***********************************************************************// + +/** + * @brief Benchmark naive matrix transpose for a randomly generated matrix + * of complex numbers. + * + * @tparam T Floating point precision type. + */ +template static void naive_transpose_cmplx(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_real_distribution distr; + const auto sz = static_cast(state.range(0)); + + std::vector> mat1; + for (size_t i = 0; i < sz * sz; i++) + mat1.push_back({distr(eng), distr(eng)}); + + for (auto _ : state) { + std::vector> mat2(sz * sz); + + for (size_t r = 0; r < sz; r++) { + for (size_t s = 0; s < sz; s++) { + mat2[s * sz + r] = mat1[r * sz + s]; + } + } + + benchmark::DoNotOptimize(mat2[sz * sz - 1]); + } +} +BENCHMARK(naive_transpose_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +BENCHMARK(naive_transpose_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +/** + * @brief Benchmark Pennylane::Util::CFTranspose for a randomly generated matrix + * of complex numbers. + * + * @tparam T Floating point precision type. + * @tparam BLOCKSIZE Size of submatrices in the blocking technique. + */ +template +static void cf_transpose_cmplx(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_real_distribution distr; + const auto sz = static_cast(state.range(0)); + + std::vector> mat1; + for (size_t i = 0; i < sz * sz; i++) + mat1.push_back({distr(eng), distr(eng)}); + + for (auto _ : state) { + std::vector> mat2(sz * sz); + + Pennylane::Util::CFTranspose(mat1.data(), mat2.data(), sz, + sz, 0, sz, 0, sz); + benchmark::DoNotOptimize(mat2[sz * sz - 1]); + } +} +BENCHMARK(cf_transpose_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +BENCHMARK(cf_transpose_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +BENCHMARK(cf_transpose_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +BENCHMARK(cf_transpose_cmplx) + ->RangeMultiplier(1l << 3) + ->Range(1l << 5, 1l << 10); + +//***********************************************************************// +// Matrix-Vector Product +//***********************************************************************// + +/** + * @brief Benchmark PennyLane::Util::omp_matrixVecProd for a randomly generated + * matrix and vector of complex numbers. + * + * @tparam T Floating point precision type. + */ +template +static void omp_matrixVecProd_cmplx(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_real_distribution distr; + const auto sz = static_cast(state.range(0)); + + std::vector> mat; + for (size_t i = 0; i < sz * sz; i++) + mat.push_back({distr(eng), distr(eng)}); + + std::vector> vec1; + for (size_t i = 0; i < sz; i++) + vec1.push_back({distr(eng), distr(eng)}); + + for (auto _ : state) { + std::vector> vec2(sz); + + Pennylane::Util::omp_matrixVecProd(mat.data(), vec1.data(), vec2.data(), + sz, sz, Trans::NoTranspose); + benchmark::DoNotOptimize(vec2[sz - 1]); + } +} +BENCHMARK(omp_matrixVecProd_cmplx) + ->RangeMultiplier(1l << 2) + ->Range(1l << 4, 1l << 8); + +BENCHMARK(omp_matrixVecProd_cmplx) + ->RangeMultiplier(1l << 2) + ->Range(1l << 4, 1l << 8); + +#if __has_include() && defined _ENABLE_BLAS +/** + * @brief Benchmark cblas_cgemv and cblas_zgemv for a randomly generated + * matrix and vector of complex numbers. + * + * @tparam T Floating point precision type. + */ +template +static void blas_matrixVecProd_cmplx(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_real_distribution distr; + const auto sz = static_cast(state.range(0)); + + std::vector> mat; + for (size_t i = 0; i < sz * sz; i++) + mat.push_back({distr(eng), distr(eng)}); + + std::vector> vec1; + for (size_t i = 0; i < sz; i++) + vec1.push_back({distr(eng), distr(eng)}); + + const auto tr = static_cast(Trans::NoTranspose); + constexpr std::complex co{1, 0}; + constexpr std::complex cz{0, 0}; + + for (auto _ : state) { + std::vector> vec2(sz); + + if constexpr (std::is_same_v) { + cblas_cgemv(CblasRowMajor, tr, sz, sz, &co, mat.data(), sz, + vec1.data(), 1, &cz, vec2.data(), 1); + } else if constexpr (std::is_same_v) { + cblas_zgemv(CblasRowMajor, tr, sz, sz, &co, mat.data(), sz, + vec1.data(), 1, &cz, vec2.data(), 1); + } + + benchmark::DoNotOptimize(vec2[sz - 1]); + } +} +BENCHMARK(blas_matrixVecProd_cmplx) + ->RangeMultiplier(1l << 2) + ->Range(1l << 4, 1l << 8); + +BENCHMARK(blas_matrixVecProd_cmplx) + ->RangeMultiplier(1l << 2) + ->Range(1l << 4, 1l << 8); +#endif + +//***********************************************************************// +// Matrix-Matrix Product +//***********************************************************************// + +/** + * @brief Benchmark Pennylane::Util::omp_matrixMatProd for two randomly + * generated matrices of complex numbers. + * + * @tparam T Floating point precision type. + */ +template +static void omp_matrixMatProd_cmplx(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_real_distribution distr; + const auto sz = static_cast(state.range(0)); + + std::vector> m_left; + for (size_t i = 0; i < sz * sz; i++) + m_left.push_back({distr(eng), distr(eng)}); + + std::vector> m_right; + for (size_t i = 0; i < sz * sz; i++) + m_right.push_back({distr(eng), distr(eng)}); + + const auto m_right_tr = Pennylane::Util::Transpose(m_right, sz, sz); + + for (auto _ : state) { + std::vector> m_out(sz * sz); + + Pennylane::Util::omp_matrixMatProd(m_left.data(), m_right_tr.data(), + m_out.data(), sz, sz, sz, + Trans::Transpose); + benchmark::DoNotOptimize(m_out[sz * sz - 1]); + } +} +BENCHMARK(omp_matrixMatProd_cmplx) + ->RangeMultiplier(1l << 2) + ->Range(1l << 4, 1l << 8); + +BENCHMARK(omp_matrixMatProd_cmplx) + ->RangeMultiplier(1l << 2) + ->Range(1l << 4, 1l << 8); + +#if __has_include() && defined _ENABLE_BLAS +/** + * @brief Benchmark cblas_cgemm and cblas_zgemm for two randomly + * generated matrices of complex numbers. + * + * @tparam T Floating point precision type. + */ +template +static void blas_matrixMatProd_cmplx(benchmark::State &state) { + std::random_device rd; + std::mt19937_64 eng(rd()); + std::uniform_real_distribution distr; + const auto sz = static_cast(state.range(0)); + + std::vector> m_left; + for (size_t i = 0; i < sz * sz; i++) + m_left.push_back({distr(eng), distr(eng)}); + + std::vector> m_right; + for (size_t i = 0; i < sz * sz; i++) + m_right.push_back({distr(eng), distr(eng)}); + + const auto tr = static_cast(Trans::NoTranspose); + constexpr std::complex co{1, 0}; + constexpr std::complex cz{0, 0}; + + for (auto _ : state) { + std::vector> m_out(sz * sz); + + if constexpr (std::is_same_v) { + cblas_cgemm(CblasRowMajor, CblasNoTrans, tr, sz, sz, sz, &co, + m_left.data(), sz, m_right.data(), sz, &cz, + m_out.data(), sz); + } else if constexpr (std::is_same_v) { + cblas_zgemm(CblasRowMajor, CblasNoTrans, tr, sz, sz, sz, &co, + m_left.data(), sz, m_right.data(), sz, &cz, + m_out.data(), sz); + } + benchmark::DoNotOptimize(m_out[sz * sz - 1]); + } +} +BENCHMARK(blas_matrixMatProd_cmplx) + ->RangeMultiplier(1l << 2) + ->Range(1l << 4, 1l << 8); + +BENCHMARK(blas_matrixMatProd_cmplx) + ->RangeMultiplier(1l << 2) + ->Range(1l << 4, 1l << 8); +#endif diff --git a/pennylane_lightning/src/benchmarks/Bench_Utils.hpp b/pennylane_lightning/src/benchmarks/Bench_Utils.hpp new file mode 100644 index 0000000000..de5dcadb19 --- /dev/null +++ b/pennylane_lightning/src/benchmarks/Bench_Utils.hpp @@ -0,0 +1,28 @@ +// Copyright 2022 Xanadu Quantum Technologies Inc. + +// 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. +#pragma once + +#include + +/** + * @brief A benchmark macro to register func(...) + * using benchmark::internal::FunctionBenchmark. + */ +#define BENCHMARK_APPLYOPS(func, t, test_case_name, ...) \ + BENCHMARK_PRIVATE_DECLARE(func) = \ + (benchmark::internal::RegisterBenchmarkInternal( \ + new benchmark::internal::FunctionBenchmark( \ + #func "<" #t ">" \ + "/" #test_case_name, \ + [](benchmark::State &st) { func(st, __VA_ARGS__); }))) diff --git a/pennylane_lightning/src/benchmarks/CMakeLists.txt b/pennylane_lightning/src/benchmarks/CMakeLists.txt new file mode 100644 index 0000000000..ec36630d8b --- /dev/null +++ b/pennylane_lightning/src/benchmarks/CMakeLists.txt @@ -0,0 +1,85 @@ +cmake_minimum_required(VERSION 3.14) + +project(pennylane_lightning_benchmarks) + +set(CMAKE_CXX_STANDARD 17) + +message(STATUS "ENABLE_GB is ON. Find GBenchmark.") +find_package(benchmark QUIET) + +if (NOT benchmark_FOUND) + message(STATUS "GBenchmark is not found. Fetch GBenchmark.") + + include(FetchContent) + + # Fetch GTest; required for GBenchmark + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 # latest + ) + FetchContent_GetProperties(googletest) + if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) + endif() + + # Fetch GBenchmark and surpress internal tests. + FetchContent_Declare( + googlebenchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG v1.6.1 # latest + ) + set(BENCHMARK_ENABLE_TESTING off) + FetchContent_GetProperties(googlebenchmark) + if(NOT googlebenchmark_POPULATED) + FetchContent_Populate(googlebenchmark) + add_subdirectory(${googlebenchmark_SOURCE_DIR} ${googlebenchmark_BINARY_DIR}) + endif() +else() + message(STATUS "GBenchmark found.") +endif() + +################################################################################ +# Define a library for dependencies +################################################################################ + +add_library(lightning_benchmarks_dependency INTERFACE) +target_link_libraries(lightning_benchmarks_dependency INTERFACE lightning_utils + lightning_external_libs + benchmark::benchmark_main) + +################################################################################ +# Add benchmark_utils exe +################################################################################ + +set(GBENCH_SRC Bench_BitUtil.cpp + Bench_LinearAlgebra.cpp) + +add_executable(utils ${GBENCH_SRC}) + +add_compile_definitions(utils INTERFACE _ENABLE_BLAS=1) + +target_link_libraries(utils PRIVATE lightning_benchmarks_dependency) + +################################################################################ +# Add benchmark_apply_operations exe +################################################################################ + +add_executable(apply_operations Bench_ApplyOperations.cpp) + +target_link_libraries(apply_operations PRIVATE lightning_compile_options + lightning_gates + lightning_simulator + lightning_benchmarks_dependency) + +################################################################################ +# Add benchmark_apply_multirz exe +################################################################################ + +add_executable(apply_multirz Bench_ApplyMultiRZ.cpp) + +target_link_libraries(apply_multirz PRIVATE lightning_compile_options + lightning_gates + lightning_simulator + lightning_benchmarks_dependency) \ No newline at end of file diff --git a/pennylane_lightning/src/benchmarks/README.md b/pennylane_lightning/src/benchmarks/README.md new file mode 100644 index 0000000000..d5001a82c2 --- /dev/null +++ b/pennylane_lightning/src/benchmarks/README.md @@ -0,0 +1,355 @@ +# Lightning Google-Benchmark Suite +The PennyLane-Lightning benchmark suite powered by [google-benchmark](https://github.com/google/benchmark) (GB). +To use GB scripts, you can perform `make gbenchmark` or run +```console +$ cmake pennylane_lightning/src/ -BBuildGBench -DBUILD_BENCHMARKS=ON -DENABLE_OPENMP=ON -DENABLE_BLAS=ON -DCMAKE_BUILD_TYPE=Release +$ cmake --build ./BuildGBench --target utils apply_operations apply_multirz +``` + +## Google-Benchmark +The main requirement for these scripts is [google-benchmark](https://github.com/google/benchmark). +The CMake uses `FetchContent` to fetch and install the library if the `find_package` command fails +to find and load GB. + +### GB CLI Flags +```console +benchmark [--benchmark_list_tests={true|false}] + [--benchmark_filter=] + [--benchmark_min_time=] + [--benchmark_repetitions=] + [--benchmark_enable_random_interleaving={true|false}] + [--benchmark_report_aggregates_only={true|false}] + [--benchmark_display_aggregates_only={true|false}] + [--benchmark_format=] + [--benchmark_out=] + [--benchmark_out_format=] + [--benchmark_color={auto|true|false}] + [--benchmark_counters_tabular={true|false}] + [--benchmark_context==,...] + [--benchmark_time_unit={ns|us|ms|s}] + [--v=] +``` + +## Implementation details +`make gbenchmark` compiles benchmark executables from, +- `Bench_BitUtil.cpp`, +- `Bench_LinearAlgebra.cpp`, +- `Bench_ApplyOperations.cpp`, +- `Bench_ApplyMultiRZ.cpp`. + + +### `benchmarks/utils` +To benchmark the linear algebra and bit operations in PennyLane-Lightning, one can run: +```console +$ make gbenchmark +$ ./BuildGBench/benchmarks/utils +``` + +The `std_innerProd_cmplx` method in `Bench_LinearAlgebra.cpp` runs benchmarks computing +the inner product of two randomly generated vectors with complex numbers (`std::complex`) +using `std::inner_product`. +If one wants to try a few arguments in some ranges and generate a benchmark for each such value, +GB offers `Range` and `Ranges` to do so, +```C +BENCHMARK(std_innerProd_cmplx) + ->Range(1l << 5, 1l << 10); +``` + +By default the arguments in the range are generated in multiples of 8 and the command above picks +`{32, 64, 512, 1024}`. To change the range multiplier, one should use `RangeMultiplier`. +The following code selects `{4, 8, 16, 32, 64, 128, 256, 512, 1024}`, +```C +BENCHMARK(std_innerProd_cmplx) + ->RangeMultiplier(2) + ->Range(1l << 2, 1l << 10); +``` + +To filter the results one can use regex and `--benchmark_filter`. For example, +the following command runs only `std_innerProd_cmplx` benchmark tests in `./benchmarks/utils`: + +```console +$ ./BuildGBench/benchmarks/utils --benchmark_filter=std_innerProd_cmplx + +Running ./BuildGBench/benchmarks/utils +Run on (8 X 2270.36 MHz CPU s) +CPU Caches: + L1 Data 48 KiB (x4) + L1 Instruction 32 KiB (x4) + L2 Unified 1280 KiB (x4) + L3 Unified 12288 KiB (x1) +Load Average: 0.34, 0.38, 0.48 +--------------------------------------------------------------------------- +Benchmark Time CPU Iterations +--------------------------------------------------------------------------- +std_innerProd_cmplx/32 20.8 ns 20.8 ns 27948132 +std_innerProd_cmplx/64 43.2 ns 43.2 ns 16170905 +std_innerProd_cmplx/512 420 ns 420 ns 1665298 +std_innerProd_cmplx/1024 847 ns 847 ns 817415 +std_innerProd_cmplx/32 26.7 ns 26.7 ns 28857715 +std_innerProd_cmplx/64 62.6 ns 62.6 ns 10719664 +std_innerProd_cmplx/512 518 ns 518 ns 1305655 +std_innerProd_cmplx/1024 1028 ns 1028 ns 662981 +``` + +Besides, one can use `--benchmark_time_unit` to get the results in `ns`, `us`, `ms`, or `s`. +Check **GB CLI Flags** for the list of flags. + + +### `benchmarks/apply_operations` +To benchmark the `Pennylane::StateVectorManaged` and `applyOperation` in PennyLane-Lightning, one can run: +```console +$ make gbenchmark +$ ./BuildGBench/benchmarks/apply_operations +``` + +The following arguments could be altered: +- `Pennylane::Gates::KernelType`; +- Floating point precision type; +- List of gates that script would randomly pick from to apply to the state vector; +- Range for the number of gates to be applied; and +- Range for the number of qubits. + +For example, in the code below, +- `applyOperations_RandOps` is the name of the method in `Bench_ApplyOperations.cpp`, +- `double` is the floating point precision type, +- `LM_RXYZ` is just a name for the parameters, +- `Kernel::LM` is the kernel type, +- `{"RX", "RY", "RZ"}` is the list of gates, +- `CreateRange(8, 16, 2)` creates a (sparse) range for the number of gates, and +- `CreateDenseRange(6, 10, 2)` creates a (dense) range for the number of qubits. + +```C +BENCHMARK_APPLYOPS(applyOperations_RandOps, double, LM_RXYZ, Kernel::LM, + {"RX", "RY", "RZ"}) + ->ArgsProduct({ + benchmark::CreateRange(8, 16, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 10, /*step=*/2), // num_qubits + }); +``` + +```console +Run on (8 X 3002.54 MHz CPU s) +CPU Caches: + L1 Data 48 KiB (x4) + L1 Instruction 32 KiB (x4) + L2 Unified 1280 KiB (x4) + L3 Unified 12288 KiB (x1) +Load Average: 0.81, 0.66, 0.50 +---------------------------------------------------------------------------------------- +Benchmark Time CPU Iterations +---------------------------------------------------------------------------------------- +applyOperations_RandOps/LM_RXYZ/8/6 1254 ns 1254 ns 529355 +applyOperations_RandOps/LM_RXYZ/16/6 2630 ns 2630 ns 281743 +applyOperations_RandOps/LM_RXYZ/8/8 3794 ns 3794 ns 171244 +applyOperations_RandOps/LM_RXYZ/16/8 7969 ns 7969 ns 82555 +applyOperations_RandOps/LM_RXYZ/8/10 14773 ns 14773 ns 46396 +applyOperations_RandOps/LM_RXYZ/16/10 30174 ns 30174 ns 23573 +``` + +One can use `--benchmark_format` to get the results in other formats: ``. +Check **GB CLI Flags** for the list of flags. + + +### `benchmarks/apply_multirz` +To benchmark the `Pennylane::StateVectorManaged` and `applyOperation` for `"MultiRZ"` in PennyLane-Lightning, +one can run: +```console +$ make gbenchmark +$ ./BuildGBench/benchmarks/apply_multirz +``` + +For example, in the code below, +- `applyOperation_MultiRZ` is the name of the method in `Bench_ApplyMultiRZ.cpp`, +- `double` is the floating point precision type, +- `LM` is just a name for the parameters, +- `Pennylane::Gates::KernelType::LM` is the kernel type, +- `CreateRange(8, 16, 2)` creates a (sparse) range for the number of gates, +- `CreateDenseRange(6, 10, 2)` creates a (dense) range for the number of qubits, and +- `{2}` is a list of values for the number of wires involved in `MultiRZ`. + +```C +BENCHMARK_APPLYOPS(applyOperation_MultiRZ, double, LM, Kernel::LM) + ->ArgsProduct({ + benchmark::CreateRange(8, 16, /*mul=*/2), // num_gates + benchmark::CreateDenseRange(6, 10, /*step=*/2), // num_qubits + {2}, // num_wires + }); +``` + +```console +Run on (8 X 2565.86 MHz CPU s) +CPU Caches: + L1 Data 48 KiB (x4) + L1 Instruction 32 KiB (x4) + L2 Unified 1280 KiB (x4) + L3 Unified 12288 KiB (x1) +Load Average: 0.70, 0.56, 0.53 +------------------------------------------------------------------------------------ +Benchmark Time CPU Iterations +------------------------------------------------------------------------------------ +applyOperation_MultiRZ/LM/8/6/2 1803 ns 1803 ns 402773 +applyOperation_MultiRZ/LM/16/6/2 3636 ns 3636 ns 178912 +applyOperation_MultiRZ/LM/8/8/2 6310 ns 6310 ns 109673 +applyOperation_MultiRZ/LM/16/8/2 12540 ns 12540 ns 56605 +applyOperation_MultiRZ/LM/8/10/2 25478 ns 25478 ns 29963 +applyOperation_MultiRZ/LM/16/10/2 54317 ns 54316 ns 12293 +``` + +## GB Compare Tooling +One can use [`compare.py`](https://github.com/google/benchmark/blob/main/tools/compare.py) to compare the results of the GB scripts. + +### Compare float vs double +```console +$ python3 compare.py filters ./BuildGBench/benchmarks/utils float double + +Run on (8 X 1853.84 MHz CPU s) +CPU Caches: + L1 Data 48 KiB (x4) + L1 Instruction 32 KiB (x4) + L2 Unified 1280 KiB (x4) + L3 Unified 12288 KiB (x1) +Load Average: 2.14, 1.03, 0.72 + +Comparing float to double (from ./BuildGBench/benchmarks/utils) +Benchmark Time CPU Time Old Time New CPU Old CPU New +-------------------------------------------------------------------------------------------------------------------------------------------------- +create_random_cmplx_vector<[float vs. double]>/32 +0.1779 +0.1809 756 890 754 890 +create_random_cmplx_vector<[float vs. double]>/64 +0.1846 +0.1846 1446 1713 1446 1713 +create_random_cmplx_vector<[float vs. double]>/512 +0.2030 +0.2030 10894 13105 10894 13105 +create_random_cmplx_vector<[float vs. double]>/1024 +0.1904 +0.1904 21741 25879 21741 25879 +std_innerProd_cmplx<[float vs. double]>/32 +0.4076 +0.4075 21 29 21 29 +std_innerProd_cmplx<[float vs. double]>/64 +0.2559 +0.2559 50 63 50 63 +std_innerProd_cmplx<[float vs. double]>/512 +0.0820 +0.0820 481 520 481 520 +std_innerProd_cmplx<[float vs. double]>/1024 +0.0677 +0.0677 964 1030 964 1030 +omp_innerProd_cmplx<[float vs. double]>/32 -0.0439 -0.0439 201 193 201 193 +omp_innerProd_cmplx<[float vs. double]>/64 -0.0501 -0.0501 237 225 237 225 +omp_innerProd_cmplx<[float vs. double]>/512 +0.0556 +0.0556 661 698 661 698 +omp_innerProd_cmplx<[float vs. double]>/1024 +0.0599 +0.0599 1159 1228 1159 1228 +blas_innerProd_cmplx<[float vs. double]>/32 +1.6790 +1.6789 12 33 12 33 +blas_innerProd_cmplx<[float vs. double]>/64 +2.4392 +2.4391 21 73 21 73 +blas_innerProd_cmplx<[float vs. double]>/512 +3.1349 +3.1348 139 577 139 577 +blas_innerProd_cmplx<[float vs. double]>/1024 +3.1949 +3.1947 281 1177 281 1177 +naive_transpose_cmplx<[float vs. double]>/32 +1.5293 +1.5293 867 2192 867 2192 +naive_transpose_cmplx<[float vs. double]>/64 +5.6706 +5.6704 3104 20706 3104 20706 +naive_transpose_cmplx<[float vs. double]>/512 +1.3715 +1.3714 772694 1832435 772680 1832363 +naive_transpose_cmplx<[float vs. double]>/1024 +1.8269 +1.8266 3528387 9974273 3528267 9972909 +cf_transpose_cmplx<[float vs. double], 16>/32 +1.5507 +1.5507 754 1924 754 1924 +cf_transpose_cmplx<[float vs. double], 16>/64 +1.9015 +1.9012 3245 9416 3245 9415 +cf_transpose_cmplx<[float vs. double], 16>/512 +1.3707 +1.3708 687593 1630107 687562 1630078 +cf_transpose_cmplx<[float vs. double], 16>/1024 +1.4324 +1.4324 3019284 7344169 3019256 7343956 +cf_transpose_cmplx<[float vs. double], 32>/32 -0.1493 -0.1493 860 732 860 732 +cf_transpose_cmplx<[float vs. double], 32>/64 +0.1806 +0.1806 3202 3780 3202 3780 +cf_transpose_cmplx<[float vs. double], 32>/512 +0.1310 +0.1310 692794 783577 692789 783567 +cf_transpose_cmplx<[float vs. double], 32>/1024 +0.3344 +0.3344 3035260 4050185 3035148 4050084 +omp_matrixVecProd_cmplx<[float vs. double]>/16 +0.4616 +0.4612 2173 3177 2173 3175 +omp_matrixVecProd_cmplx<[float vs. double]>/64 +0.8316 +0.8329 4407 8072 4401 8066 +omp_matrixVecProd_cmplx<[float vs. double]>/256 +0.3514 +0.3484 43206 58388 43169 58209 +blas_matrixVecProd_cmplx<[float vs. double]>/16 +0.2655 +0.2655 327 414 327 414 +blas_matrixVecProd_cmplx<[float vs. double]>/64 +0.5087 +0.5475 7924 11956 7684 11891 +blas_matrixVecProd_cmplx<[float vs. double]>/256 -0.1335 -0.1542 34236 29665 33248 28123 +omp_matrixMatProd_cmplx<[float vs. double]>/16 +0.8089 +0.8214 4978 9006 4846 8827 +omp_matrixMatProd_cmplx<[float vs. double]>/64 +0.3022 +0.3174 164390 214068 162245 213740 +omp_matrixMatProd_cmplx<[float vs. double]>/256 +0.1685 +0.2645 10982033 12832305 10108172 12781493 +blas_matrixMatProd_cmplx<[float vs. double]>/16 +0.3376 +0.3376 1813 2426 1813 2426 +blas_matrixMatProd_cmplx<[float vs. double]>/64 -0.2104 -0.1625 75876 59908 70623 59146 +blas_matrixMatProd_cmplx<[float vs. double]>/256 +1.0512 +1.1981 3147247 6455757 2926291 6432367 +OVERALL_GEOMEAN +0.6082 +0.6174 0 0 0 0 +``` + +### Compare LM vs PI +```console +$ python3 compare.py filters ./BuildGBench/benchmarks/apply_operations LM_all PI_all + +Run on (8 X 1853.84 MHz CPU s) +CPU Caches: + L1 Data 48 KiB (x4) + L1 Instruction 32 KiB (x4) + L2 Unified 1280 KiB (x4) + L3 Unified 12288 KiB (x1) +Load Average: 2.14, 1.03, 0.72 + +Comparing LM_all to PI_all (from ./BuildGBench/benchmarks/apply_operations) +Benchmark Time CPU Time Old Time New CPU Old CPU New +-------------------------------------------------------------------------------------------------------------------------------------------------------- +applyOperations_RandOps/[LM_all vs. PI_all]/8/6 +1.9704 +1.9704 740 2199 740 2199 +applyOperations_RandOps/[LM_all vs. PI_all]/16/6 +2.0117 +2.0122 1674 5043 1674 5043 +applyOperations_RandOps/[LM_all vs. PI_all]/32/6 +2.2919 +2.2922 3346 11015 3346 11014 +applyOperations_RandOps/[LM_all vs. PI_all]/64/6 +2.5689 +2.5689 5935 21183 5935 21183 +applyOperations_RandOps/[LM_all vs. PI_all]/8/8 +2.7298 +2.7300 1786 6663 1786 6663 +applyOperations_RandOps/[LM_all vs. PI_all]/16/8 +3.3816 +3.3817 4096 17949 4096 17949 +applyOperations_RandOps/[LM_all vs. PI_all]/32/8 +3.3162 +3.3160 8413 36313 8413 36312 +applyOperations_RandOps/[LM_all vs. PI_all]/64/8 +3.3376 +3.3383 17901 77648 17898 77648 +applyOperations_RandOps/[LM_all vs. PI_all]/8/10 +0.8145 +0.8149 12753 23140 12750 23140 +applyOperations_RandOps/[LM_all vs. PI_all]/16/10 +3.3284 +3.3283 12224 52910 12224 52908 +applyOperations_RandOps/[LM_all vs. PI_all]/32/10 +3.5780 +3.5782 29088 133166 29087 133165 +applyOperations_RandOps/[LM_all vs. PI_all]/64/10 +3.6910 +3.6910 57028 267523 57027 267516 +applyOperations_RandOps/[LM_all vs. PI_all]/8/12 +2.1417 +2.1422 34042 106947 34035 106946 +applyOperations_RandOps/[LM_all vs. PI_all]/16/12 +1.2923 +1.2924 69446 159194 69445 159193 +applyOperations_RandOps/[LM_all vs. PI_all]/32/12 +0.3308 +0.3311 123251 164026 123225 164027 +applyOperations_RandOps/[LM_all vs. PI_all]/64/12 +0.4931 +0.4932 228365 340976 228358 340977 +applyOperations_RandOps/[LM_all vs. PI_all]/8/14 +0.2166 +0.2170 166710 202824 166660 202826 +applyOperations_RandOps/[LM_all vs. PI_all]/16/14 +0.2849 +0.2850 290310 373017 290278 373015 +applyOperations_RandOps/[LM_all vs. PI_all]/32/14 +0.7255 +0.7255 487546 841280 487537 841262 +applyOperations_RandOps/[LM_all vs. PI_all]/64/14 +0.2781 +0.2782 1183131 1512171 1183031 1512189 +applyOperations_RandOps/[LM_all vs. PI_all]/8/16 +0.4468 +0.4468 603890 873689 603850 873680 +applyOperations_RandOps/[LM_all vs. PI_all]/16/16 +0.7906 +0.7906 818479 1465578 818477 1465574 +applyOperations_RandOps/[LM_all vs. PI_all]/32/16 +0.6816 +0.6816 2073721 3487107 2073704 3487078 +applyOperations_RandOps/[LM_all vs. PI_all]/64/16 +1.6422 +1.6422 3754217 9919561 3754227 9919574 +applyOperations_RandOps/[LM_all vs. PI_all]/8/18 +5.3381 +5.3383 1148955 7282233 1148880 7281975 +applyOperations_RandOps/[LM_all vs. PI_all]/16/18 +3.6852 +3.6851 2853039 13366939 2852961 13366542 +applyOperations_RandOps/[LM_all vs. PI_all]/32/18 +2.8063 +2.8062 7753666 29512416 7753528 29511707 +applyOperations_RandOps/[LM_all vs. PI_all]/64/18 +3.7002 +3.7001 12625921 59344364 12625599 59342202 +applyOperations_RandOps/[LM_all vs. PI_all]/8/20 +5.1516 +5.1515 5338621 32840815 5338531 32840042 +applyOperations_RandOps/[LM_all vs. PI_all]/16/20 +2.2917 +2.2918 19277997 63457796 19277350 63456982 +applyOperations_RandOps/[LM_all vs. PI_all]/32/20 +3.9042 +3.9043 26541069 130163532 26540619 130161877 +applyOperations_RandOps/[LM_all vs. PI_all]/64/20 +2.4273 +2.4273 59168965 202787913 59166800 202783283 +applyOperations_RandOps/[LM_all vs. PI_all]/8/22 +1.9594 +1.9594 47074334 139312151 47072997 139308537 +applyOperations_RandOps/[LM_all vs. PI_all]/16/22 +0.2689 +0.2689 81669457 103631032 81668216 103629527 +applyOperations_RandOps/[LM_all vs. PI_all]/32/22 +0.6116 +0.6116 140642752 226654300 140633151 226648168 +applyOperations_RandOps/[LM_all vs. PI_all]/64/22 +0.7737 +0.7739 274673406 487190407 274632334 487182892 +applyOperations_RandOps/[LM_all vs. PI_all]/8/24 +1.5876 +1.5877 134665624 348462053 134658560 348456864 +applyOperations_RandOps/[LM_all vs. PI_all]/16/24 +1.1122 +1.1124 299975114 633617035 299955193 633610974 +applyOperations_RandOps/[LM_all vs. PI_all]/32/24 +1.2715 +1.2717 586053918 1331235001 585993913 1331231522 +applyOperations_RandOps/[LM_all vs. PI_all]/64/24 +2.6378 +2.6360 1310929400 4768865343 1310874809 4766317332 +applyOperations_RandOps/[LM_all vs. PI_all]/8/6 +4.3271 +4.3269 1111 5917 1111 5917 +applyOperations_RandOps/[LM_all vs. PI_all]/16/6 +4.8872 +4.8872 2067 12167 2067 12166 +applyOperations_RandOps/[LM_all vs. PI_all]/32/6 +1.6727 +1.6728 3856 10306 3856 10306 +applyOperations_RandOps/[LM_all vs. PI_all]/64/6 +2.0128 +2.0128 7517 22649 7517 22647 +applyOperations_RandOps/[LM_all vs. PI_all]/8/8 +1.2648 +1.2649 2667 6041 2667 6041 +applyOperations_RandOps/[LM_all vs. PI_all]/16/8 +1.5491 +1.5495 4749 12107 4749 12106 +applyOperations_RandOps/[LM_all vs. PI_all]/32/8 +1.2613 +1.2615 10592 23952 10591 23952 +applyOperations_RandOps/[LM_all vs. PI_all]/64/8 +1.3703 +1.3703 19844 47037 19844 47036 +applyOperations_RandOps/[LM_all vs. PI_all]/8/10 +0.4299 +0.4299 11146 15937 11146 15937 +applyOperations_RandOps/[LM_all vs. PI_all]/16/10 +1.5375 +1.5374 16193 41090 16193 41088 +applyOperations_RandOps/[LM_all vs. PI_all]/32/10 +2.1457 +2.1459 42644 134147 42642 134144 +applyOperations_RandOps/[LM_all vs. PI_all]/64/10 +1.1057 +1.1057 125253 263739 125246 263736 +applyOperations_RandOps/[LM_all vs. PI_all]/8/12 +0.5885 +0.5886 78426 124582 78420 124578 +applyOperations_RandOps/[LM_all vs. PI_all]/16/12 +0.9362 +0.9362 115317 223274 115315 223275 +applyOperations_RandOps/[LM_all vs. PI_all]/32/12 +0.6730 +0.6736 288087 481955 287962 481934 +applyOperations_RandOps/[LM_all vs. PI_all]/64/12 +0.8035 +0.8038 562554 1014588 562473 1014591 +applyOperations_RandOps/[LM_all vs. PI_all]/8/14 +0.8138 +0.8137 321458 583072 321441 583007 +applyOperations_RandOps/[LM_all vs. PI_all]/16/14 +1.3614 +1.3614 441768 1043179 441746 1043136 +applyOperations_RandOps/[LM_all vs. PI_all]/32/14 -0.2703 -0.2703 1039344 758407 1039229 758344 +applyOperations_RandOps/[LM_all vs. PI_all]/64/14 -0.3541 -0.3540 2447532 1580937 2447451 1580945 +applyOperations_RandOps/[LM_all vs. PI_all]/8/16 -0.2848 -0.2846 1168091 835419 1167696 835421 +applyOperations_RandOps/[LM_all vs. PI_all]/16/16 -0.2519 -0.2519 2576772 1927698 2576744 1927609 +applyOperations_RandOps/[LM_all vs. PI_all]/32/16 -0.1967 -0.1967 4287545 3444148 4287377 3444219 +applyOperations_RandOps/[LM_all vs. PI_all]/64/16 +0.9295 +0.9297 3517705 6787403 3517485 6787529 +applyOperations_RandOps/[LM_all vs. PI_all]/8/18 +0.6427 +0.6431 2452274 4028443 2451798 4028488 +applyOperations_RandOps/[LM_all vs. PI_all]/16/18 +0.8408 +0.8409 5010837 9224016 5010720 9224049 +applyOperations_RandOps/[LM_all vs. PI_all]/32/18 +2.4354 +2.4354 8253116 28352787 8253155 28353130 +applyOperations_RandOps/[LM_all vs. PI_all]/64/18 +3.0803 +3.0807 15936887 65027739 15934953 65025322 +applyOperations_RandOps/[LM_all vs. PI_all]/8/20 +2.1608 +2.1611 11403578 36044911 11401519 36040897 +applyOperations_RandOps/[LM_all vs. PI_all]/16/20 +1.7726 +1.7726 23552648 65301677 23551243 65298261 +applyOperations_RandOps/[LM_all vs. PI_all]/32/20 +1.7032 +1.7036 50289215 135940574 50283336 135945470 +applyOperations_RandOps/[LM_all vs. PI_all]/64/20 +1.0846 +1.0854 121426703 253123057 121355090 253072588 +applyOperations_RandOps/[LM_all vs. PI_all]/8/22 +0.3131 +0.3113 136573314 179341161 136557503 179070048 +applyOperations_RandOps/[LM_all vs. PI_all]/16/22 -0.2532 -0.2532 164691973 122992503 164689541 122992680 +applyOperations_RandOps/[LM_all vs. PI_all]/32/22 -0.2946 -0.2946 339590201 239558327 339567138 239522753 +applyOperations_RandOps/[LM_all vs. PI_all]/64/22 -0.2928 -0.2927 739949287 523301029 739828092 523291634 +applyOperations_RandOps/[LM_all vs. PI_all]/8/24 -0.2177 -0.2177 559569887 437775862 559545239 437751825 +applyOperations_RandOps/[LM_all vs. PI_all]/16/24 +0.0081 +0.0082 789837195 796244100 789800171 796240876 +applyOperations_RandOps/[LM_all vs. PI_all]/32/24 -0.0240 -0.0239 1511013927 1474718667 1510856430 1474724199 +applyOperations_RandOps/[LM_all vs. PI_all]/64/24 +1.8154 +1.8156 1931502602 5438015975 1931394005 5438018004 +OVERALL_GEOMEAN +1.1905 +1.1906 0 0 0 0 +``` \ No newline at end of file diff --git a/pennylane_lightning/src/bindings/Bindings.cpp b/pennylane_lightning/src/bindings/Bindings.cpp index eb1e73daf8..3abfea0888 100644 --- a/pennylane_lightning/src/bindings/Bindings.cpp +++ b/pennylane_lightning/src/bindings/Bindings.cpp @@ -324,6 +324,23 @@ void lightning_class_bindings(py::module &m) { const std::vector &wires) { return M.expval(operation, wires); }) + .def("generate_samples", + [](Measures &M, size_t num_wires, size_t num_shots) { + auto &&result = M.generate_samples(num_shots); + const size_t ndim = 2; + const std::vector shape{num_shots, num_wires}; + constexpr auto sz = sizeof(size_t); + const std::vector strides{sz * num_wires, sz}; + // return 2-D NumPy array + return py::array(py::buffer_info( + result.data(), /* data as contiguous array */ + sz, /* size of one scalar */ + py::format_descriptor::format(), /* data type */ + ndim, /* number of dimensions */ + shape, /* shape of the matrix */ + strides /* strides for each axis */ + )); + }) .def("var", [](Measures &M, const std::string &operation, const std::vector &wires) { return M.var(operation, wires); @@ -364,6 +381,12 @@ PYBIND11_MODULE(lightning_qubit_ops, // NOLINT: No control over Pybind internals &Gates::getIndicesAfterExclusion), "Get statevector indices for gate application"); + /* Add compile info */ + m.def("compile_info", &getCompileInfo, "Compiled binary information."); + + /* Add runtime info */ + m.def("runtime_info", &getRuntimeInfo, "Runtime information."); + /* Add EXPORTED_KERNELS */ std::vector> exported_kernel_ops; @@ -372,7 +395,7 @@ PYBIND11_MODULE(lightning_qubit_ops, // NOLINT: No control over Pybind internals const auto implemented_gates = implementedGatesForKernel(kernel); for (const auto gate_op : implemented_gates) { const auto gate_name = - std::string(lookup(Constant::gate_names, gate_op)); + std::string(lookup(Gates::Constant::gate_names, gate_op)); exported_kernel_ops.emplace_back(kernel_name, gate_name); } } @@ -381,8 +404,9 @@ PYBIND11_MODULE(lightning_qubit_ops, // NOLINT: No control over Pybind internals /* Add DEFAULT_KERNEL_FOR_OPS */ std::map default_kernel_ops_map; - for (const auto &[gate_op, name] : Constant::gate_names) { - const auto kernel = lookup(Constant::default_kernel_for_gates, gate_op); + for (const auto &[gate_op, name] : Gates::Constant::gate_names) { + const auto kernel = + lookup(Gates::Constant::default_kernel_for_gates, gate_op); const auto kernel_name = Util::lookup(kernel_id_name_pairs, kernel); default_kernel_ops_map.emplace(std::string(name), kernel_name); } diff --git a/pennylane_lightning/src/bindings/Bindings.hpp b/pennylane_lightning/src/bindings/Bindings.hpp index 5d79774ffd..142f11223b 100644 --- a/pennylane_lightning/src/bindings/Bindings.hpp +++ b/pennylane_lightning/src/bindings/Bindings.hpp @@ -19,8 +19,10 @@ #pragma once #include "AdjointDiff.hpp" #include "JacobianProd.hpp" +#include "Macros.hpp" #include "Measures.hpp" #include "OpToMemberFuncPtr.hpp" +#include "RuntimeInfo.hpp" #include "StateVectorRaw.hpp" #include "pybind11/complex.h" @@ -227,4 +229,61 @@ template void registerKernelsToPyexport(PyClass &pyclass) { registerKernelsToPyexportIter(pyclass); } + +/** + * @brief Return basic information of the compiled binary. + */ +auto getCompileInfo() -> pybind11::dict { + using namespace Util::Constant; + using namespace pybind11::literals; + + const std::string_view cpu_arch_str = [] { + switch (cpu_arch) { + case CPUArch::X86_64: + return "x86_64"; + case CPUArch::PPC64: + return "PPC64"; + case CPUArch::ARM: + return "ARM"; + default: + return "Unknown"; + } + }(); + + const std::string_view compiler_name_str = [] { + switch (compiler) { + case Compiler::GCC: + return "GCC"; + case Compiler::Clang: + return "Clang"; + case Compiler::MSVC: + return "MSVC"; + case Compiler::NVCC: + return "NVCC"; + case Compiler::NVHPC: + return "NVHPC"; + default: + return "Unknown"; + } + }(); + + const auto compiler_version_str = getCompilerVersion(); + + return pybind11::dict("cpu.arch"_a = cpu_arch_str, + "compiler.name"_a = compiler_name_str, + "compiler.version"_a = compiler_version_str, + "AVX2"_a = use_avx2, "AVX512F"_a = use_avx512f); +} + +/** + * @brief Return basic information of runtime environment + */ +auto getRuntimeInfo() -> pybind11::dict { + using namespace Util::Constant; + using namespace pybind11::literals; + + return pybind11::dict("AVX"_a = RuntimeInfo::AVX(), + "AVX2"_a = RuntimeInfo::AVX2(), + "AVX512F"_a = RuntimeInfo::AVX512F()); +} } // namespace Pennylane diff --git a/pennylane_lightning/src/gates/CMakeLists.txt b/pennylane_lightning/src/gates/CMakeLists.txt index 3bc00ff329..36799dc02a 100644 --- a/pennylane_lightning/src/gates/CMakeLists.txt +++ b/pennylane_lightning/src/gates/CMakeLists.txt @@ -1,9 +1,9 @@ project(lightning_gates) set(CMAKE_CXX_STANDARD 17) -set(SIMULATOR_FILES GateUtil.cpp CACHE INTERNAL "" FORCE) +set(GATES_FILES GateUtil.cpp CACHE INTERNAL "" FORCE) -add_library(lightning_gates STATIC ${SIMULATOR_FILES}) +add_library(lightning_gates STATIC ${GATES_FILES}) target_link_libraries(lightning_gates PRIVATE lightning_compile_options lightning_external_libs lightning_utils) diff --git a/pennylane_lightning/src/gates/GateImplementationsLM.hpp b/pennylane_lightning/src/gates/GateImplementationsLM.hpp index 0cc6a61950..1094106dd3 100644 --- a/pennylane_lightning/src/gates/GateImplementationsLM.hpp +++ b/pennylane_lightning/src/gates/GateImplementationsLM.hpp @@ -248,6 +248,7 @@ class GateImplementationsLM : public PauliGenerator { static void applyMatrix(std::complex *arr, size_t num_qubits, const std::complex *matrix, const std::vector &wires, bool inverse) { + using Util::Trans; assert(num_qubits >= wires.size()); switch (wires.size()) { @@ -258,7 +259,7 @@ class GateImplementationsLM : public PauliGenerator { applyTwoQubitOp(arr, num_qubits, matrix, wires, inverse); break; default: { - size_t dim = 1U << wires.size(); + size_t dim = size_t{1U} << wires.size(); std::vector indices; indices.resize(dim); @@ -270,8 +271,8 @@ class GateImplementationsLM : public PauliGenerator { size_t idx = k | inner_idx; size_t n_wires = wires.size(); for (size_t pos = 0; pos < n_wires; pos++) { - bitswap(idx, n_wires - pos - 1, - num_qubits - wires[pos] - 1); + idx = bitswap(idx, n_wires - pos - 1, + num_qubits - wires[pos] - 1); } indices[inner_idx] = idx; coeffs_in[inner_idx] = arr[idx]; @@ -1112,8 +1113,8 @@ class GateImplementationsLM : public PauliGenerator { const size_t i10 = i00 | rev_wire1_shift; const size_t i11 = i00 | rev_wire0_shift | rev_wire1_shift; - arr[i00] = ComplexPrecisionT{}; - arr[i01] = ComplexPrecisionT{}; + arr[i00] = ComplexPrecisionT{0.0, 0.0}; + arr[i01] = ComplexPrecisionT{0.0, 0.0}; std::swap(arr[i10], arr[i11]); } @@ -1212,7 +1213,8 @@ class GateImplementationsLM : public PauliGenerator { } for (size_t k = 0; k < Util::exp2(num_qubits); k++) { - arr[k] *= (2 * int(Util::popcount(k & wires_parity) % 2) - 1); + arr[k] *= static_cast( + 2 * int(Util::popcount(k & wires_parity) % 2) - 1); } // NOLINTNEXTLINE(readability-magic-numbers) return static_cast(0.5); diff --git a/pennylane_lightning/src/gates/SelectKernel.hpp b/pennylane_lightning/src/gates/SelectKernel.hpp index 5057ed9b42..54056db379 100644 --- a/pennylane_lightning/src/gates/SelectKernel.hpp +++ b/pennylane_lightning/src/gates/SelectKernel.hpp @@ -28,38 +28,6 @@ #include namespace Pennylane::Gates { -/** - * @brief For lookup from any array of pair whose first elements are - * GateOperation. - * - * As Util::lookup can be used in constexpr context, this function is redundant - * (by the standard). But GCC 9 still does not accept Util::lookup in constexpr - * some cases. - */ -///@{ -template -constexpr auto -static_lookup(const std::array, size> &arr) -> T { - for (size_t idx = 0; idx < size; idx++) { - if (std::get<0>(arr[idx]) == op) { - return std::get<1>(arr[idx]); - } - } - return T{}; -} - -template -constexpr auto -static_lookup(const std::array, size> &arr) - -> T { - for (size_t idx = 0; idx < size; idx++) { - if (std::get<0>(arr[idx]) == op) { - return std::get<1>(arr[idx]); - } - } - return T{}; -} -///@} /// @cond DEV namespace Internal { diff --git a/pennylane_lightning/src/simulator/DynamicDispatcher.cpp b/pennylane_lightning/src/simulator/DynamicDispatcher.cpp index 315b7a102e..5caaf99e55 100644 --- a/pennylane_lightning/src/simulator/DynamicDispatcher.cpp +++ b/pennylane_lightning/src/simulator/DynamicDispatcher.cpp @@ -24,7 +24,6 @@ #include "SelectKernel.hpp" using namespace Pennylane; -using namespace Pennylane::Util; /// @cond DEV namespace { @@ -50,7 +49,7 @@ constexpr auto gateOpToFunctor() { Gates::GateOpToMemberFuncPtr::value; assert(params.size() == - Gates::static_lookup(Gates::Constant::gate_num_params)); + Util::static_lookup(Gates::Constant::gate_num_params)); Gates::callGateOps(func_ptr, data, num_qubits, wires, inverse, params); }; } @@ -77,7 +76,7 @@ constexpr auto constructGateOpsFunctorTupleIter() { return constructGateOpsFunctorTupleIter< PrecisionT, ParamT, GateImplementation, gate_idx + 1>(); } else { - return prepend_to_tuple( + return Util::prepend_to_tuple( std::pair{gate_op, gateOpToFunctor()}, @@ -97,7 +96,7 @@ constexpr auto constructGeneratorOpsFunctorTupleIter() { } else if (gntr_idx < GateImplementation::implemented_generators.size()) { constexpr auto gntr_op = GateImplementation::implemented_generators[gntr_idx]; - return prepend_to_tuple( + return Util::prepend_to_tuple( std::pair{gntr_op, Gates::GeneratorOpToMemberFuncPtr< PrecisionT, GateImplementation, gntr_op>::value}, @@ -144,7 +143,7 @@ void registerAllImplementedGateOps() { const auto &gate_op_func_pair) { const auto &[gate_op, func] = gate_op_func_pair; std::string op_name = - std::string(lookup(Gates::Constant::gate_names, gate_op)); + std::string(Util::lookup(Gates::Constant::gate_names, gate_op)); dispatcher.registerGateOperation(op_name, GateImplementation::kernel_id, func); return gate_op; @@ -169,8 +168,8 @@ void registerAllImplementedGeneratorOps() { auto registerGeneratorToDispatcher = [&dispatcher](const auto &gntr_op_func_pair) { const auto &[gntr_op, func] = gntr_op_func_pair; - std::string op_name = - std::string(lookup(Gates::Constant::generator_names, gntr_op)); + std::string op_name = std::string( + Util::lookup(Gates::Constant::generator_names, gntr_op)); dispatcher.registerGeneratorOperation( op_name, GateImplementation::kernel_id, func); return gntr_op; diff --git a/pennylane_lightning/src/simulator/DynamicDispatcher.hpp b/pennylane_lightning/src/simulator/DynamicDispatcher.hpp index 83536f9076..34300d0b95 100644 --- a/pennylane_lightning/src/simulator/DynamicDispatcher.hpp +++ b/pennylane_lightning/src/simulator/DynamicDispatcher.hpp @@ -53,6 +53,7 @@ template int registerAllAvailableKernels(); /// @endcond namespace Pennylane { +/// @cond DEV /** * @brief These functions are only used to register kernels to the dynamic * dispatcher. @@ -60,14 +61,15 @@ namespace Pennylane { template struct registerBeforeMain; template <> struct registerBeforeMain { - static inline int dummy = + const static inline int dummy = Internal::registerAllAvailableKernels(); }; template <> struct registerBeforeMain { - static inline int dummy = + const static inline int dummy = Internal::registerAllAvailableKernels(); }; +/// @endcond /** * @brief DynamicDispatcher class diff --git a/pennylane_lightning/src/simulator/Measures.hpp b/pennylane_lightning/src/simulator/Measures.hpp index 26208b6ba1..a4a8841b69 100644 --- a/pennylane_lightning/src/simulator/Measures.hpp +++ b/pennylane_lightning/src/simulator/Measures.hpp @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include #include #include "LinearAlgebra.hpp" @@ -30,6 +33,7 @@ #include "StateVectorRaw.hpp" namespace Pennylane { + /** * @brief Observable's Measurement Class. * @@ -46,7 +50,7 @@ class Measures { using CFP_t = std::complex; public: - Measures(const SVType &provided_statevector) + explicit Measures(const SVType &provided_statevector) : original_statevector{provided_statevector} {}; /** @@ -256,5 +260,92 @@ class Measures { return expected_value_list; }; + + /** + * @brief Generate samples using the alias method. + * Reference: https://en.wikipedia.org/wiki/Alias_method + * + * @param num_samples The number of samples to generate. + * @return 1-D vector of samples in binary, each sample is + * separated by a stride equal to the number of qubits. + */ + std::vector generate_samples(size_t num_samples) { + const size_t num_qubits = original_statevector.getNumQubits(); + auto &&probabilities = probs(); + + std::vector samples(num_samples * num_qubits, 0); + std::mt19937 generator(std::random_device{}()); + std::uniform_real_distribution distribution(0.0, 1.0); + std::unordered_map cache; + + const size_t N = probabilities.size(); + std::vector bucket(N); + std::vector bucket_partner(N); + std::stack overfull_bucket_ids; + std::stack underfull_bucket_ids; + + for (size_t i = 0; i < N; i++) { + bucket[i] = N * probabilities[i]; + bucket_partner[i] = i; + if (bucket[i] > 1.0) { + overfull_bucket_ids.push(i); + } + if (bucket[i] < 1.0) { + underfull_bucket_ids.push(i); + } + } + + // Run alias algorithm + while (!underfull_bucket_ids.empty() && !overfull_bucket_ids.empty()) { + // get an overfull bucket + size_t i = overfull_bucket_ids.top(); + + // get an underfull bucket + size_t j = underfull_bucket_ids.top(); + underfull_bucket_ids.pop(); + + // underfull bucket is partned with an overfull bucket + bucket_partner[j] = i; + bucket[i] = bucket[i] + bucket[j] - 1; + + // if overfull bucket is now underfull + // put in underfull stack + if (bucket[i] < 1) { + overfull_bucket_ids.pop(); + underfull_bucket_ids.push(i); + } + + // if overfull bucket is full -> remove + else if (bucket[i] == 1.0) { + overfull_bucket_ids.pop(); + } + } + + // Pick samples + for (size_t i = 0; i < num_samples; i++) { + fp_t pct = distribution(generator) * N; + auto idx = static_cast(pct); + if (pct - idx > bucket[idx]) { + idx = bucket_partner[idx]; + } + // If cached, retrieve sample from cache + if (cache.count(idx) != 0) { + size_t cache_id = cache[idx]; + auto it_temp = samples.begin() + cache_id * num_qubits; + std::copy(it_temp, it_temp + num_qubits, + samples.begin() + i * num_qubits); + } + // If not cached, compute + else { + for (size_t j = 0; j < num_qubits; j++) { + samples[i * num_qubits + (num_qubits - 1 - j)] = + (idx >> j) & 1U; + } + cache[idx] = i; + } + } + + return samples; + } }; // class Measures } // namespace Pennylane diff --git a/pennylane_lightning/src/simulator/StateVectorBase.hpp b/pennylane_lightning/src/simulator/StateVectorBase.hpp index a8e6477cb2..311f0d08a6 100644 --- a/pennylane_lightning/src/simulator/StateVectorBase.hpp +++ b/pennylane_lightning/src/simulator/StateVectorBase.hpp @@ -48,7 +48,7 @@ inline void apply##GATE_NAME##_(const std::vector &wires, \ bool inverse, Ts &&...args) { \ auto *arr = getData(); \ - static_assert(Gates::static_lookup( \ + static_assert(Util::static_lookup( \ Gates::Constant::gate_num_params) == sizeof...(Ts), \ "The provided number of parameters for gate " #GATE_NAME \ " is wrong."); \ @@ -65,7 +65,7 @@ inline void apply##GATE_NAME(const std::vector &wires, \ bool inverse, Ts &&...args) { \ constexpr auto kernel = \ - Gates::static_lookup( \ + Util::static_lookup( \ Gates::Constant::default_kernel_for_gates); \ apply##GATE_NAME##_(wires, inverse, \ std::forward(args)...); \ @@ -299,9 +299,8 @@ template class StateVectorBase { namespace Constant = Gates::Constant; using Gates::GateOperation; using Gates::SelectKernel; - using Gates::static_lookup; - constexpr auto kernel = static_lookup( + constexpr auto kernel = Util::static_lookup( Constant::default_kernel_for_gates); static_assert( Util::array_has_elt(SelectKernel::implemented_gates, @@ -315,9 +314,8 @@ template class StateVectorBase { namespace Constant = Gates::Constant; using Gates::GateOperation; using Gates::SelectKernel; - using Gates::static_lookup; - constexpr auto kernel = static_lookup( + constexpr auto kernel = Util::static_lookup( Constant::default_kernel_for_gates); static_assert( Util::array_has_elt(SelectKernel::implemented_gates, diff --git a/pennylane_lightning/src/tests/.clang-tidy b/pennylane_lightning/src/tests/.clang-tidy index 3ed93f21bf..75afabace1 100644 --- a/pennylane_lightning/src/tests/.clang-tidy +++ b/pennylane_lightning/src/tests/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,-llvmlibc-*,modernize-*,-modernize-use-trailing-return-type,clang-analyzer-cplusplus*,openmp-*,performance-*,portability-*,readability-*,-readability-magic-numbers,-modernize-avoid-c-arrays' +Checks: '-*,clang-diagnostic-*,clang-analyzer-*,-llvmlibc-*,modernize-*,-modernize-use-trailing-return-type,clang-analyzer-cplusplus*,openmp-*,performance-*,portability-*,readability-*,-modernize-avoid-c-arrays,-readability-magic-numbers,hicpp-*,-hicpp-no-array-decay,-hicpp-avoid-c-arrays,bugprone-suspicious-*,llvm-namespace-comment,cppcoreguidelines-slicing,cppcoreguidelines-special-member-functions' WarningsAsErrors: '*' HeaderFilterRegex: '.*' AnalyzeTemporaryDtors: false @@ -216,7 +216,7 @@ CheckOptions: - key: modernize-use-auto.RemoveStars value: 'false' - key: readability-magic-numbers.IgnorePowersOf2IntegerValues - value: 'false' + value: 'true' - key: portability-simd-intrinsics.Std value: '' - key: readability-redundant-member-init.IgnoreBaseInCopyConstructors diff --git a/pennylane_lightning/src/tests/CMakeLists.txt b/pennylane_lightning/src/tests/CMakeLists.txt index c507f938bf..8187417f30 100644 --- a/pennylane_lightning/src/tests/CMakeLists.txt +++ b/pennylane_lightning/src/tests/CMakeLists.txt @@ -68,9 +68,12 @@ endif() add_executable(compile_time_tests compile_time_tests.cpp) target_link_libraries(compile_time_tests lightning_gates lightning_utils) -set(TEST_SOURCES Test_AdjDiff.cpp +set(TEST_SOURCES CreateAllWires.cpp + Test_AdjDiff.cpp # Test_Bindings.cpp Test_DynamicDispatcher.cpp + Test_Error.cpp + Test_GateImplementations_CompareKernels.cpp Test_GateImplementations_Generator.cpp Test_GateImplementations_Inverse.cpp Test_GateImplementations_Matrix.cpp @@ -78,8 +81,10 @@ set(TEST_SOURCES Test_AdjDiff.cpp Test_GateImplementations_Param.cpp Test_GateUtil.cpp Test_Internal.cpp + Test_LinearAlgebra.cpp Test_Measures.cpp Test_OpToMemberFuncPtr.cpp + Test_RuntimeInfo.cpp Test_StateVectorManaged.cpp Test_StateVectorRaw.cpp Test_Util.cpp diff --git a/pennylane_lightning/src/tests/CreateAllWires.cpp b/pennylane_lightning/src/tests/CreateAllWires.cpp new file mode 100644 index 0000000000..6bea13f39a --- /dev/null +++ b/pennylane_lightning/src/tests/CreateAllWires.cpp @@ -0,0 +1,29 @@ +#include "CreateAllWires.hpp" +namespace Pennylane { +auto createAllWires(size_t n_qubits, Gates::GateOperation gate_op, bool order) + -> std::vector> { + if (Util::array_has_elt(Gates::Constant::multi_qubit_gates, gate_op)) { + // make all possible 2^N permutations + std::vector> res; + res.reserve((1U << n_qubits) - 1); + for (size_t k = 1; k < (static_cast(1U) << n_qubits); k++) { + std::vector wires; + wires.reserve(Util::popcount(k)); + + for (size_t i = 0; i < n_qubits; i++) { + if (((k >> i) & 1U) == 1U) { + wires.emplace_back(i); + } + } + + res.push_back(wires); + } + return res; + } // else + const size_t n_wires = Util::lookup(Gates::Constant::gate_wires, gate_op); + if (order) { + return PermutationGenerator(n_qubits, n_wires).all_perms(); + } // else + return CombinationGenerator(n_qubits, n_wires).all_perms(); +} +} // namespace Pennylane diff --git a/pennylane_lightning/src/tests/CreateAllWires.hpp b/pennylane_lightning/src/tests/CreateAllWires.hpp new file mode 100644 index 0000000000..a05a7f8e9b --- /dev/null +++ b/pennylane_lightning/src/tests/CreateAllWires.hpp @@ -0,0 +1,112 @@ +#pragma once +#include "BitUtil.hpp" +#include "Constant.hpp" +#include "ConstantUtil.hpp" +#include "GateOperation.hpp" + +#include +#include + +namespace Pennylane { + +class WiresGenerator { + public: + [[nodiscard]] virtual auto all_perms() const + -> const std::vector> & = 0; +}; + +/** + * @brief + * @rst Generating all permutation of wires without ordering (often called + * as combination). The size of all combination is given as :math:`n \choose r`. + * + * We use the recursion formula + * :math:`{n \choose r} = {n \choose r-1} + {n-1 \choose r}` + * @endrst + */ +class CombinationGenerator : public WiresGenerator { + private: + std::vector v_; + std::vector> all_perms_; + + public: + void comb(size_t n, size_t r) { + if (r == 0) { + all_perms_.push_back(v_); + return; + } + if (n < r) { + return; + } + + v_[r - 1] = n - 1; + comb(n - 1, r - 1); + + comb(n - 1, r); + } + + CombinationGenerator(size_t n, size_t r) { + v_.resize(r); + comb(n, r); + } + + [[nodiscard]] auto all_perms() const + -> const std::vector> & override { + return all_perms_; + } +}; + +/** + * @brief + * @rst Generating all permutation of wires with ordering. The size of all + * permutation is given as :math:`{}_{n}P_r=n!/(n-r)!r!`. + * @endrst + * + * We use the recursion formula + * :math:`{}_n P_r = n {}_{n-1} P_{r-1}` + */ +class PermutationGenerator : public WiresGenerator { + private: + std::vector> all_perms_; + std::vector available_elts_; + std::vector v; + + public: + void perm(size_t n, size_t r) { + if (r == 0) { + all_perms_.push_back(v); + return; + } + for (size_t i = 0; i < n; i++) { + v[r - 1] = available_elts_[i]; + std::swap(available_elts_[n - 1], available_elts_[i]); + perm(n - 1, r - 1); + std::swap(available_elts_[n - 1], available_elts_[i]); + } + } + + PermutationGenerator(size_t n, size_t r) { + v.resize(r); + + available_elts_.resize(n); + std::iota(available_elts_.begin(), available_elts_.end(), 0); + perm(n, r); + } + + [[nodiscard]] auto all_perms() const + -> const std::vector> & override { + return all_perms_; + } +}; + +/** + * @brief Create all possible combination of wires + * for a given number of qubits and gate operation + * + * @param n_qubits Number of qubits + * @param gate_op Gate operation + * @param order Whether the ordering matters (if true, permutation is used) + */ +auto createAllWires(size_t n_qubits, Gates::GateOperation gate_op, bool order) + -> std::vector>; +} // namespace Pennylane diff --git a/pennylane_lightning/src/tests/TestHelpers.hpp b/pennylane_lightning/src/tests/TestHelpers.hpp index 74faeeb5ce..b204cbd87f 100644 --- a/pennylane_lightning/src/tests/TestHelpers.hpp +++ b/pennylane_lightning/src/tests/TestHelpers.hpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -15,25 +16,14 @@ #include namespace Pennylane { -template struct remove_complex { using type = T; }; -template struct remove_complex> { - using type = T; -}; -template using remove_complex_t = typename remove_complex::type; - -template struct is_complex : std::false_type {}; - -template struct is_complex> : std::true_type {}; - -template constexpr bool is_complex_v = is_complex::value; - -template struct PLApprox { +template > struct PLApprox { const std::vector &comp_; explicit PLApprox(const std::vector &comp) : comp_{comp} {} - remove_complex_t margin_{}; - remove_complex_t epsilon_ = std::numeric_limits::epsilon() * 100; + Util::remove_complex_t margin_{}; + Util::remove_complex_t epsilon_ = + std::numeric_limits::epsilon() * 100; template [[nodiscard]] bool compare(const std::vector &lhs) const { @@ -42,7 +32,7 @@ template struct PLApprox { } for (size_t i = 0; i < lhs.size(); i++) { - if constexpr (is_complex_v) { + if constexpr (Util::is_complex_v) { if (lhs[i].real() != Approx(comp_[i].real()) .epsilon(epsilon_) .margin(margin_) || @@ -60,6 +50,7 @@ template struct PLApprox { } return true; } + [[nodiscard]] std::string describe() const { std::ostringstream ss; ss << "is Approx to {"; @@ -69,15 +60,26 @@ template struct PLApprox { ss << "}" << std::endl; return ss.str(); } - PLApprox &epsilon(remove_complex_t eps) { + + PLApprox &epsilon(Util::remove_complex_t eps) { epsilon_ = eps; return *this; } - PLApprox &margin(remove_complex_t m) { + PLApprox &margin(Util::remove_complex_t m) { margin_ = m; return *this; } }; + +/** + * @brief Simple helper for PLApprox for the cases when the class template + * deduction does not work well. + */ +template +PLApprox approx(const std::vector &vec) { + return PLApprox(vec); +} + template std::ostream &operator<<(std::ostream &os, const PLApprox &approx) { os << approx.describe(); @@ -110,7 +112,7 @@ isApproxEqual(const std::vector &data1, const typename Data_t::value_type eps = std::numeric_limits::epsilon() * 100) { - return data1 == PLApprox(data2); + return data1 == PLApprox(data2).epsilon(eps); } /** @@ -169,7 +171,8 @@ void scaleVector(std::vector> &data, Data_t scalar) { template auto createZeroState(size_t num_qubits) -> std::vector> { - std::vector> res(1U << num_qubits, {0.0, 0.0}); + std::vector> res(size_t{1U} << num_qubits, + {0.0, 0.0}); res[0] = std::complex{1.0, 0.0}; return res; } @@ -180,39 +183,29 @@ auto createZeroState(size_t num_qubits) template auto createPlusState(size_t num_qubits) -> std::vector> { - std::vector> res(1U << num_qubits, {1.0, 0.0}); + std::vector> res(size_t{1U} << num_qubits, + {1.0, 0.0}); for (auto &elt : res) { elt /= std::sqrt(1U << num_qubits); } return res; } -/** - * @brief Calculate the squared norm of a vector - */ -template -auto squaredNorm(const std::complex *data, size_t data_size) - -> PrecisionT { - return std::transform_reduce( - data, data + data_size, PrecisionT{}, std::plus(), - static_cast &)>( - &std::norm)); -} - /** * @brief create a random state */ template auto createRandomState(RandomEngine &re, size_t num_qubits) -> std::vector> { - std::vector> res(1U << num_qubits, {0.0, 0.0}); + std::vector> res(size_t{1U} << num_qubits, + {0.0, 0.0}); std::uniform_real_distribution dist; - for (size_t idx = 0; idx < (1U << num_qubits); idx++) { + for (size_t idx = 0; idx < (size_t{1U} << num_qubits); idx++) { res[idx] = {dist(re), dist(re)}; } scaleVector(res, std::complex{1.0, 0.0} / - std::sqrt(squaredNorm(res.data(), res.size()))); + std::sqrt(Util::squaredNorm(res.data(), res.size()))); return res; } @@ -224,7 +217,7 @@ auto createRandomState(RandomEngine &re, size_t num_qubits) template auto createProductState(std::string_view str) { using Pennylane::Util::INVSQRT2; std::vector> st; - st.resize(1U << str.length()); + st.resize(size_t{1U} << str.length()); std::vector zero{1.0, 0.0}; std::vector one{0.0, 1.0}; @@ -234,7 +227,7 @@ template auto createProductState(std::string_view str) { std::vector minus{INVSQRT2(), -INVSQRT2()}; - for (size_t k = 0; k < (1U << str.length()); k++) { + for (size_t k = 0; k < (size_t{1U} << str.length()); k++) { PrecisionT elt = 1.0; for (size_t n = 0; n < str.length(); n++) { char c = str[n]; @@ -286,66 +279,14 @@ auto createParams(Gates::GateOperation op) -> std::vector { case 0: return {}; case 1: - return {0.312}; + return {PrecisionT{0.312}}; case 3: - return {0.128, -0.563, 1.414}; + return {PrecisionT{0.128}, PrecisionT{-0.563}, PrecisionT{1.414}}; default: PL_ABORT("The number of parameters for a given gate is unknown."); } return {}; } -/** - * @brief Generate random unitary matrix - * - * @return Generated unitary matrix in row-major format - */ -template -auto randomUnitary(RandomEngine &re, size_t num_qubits) - -> std::vector> { - using ComplexPrecisionT = std::complex; - const size_t dim = (1U << num_qubits); - std::vector res(dim * dim, ComplexPrecisionT{}); - - std::normal_distribution dist; - - auto generator = [&dist, &re]() -> ComplexPrecisionT { - return ComplexPrecisionT{dist(re), dist(re)}; - }; - - std::generate(res.begin(), res.end(), generator); - - // Simple algorithm to make rows orthogonal with Gram-Schmidt - // This algorithm is unstable but works for a small matrix. - // Use QR decomposition when we have LAPACK support. - - for (size_t row2 = 0; row2 < dim; row2++) { - ComplexPrecisionT *row2_p = res.data() + row2 * dim; - for (size_t row1 = 0; row1 < row2; row1++) { - const ComplexPrecisionT *row1_p = res.data() + row1 * dim; - ComplexPrecisionT dot12 = Util::innerProdC(row1_p, row2_p, dim); - ComplexPrecisionT dot11 = squaredNorm(row1_p, dim); - - // orthogonalize row2 - std::transform( - row2_p, row2_p + dim, row1_p, row2_p, - [scale = dot12 / dot11](auto &elt2, const auto &elt1) { - return elt2 - scale * elt1; - }); - } - } - - // Normalize each row - for (size_t row = 0; row < dim; row++) { - ComplexPrecisionT *row_p = res.data() + row * dim; - PrecisionT norm2 = std::sqrt(squaredNorm(row_p, dim)); - - // noramlize row2 - std::transform(row_p, row_p + dim, row_p, [norm2](const auto c) { - return (static_cast(1.0) / norm2) * c; - }); - } - return res; -} template struct PrecisionToName; diff --git a/pennylane_lightning/src/tests/TestKernels.hpp b/pennylane_lightning/src/tests/TestKernels.hpp index e9b9cfa785..74085a4d75 100644 --- a/pennylane_lightning/src/tests/TestKernels.hpp +++ b/pennylane_lightning/src/tests/TestKernels.hpp @@ -10,4 +10,4 @@ using TestKernels = Pennylane::Util::TypeList; + Pennylane::Gates::GateImplementationsPI, void>; diff --git a/pennylane_lightning/src/tests/Test_AdjDiff.cpp b/pennylane_lightning/src/tests/Test_AdjDiff.cpp index dfbac67214..c487babf53 100644 --- a/pennylane_lightning/src/tests/Test_AdjDiff.cpp +++ b/pennylane_lightning/src/tests/Test_AdjDiff.cpp @@ -1,5 +1,3 @@ -#define _USE_MATH_DEFINES - #include #include #include @@ -13,11 +11,16 @@ #include #include "AdjointDiff.hpp" +#include "StateVectorManaged.hpp" #include "StateVectorRaw.hpp" #include "Util.hpp" #include "TestHelpers.hpp" +#ifndef _USE_MATH_DEFINES +#define _USE_MATH_DEFINES +#endif + using namespace Pennylane; using namespace Pennylane::Algorithms; @@ -50,7 +53,7 @@ TEST_CASE("AdjointJacobian::adjointJacobian Op=RX, Obs=Z", for (const auto &p : param) { auto ops = OpsData({"RX"}, {{p}}, {{0}}, {false}); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); cdata[0] = std::complex{1, 0}; StateVectorRaw psi(cdata.data(), cdata.size()); @@ -82,7 +85,7 @@ TEST_CASE("AdjointJacobian::adjointJacobian Op=RY, Obs=X", for (const auto &p : param) { auto ops = OpsData({"RY"}, {{p}}, {{0}}, {false}); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); cdata[0] = std::complex{1, 0}; StateVectorRaw psi(cdata.data(), cdata.size()); @@ -109,7 +112,7 @@ TEST_CASE("AdjointJacobian::adjointJacobian Op=RX, Obs=[Z,Z]", const size_t num_obs = 2; std::vector jacobian(num_obs * num_params, 0); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -127,7 +130,7 @@ TEST_CASE("AdjointJacobian::adjointJacobian Op=RX, Obs=[Z,Z]", CAPTURE(jacobian); CHECK(-sin(param[0]) == Approx(jacobian[0]).margin(1e-7)); - CHECK(0.0 == Approx(jacobian[1 * num_params + 1]).margin(1e-7)); + CHECK(0.0 == Approx(jacobian[1 * num_obs - 1]).margin(1e-7)); } } TEST_CASE("AdjointJacobian::adjointJacobian Op=[RX,RX,RX], Obs=[Z,Z,Z]", @@ -140,7 +143,7 @@ TEST_CASE("AdjointJacobian::adjointJacobian Op=[RX,RX,RX], Obs=[Z,Z,Z]", const size_t num_obs = 3; std::vector jacobian(num_obs * num_params, 0); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -179,7 +182,7 @@ TEST_CASE("AdjointJacobian::adjointJacobian Op=[RX,RX,RX], Obs=[Z,Z,Z], " std::vector jacobian(num_obs * num_params, 0); std::vector t_params{0, 2}; - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -214,7 +217,7 @@ TEST_CASE("AdjointJacobian::adjointJacobian Op=[RX,RX,RX], Obs=[ZZZ]", const size_t num_obs = 1; std::vector jacobian(num_obs * num_params, 0); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -249,7 +252,7 @@ TEST_CASE("AdjointJacobian::adjointJacobian Op=Mixed, Obs=[XXX]", const size_t num_obs = 1; std::vector jacobian(num_obs * num_params, 0); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -388,4 +391,90 @@ TEST_CASE("AdjointJacobian::adjointJacobian Mixed Ops, Obs and TParams", CHECK(expected[1] == Approx(jacobian[1])); CHECK(expected[2] == Approx(jacobian[2])); } -} \ No newline at end of file +} + +TEST_CASE("AdjointJacobian::applyObservable visitor checks", + "[AdjointJacobian]") { + SECTION("Obs with params 0") { + AdjointJacobian adj; + std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; + std::vector expec_results{0.90096887, 0.80901699, -0.5}; + + auto obs_default = ObsDatum({"PauliZ"}, {{}}, {{0}}); + auto ops = + OpsData({"RX"}, {{expec_results[0]}}, {{0}}, {false}); + std::vector out_data(1); + + for (std::size_t i = 0; i < param.size(); i++) { + StateVectorManaged psi(2); + JacobianData jd(1, psi.getLength(), psi.getData(), + {obs_default}, ops, {1}); + adj.adjointJacobian(out_data, jd, true); + } + } + SECTION("Obs with params std::vector>") { + AdjointJacobian adj; + std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; + std::vector expec_results{0.90096887, 0.80901699, -0.5}; + using v_type = std::vector>; + + v_type z_par{ONE(), ZERO(), ZERO(), + ZERO()}; + + auto obs_default = ObsDatum({"MyPauliZ"}, {z_par}, {{0}}); + + auto ops = + OpsData({"RX"}, {{expec_results[0]}}, {{0}}, {false}); + std::vector out_data(1); + + for (std::size_t i = 0; i < param.size(); i++) { + StateVectorManaged psi(2); + JacobianData jd(1, psi.getLength(), psi.getData(), + {obs_default}, ops, {1}); + adj.adjointJacobian(out_data, jd, true); + } + } + SECTION("Obs with params std::vector") { + AdjointJacobian adj; + std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; + std::vector expec_results{0.90096887, 0.80901699, -0.5}; + using v_type = std::vector; + + v_type z_par{0.123}; + + auto obs_default = ObsDatum({"RZ"}, {z_par}, {{0}}); + + auto ops = + OpsData({"RX"}, {{expec_results[0]}}, {{0}}, {false}); + std::vector out_data(1); + + for (std::size_t i = 0; i < param.size(); i++) { + StateVectorManaged psi(2); + JacobianData jd(1, psi.getLength(), psi.getData(), + {obs_default}, ops, {1}); + adj.adjointJacobian(out_data, jd, true); + } + } + SECTION("Obs no params") { + AdjointJacobian adj; + std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; + std::vector expec_results{0.90096887, 0.80901699, -0.5}; + using v_type = std::vector>; + + v_type z_par{ONE(), ZERO(), ZERO(), + ZERO()}; + + auto obs_default = ObsDatum({"PauliZ"}, {}, {{0}}); + + auto ops = + OpsData({"RX"}, {{expec_results[0]}}, {{0}}, {false}); + std::vector out_data(1); + + for (std::size_t i = 0; i < param.size(); i++) { + StateVectorManaged psi(2); + JacobianData jd(1, psi.getLength(), psi.getData(), + {obs_default}, ops, {1}); + adj.adjointJacobian(out_data, jd, true); + } + } +} diff --git a/pennylane_lightning/src/tests/Test_DynamicDispatcher.cpp b/pennylane_lightning/src/tests/Test_DynamicDispatcher.cpp index f4dcf3b4c2..1b024a2230 100644 --- a/pennylane_lightning/src/tests/Test_DynamicDispatcher.cpp +++ b/pennylane_lightning/src/tests/Test_DynamicDispatcher.cpp @@ -28,13 +28,20 @@ using Pennylane::Gates::callGateOps; * We just check DynamicDispacther calls the correct functuion by comparing * the result from it with that of the direct call. */ -template +template struct testDispatchForKernel { - template < - GateOperation gate_op, class RandomEngine, - std::enable_if_t< - Util::array_has_elt(GateImplementation::implemented_gates, gate_op), - bool> = true> + static void test(RandomEngine &re, size_t num_qubits) { + static_cast(re); + static_cast(num_qubits); + } +}; +template +struct testDispatchForKernel< + PrecisionT, ParamT, GateImplementation, gate_op, RandomEngine, + std::enable_if_t> { static void test(RandomEngine &re, size_t num_qubits) { using CFP_t = std::complex; const std::vector ini_st = @@ -55,26 +62,19 @@ struct testDispatchForKernel { // and compare it to the dynamic dispatcher auto test_st = ini_st; const auto gate_name = - std::string(static_lookup(Constant::gate_names)); + std::string(Util::static_lookup(Constant::gate_names)); DynamicDispatcher::getInstance().applyOperation( GateImplementation::kernel_id, test_st.data(), num_qubits, gate_name, wires, false, params); REQUIRE(test_st == expected); } - - template < - GateOperation gate_op, class RandomEngine, - std::enable_if_t = true> - static void test(RandomEngine &re, size_t num_qubits) { - // Keep source, but allow clang-tidy to pass for unused - static_cast(re); - static_cast(num_qubits); - } // Do nothing if not implemented; - // This could probably be replaced with an enable_if or SFINAE-like - // pattern. }; +template +void testDynamicDispatch(RandomEngine &re, size_t num_qubits) { + testDispatchForKernel::test(re, num_qubits); +} template @@ -86,11 +86,10 @@ constexpr void testAllGatesForKernelIter(RandomEngine &re, if constexpr (gate_op != GateOperation::Matrix) { // ignore Matrix for (size_t num_qubits = 3; num_qubits <= max_num_qubits; num_qubits++) { - testDispatchForKernel:: - template test(re, num_qubits); + testDynamicDispatch(re, num_qubits); } } - testAllGatesForKernelIter(re, max_num_qubits); } diff --git a/pennylane_lightning/src/tests/Test_Error.cpp b/pennylane_lightning/src/tests/Test_Error.cpp new file mode 100644 index 0000000000..db76b7e25c --- /dev/null +++ b/pennylane_lightning/src/tests/Test_Error.cpp @@ -0,0 +1,50 @@ +#include +#include + +#include + +#include "Error.hpp" + +/** + * @brief Test LightningException class behaves correctly + */ +// NOLINTNEXTLINE(readability-function-cognitive-complexity) +TEST_CASE("Error.hpp", "[Error]") { + SECTION("Raw exception") { + const auto e = Pennylane::Util::LightningException("Test exception e"); + auto e_mut = + Pennylane::Util::LightningException("Test exception e_mut"); + + REQUIRE_THROWS_WITH(throw e, + Catch::Matchers::Contains("Test exception e")); + REQUIRE_THROWS_AS(throw e, Pennylane::Util::LightningException); + + // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) + const Pennylane::Util::LightningException e_copy(e); + REQUIRE_THROWS_WITH(throw e_copy, + Catch::Matchers::Contains("Test exception e")); + REQUIRE_THROWS_AS(throw e_copy, Pennylane::Util::LightningException); + + Pennylane::Util::LightningException e_move(std::move(e_mut)); + REQUIRE_THROWS_WITH(throw e_move, + Catch::Matchers::Contains("Test exception e_mut")); + REQUIRE_THROWS_AS(throw e_move, Pennylane::Util::LightningException); + + REQUIRE(std::strcmp(e.what(), "Test exception e") == 0); + REQUIRE(std::strcmp(e_copy.what(), "Test exception e") == 0); + REQUIRE(std::strcmp(e_move.what(), "Test exception e_mut") == 0); + } + SECTION("Abort") { + REQUIRE_THROWS_WITH( + Pennylane::Util::Abort("Test abort", __FILE__, __LINE__, __func__), + Catch::Matchers::Contains("Test abort")); + REQUIRE_THROWS_AS( + Pennylane::Util::Abort("Test abort", __FILE__, __LINE__, __func__), + Pennylane::Util::LightningException); + + REQUIRE_THROWS_WITH(PL_ABORT("Test abort"), + Catch::Matchers::Contains("Test abort")); + REQUIRE_THROWS_AS(PL_ABORT("Test abort"), + Pennylane::Util::LightningException); + } +} diff --git a/pennylane_lightning/src/tests/Test_GateImplementations_CompareKernels.cpp b/pennylane_lightning/src/tests/Test_GateImplementations_CompareKernels.cpp new file mode 100644 index 0000000000..82a62b81b1 --- /dev/null +++ b/pennylane_lightning/src/tests/Test_GateImplementations_CompareKernels.cpp @@ -0,0 +1,196 @@ +#include "CreateAllWires.hpp" +#include "TestHelpers.hpp" + +#include "OpToMemberFuncPtr.hpp" +#include "TestKernels.hpp" +#include "Util.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * @file Test_GateImplementations_Nonparam.cpp + * + * This file tests all gate operations (besides matrix) by comparing results + * between different kernels (gate implementations). + */ +using namespace Pennylane; +using namespace Pennylane::Gates; +using namespace Pennylane::Util; + +namespace { +using namespace Pennylane::Gates::Constant; +} // namespace + +using std::vector; + +/** + * @brief Change the given type list of kernels to string + */ +template std::string kernelsToString() { + if constexpr (!std::is_same_v) { + return std::string(TypeList::Type::name) + ", " + + kernelsToString(); + } + return ""; +} + +/* Type transformation */ +template +struct KernelsImplementingGateHelper { + using Type = std::conditional_t< + array_has_elt(TypeList::Type::implemented_gates, gate_op), + typename PrependToTypeList< + typename TypeList::Type, + typename KernelsImplementingGateHelper< + gate_op, typename TypeList::Next>::Type>::Type, + typename KernelsImplementingGateHelper::Type>; +}; +template +struct KernelsImplementingGateHelper { + using Type = void; +}; + +/** + * @brief Type list of kernels implementing the given gate operation. + */ +template struct KernelsImplementingGate { + using Type = + typename KernelsImplementingGateHelper::Type; +}; + +/** + * @brief Apply the given gate operation with the given gate implementation. + * + * @tparam gate_op Gate operation to test + * @tparam PrecisionT Floating point data type for statevector + * @tparam ParamT Floating point data type for parameter + * @tparam GateImplementation Gate implementation class + * @param ini Initial statevector + * @param num_qubits Number of qubits + * @param wires Wires the gate applies to + * @param inverse Whether to use inverse of gate + * @param params Paramters for gate + */ +template +auto applyGate(std::vector, Alloc> ini, + size_t num_qubits, const std::vector &wires, + bool inverse, const std::vector ¶ms) + -> std::vector, Alloc> { + callGateOps(GateOpToMemberFuncPtr::value, + ini.data(), num_qubits, wires, inverse, params); + return ini; +} + +/** + * @brief Apply the given gate using all implementing kernels and return + * results in tuple. + */ +template +auto applyGateForImplemetingKernels( + const std::vector, Alloc> &ini, size_t num_qubits, + const std::vector &wires, bool inverse, + const std::vector ¶ms, + [[maybe_unused]] std::index_sequence dummy) { + return std::make_tuple( + applyGate>( + ini, num_qubits, wires, inverse, params)...); +} + +/** + * @brief Apply the given gate using all implementing kernels and compare + * the results. + */ +template +void testApplyGate(RandomEngine &re, size_t num_qubits) { + const auto ini = createRandomState(re, num_qubits); + + using Kernels = typename KernelsImplementingGate::Type; + + INFO("Kernels implementing " << lookup(gate_names, gate_op) << " are " + << kernelsToString()); + + INFO("PrecisionT, ParamT = " << PrecisionToName::value << ", " + << PrecisionToName::value); + + const auto all_wires = createAllWires(num_qubits, gate_op, true); + for (const auto &wires : all_wires) { + const auto params = createParams(gate_op); + const auto gate_name = lookup(gate_names, gate_op); + DYNAMIC_SECTION( + "Test gate " + << gate_name + << " with inverse = false") { // Test with inverse = false + const auto results = Util::tuple_to_array( + applyGateForImplemetingKernels( + ini, num_qubits, wires, false, params, + std::make_index_sequence()>())); + + for (size_t i = 0; i < results.size() - 1; i++) { + REQUIRE(results[i] == + approx(results[i + 1]) + .margin(static_cast(1e-5))); + } + } + + DYNAMIC_SECTION("Test gate " + << gate_name + << " with inverse = true") { // Test with inverse = true + const auto results = Util::tuple_to_array( + applyGateForImplemetingKernels( + ini, num_qubits, wires, true, params, + std::make_index_sequence()>())); + + for (size_t i = 0; i < results.size() - 1; i++) { + REQUIRE(results[i] == + approx(results[i + 1]) + .margin(static_cast(1e-5))); + } + } + } +} + +template +void testAllGatesIter(RandomEngine &re, size_t max_num_qubits) { + if constexpr (gate_idx < static_cast(GateOperation::END)) { + constexpr static auto gate_op = static_cast(gate_idx); + + if constexpr (gate_op != GateOperation::Matrix) { + size_t min_num_qubits = array_has_elt(multi_qubit_gates, gate_op) + ? 1 + : lookup(gate_wires, gate_op); + for (size_t num_qubits = min_num_qubits; + num_qubits < max_num_qubits; num_qubits++) { + testApplyGate(re, num_qubits); + } + testAllGatesIter(re, + max_num_qubits); + } + } +} + +template +void testAllGates(RandomEngine &re, size_t max_num_qubits) { + testAllGatesIter<0, PrecisionT, ParamT>(re, max_num_qubits); +} + +TEMPLATE_TEST_CASE("Test all kernels give the same results", + "[Test_GateImplementations_CompareKernels]", float, double) { + std::mt19937 re{1337}; + testAllGates(re, 6); +} diff --git a/pennylane_lightning/src/tests/Test_GateImplementations_Generator.cpp b/pennylane_lightning/src/tests/Test_GateImplementations_Generator.cpp index 377c45bd5f..cf4569a1d2 100644 --- a/pennylane_lightning/src/tests/Test_GateImplementations_Generator.cpp +++ b/pennylane_lightning/src/tests/Test_GateImplementations_Generator.cpp @@ -35,12 +35,10 @@ constexpr std::string_view remove_prefix(const std::string_view &str, return {str.data() + len, str.length() - len}; } -constexpr auto gate_name_to_ops = Util::reverse_pairs(Constant::gate_names); - template constexpr auto findGateOpForGenerator() -> GateOperation { - constexpr auto gntr_name = - remove_prefix(static_lookup(Constant::generator_names), 9); + constexpr auto gntr_name = remove_prefix( + Util::static_lookup(Constant::generator_names), 9); for (const auto &[gate_op, gate_name] : Constant::gate_names) { if (gate_name == gntr_name) { @@ -76,10 +74,11 @@ void testGeneratorForGate(RandomEngine &re, size_t num_qubits) { using ComplexPrecisionT = std::complex; constexpr auto I = Util::IMAG(); - constexpr ParamT eps = 1e-4; // For finite difference + constexpr auto eps = PrecisionT{1e-4}; // For finite difference - constexpr auto gate_op = static_lookup(generator_gate_pairs); - constexpr auto gate_name = static_lookup(Constant::gate_names); + constexpr auto gate_op = Util::static_lookup(generator_gate_pairs); + constexpr auto gate_name = + Util::static_lookup(Constant::gate_names); DYNAMIC_SECTION("Test generator of " << gate_name << " for kernel " << GateImplementation::name) { @@ -107,7 +106,7 @@ void testGeneratorForGate(RandomEngine &re, size_t num_qubits) { gate_func(diff_st_1.data(), num_qubits, wires, false, eps); gate_func(diff_st_2.data(), num_qubits, wires, false, -eps); - std::vector gate_der_st(1U << num_qubits); + std::vector gate_der_st(size_t{1U} << num_qubits); std::transform( diff_st_1.cbegin(), diff_st_1.cend(), diff_st_2.cbegin(), @@ -116,7 +115,7 @@ void testGeneratorForGate(RandomEngine &re, size_t num_qubits) { scaleVector(gate_der_st, static_cast(0.5) / eps); - REQUIRE(gntr_st == PLApprox(gate_der_st).margin(1e-3)); + REQUIRE(gntr_st == approx(gate_der_st).margin(PrecisionT{1e-3})); } } template void testInverseKernelGate(RandomEngine &re, size_t num_qubits) { if constexpr (gate_op != GateOperation::Matrix) { - constexpr auto gate_name = static_lookup(Constant::gate_names); + constexpr auto gate_name = + Util::static_lookup(Constant::gate_names); DYNAMIC_SECTION("Test inverse of " << gate_name << " for kernel " << GateImplementation::name) { const auto ini_st = createRandomState(re, num_qubits); @@ -44,7 +45,7 @@ void testInverseKernelGate(RandomEngine &re, size_t num_qubits) { callGateOps(func_ptr, st.data(), num_qubits, wires, false, params); callGateOps(func_ptr, st.data(), num_qubits, wires, true, params); - REQUIRE(st == PLApprox(ini_st).margin(1e-7)); + REQUIRE(st == approx(ini_st).margin(PrecisionT{1e-7})); } } else { static_cast(re); diff --git a/pennylane_lightning/src/tests/Test_GateImplementations_Matrix.cpp b/pennylane_lightning/src/tests/Test_GateImplementations_Matrix.cpp index dfda96073f..1982b41d6b 100644 --- a/pennylane_lightning/src/tests/Test_GateImplementations_Matrix.cpp +++ b/pennylane_lightning/src/tests/Test_GateImplementations_Matrix.cpp @@ -5,7 +5,12 @@ #include +#if defined(_MSC_VER) +#pragma warning(disable : 4305) +#endif + using namespace Pennylane; +using Pennylane::Util::randomUnitary; template using ApplyMatrixType = void (*)(std::complex *, size_t, @@ -82,7 +87,7 @@ void testApplyMatrix() { auto st = ini_st; GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, false); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name @@ -138,7 +143,7 @@ void testApplyMatrix() { auto st = ini_st; GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, false); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name @@ -194,7 +199,7 @@ void testApplyMatrix() { auto st = ini_st; GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, false); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name @@ -262,7 +267,7 @@ void testApplyMatrix() { auto st = ini_st; GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, false); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name @@ -330,7 +335,7 @@ void testApplyMatrix() { auto st = ini_st; GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, false); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name @@ -447,7 +452,7 @@ void testApplyMatrix() { auto st = ini_st; GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, false); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name @@ -756,7 +761,7 @@ void testApplyMatrix() { auto st = ini_st; GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, false); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } } @@ -787,6 +792,7 @@ template void testApplyMatrixInverse() { std::mt19937 re{1337}; const int num_qubits = 4; + const auto margin = PrecisionT{1e-5}; DYNAMIC_SECTION(GateImplementation::name << ", wires = {0} - " @@ -802,7 +808,7 @@ void testApplyMatrixInverse() { wires, false); GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, true); - REQUIRE(st == PLApprox(ini_st).margin(1e-5)); + REQUIRE(st == approx(ini_st).margin(margin)); } DYNAMIC_SECTION(GateImplementation::name @@ -820,7 +826,7 @@ void testApplyMatrixInverse() { GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, true); - REQUIRE(st == PLApprox(ini_st).margin(1e-5)); + REQUIRE(st == approx(ini_st).margin(margin)); } DYNAMIC_SECTION(GateImplementation::name @@ -838,7 +844,7 @@ void testApplyMatrixInverse() { GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, true); - REQUIRE(st == PLApprox(ini_st).margin(1e-5)); + REQUIRE(st == approx(ini_st).margin(margin)); } DYNAMIC_SECTION(GateImplementation::name @@ -856,7 +862,7 @@ void testApplyMatrixInverse() { GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, true); - REQUIRE(st == PLApprox(ini_st).margin(1e-5)); + REQUIRE(st == approx(ini_st).margin(margin)); } DYNAMIC_SECTION(GateImplementation::name @@ -874,7 +880,7 @@ void testApplyMatrixInverse() { GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, true); - REQUIRE(st == PLApprox(ini_st).margin(1e-5)); + REQUIRE(st == approx(ini_st).margin(margin)); } DYNAMIC_SECTION(GateImplementation::name << ", wires = {1,2} - " @@ -890,7 +896,7 @@ void testApplyMatrixInverse() { GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, true); - REQUIRE(st == PLApprox(ini_st).margin(1e-5)); + REQUIRE(st == approx(ini_st).margin(margin)); } DYNAMIC_SECTION(GateImplementation::name << ", wires = {1,3} - " @@ -906,7 +912,7 @@ void testApplyMatrixInverse() { GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, true); - REQUIRE(st == PLApprox(ini_st).margin(1e-5)); + REQUIRE(st == approx(ini_st).margin(margin)); } DYNAMIC_SECTION(GateImplementation::name @@ -923,7 +929,7 @@ void testApplyMatrixInverse() { GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, true); - REQUIRE(st == PLApprox(ini_st).margin(1e-5)); + REQUIRE(st == approx(ini_st).margin(margin)); } DYNAMIC_SECTION(GateImplementation::name << ", wires = {0,1,2,3} - " @@ -939,7 +945,7 @@ void testApplyMatrixInverse() { GateImplementation::applyMatrix(st.data(), num_qubits, matrix.data(), wires, true); - REQUIRE(st == PLApprox(ini_st).margin(1e-5)); + REQUIRE(st == approx(ini_st).margin(margin)); } } diff --git a/pennylane_lightning/src/tests/Test_GateImplementations_Nonparam.cpp b/pennylane_lightning/src/tests/Test_GateImplementations_Nonparam.cpp index b38d05fc75..a131f19e21 100644 --- a/pennylane_lightning/src/tests/Test_GateImplementations_Nonparam.cpp +++ b/pennylane_lightning/src/tests/Test_GateImplementations_Nonparam.cpp @@ -21,10 +21,6 @@ */ using namespace Pennylane; -namespace { -using std::vector; -} - /** * @brief Run test suit only when the gate is defined */ @@ -103,7 +99,8 @@ void testApplyPauliX() { GateImplementation::applyPauliX(st.data(), num_qubits, {index}, false); CHECK(st[0] == Util::ZERO()); - CHECK(st[0b1 << (num_qubits - index - 1)] == Util::ONE()); + CHECK(st[size_t{1U} << (num_qubits - index - 1)] == + Util::ONE()); } } PENNYLANE_RUN_TEST(PauliX); @@ -129,7 +126,7 @@ void testApplyPauliY() { GateImplementation::applyPauliY(st.data(), num_qubits, {index}, false); - CHECK(st == PLApprox(expected_results[index])); + CHECK(st == approx(expected_results[index])); } } PENNYLANE_RUN_TEST(PauliY); @@ -152,7 +149,7 @@ void testApplyPauliZ() { auto st = createPlusState(num_qubits); GateImplementation::applyPauliZ(st.data(), num_qubits, {index}, false); - CHECK(st == PLApprox(expected_results[index])); + CHECK(st == approx(expected_results[index])); } } PENNYLANE_RUN_TEST(PauliZ); @@ -173,9 +170,9 @@ void testApplyHadamard() { CHECK(expected.imag() == Approx(st[0].imag())); CHECK(expected.real() == - Approx(st[0b1 << (num_qubits - index - 1)].real())); + Approx(st[size_t{1U} << (num_qubits - index - 1)].real())); CHECK(expected.imag() == - Approx(st[0b1 << (num_qubits - index - 1)].imag())); + Approx(st[size_t{1U} << (num_qubits - index - 1)].imag())); } } PENNYLANE_RUN_TEST(Hadamard); @@ -198,7 +195,7 @@ template void testApplyS() { GateImplementation::applyS(st.data(), num_qubits, {index}, false); - CHECK(st == PLApprox(expected_results[index])); + CHECK(st == approx(expected_results[index])); } } PENNYLANE_RUN_TEST(S); @@ -221,7 +218,7 @@ template void testApplyT() { GateImplementation::applyT(st.data(), num_qubits, {index}, false); - CHECK(st == PLApprox(expected_results[index])); + CHECK(st == approx(expected_results[index])); } } PENNYLANE_RUN_TEST(T); diff --git a/pennylane_lightning/src/tests/Test_GateImplementations_Param.cpp b/pennylane_lightning/src/tests/Test_GateImplementations_Param.cpp index cc6f687e11..b1161091c1 100644 --- a/pennylane_lightning/src/tests/Test_GateImplementations_Param.cpp +++ b/pennylane_lightning/src/tests/Test_GateImplementations_Param.cpp @@ -12,6 +12,10 @@ #include #include +#if defined(_MSC_VER) +#pragma warning(disable : 4305) +#endif + /** * @file This file contains tests for parameterized gates. List of such gates is * [RX, RY, RZ, PhaseShift, Rot, ControlledPhaseShift, CRX, CRY, CRZ, CRot] @@ -71,9 +75,9 @@ void testApplyPhaseShift() { const size_t num_qubits = 3; // Test using |+++> state - + const auto isqrt2 = PrecisionT{Util::INVSQRT2()}; const std::vector angles{0.3, 0.8, 2.4}; - const ComplexPrecisionT coef(1.0 / (2 * std::sqrt(2)), 0); + const ComplexPrecisionT coef{isqrt2 / PrecisionT{2.0}, PrecisionT{0.0}}; std::vector> ps_data; ps_data.reserve(angles.size()); @@ -107,7 +111,7 @@ void testApplyPhaseShift() { GateImplementation::applyPhaseShift(st.data(), num_qubits, {index}, false, {angles[index]}); - CHECK(st == PLApprox(expected_results[index])); + CHECK(st == approx(expected_results[index])); } } PENNYLANE_RUN_TEST(PhaseShift); @@ -132,7 +136,7 @@ void testApplyRX() { GateImplementation::applyRX(st.data(), num_qubits, {0}, false, {angles[index]}); - CHECK(st == PLApprox(expected_results[index]).epsilon(1e-7)); + CHECK(st == approx(expected_results[index]).epsilon(1e-7)); } } PENNYLANE_RUN_TEST(RX); @@ -172,7 +176,7 @@ void testApplyRY() { auto st = init_state; GateImplementation::applyRY(st.data(), num_qubits, {0}, false, {angles[index]}); - CHECK(st == PLApprox(expected_results[index]).epsilon(1e-5)); + CHECK(st == approx(expected_results[index]).epsilon(1e-5)); } } } @@ -184,9 +188,10 @@ void testApplyRZ() { const size_t num_qubits = 3; // Test using |+++> state + const auto isqrt2 = PrecisionT{Util::INVSQRT2()}; const std::vector angles{0.2, 0.7, 2.9}; - const ComplexPrecisionT coef(1.0 / (2 * std::sqrt(2)), 0); + const ComplexPrecisionT coef{isqrt2 / PrecisionT{2.0}, PrecisionT{0.0}}; std::vector> rz_data; rz_data.reserve(angles.size()); @@ -220,7 +225,7 @@ void testApplyRZ() { GateImplementation::applyRZ(st.data(), num_qubits, {index}, false, {angles[index]}); - CHECK(st == PLApprox(expected_results[index])); + CHECK(st == approx(expected_results[index])); } } PENNYLANE_RUN_TEST(RZ); @@ -237,15 +242,15 @@ void testApplyRot() { std::vector{2.3, 0.1, 0.4}}; std::vector> expected_results{ - std::vector(0b1 << num_qubits), - std::vector(0b1 << num_qubits), - std::vector(0b1 << num_qubits)}; + std::vector(1U << num_qubits), + std::vector(1U << num_qubits), + std::vector(1U << num_qubits)}; for (size_t i = 0; i < angles.size(); i++) { const auto rot_mat = Gates::getRot(angles[i][0], angles[i][1], angles[i][2]); expected_results[i][0] = rot_mat[0]; - expected_results[i][0b1 << (num_qubits - i - 1)] = rot_mat[2]; + expected_results[i][size_t{1U} << (num_qubits - i - 1)] = rot_mat[2]; } for (size_t index = 0; index < num_qubits; index++) { @@ -254,7 +259,7 @@ void testApplyRot() { angles[index][0], angles[index][1], angles[index][2]); - CHECK(st == PLApprox(expected_results[index])); + CHECK(st == approx(expected_results[index])); } } PENNYLANE_RUN_TEST(Rot); @@ -289,7 +294,7 @@ void testApplyIsingXX() { auto st = ini_st; GateImplementation::applyIsingXX(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", IsingXX0,1 |100> -> a|100> + b|010> - " @@ -312,7 +317,7 @@ void testApplyIsingXX() { auto st = ini_st; GateImplementation::applyIsingXX(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", IsingXX0,1 |010> -> a|010> + b|100> - " @@ -335,7 +340,7 @@ void testApplyIsingXX() { auto st = ini_st; GateImplementation::applyIsingXX(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", IsingXX0,1 |110> -> a|110> + b|000> - " @@ -358,7 +363,7 @@ void testApplyIsingXX() { auto st = ini_st; GateImplementation::applyIsingXX(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", IsingXX0,2 - " @@ -390,7 +395,7 @@ void testApplyIsingXX() { auto st = ini_st; GateImplementation::applyIsingXX(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } } PENNYLANE_RUN_TEST(IsingXX); @@ -422,7 +427,7 @@ void testApplyIsingYY() { auto st = ini_st; GateImplementation::applyIsingYY(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", IsingYY0,1 |100> -> a|100> + b|010> - " @@ -445,7 +450,7 @@ void testApplyIsingYY() { auto st = ini_st; GateImplementation::applyIsingYY(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", IsingYY0,1 |010> -> a|010> + b|100> - " @@ -468,7 +473,7 @@ void testApplyIsingYY() { auto st = ini_st; GateImplementation::applyIsingYY(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", IsingYY0,1 |110> -> a|110> + b|000> - " @@ -491,7 +496,7 @@ void testApplyIsingYY() { auto st = ini_st; GateImplementation::applyIsingYY(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", IsingYY0,1 - " @@ -542,7 +547,7 @@ void testApplyIsingYY() { auto st = ini_st; GateImplementation::applyIsingYY(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } } PENNYLANE_RUN_TEST(IsingYY); @@ -574,7 +579,7 @@ void testApplyIsingZZ() { auto st = ini_st; GateImplementation::applyIsingZZ(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", IsingZZ0,1 |100> -> |100> - " @@ -597,7 +602,7 @@ void testApplyIsingZZ() { auto st = ini_st; GateImplementation::applyIsingZZ(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name @@ -621,7 +626,7 @@ void testApplyIsingZZ() { auto st = ini_st; GateImplementation::applyIsingZZ(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name @@ -645,7 +650,7 @@ void testApplyIsingZZ() { auto st = ini_st; GateImplementation::applyIsingZZ(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected_results).margin(1e-7)); + REQUIRE(st == approx(expected_results).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", IsingZZ0,1 - " @@ -696,7 +701,7 @@ void testApplyIsingZZ() { auto st = ini_st; GateImplementation::applyIsingZZ(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } } PENNYLANE_RUN_TEST(IsingZZ); @@ -710,8 +715,10 @@ void testApplyControlledPhaseShift() { // Test using |+++> state auto ini_st = createPlusState(num_qubits); + const auto isqrt2 = Util::INVSQRT2(); + const std::vector angles{0.3, 2.4}; - const ComplexPrecisionT coef(1.0 / (2 * std::sqrt(2)), 0); + const ComplexPrecisionT coef{isqrt2 / PrecisionT{2.0}, PrecisionT{0.0}}; std::vector> ps_data; ps_data.reserve(angles.size()); @@ -734,7 +741,7 @@ void testApplyControlledPhaseShift() { GateImplementation::applyControlledPhaseShift(st.data(), num_qubits, {0, 1}, false, angles[0]); CAPTURE(st); - CHECK(st == PLApprox(expected_results[0])); + CHECK(st == approx(expected_results[0])); } PENNYLANE_RUN_TEST(ControlledPhaseShift); @@ -789,7 +796,7 @@ void testApplyCRX() { auto st = ini_st; GateImplementation::applyCRX(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name << ", CRX0,2 - " << PrecisionToName::value) { @@ -839,7 +846,7 @@ void testApplyCRX() { auto st = ini_st; GateImplementation::applyCRX(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name << ", CRX1,3 - " << PrecisionToName::value) { @@ -889,7 +896,7 @@ void testApplyCRX() { auto st = ini_st; GateImplementation::applyCRX(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } } PENNYLANE_RUN_TEST(CRX); @@ -946,7 +953,7 @@ void testApplyCRY() { auto st = ini_st; GateImplementation::applyCRY(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name @@ -997,7 +1004,7 @@ void testApplyCRY() { auto st = ini_st; GateImplementation::applyCRY(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name @@ -1048,7 +1055,7 @@ void testApplyCRY() { auto st = ini_st; GateImplementation::applyCRY(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } } @@ -1106,7 +1113,7 @@ void testApplyCRZ() { auto st = ini_st; GateImplementation::applyCRZ(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name @@ -1157,7 +1164,7 @@ void testApplyCRZ() { auto st = ini_st; GateImplementation::applyCRZ(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } DYNAMIC_SECTION(GateImplementation::name @@ -1208,7 +1215,7 @@ void testApplyCRZ() { auto st = ini_st; GateImplementation::applyCRZ(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } } PENNYLANE_RUN_TEST(CRZ); @@ -1216,37 +1223,40 @@ PENNYLANE_RUN_TEST(CRZ); template void testApplyCRot() { using ComplexPrecisionT = std::complex; - const size_t num_qubits = 3; - - const auto ini_st = createZeroState(num_qubits); const std::vector angles{0.3, 0.8, 2.4}; - std::vector expected_results(8); - const auto rot_mat = - Gates::getRot(angles[0], angles[1], angles[2]); - expected_results[0b1 << (num_qubits - 1)] = rot_mat[0]; - expected_results[(0b1 << num_qubits) - 2] = rot_mat[2]; - DYNAMIC_SECTION(GateImplementation::name << ", CRot0,1 |000> -> |000> - " << PrecisionToName::value) { + const size_t num_qubits = 3; + const auto ini_st = createZeroState(num_qubits); + auto st = createZeroState(num_qubits); GateImplementation::applyCRot(st.data(), num_qubits, {0, 1}, false, angles[0], angles[1], angles[2]); - CHECK(st == PLApprox(ini_st)); + CHECK(st == approx(ini_st)); } DYNAMIC_SECTION(GateImplementation::name << ", CRot0,1 |100> -> |1>(a|0>+b|1>)|0> - " << PrecisionToName::value) { + const size_t num_qubits = 3; + auto st = createZeroState(num_qubits); + + std::vector expected_results(8); + const auto rot_mat = + Gates::getRot(angles[0], angles[1], angles[2]); + expected_results[size_t{1U} << (num_qubits - 1)] = rot_mat[0]; + expected_results[(size_t{1U} << num_qubits) - 2] = rot_mat[2]; + GateImplementation::applyPauliX(st.data(), num_qubits, {0}, false); GateImplementation::applyCRot(st.data(), num_qubits, {0, 1}, false, angles[0], angles[1], angles[2]); - CHECK(st == PLApprox(expected_results)); + CHECK(st == approx(expected_results)); } DYNAMIC_SECTION(GateImplementation::name @@ -1299,7 +1309,7 @@ void testApplyCRot() { auto st = ini_st; GateImplementation::applyCRot(st.data(), num_qubits, wires, false, phi, theta, omega); - REQUIRE(st == PLApprox(expected).margin(1e-5)); + REQUIRE(st == approx(expected).margin(1e-5)); } } PENNYLANE_RUN_TEST(CRot); @@ -1332,7 +1342,7 @@ void testApplyMultiRZ() { GateImplementation::applyMultiRZ(st.data(), num_qubits, {0}, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-7)); + REQUIRE(st == approx(expected).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", MultiRZ0 |++++> - " @@ -1355,7 +1365,7 @@ void testApplyMultiRZ() { GateImplementation::applyMultiRZ(st.data(), num_qubits, {0}, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-7)); + REQUIRE(st == approx(expected).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", MultiRZ01 |++++> - " @@ -1378,7 +1388,7 @@ void testApplyMultiRZ() { GateImplementation::applyMultiRZ(st.data(), num_qubits, {0, 1}, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-7)); + REQUIRE(st == approx(expected).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", MultiRZ012 |++++> - " @@ -1401,7 +1411,7 @@ void testApplyMultiRZ() { GateImplementation::applyMultiRZ(st.data(), num_qubits, {0, 1, 2}, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-7)); + REQUIRE(st == approx(expected).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name << ", MultiRZ0123 |++++> - " @@ -1424,7 +1434,7 @@ void testApplyMultiRZ() { GateImplementation::applyMultiRZ(st.data(), num_qubits, {0, 1, 2, 3}, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-7)); + REQUIRE(st == approx(expected).margin(1e-7)); } DYNAMIC_SECTION(GateImplementation::name @@ -1474,7 +1484,7 @@ void testApplyMultiRZ() { GateImplementation::applyMultiRZ(st.data(), num_qubits, wires, false, angle); - REQUIRE(st == PLApprox(expected).margin(1e-7)); + REQUIRE(st == approx(expected).margin(1e-7)); } } PENNYLANE_RUN_TEST(MultiRZ); diff --git a/pennylane_lightning/src/tests/Test_GateUtil.cpp b/pennylane_lightning/src/tests/Test_GateUtil.cpp index 15642d4303..4206321ea9 100644 --- a/pennylane_lightning/src/tests/Test_GateUtil.cpp +++ b/pennylane_lightning/src/tests/Test_GateUtil.cpp @@ -25,7 +25,7 @@ TEST_CASE("generateBitPatterns", "[IndicesUtil]") { } SECTION("Qubit indices {i}") { for (size_t i = 0; i < num_qubits; i++) { - std::vector expected{0, 0b1UL << (num_qubits - i - 1)}; + std::vector expected{0, size_t{1U} << (num_qubits - i - 1)}; auto bit_pattern = generateBitPatterns({i}, num_qubits); CHECK(bit_pattern == expected); } diff --git a/pennylane_lightning/src/tests/Test_Internal.cpp b/pennylane_lightning/src/tests/Test_Internal.cpp index 00ead21271..3383678d77 100644 --- a/pennylane_lightning/src/tests/Test_Internal.cpp +++ b/pennylane_lightning/src/tests/Test_Internal.cpp @@ -1,10 +1,16 @@ +#include "CreateAllWires.hpp" #include "GateImplementationsPI.hpp" #include "TestHelpers.hpp" #include +#include #include +#if defined(_MSC_VER) +#pragma warning(disable : 4305) +#endif + /** * We test internal functions for test suite. */ @@ -16,6 +22,8 @@ TEMPLATE_TEST_CASE("Approx", "[Test_Internal]", float, double) { using PrecisionT = TestType; using ComplexPrecisionT = std::complex; + const auto margin = PrecisionT{0.00015}; + SECTION("vector{1.0, 1.0*I} approx vector{1.0001, 0.9999*I} with margin " "0.00015") { const std::vector test1{ @@ -26,7 +34,7 @@ TEMPLATE_TEST_CASE("Approx", "[Test_Internal]", float, double) { ComplexPrecisionT{1.0001, 0.0}, ComplexPrecisionT{0.0, 0.9999}, }; - REQUIRE(test1 == PLApprox(test2).margin(0.00015)); + REQUIRE(test1 == approx(test2).margin(margin)); } SECTION("vector{1.0, 1.0*I} does not approx vector{1.0002, 0.9998*I} with " "margin 0.00015") { @@ -38,7 +46,7 @@ TEMPLATE_TEST_CASE("Approx", "[Test_Internal]", float, double) { ComplexPrecisionT{1.0002, 0.0}, ComplexPrecisionT{0.0, 0.9998}, }; - REQUIRE(test1 != PLApprox(test2).margin(0.00015)); + REQUIRE(test1 != approx(test2).margin(margin)); } SECTION("vector{1.0, 1.0*I} does not approx vector{1.0I, 1.0} with margin " "0.00015") { @@ -50,7 +58,7 @@ TEMPLATE_TEST_CASE("Approx", "[Test_Internal]", float, double) { ComplexPrecisionT{0.0, 1.0}, ComplexPrecisionT{1.0, 0.0}, }; - REQUIRE(test1 != PLApprox(test2).margin(0.00015)); + REQUIRE(test1 != approx(test2).margin(margin)); } } @@ -66,7 +74,7 @@ TEMPLATE_TEST_CASE("createProductState", "[Test_Internal]", float, double) { GateImplementationsPI::applyPauliX(expected.data(), 3, {1}, false); GateImplementationsPI::applyHadamard(expected.data(), 3, {1}, false); - REQUIRE(st == PLApprox(expected).margin(1e-7)); + REQUIRE(st == approx(expected).margin(PrecisionT{1e-7})); } SECTION("createProductState(\"+-0\") == |+-1> ") { const auto st = createProductState("+-0"); @@ -79,39 +87,81 @@ TEMPLATE_TEST_CASE("createProductState", "[Test_Internal]", float, double) { GateImplementationsPI::applyPauliX(expected.data(), 3, {2}, false); - REQUIRE(st != PLApprox(expected).margin(1e-7)); + REQUIRE(st != approx(expected).margin(PrecisionT{1e-7})); } } -/** - * @brief Test randomUnitary is correct - */ -TEMPLATE_TEST_CASE("randomUnitary", "[Test_Internal]", float, double) { - using PrecisionT = TestType; - - std::mt19937 re{1337}; - - for (size_t num_qubits = 1; num_qubits <= 5; num_qubits++) { - const size_t dim = (1U << num_qubits); - const auto unitary = randomUnitary(re, num_qubits); - - std::vector> unitary_dagger = - Util::Transpose(unitary, dim, dim); - std::transform( - unitary_dagger.begin(), unitary_dagger.end(), - unitary_dagger.begin(), - [](const std::complex &v) { return std::conj(v); }); +size_t binomialCeff(size_t n, size_t r) { + size_t num = 1; + size_t dem = 1; + for (size_t k = 0; k < r; k++) { + num *= (n - k); + } + for (size_t k = 1; k <= r; k++) { + dem *= k; + } + return num / dem; +} - std::vector> mat(dim * dim); - Util::matrixMatProd(unitary.data(), unitary_dagger.data(), mat.data(), - dim, dim, dim); +size_t permSize(size_t n, size_t r) { + size_t res = 1; + for (size_t k = 0; k < r; k++) { + res *= (n - k); + } + return res; +} - std::vector> identity( - dim * dim, std::complex{}); - for (size_t i = 0; i < dim; i++) { - identity[i * dim + i] = std::complex{1.0, 0.0}; +/** + * @brief Test create all wires + */ +TEST_CASE("createAllWires", "[Test_Internal]") { + SECTION("order = false") { + const std::vector> test_pairs{ + {4, 2}, {8, 3}, {12, 1}, {12, 2}, {12, 3}, {12, 4}, {12, 5}, + {12, 6}, {12, 7}, {12, 8}, {12, 9}, {12, 10}, {12, 11}, {12, 12}}; + + for (const auto &[n, r] : test_pairs) { + std::vector> vec; + auto v = CombinationGenerator(n, r).all_perms(); + + REQUIRE(v.size() == binomialCeff(n, r)); + for (const auto &perm : v) { + REQUIRE(perm.size() == r); + vec.emplace_back(perm.begin(), perm.end()); + } + + std::sort(v.begin(), v.end(), + [](const std::vector &v1, + const std::vector &v2) { + return std::lexicographical_compare( + v1.begin(), v1.end(), v2.begin(), v2.end()); + }); // sort lexicographically + for (size_t i = 0; i < v.size() - 1; i++) { + REQUIRE(v[i] != v[i + 1]); // all combinations must be different + } + } + } + SECTION("order = true") { + const std::vector> test_pairs{ + {4, 2}, {8, 3}, {12, 1}, {12, 2}, {12, 3}, {12, 4}, {12, 5}}; + + for (const auto &[n, r] : test_pairs) { + auto v = PermutationGenerator(n, r).all_perms(); + + REQUIRE(v.size() == permSize(n, r)); + for (const auto &perm : v) { + REQUIRE(perm.size() == r); + } + + std::sort(v.begin(), v.end(), + [](const std::vector &v1, + const std::vector &v2) { + return std::lexicographical_compare( + v1.begin(), v1.end(), v2.begin(), v2.end()); + }); // sort lexicographically + for (size_t i = 0; i < v.size() - 1; i++) { + REQUIRE(v[i] != v[i + 1]); // all permutations must be different + } } - - REQUIRE(mat == PLApprox(identity).margin(1e-5)); } } diff --git a/pennylane_lightning/src/tests/Test_LinearAlgebra.cpp b/pennylane_lightning/src/tests/Test_LinearAlgebra.cpp new file mode 100644 index 0000000000..544043ed52 --- /dev/null +++ b/pennylane_lightning/src/tests/Test_LinearAlgebra.cpp @@ -0,0 +1,635 @@ +#include +#include +#include + +#include + +#include "LinearAlgebra.hpp" +#include "Util.hpp" + +#include "TestHelpers.hpp" + +#if defined(_MSC_VER) +#pragma warning(disable : 4305) +#endif + +using namespace Pennylane; + +// NOLINTNEXTLINE: Avoid complexity errors +TEMPLATE_TEST_CASE("Test linear algebra functions", "[Util][LinearAlgebra]", + float, double) { + using Util::Trans; + SECTION("innerProd") { + SECTION("Iterative increment") { + for (size_t i = 0; i < 12; i++) { + auto sz = static_cast(1U << i); + std::vector> data1(sz, {1.0, 1.0}); + std::vector> data2(sz, {1.0, 1.0}); + std::complex expected_result(0, + size_t{1U} << (i + 1)); + std::complex result = Util::innerProd(data1, data2); + CHECK(isApproxEqual(result, expected_result)); + } + } + SECTION("Random complex") { + std::vector> data1{ + {0.326417, 0}, {-0, 0.343918}, {0, 0.508364}, {-0.53562, -0}, + {0, -0.178322}, {0.187883, -0}, {0.277721, 0}, {-0, 0.292611}}; + std::vector> data2{ + {0, -0.479426}, {0, 0}, {2.77556e-17, 0}, {0, 0}, + {0.877583, 0}, {0, 0}, {0, 0}, {0, 0}}; + std::complex expected_result(0, -0.312985152368); + std::complex result = Util::innerProd(data1, data2); + CHECK(isApproxEqual(result, expected_result)); + } + } + SECTION("innerProd-inline") { + SECTION("Iterative increment") { + for (size_t i = 0; i < 12; i++) { + auto sz = static_cast(1U << i); + std::vector> data1(sz, {1.0, 1.0}); + std::vector> data2(sz, {1.0, 1.0}); + std::complex expected_result(0, + size_t{1U} << (i + 1)); + std::complex result = Util::innerProd( + data1.data(), data2.data(), sz); + CHECK(isApproxEqual(result, expected_result)); + } + } + SECTION("Random complex") { + std::vector> data1{ + {0.326417, 0}, {-0, 0.343918}, {0, 0.508364}, {-0.53562, -0}, + {0, -0.178322}, {0.187883, -0}, {0.277721, 0}, {-0, 0.292611}}; + std::vector> data2{ + {0, -0.479426}, {0, 0}, {2.77556e-17, 0}, {0, 0}, + {0.877583, 0}, {0, 0}, {0, 0}, {0, 0}}; + std::complex expected_result(0, -0.312985152368); + std::complex result = + Util::innerProd(data1.data(), data2.data(), 8); + CHECK(isApproxEqual(result, expected_result)); + } + } + SECTION("innerProdC") { + SECTION("Iterative increment") { + for (size_t i = 0; i < 12; i++) { + auto sz = static_cast(1U << i); + std::vector> data1(sz, {1.0, 1.0}); + std::vector> data2(sz, {1.0, 1.0}); + std::complex expected_result(size_t{1U} << (i + 1), + 0); + std::complex result = Util::innerProdC(data1, data2); + CAPTURE(result); + CAPTURE(expected_result); + CHECK(isApproxEqual(result, expected_result)); + } + } + SECTION("Random complex") { + std::vector> data2{ + {0, -0.479426}, {0, 0}, {2.77556e-17, 0}, {0, 0}, + {0.877583, 0}, {0, 0}, {0, 0}, {0, 0}}; + std::vector> data1{ + {0.326417, 0}, {-0, 0.343918}, {0, 0.508364}, {-0.53562, -0}, + {0, -0.178322}, {0.187883, -0}, {0.277721, 0}, {-0, 0.292611}}; + std::complex expected_result(0, -4.40916e-7); + std::complex result = Util::innerProdC(data1, data2); + CAPTURE(result); + CAPTURE(expected_result); + CHECK(real(result) == Approx(real(expected_result)).margin(1e-7)); + CHECK(imag(result) == Approx(imag(expected_result)).margin(1e-7)); + } + } + SECTION("innerProdC-inline") { + SECTION("Iterative increment") { + for (size_t i = 0; i < 12; i++) { + auto sz = static_cast(1U << i); + std::vector> data1(sz, {1.0, 1.0}); + std::vector> data2(sz, {1.0, 1.0}); + std::complex expected_result(size_t{1U} << (i + 1), + 0); + std::complex result = Util::innerProdC( + data1.data(), data2.data(), sz); + CAPTURE(result); + CAPTURE(expected_result); + CHECK(isApproxEqual(result, expected_result)); + } + } + SECTION("Random complex") { + std::vector> data2{ + {0, -0.479426}, {0, 0}, {2.77556e-17, 0}, {0, 0}, + {0.877583, 0}, {0, 0}, {0, 0}, {0, 0}}; + std::vector> data1{ + {0.326417, 0}, {-0, 0.343918}, {0, 0.508364}, {-0.53562, -0}, + {0, -0.178322}, {0.187883, -0}, {0.277721, 0}, {-0, 0.292611}}; + std::complex expected_result(0, -4.40916e-7); + std::complex result = + Util::innerProdC(data1.data(), data2.data(), 8); + CAPTURE(result); + CAPTURE(expected_result); + CHECK(real(result) == Approx(real(expected_result)).margin(1e-7)); + CHECK(imag(result) == Approx(imag(expected_result)).margin(1e-7)); + } + } + SECTION("matrixVecProd") { + SECTION("Simple Iterative with NoTranspose") { + for (size_t m = 2; m < 8; m++) { + std::vector> mat(m * m, {1.0, 1.0}); + std::vector> v_in(m, {1.0, 1.0}); + std::vector> v_expected( + m, {0, static_cast(2 * m)}); + std::vector> v_out = + Util::matrixVecProd(mat, v_in, m, m, Trans::NoTranspose); + CAPTURE(v_out); + CAPTURE(v_expected); + + CHECK(v_out == approx(v_expected).margin(1e-7)); + } + } + SECTION("Simple Iterative with Transpose") { + for (size_t m = 2; m < 8; m++) { + std::vector> mat(m * m, {1.0, 1.0}); + std::vector> v_in(m, {1.0, 1.0}); + std::vector> v_expected( + m, {0, static_cast(2 * m)}); + std::vector> v_out = + Util::matrixVecProd(mat, v_in, m, m, Trans::Transpose); + CAPTURE(v_out); + CAPTURE(v_expected); + + CHECK(v_out == approx(v_expected).margin(1e-7)); + } + } + SECTION("Random Complex with NoTranspose") { + std::vector> mat{ + {0.417876, 0.27448}, {0.601209, 0.723548}, + {0.781624, 0.538222}, {0.0597232, 0.27755}, + {0.0431741, 0.593319}, {0.224124, 0.130335}, + {0.237877, 0.01557}, {0.931634, 0.786367}, + {0.378397, 0.894381}, {0.840747, 0.889789}, + {0.530623, 0.463644}, {0.868736, 0.760685}, + {0.258175, 0.836569}, {0.495012, 0.667726}, + {0.298962, 0.384992}, {0.659472, 0.232696}}; + std::vector> v_in{{0.417876, 0.27448}, + {0.601209, 0.723548}, + {0.781624, 0.538222}, + {0.0597232, 0.27755}}; + std::vector> v_expected{ + {0.184998, 1.97393}, + {-0.0894368, 0.946047}, + {-0.219747, 2.55541}, + {-0.305997, 1.83881}}; + std::vector> v_out = + Util::matrixVecProd(mat, v_in, 4, 4, Trans::NoTranspose); + CAPTURE(v_out); + + CHECK(v_out == approx(v_expected).margin(1e-7)); + } + SECTION("Random Complex with Transpose") { + std::vector> mat{ + {0.417876, 0.27448}, {0.601209, 0.723548}, + {0.781624, 0.538222}, {0.0597232, 0.27755}, + {0.0431741, 0.593319}, {0.224124, 0.130335}, + {0.237877, 0.01557}, {0.931634, 0.786367}, + {0.378397, 0.894381}, {0.840747, 0.889789}, + {0.530623, 0.463644}, {0.868736, 0.760685}, + {0.258175, 0.836569}, {0.495012, 0.667726}, + {0.298962, 0.384992}, {0.659472, 0.232696}}; + std::vector> v_in{{0.417876, 0.27448}, + {0.601209, 0.723548}, + {0.781624, 0.538222}, + {0.0597232, 0.27755}}; + std::vector> v_expected{{-0.706439, 1.64169}, + {0.115553, 2.03315}, + {0.386844, 1.37488}, + {0.184316, 2.5383}}; + std::vector> v_out = + Util::matrixVecProd(mat, v_in, 4, 4, Trans::Transpose); + CAPTURE(v_out); + + CHECK(v_out == approx(v_expected).margin(1e-7)); + } + SECTION("Invalid Arguments") { + using namespace Catch::Matchers; + std::vector> mat(2 * 3, {1.0, 1.0}); + std::vector> v_in(2, {1.0, 1.0}); + CHECK_THROWS_AS(Util::matrixVecProd(mat, v_in, 2, 3), + std::invalid_argument); + CHECK_THROWS_WITH(Util::matrixVecProd(mat, v_in, 2, 3), + Contains("Invalid size for the input vector")); + CHECK_THROWS_AS(Util::matrixVecProd(mat, v_in, 2, 2), + std::invalid_argument); + CHECK_THROWS_WITH( + Util::matrixVecProd(mat, v_in, 2, 2), + Contains( + "Invalid number of rows and columns for the input matrix")); + } + SECTION("nullptr for v_out") { + std::vector> mat(2 * 3, {1.0, 1.0}); + std::vector> v_in(2, {1.0, 1.0}); + auto v_out = nullptr; + Util::matrixVecProd(mat.data(), v_in.data(), v_out, 2, 3, + Trans::NoTranspose); + CHECK(v_out == nullptr); + + Util::omp_matrixVecProd(mat.data(), v_in.data(), v_out, 2, + 3, Trans::NoTranspose); + CHECK(v_out == nullptr); + } + } + SECTION("vecMatrixProd") { + SECTION("Simple Iterative") { + for (size_t m = 2; m < 8; m++) { + std::vector mat(m * m, TestType{1.0}); + std::vector v_in(m, TestType{1.0}); + std::vector v_expected(m, static_cast(m)); + std::vector v_out = + Util::vecMatrixProd(v_in, mat, m, m); + + CAPTURE(v_out); + CAPTURE(v_expected); + + CHECK(v_out == approx(v_expected).margin(1e-7)); + } + } + SECTION("Zero Vector") { + for (size_t m = 2; m < 8; m++) { + std::vector mat(m * m, 1); + std::vector v_in(m, 0); + std::vector v_expected(m, 0); + std::vector v_out = + Util::vecMatrixProd(v_in, mat, m, m); + + CAPTURE(v_out); + CAPTURE(v_expected); + + CHECK(v_out == approx(v_expected).margin(1e-7)); + } + } + SECTION("Random Matrix") { + std::vector v_in{1.0, 2.0, 3.0, 4.0}; + std::vector mat{1.0, 0.1, 0.2, 0.2, 0.6, 0.1, + 0.4, -0.7, 1.2, -0.5, -0.6, 0.7}; + std::vector v_expected{0.6, -3.2, 6.8}; + std::vector v_out = Util::vecMatrixProd(v_in, mat, 4, 3); + + CAPTURE(v_out); + CAPTURE(v_expected); + + CHECK(v_out == approx(v_expected).margin(1e-7)); + } + SECTION("Invalid Arguments") { + using namespace Catch::Matchers; + std::vector v_in(4, {1.0}); + std::vector mat(8, {1.0}); + CHECK_THROWS_AS(Util::vecMatrixProd(v_in, mat, 2, 3), + std::invalid_argument); + CHECK_THROWS_WITH(Util::vecMatrixProd(v_in, mat, 2, 3), + Contains("Invalid size for the input vector")); + CHECK_THROWS_AS(Util::vecMatrixProd(v_in, mat, 4, 3), + std::invalid_argument); + CHECK_THROWS_WITH( + Util::vecMatrixProd(v_in, mat, 4, 3), + Contains( + "Invalid number of rows and columns for the input matrix")); + + std::vector v_out(3); + CHECK_THROWS_AS(Util::vecMatrixProd(v_out, v_in, mat, 2, 3), + std::invalid_argument); + CHECK_THROWS_WITH( + Util::vecMatrixProd(v_out, v_in, mat, 2, 3), + Contains( + "Invalid number of rows and columns for the input matrix")); + CHECK_THROWS_AS(Util::vecMatrixProd(v_out, v_in, mat, 2, 4), + std::invalid_argument); + CHECK_THROWS_WITH(Util::vecMatrixProd(v_out, v_in, mat, 2, 4), + Contains("Invalid size for the input vector")); + CHECK_THROWS_AS(Util::vecMatrixProd(v_out, v_in, mat, 4, 2), + std::invalid_argument); + CHECK_THROWS_WITH( + Util::vecMatrixProd(v_out, v_in, mat, 4, 2), + Contains("Invalid preallocated size for the result")); + } + SECTION("nullptr for v_out") { + std::vector v_in(4, {1.0}); + std::vector mat{4 * 2, {1.0}}; + auto v_out = nullptr; + Util::vecMatrixProd(v_in.data(), mat.data(), v_out, 4, 2); + CHECK(v_out == nullptr); + } + } + SECTION("CFTranspose") { + SECTION("Simple Matrix") { + for (size_t m = 2; m < 10; m++) { + std::vector mat(m * m, {0}); + for (size_t i = 0; i < m; i++) { + mat[i * m + i] = 1.0; + } + std::vector mat_t(m * m); + Util::CFTranspose(mat.data(), mat_t.data(), m, m, + 0, m, 0, m); + + CAPTURE(mat_t); + CAPTURE(mat); + + CHECK(mat_t == approx(mat).margin(1e-7)); + } + } + SECTION("Random Complex") { + std::vector mat{ + 0.417876, 0.27448, 0.601209, 0.723548, 0.781624, + 0.538222, 0.0597232, 0.27755, 0.836569, + }; + std::vector mat_t_exp{ + 0.417876, 0.723548, 0.0597232, 0.27448, 0.781624, + 0.27755, 0.601209, 0.538222, 0.836569, + }; + std::vector mat_t(9); + Util::CFTranspose(mat.data(), mat_t.data(), 3, 3, 0, 3, + 0, 3); + + CAPTURE(mat_t); + CAPTURE(mat_t_exp); + + CHECK(mat_t == approx(mat_t_exp)); + } + SECTION("Random Complex non-square") { + std::vector mat{ + 0.417876, 0.27448, 0.601209, 0.723548, + 0.781624, 0.538222, 0.0597232, 0.27755, + }; + std::vector mat_t_exp{0.417876, 0.781624, 0.27448, + 0.538222, 0.601209, 0.0597232, + 0.723548, 0.27755}; + std::vector mat_t(8); + Util::CFTranspose(mat.data(), mat_t.data(), 2, 4, 0, 2, + 0, 4); + + CAPTURE(mat_t); + CAPTURE(mat_t_exp); + + CHECK(mat_t == approx(mat_t_exp)); + } + SECTION("Invalid Arguments") { + using namespace Catch::Matchers; + std::vector mat(2 * 3, {1.0}); + CHECK_THROWS_AS(Util::Transpose(mat, 2, 2), std::invalid_argument); + CHECK_THROWS_WITH( + Util::Transpose(mat, 2, 2), + Contains( + "Invalid number of rows and columns for the input matrix")); + } + } + SECTION("Transpose>") { + SECTION("Simple Matrix") { + for (size_t m = 2; m < 8; m++) { + std::vector> mat(m * m, {0, 0}); + for (size_t i = 0; i < m; i++) { + mat[i * m + i] = {1.0, 1.0}; + } + std::vector> mat_t = + Util::Transpose(mat, m, m); + + CAPTURE(mat_t); + CAPTURE(mat); + + CHECK(mat_t == approx(mat).margin(1e-7)); + } + } + SECTION("Random Complex") { + std::vector> mat{ + {0.417876, 0.27448}, {0.601209, 0.723548}, + {0.781624, 0.538222}, {0.0597232, 0.27755}, + {0.0431741, 0.593319}, {0.224124, 0.130335}, + {0.237877, 0.01557}, {0.931634, 0.786367}, + {0.378397, 0.894381}, {0.840747, 0.889789}, + {0.530623, 0.463644}, {0.868736, 0.760685}, + {0.258175, 0.836569}, {0.495012, 0.667726}, + {0.298962, 0.384992}, {0.659472, 0.232696}}; + std::vector> mat_t_exp{ + {0.417876, 0.27448}, {0.0431741, 0.593319}, + {0.378397, 0.894381}, {0.258175, 0.836569}, + {0.601209, 0.723548}, {0.224124, 0.130335}, + {0.840747, 0.889789}, {0.495012, 0.667726}, + {0.781624, 0.538222}, {0.237877, 0.01557}, + {0.530623, 0.463644}, {0.298962, 0.384992}, + {0.0597232, 0.27755}, {0.931634, 0.786367}, + {0.868736, 0.760685}, {0.659472, 0.232696}}; + std::vector> mat_t = + Util::Transpose(mat, 4, 4); + + CAPTURE(mat_t); + CAPTURE(mat_t_exp); + + CHECK(mat_t == approx(mat_t_exp)); + } + SECTION("Invalid Arguments") { + using namespace Catch::Matchers; + std::vector> mat(2 * 3, {1.0, 1.0}); + CHECK_THROWS_AS(Util::Transpose(mat, 2, 2), std::invalid_argument); + CHECK_THROWS_WITH( + Util::Transpose(mat, 2, 2), + Contains( + "Invalid number of rows and columns for the input matrix")); + } + } + SECTION("matrixMatProd") { + SECTION("Simple Iterative (Trans::Transpose)") { + for (size_t m = 2; m < 8; m++) { + std::vector> m_left(m * m, {1.0, 1.0}); + std::vector> m_right(m * m, {1.0, 1.0}); + std::vector> m_out_exp( + m * m, {0, static_cast(2 * m)}); + std::vector> m_out = Util::matrixMatProd( + m_left, m_right, m, m, m, Trans::Transpose); + + CAPTURE(m_out); + CAPTURE(m_out_exp); + + CHECK(m_out == approx(m_out_exp)); + } + } + SECTION("Simple Iterative (Trans::Adjoint)") { + for (size_t m = 2; m < 8; m++) { + std::vector> m_left(m * m, {1.0, 1.0}); + std::vector> m_right(m * m, {1.0, 1.0}); + std::vector> m_out_exp( + m * m, {static_cast(2 * m), 0}); + std::vector> m_out = Util::matrixMatProd( + m_left, m_right, m, m, m, Trans::Adjoint); + + CAPTURE(m_out); + CAPTURE(m_out_exp); + + CHECK(m_out == approx(m_out_exp)); + } + } + SECTION("Random Complex") { + std::vector> m_left{ + {0.94007, 0.424517}, {0.256163, 0.0615097}, + {0.505297, 0.343107}, {0.729021, 0.241991}, + {0.860825, 0.633264}, {0.987668, 0.195166}, + {0.606897, 0.144482}, {0.0183697, 0.375071}, + {0.355853, 0.152383}, {0.985341, 0.0888863}, + {0.608352, 0.653375}, {0.268477, 0.58398}, + {0.960381, 0.786669}, {0.498357, 0.185307}, + {0.283511, 0.844801}, {0.269318, 0.792981}}; + std::vector> m_right{ + {0.94007, 0.424517}, {0.256163, 0.0615097}, + {0.505297, 0.343107}, {0.729021, 0.241991}, + {0.860825, 0.633264}, {0.987668, 0.195166}, + {0.606897, 0.144482}, {0.0183697, 0.375071}, + {0.355853, 0.152383}, {0.985341, 0.0888863}, + {0.608352, 0.653375}, {0.268477, 0.58398}, + {0.960381, 0.786669}, {0.498357, 0.185307}, + {0.283511, 0.844801}, {0.269318, 0.792981}}; + std::vector> m_right_tp{ + {0.94007, 0.424517}, {0.860825, 0.633264}, + {0.355853, 0.152383}, {0.960381, 0.786669}, + {0.256163, 0.0615097}, {0.987668, 0.195166}, + {0.985341, 0.0888863}, {0.498357, 0.185307}, + {0.505297, 0.343107}, {0.606897, 0.144482}, + {0.608352, 0.653375}, {0.283511, 0.844801}, + {0.729021, 0.241991}, {0.0183697, 0.375071}, + {0.268477, 0.58398}, {0.269318, 0.792981}}; + std::vector> m_out_exp{ + {1.522375435807200, 2.018315393556500}, + {1.241561065671800, 0.915996420839700}, + {0.561409446565600, 1.834755796266900}, + {0.503973820211400, 1.664651528374090}, + {1.183556828429700, 2.272762769584300}, + {1.643767359748500, 0.987318478828500}, + {0.752063484100700, 1.482770126810700}, + {0.205343773497200, 1.552791421044900}, + {0.977117116888800, 2.092066653216500}, + {1.604565422784600, 1.379671036009100}, + {0.238648365886400, 1.582741563052100}, + {-0.401698027789600, 1.469264325654110}, + {0.487510164243000, 2.939585667799000}, + {0.845207296911400, 1.843583823364000}, + {-0.482010055957000, 2.062995137499000}, + {-0.524094900662100, 1.815727577737900}}; + std::vector> m_out_1 = Util::matrixMatProd( + m_left, m_right_tp, 4, 4, 4, Trans::Transpose); + std::vector> m_out_2 = Util::matrixMatProd( + m_left, m_right, 4, 4, 4, Trans::NoTranspose); + + CAPTURE(m_out_1); + CAPTURE(m_out_2); + CAPTURE(m_out_exp); + + CHECK(m_out_1 == approx(m_out_2)); + CHECK(m_out_1 == approx(m_out_exp)); + } + SECTION("Random complex non-square") { + const size_t m = 4; + const size_t k = 2; + const size_t n = 8; + std::vector> mat1{ + {-0.08981826740301613, -0.27637263739311546}, + {0.8727813226706924, 0.258896589429058}, + {-0.5949864309922819, 0.18285525036841555}, + {-0.11134824896298667, -0.13897161071967146}, + {-0.1735039889140335, 0.21741315096734315}, + {0.5297588834950917, -0.7710643565719117}, + {-0.8579225641269561, 0.9468802886927876}, + {-0.9547436543380183, -0.42965580917488455}, + }; // m x k + + std::vector> mat2{ + {-0.2737437848727584, 0.049006595173545886}, + {0.5192576146240857, 0.7301480202235375}, + {0.653209372398236, 0.6769135138733755}, + {0.24089266818363964, -0.31764940703302114}, + {-0.15687970845233856, 0.733905243784096}, + {-0.6359345637324041, 0.539820653560259}, + {-0.6134456192663982, -0.931661049054201}, + {0.24880944149427253, 0.10974806658737091}, + {-0.940232124693291, -0.5232886593299633}, + {0.4843048111315602, 0.35624698471285265}, + {0.2799604418471606, 0.45969907217442474}, + {-0.4750846673676159, 0.8376958296863313}, + {-0.06299227175924127, 0.8246636234326061}, + {0.5033921732959004, -0.21101881518679866}, + {0.005396787998366959, -0.17840690755422184}, + {-0.6585640941981321, 0.14497152454268059}, + }; // k x n + + const std::vector> expected{ + {-0.6470081137924694, -0.6288858542578596}, + {0.4856151790538221, 0.22722135179683745}, + {0.25373996181718034, 0.2323691717947702}, + {-0.7409477841248294, 0.5700820515045693}, + {-0.05155908637184568, 0.68088168504282}, + {0.7002935368283547, 0.07342239296147188}, + {-0.15148784295130674, 0.09890675818471939}, + {-0.6043313448030839, -0.12259306171417446}, + {0.18588365718252325, 0.10971910126048831}, + {-0.44688090933900626, -0.4464512811494489}, + {-0.47971588770318185, -0.37340483265491176}, + {0.08407177752281807, 0.20579289380315458}, + {0.0807617819372747, -0.5484206530358158}, + {0.19428593398213712, -0.48393058348427054}, + {0.5099565157985027, 0.46126922661113773}, + {-0.07462936679210332, 0.05557688979687145}, + {-0.864744593670385, 0.3797443237699707}, + {0.28241707890402445, -0.19849533255712323}, + {0.24226476870790875, 0.05223126234803932}, + {0.4215023926257107, 0.9175843340352592}, + {0.4701566142679236, 0.3240007768827886}, + {0.09694046297691916, -0.7318584281365454}, + {0.17428641523616784, -0.07039815172433624}, + {-0.304127907507671, 0.6196479698717412}, + {0.8612942331452932, 0.6023362633910544}, + {-1.4461689510687892, -0.6829445852068847}, + {-1.2711372016033236, -0.5214097006435647}, + {0.9076136576558541, -0.09504677750515506}, + {-0.14586677921501573, -1.538457538144058}, + {-0.5368388676391003, -1.080094719371998}, + {1.3266542084028976, 0.38644757259657603}, + {0.3736702325957464, 0.28598265876636564}, + }; // m x n + + const auto m_out = Util::matrixMatProd(mat1, mat2, m, n, k); + + CHECK(m_out == approx(expected)); + } + SECTION("Invalid Arguments") { + using namespace Catch::Matchers; + std::vector> m_left(2 * 3, {1.0, 1.0}); + std::vector> m_right(3 * 4, {1.0, 1.0}); + CHECK_THROWS_AS(Util::matrixMatProd(m_left, m_right, 2, 3, 4), + std::invalid_argument); + CHECK_THROWS_WITH(Util::matrixMatProd(m_left, m_right, 2, 3, 4), + Contains("Invalid number of rows and columns for " + "the input left matrix")); + CHECK_THROWS_AS(Util::matrixMatProd(m_left, m_right, 2, 3, 3), + std::invalid_argument); + CHECK_THROWS_WITH(Util::matrixMatProd(m_left, m_right, 2, 3, 3), + Contains("Invalid number of rows and columns for " + "the input right matrix")); + } + SECTION("nullptr for m_out") { + std::vector> m_left(2 * 3, {1.0, 1.0}); + std::vector> m_right(3 * 4, {1.0, 1.0}); + auto m_out = nullptr; + Util::matrixMatProd(m_left.data(), m_right.data(), m_out, + 2, 3, 4, Trans::NoTranspose); + CHECK(m_out == nullptr); + + Util::omp_matrixMatProd(m_left.data(), m_right.data(), + m_out, 2, 3, 4, + Trans::NoTranspose); + CHECK(m_out == nullptr); + } + } + SECTION("SquaredNorm") { + SECTION("For real type") { + std::vector vec{0.0, 1.0, 3.0, 10.0}; + CHECK(Util::squaredNorm(vec) == Approx(110.0)); + } + + SECTION("For complex type") { + std::vector> vec{{0.0, 1.0}, {3.0, 10.0}}; + CHECK(Util::squaredNorm(vec) == Approx(110.0)); + } + } +} diff --git a/pennylane_lightning/src/tests/Test_Measures.cpp b/pennylane_lightning/src/tests/Test_Measures.cpp index c72a9b68cf..7d2c7c3d4c 100644 --- a/pennylane_lightning/src/tests/Test_Measures.cpp +++ b/pennylane_lightning/src/tests/Test_Measures.cpp @@ -9,6 +9,10 @@ #include +#if defined(_MSC_VER) +#pragma warning(disable : 4305) +#endif + using namespace Pennylane; namespace { @@ -18,20 +22,21 @@ using std::string; using std::vector; }; // namespace -StateVectorManaged Initializing_StateVector() { +template +StateVectorManaged Initializing_StateVector() { // Defining a StateVector in a non-trivial configuration: size_t num_qubits = 3; size_t data_size = std::pow(2, num_qubits); - std::vector> arr(data_size, 0); + std::vector> arr(data_size, 0); arr[0] = 1; - StateVectorManaged Measured_StateVector(arr.data(), data_size); + StateVectorManaged Measured_StateVector(arr.data(), data_size); std::vector wires; - double alpha = 0.7; - double beta = 0.5; - double gamma = 0.2; + T alpha = 0.7; + T beta = 0.5; + T gamma = 0.2; Measured_StateVector.applyOperations( {"RX", "RY", "RX", "RY", "RX", "RY"}, {{0}, {0}, {1}, {1}, {2}, {2}}, {false, false, false, false, false, false}, @@ -163,39 +168,95 @@ TEST_CASE("Expected Values", "[Measures]") { } } -TEST_CASE("Variances", "[Measures]") { +TEMPLATE_TEST_CASE("Sample", "[Measures]", float, double) { + constexpr uint32_t twos[] = { + 1U << 0U, 1U << 1U, 1U << 2U, 1U << 3U, 1U << 4U, 1U << 5U, + 1U << 6U, 1U << 7U, 1U << 8U, 1U << 9U, 1U << 10U, 1U << 11U, + 1U << 12U, 1U << 13U, 1U << 14U, 1U << 15U, 1U << 16U, 1U << 17U, + 1U << 18U, 1U << 19U, 1U << 20U, 1U << 21U, 1U << 22U, 1U << 23U, + 1U << 24U, 1U << 25U, 1U << 26U, 1U << 27U, 1U << 28U, 1U << 29U, + 1U << 30U, 1U << 31U}; + // Defining the State Vector that will be measured. - StateVectorManaged Measured_StateVector = - Initializing_StateVector(); + StateVectorManaged Measured_StateVector = + Initializing_StateVector(); // Initializing the measures class. // It will attach to the StateVector, allowing measures to keep been taken. - Measures> Measurer(Measured_StateVector); + Measures> Measurer( + Measured_StateVector); + vector expected_probabilities = {0.687573, 0.013842, 0.089279, + 0.001797, 0.180036, 0.003624, + 0.023377, 0.000471}; + + size_t num_qubits = 3; + size_t N = std::pow(2, num_qubits); + size_t num_samples = 100000; + auto &&samples = Measurer.generate_samples(num_samples); + + std::vector counts(N, 0); + std::vector samples_decimal(num_samples, 0); + + // convert samples to decimal and then bin them in counts + for (size_t i = 0; i < num_samples; i++) { + for (size_t j = 0; j < num_qubits; j++) { + if (samples[i * num_qubits + j] != 0) { + samples_decimal[i] += twos[(num_qubits - 1 - j)]; + } + } + counts[samples_decimal[i]] += 1; + } + + // compute estimated probabilities from histogram + std::vector probabilities(counts.size()); + for (size_t i = 0; i < counts.size(); i++) { + probabilities[i] = counts[i] / (TestType)num_samples; + } + + // compare estimated probabilities to real probabilities + SECTION("No wires provided:") { + REQUIRE_THAT(probabilities, + Catch::Approx(expected_probabilities).margin(.05)); + } +} + +TEMPLATE_TEST_CASE("Variances", "[Measures]", float, double) { + // Defining the State Vector that will be measured. + StateVectorManaged Measured_StateVector = + Initializing_StateVector(); + + // Initializing the measures class. + // It will attach to the StateVector, allowing measures to keep been taken. + Measures> Measurer( + Measured_StateVector); SECTION("Testing single operation defined by a matrix:") { - vector> PauliX = {0, 1, 1, 0}; + vector> PauliX = {0, 1, 1, 0}; vector wires_single = {0}; - double variance = Measurer.var(PauliX, wires_single); - double variances_ref = 0.757222; + TestType variance = Measurer.var(PauliX, wires_single); + TestType variances_ref = 0.757222; REQUIRE(variance == Approx(variances_ref).margin(1e-6)); } SECTION("Testing single operation defined by its name:") { vector wires_single = {0}; - double variance = Measurer.var("PauliX", wires_single); - double variances_ref = 0.757222; + TestType variance = Measurer.var("PauliX", wires_single); + TestType variances_ref = 0.757222; REQUIRE(variance == Approx(variances_ref).margin(1e-6)); } SECTION("Testing list of operators defined by a matrix:") { - vector> PauliX = {0, 1, 1, 0}; - vector> PauliY = {0, {0, -1}, {0, 1}, 0}; - vector> PauliZ = {1, 0, 0, -1}; - - vector variances; - vector variances_ref; + vector> PauliX = { + {0, 0}, {1, 0}, {1, 0}, {0, 0}}; + vector> PauliY = { + {0, 0}, {0, -1}, {0, 1}, {0, 0}}; + vector> PauliZ = { + {1, 0}, {0, 0}, {0, 0}, {-1, 0}}; + + vector variances; + vector variances_ref; vector> wires_list = {{0}, {1}, {2}}; - vector>> operations_list; + vector>> operations_list; operations_list = {PauliX, PauliX, PauliX}; variances = Measurer.var(operations_list, wires_list); @@ -214,8 +275,8 @@ TEST_CASE("Variances", "[Measures]") { } SECTION("Testing list of operators defined by its name:") { - vector variances; - vector variances_ref; + vector variances; + vector variances_ref; vector> wires_list = {{0}, {1}, {2}}; vector operations_list; diff --git a/pennylane_lightning/src/tests/Test_OpToMemberFuncPtr.cpp b/pennylane_lightning/src/tests/Test_OpToMemberFuncPtr.cpp index 551cc5fc2e..d760a765d6 100644 --- a/pennylane_lightning/src/tests/Test_OpToMemberFuncPtr.cpp +++ b/pennylane_lightning/src/tests/Test_OpToMemberFuncPtr.cpp @@ -104,6 +104,7 @@ class DummyImplementation { static_cast(arr); static_cast(num_qubits); static_cast(matrix); + static_cast(wires); static_cast(inverse); } @@ -210,8 +211,8 @@ constexpr auto gateOpFuncPtrPairsWithNumParamsIter() { decltype(gate_op_func_ptr_pairs)>) { constexpr auto elt = std::get(gate_op_func_ptr_pairs); - if constexpr (static_lookup(Constant::gate_num_params) == - num_params) { + if constexpr (Util::static_lookup( + Constant::gate_num_params) == num_params) { return Util::prepend_to_tuple( elt, gateOpFuncPtrPairsWithNumParamsIter< PrecisionT, ParamT, num_params, tuple_idx + 1>()); diff --git a/pennylane_lightning/src/tests/Test_RuntimeInfo.cpp b/pennylane_lightning/src/tests/Test_RuntimeInfo.cpp new file mode 100644 index 0000000000..93823e386b --- /dev/null +++ b/pennylane_lightning/src/tests/Test_RuntimeInfo.cpp @@ -0,0 +1,13 @@ +#include "Macros.hpp" +#include "RuntimeInfo.hpp" + +#include + +using namespace Pennylane::Util; + +TEST_CASE("Runtime information is correct", "[Test_RuntimeInfo]") { + INFO("RuntimeInfo::AVX " << RuntimeInfo::AVX()); + INFO("RuntimeInfo::AVX2 " << RuntimeInfo::AVX2()); + INFO("RuntimeInfo::AVX512F " << RuntimeInfo::AVX512F()); + REQUIRE(true); +} diff --git a/pennylane_lightning/src/tests/Test_StateVectorRaw.cpp b/pennylane_lightning/src/tests/Test_StateVectorRaw.cpp index 4700c74881..6fe8ef6e7b 100644 --- a/pennylane_lightning/src/tests/Test_StateVectorRaw.cpp +++ b/pennylane_lightning/src/tests/Test_StateVectorRaw.cpp @@ -14,12 +14,12 @@ std::mt19937_64 re{1337}; TEMPLATE_TEST_CASE("StateVectorRaw::StateVectorRaw", "[StateVectorRaw]", float, double) { - using fp_t = TestType; + using PrecisionT = TestType; SECTION("StateVectorRaw {std::complex*, size_t}") { const size_t num_qubits = 4; - auto st_data = createRandomState(re, num_qubits); - StateVectorRaw sv(st_data.data(), st_data.size()); + auto st_data = createRandomState(re, num_qubits); + StateVectorRaw sv(st_data.data(), st_data.size()); REQUIRE(sv.getNumQubits() == 4); REQUIRE(sv.getData() == st_data.data()); @@ -27,21 +27,33 @@ TEMPLATE_TEST_CASE("StateVectorRaw::StateVectorRaw", "[StateVectorRaw]", float, } SECTION("StateVectorRaw {std::complex*, size_t}") { std::vector> st_data(14, 0.0); - REQUIRE_THROWS(StateVectorRaw(st_data.data(), st_data.size())); + REQUIRE_THROWS( + StateVectorRaw(st_data.data(), st_data.size())); } } TEMPLATE_TEST_CASE("StateVectorRaw::setData", "[StateVectorRaw]", float, double) { - using fp_t = TestType; + using PrecisionT = TestType; - auto st_data = createRandomState(re, 4); - StateVectorRaw sv(st_data.data(), st_data.size()); + SECTION("setData correctly update data") { + auto st_data = createRandomState(re, 4); + StateVectorRaw sv(st_data.data(), st_data.size()); - auto st_data2 = createRandomState(re, 8); - sv.setData(st_data2.data(), st_data2.size()); + auto st_data2 = createRandomState(re, 8); + sv.setData(st_data2.data(), st_data2.size()); - REQUIRE(sv.getNumQubits() == 8); - REQUIRE(sv.getData() == st_data2.data()); - REQUIRE(sv.getLength() == (1U << 8)); + REQUIRE(sv.getNumQubits() == 8); + REQUIRE(sv.getData() == st_data2.data()); + REQUIRE(sv.getLength() == (1U << 8U)); + } + + SECTION("setData throws an exception when the data is incorrect") { + auto st_data = createRandomState(re, 4); + StateVectorRaw sv(st_data.data(), st_data.size()); + + std::vector> new_data(7, PrecisionT{0.0}); + + REQUIRE_THROWS(sv.setData(new_data.data(), new_data.size())); + } } diff --git a/pennylane_lightning/src/tests/Test_Util.cpp b/pennylane_lightning/src/tests/Test_Util.cpp index 4360a793f5..89b15dc166 100644 --- a/pennylane_lightning/src/tests/Test_Util.cpp +++ b/pennylane_lightning/src/tests/Test_Util.cpp @@ -1,6 +1,8 @@ #include #include + #include +#include #include #include #include @@ -8,11 +10,16 @@ #include #include "BitUtil.hpp" +#include "Error.hpp" #include "LinearAlgebra.hpp" #include "Util.hpp" #include "TestHelpers.hpp" +#if defined(_MSC_VER) +#pragma warning(disable : 4305) +#endif + using namespace Pennylane; /** @@ -20,9 +27,9 @@ using namespace Pennylane; * multiplication. */ TEMPLATE_TEST_CASE("Util::ConstMult", "[Util]", float, double) { - constexpr TestType r_val = 0.679; - constexpr std::complex c0_val{1.321, -0.175}; - constexpr std::complex c1_val{0.579, 1.334}; + constexpr TestType r_val{0.679}; + constexpr std::complex c0_val{TestType{1.321}, TestType{-0.175}}; + constexpr std::complex c1_val{TestType{0.579}, TestType{1.334}}; SECTION("Real times Complex") { constexpr std::complex result = @@ -58,8 +65,7 @@ TEMPLATE_TEST_CASE("Constant values", "[Util]", float, double) { } // NOLINTNEXTLINE: Avoid complexity errors -TEMPLATE_TEST_CASE("Utility math functions", "[Util][LinearAlgebra]", float, - double) { +TEMPLATE_TEST_CASE("Utility math functions", "[Util]", float, double) { SECTION("exp2: 2^n") { for (size_t i = 0; i < 10; i++) { CHECK(Util::exp2(i) == static_cast(std::pow(2, i))); @@ -78,7 +84,7 @@ TEMPLATE_TEST_CASE("Utility math functions", "[Util][LinearAlgebra]", float, for (size_t i = 0; i < 64; i++) { std::vector data(i); TestType rem; - std::modf(sqrt(i), &rem); + std::modf(sqrt(static_cast(i)), &rem); if (i < 4) { CHECK_THROWS_AS(Util::dimSize(data), std::invalid_argument); CHECK_THROWS_WITH(Util::dimSize(data), @@ -97,368 +103,6 @@ TEMPLATE_TEST_CASE("Utility math functions", "[Util][LinearAlgebra]", float, } } } - SECTION("innerProd") { - SECTION("Iterative increment") { - for (size_t i = 0; i < 12; i++) { - std::vector> data1(1UL << i, {1, 1}); - std::vector> data2(1UL << i, {1, 1}); - std::complex expected_result(0, 1UL << (i + 1)); - std::complex result = Util::innerProd(data1, data2); - CHECK(isApproxEqual(result, expected_result)); - } - } - SECTION("Random complex") { - std::vector> data1{ - {0.326417, 0}, {-0, 0.343918}, {0, 0.508364}, {-0.53562, -0}, - {0, -0.178322}, {0.187883, -0}, {0.277721, 0}, {-0, 0.292611}}; - std::vector> data2{ - {0, -0.479426}, {0, 0}, {2.77556e-17, 0}, {0, 0}, - {0.877583, 0}, {0, 0}, {0, 0}, {0, 0}}; - std::complex expected_result(0, -0.312985152368); - std::complex result = Util::innerProd(data1, data2); - CHECK(isApproxEqual(result, expected_result)); - } - } - SECTION("innerProdC") { - SECTION("Iterative increment") { - for (size_t i = 0; i < 12; i++) { - std::vector> data1(1UL << i, {1, 1}); - std::vector> data2(1UL << i, {1, 1}); - std::complex expected_result(1UL << (i + 1), 0); - std::complex result = Util::innerProdC(data1, data2); - CAPTURE(result); - CAPTURE(expected_result); - CHECK(isApproxEqual(result, expected_result)); - } - } - SECTION("Random complex") { - std::vector> data2{ - {0, -0.479426}, {0, 0}, {2.77556e-17, 0}, {0, 0}, - {0.877583, 0}, {0, 0}, {0, 0}, {0, 0}}; - std::vector> data1{ - {0.326417, 0}, {-0, 0.343918}, {0, 0.508364}, {-0.53562, -0}, - {0, -0.178322}, {0.187883, -0}, {0.277721, 0}, {-0, 0.292611}}; - std::complex expected_result(0, -4.40916e-7); - std::complex result = Util::innerProdC(data1, data2); - CAPTURE(result); - CAPTURE(expected_result); - CHECK(real(result) == Approx(real(expected_result)).margin(1e-7)); - CHECK(imag(result) == Approx(imag(expected_result)).margin(1e-7)); - } - } - SECTION("matrixVecProd") { - SECTION("Simple Iterative") { - for (size_t m = 2; m < 8; m++) { - std::vector> mat(m * m, {1, 1}); - std::vector> v_in(m, {1, 1}); - std::vector> v_expected( - m, {0, static_cast(2 * m)}); - std::vector> v_out = - Util::matrixVecProd(mat, v_in, m, m); - CAPTURE(v_out); - CAPTURE(v_expected); - - CHECK(v_out == PLApprox(v_expected).margin(1e-7)); - } - } - SECTION("Random Complex") { - std::vector> mat{ - {0.417876, 0.27448}, {0.601209, 0.723548}, - {0.781624, 0.538222}, {0.0597232, 0.27755}, - {0.0431741, 0.593319}, {0.224124, 0.130335}, - {0.237877, 0.01557}, {0.931634, 0.786367}, - {0.378397, 0.894381}, {0.840747, 0.889789}, - {0.530623, 0.463644}, {0.868736, 0.760685}, - {0.258175, 0.836569}, {0.495012, 0.667726}, - {0.298962, 0.384992}, {0.659472, 0.232696}}; - std::vector> v_in{{0.417876, 0.27448}, - {0.601209, 0.723548}, - {0.781624, 0.538222}, - {0.0597232, 0.27755}}; - std::vector> v_expected{ - {0.184998, 1.97393}, - {-0.0894368, 0.946047}, - {-0.219747, 2.55541}, - {-0.305997, 1.83881}}; - std::vector> v_out = - Util::matrixVecProd(mat, v_in, 4, 4); - CAPTURE(v_out); - - CHECK(v_out == PLApprox(v_expected).margin(1e-7)); - } - SECTION("Invalid Arguments") { - using namespace Catch::Matchers; - std::vector> mat(2 * 3, {1, 1}); - std::vector> v_in(2, {1, 1}); - CHECK_THROWS_AS(Util::matrixVecProd(mat, v_in, 2, 3), - std::invalid_argument); - CHECK_THROWS_WITH(Util::matrixVecProd(mat, v_in, 2, 3), - Contains("Invalid size for the input vector")); - CHECK_THROWS_AS(Util::matrixVecProd(mat, v_in, 2, 2), - std::invalid_argument); - CHECK_THROWS_WITH( - Util::matrixVecProd(mat, v_in, 2, 2), - Contains( - "Invalid number of rows and columns for the input matrix")); - } - } - SECTION("vecMatrixProd") { - SECTION("Simple Iterative") { - for (size_t m = 2; m < 8; m++) { - std::vector mat(m * m, 1); - std::vector v_in(m, 1); - std::vector v_expected(m, m); - std::vector v_out = - Util::vecMatrixProd(v_in, mat, m, m); - - CAPTURE(v_out); - CAPTURE(v_expected); - - CHECK(v_out == PLApprox(v_expected).margin(1e-7)); - } - } - SECTION("Zero Vector") { - for (size_t m = 2; m < 8; m++) { - std::vector mat(m * m, 1); - std::vector v_in(m, 0); - std::vector v_expected(m, 0); - std::vector v_out = - Util::vecMatrixProd(v_in, mat, m, m); - - CAPTURE(v_out); - CAPTURE(v_expected); - - CHECK(v_out == PLApprox(v_expected).margin(1e-7)); - } - } - SECTION("Random Matrix") { - std::vector v_in{1.0, 2.0, 3.0, 4.0}; - std::vector mat{1.0, 0.1, 0.2, 0.2, 0.6, 0.1, - 0.4, -0.7, 1.2, -0.5, -0.6, 0.7}; - std::vector v_expected{0.6, -3.2, 6.8}; - std::vector v_out = Util::vecMatrixProd(v_in, mat, 4, 3); - - CAPTURE(v_out); - CAPTURE(v_expected); - - CHECK(v_out == PLApprox(v_expected).margin(1e-7)); - } - } - SECTION("Transpose") { - SECTION("Simple Matrix") { - for (size_t m = 2; m < 8; m++) { - std::vector> mat(m * m, {0, 0}); - for (size_t i = 0; i < m; i++) { - mat[i * m + i] = {1, 1}; - } - std::vector> mat_t = - Util::Transpose(mat, m, m); - - CAPTURE(mat_t); - CAPTURE(mat); - - CHECK(mat_t == PLApprox(mat).margin(1e-7)); - } - } - SECTION("Random Complex") { - std::vector> mat{ - {0.417876, 0.27448}, {0.601209, 0.723548}, - {0.781624, 0.538222}, {0.0597232, 0.27755}, - {0.0431741, 0.593319}, {0.224124, 0.130335}, - {0.237877, 0.01557}, {0.931634, 0.786367}, - {0.378397, 0.894381}, {0.840747, 0.889789}, - {0.530623, 0.463644}, {0.868736, 0.760685}, - {0.258175, 0.836569}, {0.495012, 0.667726}, - {0.298962, 0.384992}, {0.659472, 0.232696}}; - std::vector> mat_t_exp{ - {0.417876, 0.27448}, {0.0431741, 0.593319}, - {0.378397, 0.894381}, {0.258175, 0.836569}, - {0.601209, 0.723548}, {0.224124, 0.130335}, - {0.840747, 0.889789}, {0.495012, 0.667726}, - {0.781624, 0.538222}, {0.237877, 0.01557}, - {0.530623, 0.463644}, {0.298962, 0.384992}, - {0.0597232, 0.27755}, {0.931634, 0.786367}, - {0.868736, 0.760685}, {0.659472, 0.232696}}; - std::vector> mat_t = - Util::Transpose(mat, 4, 4); - - CAPTURE(mat_t); - CAPTURE(mat_t_exp); - - CHECK(mat_t == PLApprox(mat_t_exp)); - } - SECTION("Invalid Arguments") { - using namespace Catch::Matchers; - std::vector> mat(2 * 3, {1, 1}); - CHECK_THROWS_AS(Util::Transpose(mat, 2, 2), std::invalid_argument); - CHECK_THROWS_WITH( - Util::Transpose(mat, 2, 2), - Contains( - "Invalid number of rows and columns for the input matrix")); - } - } - SECTION("matrixMatProd") { - SECTION("Simple Iterative") { - for (size_t m = 2; m < 8; m++) { - std::vector> m_left(m * m, {1, 1}); - std::vector> m_right(m * m, {1, 1}); - std::vector> m_out_exp( - m * m, {0, static_cast(2 * m)}); - std::vector> m_out = Util::matrixMatProd( - m_left, m_right, m, m, m, Trans::Transpose); - - CAPTURE(m_out); - CAPTURE(m_out_exp); - - CHECK(m_out == PLApprox(m_out_exp)); - } - } - SECTION("Random Complex") { - std::vector> m_left{ - {0.94007, 0.424517}, {0.256163, 0.0615097}, - {0.505297, 0.343107}, {0.729021, 0.241991}, - {0.860825, 0.633264}, {0.987668, 0.195166}, - {0.606897, 0.144482}, {0.0183697, 0.375071}, - {0.355853, 0.152383}, {0.985341, 0.0888863}, - {0.608352, 0.653375}, {0.268477, 0.58398}, - {0.960381, 0.786669}, {0.498357, 0.185307}, - {0.283511, 0.844801}, {0.269318, 0.792981}}; - std::vector> m_right{ - {0.94007, 0.424517}, {0.256163, 0.0615097}, - {0.505297, 0.343107}, {0.729021, 0.241991}, - {0.860825, 0.633264}, {0.987668, 0.195166}, - {0.606897, 0.144482}, {0.0183697, 0.375071}, - {0.355853, 0.152383}, {0.985341, 0.0888863}, - {0.608352, 0.653375}, {0.268477, 0.58398}, - {0.960381, 0.786669}, {0.498357, 0.185307}, - {0.283511, 0.844801}, {0.269318, 0.792981}}; - std::vector> m_right_tp{ - {0.94007, 0.424517}, {0.860825, 0.633264}, - {0.355853, 0.152383}, {0.960381, 0.786669}, - {0.256163, 0.0615097}, {0.987668, 0.195166}, - {0.985341, 0.0888863}, {0.498357, 0.185307}, - {0.505297, 0.343107}, {0.606897, 0.144482}, - {0.608352, 0.653375}, {0.283511, 0.844801}, - {0.729021, 0.241991}, {0.0183697, 0.375071}, - {0.268477, 0.58398}, {0.269318, 0.792981}}; - std::vector> m_out_exp{ - {1.522375435807200, 2.018315393556500}, - {1.241561065671800, 0.915996420839700}, - {0.561409446565600, 1.834755796266900}, - {0.503973820211400, 1.664651528374090}, - {1.183556828429700, 2.272762769584300}, - {1.643767359748500, 0.987318478828500}, - {0.752063484100700, 1.482770126810700}, - {0.205343773497200, 1.552791421044900}, - {0.977117116888800, 2.092066653216500}, - {1.604565422784600, 1.379671036009100}, - {0.238648365886400, 1.582741563052100}, - {-0.401698027789600, 1.469264325654110}, - {0.487510164243000, 2.939585667799000}, - {0.845207296911400, 1.843583823364000}, - {-0.482010055957000, 2.062995137499000}, - {-0.524094900662100, 1.815727577737900}}; - std::vector> m_out_1 = Util::matrixMatProd( - m_left, m_right_tp, 4, 4, 4, Trans::Transpose); - std::vector> m_out_2 = Util::matrixMatProd( - m_left, m_right, 4, 4, 4, Trans::NoTranspose); - - CAPTURE(m_out_1); - CAPTURE(m_out_2); - CAPTURE(m_out_exp); - - CHECK(m_out_1 == PLApprox(m_out_2)); - CHECK(m_out_1 == PLApprox(m_out_exp)); - } - SECTION("Random complex non-square") { - const size_t m = 4; - const size_t k = 2; - const size_t n = 8; - std::vector> mat1{ - {-0.08981826740301613, -0.27637263739311546}, - {0.8727813226706924, 0.258896589429058}, - {-0.5949864309922819, 0.18285525036841555}, - {-0.11134824896298667, -0.13897161071967146}, - {-0.1735039889140335, 0.21741315096734315}, - {0.5297588834950917, -0.7710643565719117}, - {-0.8579225641269561, 0.9468802886927876}, - {-0.9547436543380183, -0.42965580917488455}, - }; // m x k - - std::vector> mat2{ - {-0.2737437848727584, 0.049006595173545886}, - {0.5192576146240857, 0.7301480202235375}, - {0.653209372398236, 0.6769135138733755}, - {0.24089266818363964, -0.31764940703302114}, - {-0.15687970845233856, 0.733905243784096}, - {-0.6359345637324041, 0.539820653560259}, - {-0.6134456192663982, -0.931661049054201}, - {0.24880944149427253, 0.10974806658737091}, - {-0.940232124693291, -0.5232886593299633}, - {0.4843048111315602, 0.35624698471285265}, - {0.2799604418471606, 0.45969907217442474}, - {-0.4750846673676159, 0.8376958296863313}, - {-0.06299227175924127, 0.8246636234326061}, - {0.5033921732959004, -0.21101881518679866}, - {0.005396787998366959, -0.17840690755422184}, - {-0.6585640941981321, 0.14497152454268059}, - }; // k x n - - const std::vector> expected{ - {-0.6470081137924694, -0.6288858542578596}, - {0.4856151790538221, 0.22722135179683745}, - {0.25373996181718034, 0.2323691717947702}, - {-0.7409477841248294, 0.5700820515045693}, - {-0.05155908637184568, 0.68088168504282}, - {0.7002935368283547, 0.07342239296147188}, - {-0.15148784295130674, 0.09890675818471939}, - {-0.6043313448030839, -0.12259306171417446}, - {0.18588365718252325, 0.10971910126048831}, - {-0.44688090933900626, -0.4464512811494489}, - {-0.47971588770318185, -0.37340483265491176}, - {0.08407177752281807, 0.20579289380315458}, - {0.0807617819372747, -0.5484206530358158}, - {0.19428593398213712, -0.48393058348427054}, - {0.5099565157985027, 0.46126922661113773}, - {-0.07462936679210332, 0.05557688979687145}, - {-0.864744593670385, 0.3797443237699707}, - {0.28241707890402445, -0.19849533255712323}, - {0.24226476870790875, 0.05223126234803932}, - {0.4215023926257107, 0.9175843340352592}, - {0.4701566142679236, 0.3240007768827886}, - {0.09694046297691916, -0.7318584281365454}, - {0.17428641523616784, -0.07039815172433624}, - {-0.304127907507671, 0.6196479698717412}, - {0.8612942331452932, 0.6023362633910544}, - {-1.4461689510687892, -0.6829445852068847}, - {-1.2711372016033236, -0.5214097006435647}, - {0.9076136576558541, -0.09504677750515506}, - {-0.14586677921501573, -1.538457538144058}, - {-0.5368388676391003, -1.080094719371998}, - {1.3266542084028976, 0.38644757259657603}, - {0.3736702325957464, 0.28598265876636564}, - }; // m x n - - const auto m_out = Util::matrixMatProd(mat1, mat2, m, n, k); - - CHECK(m_out == PLApprox(expected)); - } - SECTION("Invalid Arguments") { - using namespace Catch::Matchers; - std::vector> m_left(2 * 3, {1, 1}); - std::vector> m_right(3 * 4, {1, 1}); - CHECK_THROWS_AS(Util::matrixMatProd(m_left, m_right, 2, 3, 4), - std::invalid_argument); - CHECK_THROWS_WITH(Util::matrixMatProd(m_left, m_right, 2, 3, 4), - Contains("Invalid number of rows and columns for " - "the input left matrix")); - CHECK_THROWS_AS(Util::matrixMatProd(m_left, m_right, 2, 3, 3), - std::invalid_argument); - CHECK_THROWS_WITH(Util::matrixMatProd(m_left, m_right, 2, 3, 3), - Contains("Invalid number of rows and columns for " - "the input right matrix")); - } - } } /** @@ -468,7 +112,7 @@ TEMPLATE_TEST_CASE("Utility math functions", "[Util][LinearAlgebra]", float, */ size_t popcount_slow(uint64_t x) { size_t c = 0; - for (; x != 0; x >>= 1) { + for (; x != 0U; x >>= 1U) { if ((x & 1U) != 0U) { c++; } @@ -483,8 +127,8 @@ size_t popcount_slow(uint64_t x) { */ size_t ctz_slow(uint64_t x) { size_t c = 0; - while ((x & 1) == 0) { - x >>= 1; + while ((x & 1U) == 0) { + x >>= 1U; c++; } return c; @@ -546,7 +190,8 @@ TEST_CASE("Utility bit operations", "[Util][BitUtil]") { uint64_t n = static_cast(1U) << static_cast(c); CHECK(Util::Internal::countTrailing0(n) == c); - CHECK(Util::Internal::countTrailing0(n | (1UL << 63U)) == c); + CHECK(Util::Internal::countTrailing0( + n | (uint64_t{1U} << 63U)) == c); } } } @@ -593,3 +238,77 @@ TEST_CASE("Utility array and tuples", "[Util]") { std::pair("Four", 4), }); } + +/** + * @brief Test randomUnitary is correct + */ +TEMPLATE_TEST_CASE("randomUnitary", "[Util]", float, double) { + using PrecisionT = TestType; + + std::mt19937 re{1337}; + + for (size_t num_qubits = 1; num_qubits <= 5; num_qubits++) { + const size_t dim = (1U << num_qubits); + const auto unitary = Util::randomUnitary(re, num_qubits); + + auto unitary_dagger = Util::Transpose(unitary, dim, dim); + std::transform( + unitary_dagger.begin(), unitary_dagger.end(), + unitary_dagger.begin(), + [](const std::complex &v) { return std::conj(v); }); + + std::vector> mat(dim * dim); + Util::matrixMatProd(unitary.data(), unitary_dagger.data(), mat.data(), + dim, dim, dim); + + std::vector> identity( + dim * dim, std::complex{}); + for (size_t i = 0; i < dim; i++) { + identity[i * dim + i] = std::complex{1.0, 0.0}; + } + + REQUIRE(mat == approx(identity).margin(1e-5)); + } +} + +enum class TestEnum { One, Two, Many }; + +TEST_CASE("Test utility functions for constants", "[Util][ConstantUtil]") { + using namespace std::literals; + + SECTION("lookup") { + constexpr std::array test_pairs = { + std::pair{"Pennylane"sv, "-"sv}, + std::pair{"Lightning"sv, "is"sv}, + std::pair{"the"sv, "best"sv}, + std::pair{"QML"sv, "library"sv}, + }; + + REQUIRE(Util::lookup(test_pairs, "Pennylane"sv) == "-"sv); + REQUIRE(Util::lookup(test_pairs, "Lightning"sv) == "is"sv); + REQUIRE(Util::lookup(test_pairs, "the"sv) == "best"sv); + REQUIRE(Util::lookup(test_pairs, "QML"sv) == "library"sv); + REQUIRE_THROWS(Util::lookup(test_pairs, "bad"sv)); + } + + SECTION("count_unique") { + constexpr std::array test_arr1 = {"This"sv, "is"sv, "a"sv, "test"sv, + "arr"sv}; + constexpr std::array test_arr2 = {"This"sv, "is"sv, "a"sv, + "test"sv, "arr"sv, "is"sv}; + + REQUIRE(Util::count_unique(test_arr1) == 5); + REQUIRE(Util::count_unique(test_arr2) == 5); + } + + SECTION("static_lookup") { + std::array test_pairs = { + std::pair{TestEnum::One, uint32_t{1U}}, + std::pair{TestEnum::Two, uint32_t{2U}}, + }; + + REQUIRE(Util::static_lookup(test_pairs) == 1U); + REQUIRE(Util::static_lookup(test_pairs) == 2U); + REQUIRE(Util::static_lookup(test_pairs) == uint32_t{}); + } +} diff --git a/pennylane_lightning/src/tests/Test_VectorJacobianProduct.cpp b/pennylane_lightning/src/tests/Test_VectorJacobianProduct.cpp index babee6b726..e7c00008c2 100644 --- a/pennylane_lightning/src/tests/Test_VectorJacobianProduct.cpp +++ b/pennylane_lightning/src/tests/Test_VectorJacobianProduct.cpp @@ -36,15 +36,76 @@ TEMPLATE_TEST_CASE("VectorJacobianProduct::VectorJacobianProduct", } } +TEST_CASE("VectorJacobianProduct::computeVJP", "[computeVJP]") { + SECTION("dy.empty() True") { + VectorJacobianProduct VJP; + std::vector jac(3); + std::vector vjp(3); + std::vector dy; + VJP.computeVJP(vjp, jac, dy, 1, 3); + CHECK(vjp.empty()); + } + SECTION("jac.empty() True") { + VectorJacobianProduct VJP; + std::vector jac; + std::vector vjp(3); + std::vector dy(1); + VJP.computeVJP(vjp, jac, dy, 1, 3); + CHECK(vjp.empty()); + } + SECTION("Invalid Arguments") { + using namespace Catch::Matchers; + VectorJacobianProduct VJP; + std::vector jac(3); + std::vector vjp(3); + std::vector dy(1); + CHECK_THROWS_AS(VJP.computeVJP(vjp, jac, dy, 2, 3), + std::invalid_argument); + CHECK_THROWS_WITH( + VJP.computeVJP(vjp, jac, dy, 2, 3), + Contains("Invalid size for the gradient-output vector")); + } +} + +TEST_CASE( + "VectorJacobianProduct::vectorJacobianProduct without trainable-params", + "[VectorJacobianProduct]") { + VectorJacobianProduct VJP; + constexpr size_t num_qubits = 1; + constexpr size_t num_params = 3; + constexpr size_t num_obs = 1; + auto obs = ObsDatum({"PauliZ"}, {{}}, {{0}}); + std::vector> jacobian( + num_obs, std::vector(num_params, 0)); + std::vector vjp_res(num_params); + std::vector dy(num_obs, 1); + + auto ops = OpsData({"RX"}, {{-M_PI / 7}}, {{0}}, {false}); + std::vector> cdata(1U << num_qubits); + cdata[0] = std::complex{1, 0}; + + StateVectorRaw psi(cdata.data(), cdata.size()); + + std::vector> obs_ls{obs}; + JacobianData tape{ + num_params, psi.getLength(), psi.getData(), obs_ls, ops, {}}; + + auto fn = VJP.vectorJacobianProduct(dy, num_params, true); + vjp_res = fn(tape); + + CAPTURE(vjp_res); + CHECK(vjp_res.empty()); +} + TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=RX, Obs=Z dy={0}", "[VectorJacobianProduct]") { VectorJacobianProduct VJP; std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_qubits = 1; - const size_t num_params = 3; - const size_t num_obs = 1; + constexpr size_t num_qubits = 1; + constexpr size_t num_params = 3; + constexpr size_t num_obs = 1; auto obs = ObsDatum({"PauliZ"}, {{}}, {{0}}); std::vector> jacobian( num_obs, std::vector(num_params, 0)); @@ -53,7 +114,7 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=RX, Obs=Z dy={0}", for (const auto &p : param) { auto ops = OpsData({"RX"}, {{p}}, {{0}}, {false}); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); cdata[0] = std::complex{1, 0}; StateVectorRaw psi(cdata.data(), cdata.size()); @@ -79,9 +140,9 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=RX, Obs=Z dy={1}", std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_qubits = 1; - const size_t num_params = 3; - const size_t num_obs = 1; + constexpr size_t num_qubits = 1; + constexpr size_t num_params = 3; + constexpr size_t num_obs = 1; auto obs = ObsDatum({"PauliZ"}, {{}}, {{0}}); std::vector> jacobian( num_obs, std::vector(num_params, 0)); @@ -91,7 +152,7 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=RX, Obs=Z dy={1}", for (const auto &p : param) { auto ops = OpsData({"RX"}, {{p}}, {{0}}, {false}); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); cdata[0] = std::complex{1, 0}; StateVectorRaw psi(cdata.data(), cdata.size()); @@ -117,9 +178,9 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=RX, Obs=Z dy={0.4}", std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_qubits = 1; - const size_t num_params = 3; - const size_t num_obs = 1; + constexpr size_t num_qubits = 1; + constexpr size_t num_params = 3; + constexpr size_t num_obs = 1; auto obs = ObsDatum({"PauliZ"}, {{}}, {{0}}); std::vector> jacobian( num_obs, std::vector(num_params, 0)); @@ -129,7 +190,7 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=RX, Obs=Z dy={0.4}", for (const auto &p : param) { auto ops = OpsData({"RX"}, {{p}}, {{0}}, {false}); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); cdata[0] = std::complex{1, 0}; StateVectorRaw psi(cdata.data(), cdata.size()); @@ -155,9 +216,9 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=RY, Obs=X dy={0.4}", VectorJacobianProduct VJP; std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_qubits = 1; - const size_t num_params = 3; - const size_t num_obs = 1; + constexpr size_t num_qubits = 1; + constexpr size_t num_params = 3; + constexpr size_t num_obs = 1; auto obs = ObsDatum({"PauliX"}, {{}}, {{0}}); std::vector> jacobian( @@ -168,7 +229,7 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=RY, Obs=X dy={0.4}", for (const auto &p : param) { auto ops = OpsData({"RY"}, {{p}}, {{0}}, {false}); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); cdata[0] = std::complex{1, 0}; StateVectorRaw psi(cdata.data(), cdata.size()); @@ -195,15 +256,15 @@ TEST_CASE( VectorJacobianProduct VJP; std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_qubits = 2; - const size_t num_params = 1; - const size_t num_obs = 2; + constexpr size_t num_qubits = 2; + constexpr size_t num_params = 1; + constexpr size_t num_obs = 2; std::vector> jacobian( num_obs, std::vector(num_params, 0)); std::vector vjp_res(num_params); std::vector dy(num_obs, 1); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -231,15 +292,15 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=[RX,RX,RX], " VectorJacobianProduct VJP; std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_qubits = 3; - const size_t num_params = 3; - const size_t num_obs = 3; + constexpr size_t num_qubits = 3; + constexpr size_t num_params = 3; + constexpr size_t num_obs = 3; std::vector> jacobian( num_obs, std::vector(num_params, 0)); std::vector vjp_res(num_params); std::vector dy(num_obs, 0.4); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -273,16 +334,16 @@ TEST_CASE( VectorJacobianProduct VJP; std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_qubits = 3; - const size_t num_params = 3; - const size_t num_obs = 3; + constexpr size_t num_qubits = 3; + constexpr size_t num_params = 3; + constexpr size_t num_obs = 3; std::vector> jacobian( num_obs, std::vector(num_params, 0)); std::vector t_params{0, 2}; std::vector vjp_res(num_params); std::vector dy(num_obs, 1); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -314,15 +375,15 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=[RX,RX,RX], " VectorJacobianProduct VJP; std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_qubits = 3; - const size_t num_params = 3; - const size_t num_obs = 1; + constexpr size_t num_qubits = 3; + constexpr size_t num_params = 3; + constexpr size_t num_obs = 1; std::vector> jacobian( num_obs, std::vector(num_params, 0)); std::vector vjp_res(num_params); std::vector dy(num_obs, 0.4); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -353,15 +414,15 @@ TEST_CASE( VectorJacobianProduct VJP; std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_qubits = 3; - const size_t num_params = 6; - const size_t num_obs = 1; + constexpr size_t num_qubits = 3; + constexpr size_t num_params = 6; + constexpr size_t num_obs = 1; std::vector> jacobian( num_obs, std::vector(num_params, 0)); std::vector vjp_res(num_params); std::vector dy(num_obs, 1); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -404,15 +465,15 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Op=Mixed, Obs=[XXX], " VectorJacobianProduct VJP; std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_qubits = 3; - const size_t num_params = 6; - const size_t num_obs = 1; + constexpr size_t num_qubits = 3; + constexpr size_t num_params = 6; + constexpr size_t num_obs = 1; std::vector> jacobian( num_obs, std::vector(num_params, 0)); std::vector vjp_res(num_params); std::vector dy(num_obs, -0.2); - std::vector> cdata(0b1 << num_qubits); + std::vector> cdata(1U << num_qubits); StateVectorRaw psi(cdata.data(), cdata.size()); cdata[0] = std::complex{1, 0}; @@ -457,8 +518,8 @@ TEST_CASE( std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { - const size_t num_params = 3; - const size_t num_obs = 1; + constexpr size_t num_params = 3; + constexpr size_t num_obs = 1; const auto thetas = Util::linspace(-2 * M_PI, 2 * M_PI, 7); std::unordered_map> expec_results{ @@ -512,7 +573,7 @@ TEST_CASE( std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { const std::vector t_params{1, 2, 3}; - const size_t num_obs = 1; + constexpr size_t num_obs = 1; const auto thetas = Util::linspace(-2 * M_PI, 2 * M_PI, 8); @@ -571,7 +632,7 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Mixed Ops, Obs and " std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; { const std::vector t_params{1, 2, 3}; - const size_t num_obs = 1; + constexpr size_t num_obs = 1; const auto thetas = Util::linspace(-2 * M_PI, 2 * M_PI, 8); @@ -622,4 +683,4 @@ TEST_CASE("VectorJacobianProduct::vectorJacobianProduct Mixed Ops, Obs and " CHECK(-0.5 * expected[1] == Approx(vjp_res[1]).margin(1e-7)); CHECK(-0.5 * expected[2] == Approx(vjp_res[2]).margin(1e-7)); } -} \ No newline at end of file +} diff --git a/pennylane_lightning/src/util/BitUtil.hpp b/pennylane_lightning/src/util/BitUtil.hpp index 8b7251ddc3..48fe1ddfcf 100644 --- a/pennylane_lightning/src/util/BitUtil.hpp +++ b/pennylane_lightning/src/util/BitUtil.hpp @@ -196,8 +196,9 @@ inline auto constexpr fillLeadingOnes(size_t pos) -> size_t { /** * @brief Swap bits in i-th and j-th position in place */ -inline void constexpr bitswap(size_t bits, const size_t i, const size_t j) { +inline auto constexpr bitswap(size_t bits, const size_t i, const size_t j) + -> size_t { size_t x = ((bits >> i) ^ (bits >> j)) & 1U; - bits ^= ((x << i) | (x << j)); + return bits ^ ((x << i) | (x << j)); } } // namespace Pennylane::Util diff --git a/pennylane_lightning/src/util/CMakeLists.txt b/pennylane_lightning/src/util/CMakeLists.txt index 20e75282f5..4be184b44d 100644 --- a/pennylane_lightning/src/util/CMakeLists.txt +++ b/pennylane_lightning/src/util/CMakeLists.txt @@ -1,7 +1,12 @@ project(lightning_utils LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) -add_library(lightning_utils INTERFACE) -target_include_directories(lightning_utils INTERFACE $ - $ -) \ No newline at end of file +set(UTIL_FILES RuntimeInfo.cpp CACHE INTERNAL "" FORCE) + +add_library(lightning_utils STATIC ${UTIL_FILES}) + +target_link_libraries(lightning_utils PRIVATE lightning_compile_options + lightning_external_libs) +target_include_directories(lightning_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +set_property(TARGET lightning_utils PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/pennylane_lightning/src/util/ConstantUtil.hpp b/pennylane_lightning/src/util/ConstantUtil.hpp index 532b49ee01..208ab30a28 100644 --- a/pennylane_lightning/src/util/ConstantUtil.hpp +++ b/pennylane_lightning/src/util/ConstantUtil.hpp @@ -42,7 +42,7 @@ constexpr auto lookup(const std::array, size> &arr, } } throw std::range_error("The given key does not exist."); -}; +} /** * @brief Check an array has an element. @@ -61,7 +61,7 @@ constexpr auto array_has_elt(const std::array &arr, const U &elt) } } return false; -}; +} /** * @brief Extract first elements from the array of pairs. @@ -200,6 +200,8 @@ reverse_pairs_helper(const std::array, size> &arr, * @tparam T Type of first elements * @tparam U Type of second elements * @tparam size Size of the array + * @param arr Array to reverse + * @return reversed array */ template constexpr auto reverse_pairs(const std::array, size> &arr) @@ -207,4 +209,19 @@ constexpr auto reverse_pairs(const std::array, size> &arr) return Internal::reverse_pairs_helper(arr, std::make_index_sequence{}); } + +/** + * @brief Constexpr function that check whether the given value is a power of 2. + * + * Can be merged with isPerfectPowerOf2 in C++20 using constexpr std::popcount. + * + * @param value Value to check + * @return True when the given value is a power of 2 + */ +constexpr auto constIsPerfectPowerOf2(size_t value) -> bool { + while ((value & 1U) == 0) { + value >>= 1U; + } + return value == 1; +} } // namespace Pennylane::Util diff --git a/pennylane_lightning/src/util/LinearAlgebra.hpp b/pennylane_lightning/src/util/LinearAlgebra.hpp index 40ea4292a8..4eb1cbecfd 100644 --- a/pennylane_lightning/src/util/LinearAlgebra.hpp +++ b/pennylane_lightning/src/util/LinearAlgebra.hpp @@ -17,9 +17,11 @@ */ #pragma once +#include #include #include #include +#include #include #include "Util.hpp" @@ -48,13 +50,13 @@ using CBLAS_LAYOUT = enum CBLAS_LAYOUT { /// @endcond // +namespace Pennylane::Util { enum class Trans : int { NoTranspose = CblasNoTrans, Transpose = CblasTrans, Adjoint = CblasConjTrans }; -namespace Pennylane::Util { /** * @brief Calculates the inner-product using OpenMP. * @@ -210,9 +212,9 @@ inline auto innerProdC(const std::complex *v1, const std::complex *v2, * @see innerProd(const std::complex *v1, const std::complex *v2, * const size_t data_size) */ -template -inline auto innerProd(const std::vector> &v1, - const std::vector> &v2) +template +inline auto innerProd(const std::vector, AllocA> &v1, + const std::vector, AllocB> &v2) -> std::complex { return innerProd(v1.data(), v2.data(), v1.size()); } @@ -224,9 +226,9 @@ inline auto innerProd(const std::vector> &v1, * @see innerProdC(const std::complex *v1, const std::complex *v2, * const size_t data_size) */ -template -inline auto innerProdC(const std::vector> &v1, - const std::vector> &v2) +template +inline auto innerProdC(const std::vector, AllocA> &v1, + const std::vector, AllocB> &v2) -> std::complex { return innerProdC(v1.data(), v2.data(), v1.size()); } @@ -369,7 +371,7 @@ inline auto matrixVecProd(const std::vector> mat, * @param n1 Index of the first column. * @param n2 Index of the last column. */ -template // NOLINT(readability-magic-numbers) +template // NOLINT(readability-magic-numbers) inline static void CFTranspose(const T *mat, T *mat_t, size_t m, size_t n, size_t m1, size_t m2, size_t n1, size_t n2) { size_t r; @@ -417,7 +419,7 @@ inline static void CFTranspose(const T *mat, T *mat_t, size_t m, size_t n, * @param n1 Index of the first column. * @param n2 Index of the last column. */ -template // NOLINT(readability-magic-numbers) +template // NOLINT(readability-magic-numbers) inline static void CFTranspose(const std::complex *mat, std::complex *mat_t, size_t m, size_t n, size_t m1, size_t m2, size_t n1, size_t n2) { @@ -461,15 +463,15 @@ inline static void CFTranspose(const std::complex *mat, * @param n Number of columns of `mat`. * @return mat transpose of shape n * m. */ -template -inline auto Transpose(const std::vector> &mat, size_t m, - size_t n) -> std::vector> { +template +inline auto Transpose(const std::vector, Alloc> &mat, size_t m, + size_t n) -> std::vector, Alloc> { if (mat.size() != m * n) { throw std::invalid_argument( "Invalid number of rows and columns for the input matrix"); } - std::vector> mat_t(n * m); + std::vector, Alloc> mat_t(n * m, mat.get_allocator()); CFTranspose(mat.data(), mat_t.data(), m, n, 0, m, 0, n); return mat_t; } @@ -484,15 +486,15 @@ inline auto Transpose(const std::vector> &mat, size_t m, * @param n Number of columns of `mat`. * @return mat transpose of shape n * m. */ -template -inline auto Transpose(const std::vector &mat, size_t m, size_t n) - -> std::vector { +template +inline auto Transpose(const std::vector &mat, size_t m, size_t n) + -> std::vector { if (mat.size() != m * n) { throw std::invalid_argument( "Invalid number of rows and columns for the input matrix"); } - std::vector mat_t(n * m); + std::vector mat_t(n * m, mat.get_allocator()); CFTranspose(mat.data(), mat_t.data(), m, n, 0, m, 0, n); return mat_t; } @@ -548,9 +550,10 @@ inline void vecMatrixProd(const T *v_in, const T *mat, T *v_out, size_t m, * @see inline void vecMatrixProd(const T *v_in, * const T *mat, T *v_out, size_t m, size_t n) */ -template -inline auto vecMatrixProd(const std::vector &v_in, const std::vector &mat, - size_t m, size_t n) -> std::vector { +template +inline auto vecMatrixProd(const std::vector &v_in, + const std::vector &mat, size_t m, size_t n) + -> std::vector { if (v_in.size() != m) { throw std::invalid_argument("Invalid size for the input vector"); } @@ -559,7 +562,7 @@ inline auto vecMatrixProd(const std::vector &v_in, const std::vector &mat, "Invalid number of rows and columns for the input matrix"); } - std::vector v_out(n); + std::vector v_out(n, mat.get_allocator()); vecMatrixProd(v_in.data(), mat.data(), v_out.data(), m, n); return v_out; @@ -745,4 +748,98 @@ inline auto matrixMatProd(const std::vector> m_left, return m_out; } + +/** + * @brief @rst + * Compute the squared norm of a real/complex vector :math:`\sum_k |v_k|^2` + * @endrst + * + * @param data Data pointer + * @param data_size Size of the data + */ +template +auto squaredNorm(const T *data, size_t data_size) -> remove_complex_t { + if constexpr (is_complex_v) { + // complex type + using PrecisionT = remove_complex_t; + return std::transform_reduce( + data, data + data_size, PrecisionT{}, std::plus(), + static_cast &)>( + &std::norm)); + } else { + using PrecisionT = T; + return std::transform_reduce( + data, data + data_size, PrecisionT{}, std::plus(), + static_cast(std::norm)); + } +} + +/** + * @brief @rst + * Compute the squared norm of a real/complex vector :math:`\sum_k |v_k|^2` + * @endrst + * + * @param vec std::vector containing data + */ +template +auto squaredNorm(const std::vector &vec) -> remove_complex_t { + return squaredNorm(vec.data(), vec.size()); +} + +/** + * @brief Generate random unitary matrix + * + * @tparam PrecisionT Floating point type + * @tparam RandomEngine Random engine type + * @param re Random engine instance + * @param num_qubits Number of qubits + * @return Generated unitary matrix in row-major format + */ +template +auto randomUnitary(RandomEngine &re, size_t num_qubits) + -> std::vector> { + using ComplexPrecisionT = std::complex; + const size_t dim = (1U << num_qubits); + std::vector res(dim * dim, ComplexPrecisionT{}); + + std::normal_distribution dist; + + auto generator = [&dist, &re]() -> ComplexPrecisionT { + return ComplexPrecisionT{dist(re), dist(re)}; + }; + + std::generate(res.begin(), res.end(), generator); + + // Simple algorithm to make rows orthogonal with Gram-Schmidt + // This algorithm is unstable but works for a small matrix. + // Use QR decomposition when we have LAPACK support. + + for (size_t row2 = 0; row2 < dim; row2++) { + ComplexPrecisionT *row2_p = res.data() + row2 * dim; + for (size_t row1 = 0; row1 < row2; row1++) { + const ComplexPrecisionT *row1_p = res.data() + row1 * dim; + ComplexPrecisionT dot12 = Util::innerProdC(row1_p, row2_p, dim); + ComplexPrecisionT dot11 = squaredNorm(row1_p, dim); + + // orthogonalize row2 + std::transform( + row2_p, row2_p + dim, row1_p, row2_p, + [scale = dot12 / dot11](auto &elt2, const auto &elt1) { + return elt2 - scale * elt1; + }); + } + } + + // Normalize each row + for (size_t row = 0; row < dim; row++) { + ComplexPrecisionT *row_p = res.data() + row * dim; + PrecisionT norm2 = std::sqrt(squaredNorm(row_p, dim)); + + // normalize row2 + std::transform(row_p, row_p + dim, row_p, [norm2](const auto c) { + return (static_cast(1.0) / norm2) * c; + }); + } + return res; +} } // namespace Pennylane::Util diff --git a/pennylane_lightning/src/util/Macros.hpp b/pennylane_lightning/src/util/Macros.hpp index 1b60d1e076..5c97b67814 100644 --- a/pennylane_lightning/src/util/Macros.hpp +++ b/pennylane_lightning/src/util/Macros.hpp @@ -13,12 +13,230 @@ // limitations under the License. /** * @file - * Define some builtin alternatives + * Define macros and compile-time constants. */ #pragma once +#include + +/** + * @brief Predefined macro variable to a string. Use std::format instead in + * C++20. + */ +#define PL_TO_STR_INDIR(x) #x +#define PL_TO_STR(VAR) PL_TO_STR_INDIR(VAR) + #if defined(__GNUC__) || defined(__clang__) #define PL_UNREACHABLE __builtin_unreachable() -#else +#elif defined(_MSC_VER) #define PL_UNREACHABLE __assume(false) +#else // Unsupported compiler +#define PL_UNREACHABLE +#endif + +#if defined(__AVX2__) +#define PL_USE_AVX2 1 +#endif + +#if defined(__AVX512F__) +#define PL_USE_AVX512F 1 +#endif + +#if defined(__AVX512DQ__) +#define PL_USE_AVX512DQ 1 +#endif + +#if defined(__AVX512VL__) +#define PL_USE_AVX512VL 1 +#endif + +#if defined(_OPENMP) +#define PL_USE_OMP 1 +#endif + +#if (_OPENMP >= 202011) +#define PL_UNROLL_LOOP _Pragma("omp unroll(8)") +#elif defined(__GNUC__) +#define PL_UNROLL_LOOP _Pragma("GCC unroll 8") +#elif defined(__clang__) +#define PL_UNROLL_LOOP _Pragma("unroll(8)") +#else +#define PL_UNROLL_LOOP +#endif + +// Define force inline +#if defined(__GNUC__) || defined(__clang__) +#if NDEBUG +#define PL_FORCE_INLINE __attribute__((always_inline)) inline +#else +#define PL_FORCE_INLINE +#endif +#elif defined(_MSC_VER) +#if NDEBUG +#define PL_FORCE_INLINE __forceinline +#else +#define PL_FORCE_INLINE +#endif +#else +#if NDEBUG +#define PL_FORCE_INLINE inline +#else +#define PL_FORCE_INLINE +#endif +#endif + +namespace Pennylane::Util::Constant { +/* Create constexpr values */ +/// @cond DEV +#if defined(PL_USE_AVX2) +[[maybe_unused]] static constexpr bool use_avx2 = true; +#else +[[maybe_unused]] static constexpr bool use_avx2 = false; +#endif +#if defined(PL_USE_AVX512F) +[[maybe_unused]] static constexpr bool use_avx512f = true; +#else +[[maybe_unused]] static constexpr bool use_avx512f = false; +#endif +#if defined(PL_USE_AVX512DQ) +[[maybe_unused]] static constexpr bool use_avx512dq = true; +#else +[[maybe_unused]] static constexpr bool use_avx512dq = false; +#endif +#if defined(PL_USE_AVX512VL) +[[maybe_unused]] static constexpr bool use_avx512vl = true; +#else +[[maybe_unused]] static constexpr bool use_avx512vl = false; +#endif +#if defined(PL_USE_OMP) +[[maybe_unused]] static constexpr bool use_openmp = true; +#else +[[maybe_unused]] static constexpr bool use_openmp = false; +#endif +/// @endcond + +enum class CPUArch { X86_64, PPC64, ARM, Unknown }; + +constexpr auto getCPUArchClangGCC() { +#if defined(__x86_64__) + return CPUArch::X86_64; +#elif defined(__powerpc64__) + return CPUArch::PPC64; +#elif defined(__arm__) + return CPUArch::ARM; +#else + return CPUArch::Unknown; +#endif +} + +constexpr auto getCPUArchMSVC() { +#if defined(_M_AMD64) + return CPUArch::X86_64; +#elif defined(_M_PPC) + return CPUArch::PPC64; +#elif defined(_M_ARM) + return CPUArch::ARM; +#else + return CPUArch::Unknown; +#endif +} + +/// @cond DEV +#if defined(__GNUC__) || defined(__clang__) +[[maybe_unused]] constexpr static auto cpu_arch = getCPUArchClangGCC(); +#elif defined(_MSC_VER) +[[maybe_unused]] constexpr static auto cpu_arch = getCPUArchMSVC(); +#else +[[maybe_unused]] constexpr static auto cpu_arch = CPUArch::Unknown; +#endif +/// @endcond + +enum class Compiler { GCC, Clang, MSVC, NVCC, NVHPC, Unknown }; + +/** + * @brief When none of the specialized functions is called. + */ +template +constexpr auto getCompilerVersion() -> std::string_view { + return "Unknown version"; +} +/** + * @brief Create version string for GCC. + * + * This function raises an error when instantiated (invoked) if a compiler + * does not define macros (i.e. other than GCC compatible compilers). + */ +template <> +constexpr auto getCompilerVersion() -> std::string_view { + return PL_TO_STR(__GNUC__) "." PL_TO_STR(__GNUC_MINOR__) "." PL_TO_STR( + __GNUC_PATCHLEVEL__); +} + +/** + * @brief Create version string for Clang. + * + * This function raises an error when instantiated (invoked) if a compiler + * does not define macros (i.e. other than Clang). + */ +template <> +constexpr auto getCompilerVersion() -> std::string_view { + return PL_TO_STR(__clang_major__) "." PL_TO_STR( + __clang_minor__) "." PL_TO_STR(__clang_patchlevel__); +} + +/** + * @brief Create version string for MSVC. + * + * This function raises an error when instantiated (invoked) if a compiler + * does not define macros (i.e. other than MSVC). + */ +template <> +constexpr auto getCompilerVersion() -> std::string_view { + return PL_TO_STR(_MSC_FULL_VER); +} + +/** + * @brief Create version string for NVCC. + * + * This function raises an error when instantiated (invoked) if a compiler + * does not define macros (i.e. other than NVCC). + * + * See + * https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#nvcc-identification-macro + * for related information + */ +template <> +constexpr auto getCompilerVersion() -> std::string_view { + return PL_TO_STR(__CUDACC_VER_MAJOR__) "." PL_TO_STR( + __CUDACC_VER_MINOR__) "." PL_TO_STR(__CUDACC_VER_BUILD__); +} + +/** + * @brief Create version string for NVHPC (C/C++ compilers without CUDA from + * NVIDIA). + * + * This function raises an error when instantiated (invoked) if a compiler + * does not define macros (i.e. other than NVHPC). + */ +template <> +constexpr auto getCompilerVersion() -> std::string_view { + return PL_TO_STR(__NVCOMPILER_MAJOR__) "." PL_TO_STR( + __NVCOMPILER_MINOR__) "." PL_TO_STR(__NVCOMPILER_PATCHLEVEL__); +} +/// @cond DEV +#if defined(__NVCC__) +[[maybe_unused]] constexpr static auto compiler = Compiler::NVCC; +#elif defined(__NVCOMPILER) +[[maybe_unused]] constexpr static auto compiler = Compiler::NVHPC; +#elif defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) +// All GCC compatible compilers define __GNUC__. +[[maybe_unused]] constexpr static auto compiler = Compiler::GCC; +#elif defined(__clang__) +[[maybe_unused]] constexpr static auto compiler = Compiler::Clang; +#elif defined(_MSC_VER) +[[maybe_unused]] constexpr static auto compiler = Compiler::MSVC; +#else +[[maybe_unused]] constexpr static auto compiler = Compiler::Unknown; #endif +/// @endcond +} // namespace Pennylane::Util::Constant diff --git a/pennylane_lightning/src/util/RuntimeInfo.cpp b/pennylane_lightning/src/util/RuntimeInfo.cpp new file mode 100644 index 0000000000..628c9ca6b2 --- /dev/null +++ b/pennylane_lightning/src/util/RuntimeInfo.cpp @@ -0,0 +1,69 @@ +// Copyright 2022 Xanadu Quantum Technologies Inc. + +// 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. +#include "RuntimeInfo.hpp" +#include "Macros.hpp" + +#if (defined(__GNUC__) || defined(__clang__)) && defined(__x86_64__) +#include +#elif defined(_MSC_VER) && defined(_WIN64) +#include +#include +#endif + +namespace Pennylane::Util { +#if (defined(__GNUC__) || defined(__clang__)) && defined(__x86_64__) +RuntimeInfo::InternalRuntimeInfo::InternalRuntimeInfo() { + const auto nids = __get_cpuid_max(0x00, nullptr); + + unsigned int eax = 0; + unsigned int ebx = 0; + unsigned int ecx = 0; + unsigned int edx = 0; + + if (nids >= 1) { + eax = 1; + __get_cpuid(1, &eax, &ebx, &ecx, &edx); + f_1_ecx = ecx; + f_1_edx = edx; + } + if (nids >= 7) { // NOLINT(readability-magic-numbers) + // NOLINTNEXTLINE(readability-magic-numbers) + __get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx); + f_7_ebx = ebx; + f_7_ecx = ecx; + } +} +#elif defined(_MSC_VER) && defined(_M_AMD64) +RuntimeInfo::InternalRuntimeInfo::InternalRuntimeInfo() { + std::array cpui; + __cpuid(cpui.data(), 0); + + int nids = cpui[0]; + + if (nids >= 1) { + __cpuidex(cpui.data(), 1, 0); + f_1_ecx = cpui[2]; + f_1_edx = cpui[3]; + } + + if (nids >= 7) { + __cpuidex(cpui.data(), 7, 0); + f_7_ebx = cpui[1]; + f_7_ecx = cpui[2]; + } +} +#else +RuntimeInfo::InternalRuntimeInfo::InternalRuntimeInfo(){}; +#endif +} // namespace Pennylane::Util diff --git a/pennylane_lightning/src/util/RuntimeInfo.hpp b/pennylane_lightning/src/util/RuntimeInfo.hpp new file mode 100644 index 0000000000..02ab2ebfbf --- /dev/null +++ b/pennylane_lightning/src/util/RuntimeInfo.hpp @@ -0,0 +1,54 @@ +// Copyright 2022 Xanadu Quantum Technologies Inc. + +// 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. +/** + * @file + * Runtime information based on cpuid + */ +#pragma once +#include + +namespace Pennylane::Util { +/** + * @brief This class is only usable in x86 or x86_64 architecture. + */ +class RuntimeInfo { + private: + /// @cond DEV + struct InternalRuntimeInfo { + InternalRuntimeInfo(); + + std::bitset<32> f_1_ecx{}; + std::bitset<32> f_1_edx{}; + std::bitset<32> f_7_ebx{}; + std::bitset<32> f_7_ecx{}; + }; + /// @endcond + + static const inline InternalRuntimeInfo internal_runtime_info_; + + public: + static inline bool AVX() { + // NOLINTNEXTLINE(readability-magic-numbers) + return internal_runtime_info_.f_1_ecx[28]; + } + static inline bool AVX2() { + // NOLINTNEXTLINE(readability-magic-numbers) + return internal_runtime_info_.f_7_ebx[5]; + } + static inline bool AVX512F() { + // NOLINTNEXTLINE(readability-magic-numbers) + return internal_runtime_info_.f_7_ebx[16]; + } +}; +} // namespace Pennylane::Util diff --git a/pennylane_lightning/src/util/TypeList.hpp b/pennylane_lightning/src/util/TypeList.hpp index e288bd80a5..d87c3c540a 100644 --- a/pennylane_lightning/src/util/TypeList.hpp +++ b/pennylane_lightning/src/util/TypeList.hpp @@ -18,34 +18,58 @@ #pragma once #include +#include #include +#include namespace Pennylane::Util { template struct TypeNode { using Type = T; using Next = TypeNode; }; - +///@cond DEV +template struct TypeNode { + using Type = T; + using Next = void; +}; template struct TypeNode { using Type = T; using Next = void; }; +///@endcond /** * @brief Define type list */ template using TypeList = TypeNode; -template struct getNthType { - static_assert(!std::is_same_v, - "The given n is larger than the length of the typelist."); - using Type = getNthType; +/** + * @brief Get N-th type of a type list. + * + * @tparam TypeList Type list + * @tparam n The position of a type to extract + */ +template struct getNth { + using Type = typename getNth::Type; }; -template struct getNthType { +/// @cond DEV +template struct getNth { + static_assert(!std::is_same_v, + "The given n is larger than the length of the type list."); using Type = typename TypeList::Type; }; +/// @endcod + +/** + * @brief Convenient of alias of getNth + */ +template +using getNthType = typename getNth::Type; +/** + * @brief Get the size of a type list + */ template constexpr size_t length() { if constexpr (std::is_same_v) { return 0; @@ -53,4 +77,22 @@ template constexpr size_t length() { return 1 + length(); } } + +/** + * @brief Prepend a type to a type list. + * + * @tparam T Type to prepend + * @tparam U TypeList + */ +template struct PrependToTypeList; + +/// @cond DEV +template +struct PrependToTypeList> { + using Type = TypeNode; +}; +template struct PrependToTypeList { + using Type = TypeNode; +}; +/// @endcond } // namespace Pennylane::Util diff --git a/pennylane_lightning/src/util/Util.hpp b/pennylane_lightning/src/util/Util.hpp index 3b184b82f9..274bc27f26 100644 --- a/pennylane_lightning/src/util/Util.hpp +++ b/pennylane_lightning/src/util/Util.hpp @@ -260,10 +260,11 @@ auto linspace(T start, T end, size_t num_points) -> std::vector { * * @tparam T Vector data type. * @param arr Array to be inspected. + * @param length Size of the array * @return a vector with indices that would sort the array. */ template -inline auto sorting_indices(const T &arr, size_t length) +inline auto sorting_indices(const T *arr, size_t length) -> std::vector { std::vector indices(length); iota(indices.begin(), indices.end(), 0); @@ -401,7 +402,38 @@ auto chunkData(const Container &data, std::size_t num_chunks) return chunkDataSize(data, div); } +/** + * @brief For lookup from any array of pair whose first elements are + * GateOperation. + * + * As Util::lookup can be used in constexpr context, this function is redundant + * (by the standard). But GCC 9 still does not accept Util::lookup in constexpr + * some cases. + */ +template +constexpr auto +static_lookup(const std::array, size> &arr) -> T { + for (size_t idx = 0; idx < size; idx++) { + if (std::get<0>(arr[idx]) == op) { + return std::get<1>(arr[idx]); + } + } + return T{}; +} + // type alias template using remove_cvref_t = typename remove_cvref::type; +template struct remove_complex { using type = T; }; +template struct remove_complex> { + using type = T; +}; +template using remove_complex_t = typename remove_complex::type; + +template struct is_complex : std::false_type {}; + +template struct is_complex> : std::true_type {}; + +template constexpr bool is_complex_v = is_complex::value; + } // namespace Pennylane::Util diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index cf4b10ca76..41dc5f9cac 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -75,7 +75,7 @@ def test_not_expval(self, dev): """Test if a QuantumFunctionError is raised for a tape with measurements that are not expectation values""" - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(0.1, wires=0) qml.var(qml.PauliZ(0)) @@ -87,7 +87,7 @@ def test_finite_shots_warns(self): dev = qml.device("lightning.qubit", wires=1, shots=1) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.expval(qml.PauliZ(0)) with pytest.warns( @@ -100,7 +100,7 @@ def test_unsupported_op(self, dev): """Test if a QuantumFunctionError is raised for an unsupported operation, i.e., multi-parameter operations that are not qml.Rot""" - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.CRot(0.1, 0.2, 0.3, wires=[0, 1]) qml.expval(qml.PauliZ(0)) @@ -109,7 +109,7 @@ def test_unsupported_op(self, dev): ): dev.adjoint_jacobian(tape) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.SingleExcitation(0.1, wires=[0, 1]) qml.expval(qml.PauliZ(0)) @@ -122,7 +122,7 @@ def test_unsupported_op(self, dev): @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_proj_unsupported(self, dev): """Test if a QuantumFunctionError is raised for a Projector observable""" - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.CRX(0.1, wires=[0, 1]) qml.expval(qml.Projector([0, 1], wires=[0, 1])) @@ -131,7 +131,7 @@ def test_proj_unsupported(self, dev): ): dev.adjoint_jacobian(tape) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.CRX(0.1, wires=[0, 1]) qml.expval(qml.Projector([0], wires=[0]) @ qml.PauliZ(0)) @@ -144,7 +144,7 @@ def test_proj_unsupported(self, dev): def test_unsupported_hermitian_expectation(self, dev): obs = np.array([[1, 0], [0, -1]], dtype=np.complex128, requires_grad=False) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RY(0.1, wires=(0,)) qml.expval(qml.Hermitian(obs, wires=(0,))) @@ -153,7 +153,7 @@ def test_unsupported_hermitian_expectation(self, dev): ): dev.adjoint_jacobian(tape) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RY(0.1, wires=(0,)) qml.expval(qml.Hermitian(obs, wires=(0,)) @ qml.PauliZ(wires=1)) @@ -169,7 +169,7 @@ def test_unsupported_hermitian_expectation(self, dev): def test_unsupported_complex_type(self, dev): dev._state = dev._asarray(dev._state, np.complex256) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) qml.RX(0.3, wires=[0]) qml.expval(qml.PauliZ(0)) @@ -187,7 +187,7 @@ def test_pauli_rotation_gradient(self, G, theta, tol, dev, C): dev._state = dev._asarray(dev._state, C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) G(theta, wires=[0]) qml.expval(qml.PauliZ(0)) @@ -210,7 +210,7 @@ def test_Rot_gradient(self, theta, tol, dev, C): dev._state = dev._state.astype(C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) qml.Rot(*params, wires=[0]) qml.expval(qml.PauliZ(0)) @@ -231,7 +231,7 @@ def test_ry_gradient(self, par, tol, dev, C): dev._state = dev._state.astype(C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RY(par, wires=[0]) qml.expval(qml.PauliX(0)) @@ -239,11 +239,9 @@ def test_ry_gradient(self, par, tol, dev, C): # gradients exact = np.cos(par) - grad_F = tape.jacobian(dev, method="numeric") grad_A = dev.adjoint_jacobian(tape) # different methods must agree - assert np.allclose(grad_F, exact, atol=tol, rtol=0) assert np.allclose(grad_A, exact, atol=tol, rtol=0) @pytest.mark.parametrize("C", [np.complex64, np.complex128]) @@ -253,7 +251,7 @@ def test_rx_gradient(self, tol, dev, C): dev._state = dev._state.astype(C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(a, wires=0) qml.expval(qml.PauliZ(0)) @@ -270,7 +268,7 @@ def test_multiple_rx_gradient(self, tol, C): dev._state = dev._state.astype(C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(params[0], wires=0) qml.RX(params[1], wires=1) qml.RX(params[2], wires=2) @@ -307,7 +305,7 @@ def test_gradients(self, op, obs, tol, dev, C): dev._state = dev._state.astype(C) # op.num_wires and op.num_params must be initialized a priori - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.Hadamard(wires=0) qml.RX(0.543, wires=0) qml.CNOT(wires=[0, 1]) @@ -322,11 +320,9 @@ def test_gradients(self, op, obs, tol, dev, C): qml.expval(obs(wires=0)) qml.expval(qml.PauliZ(wires=1)) - tape.execute(dev) + dev.trainable_params = set(range(1, 1 + op.num_params)) - tape.trainable_params = set(range(1, 1 + op.num_params)) - - grad_F = tape.jacobian(dev, method="numeric") + grad_F = (lambda t, fn: fn(qml.execute(t, dev, None)))(*qml.gradients.finite_diff(tape)) grad_D = dev.adjoint_jacobian(tape) assert np.allclose(grad_D, grad_F, atol=tol, rtol=0) @@ -338,7 +334,7 @@ def test_gradient_gate_with_multiple_parameters(self, tol, dev, C): dev._state = dev._state.astype(C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) @@ -347,7 +343,8 @@ def test_gradient_gate_with_multiple_parameters(self, tol, dev, C): tape.trainable_params = {1, 2, 3} grad_D = dev.adjoint_jacobian(tape) - grad_F = tape.jacobian(dev, method="numeric") + tapes, fn = qml.gradients.finite_diff(tape) + grad_F = fn(qml.execute(tapes, dev, None)) # gradient has the correct shape and every element is nonzero assert grad_D.shape == (1, 3) @@ -363,7 +360,7 @@ def test_use_device_state(self, tol, dev, C): dev._state = dev._state.astype(C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) @@ -373,7 +370,7 @@ def test_use_device_state(self, tol, dev, C): dM1 = dev.adjoint_jacobian(tape) - tape.execute(dev) + qml.execute([tape], dev, None) dM2 = dev.adjoint_jacobian(tape, use_device_state=True) assert np.allclose(dM1, dM2, atol=tol, rtol=0) @@ -385,7 +382,7 @@ def test_provide_starting_state(self, tol, dev, C): dev._state = dev._state.astype(C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) @@ -395,7 +392,7 @@ def test_provide_starting_state(self, tol, dev, C): dM1 = dev.adjoint_jacobian(tape) - tape.execute(dev) + qml.execute([tape], dev, None) dM2 = dev.adjoint_jacobian(tape, starting_state=dev._pre_rotated_state) assert np.allclose(dM1, dM2, atol=tol, rtol=0) diff --git a/tests/test_apply.py b/tests/test_apply.py index a0e2bde38e..dc09ff2526 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -118,6 +118,8 @@ def test_apply_operation_raise_type_error(self, qubit_device_1_wire): ), (qml.Hadamard, [1, 0], [1 / math.sqrt(2), 1 / math.sqrt(2)]), (qml.Hadamard, [1 / math.sqrt(2), -1 / math.sqrt(2)], [0, 1]), + (qml.Identity, [1, 0], [1, 0]), + (qml.Identity, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), 1 / math.sqrt(2)]), ] @pytest.mark.parametrize("operation,input,expected_output", test_data_no_parameters) @@ -1434,6 +1436,43 @@ def test_qubitunitary_rotation_hadamard( assert np.allclose(var, expected, atol=tolerance, rtol=0) +class TestApplyLightningMethod: + """Unit tests for the apply_lightning method.""" + + @pytest.mark.parametrize("C", [np.complex64, np.complex128]) + def test_apply_identity_skipped(self, mocker, C, tol): + """Test identity operation does not perform additional computations.""" + dev = qml.device("lightning.qubit", wires=1) + dev._state = dev._asarray(dev._state, C) + + starting_state = np.array([1, 0], dtype=C) + op = [qml.Identity(0)] + dev.apply(op) + + assert np.allclose(dev._state, starting_state, atol=tol, rtol=0) + + @pytest.mark.parametrize("C", [np.complex64, np.complex128]) + def test_iter_identity_skipped(self, mocker, C, tol): + """Test identity operations do not perform additional computations.""" + dev = qml.device("lightning.qubit", wires=2) + if not hasattr(dev, "apply_lightning"): + pytest.skip("LightningQubit object has no attribute apply_lightning") + + starting_state = np.array([1, 0, 0, 0], dtype=C) + op = [qml.Identity(0), qml.Identity(1)] + + spy_diagonal = mocker.spy(dev, "_apply_diagonal_unitary") + spy_einsum = mocker.spy(dev, "_apply_unitary_einsum") + spy_unitary = mocker.spy(dev, "_apply_unitary") + + res = dev.apply_lightning(starting_state, op, dtype=C) + assert np.allclose(res, starting_state, atol=tol, rtol=0) + + spy_diagonal.assert_not_called() + spy_einsum.assert_not_called() + spy_unitary.assert_not_called() + + def test_warning(): """Tests if a warning is raised when lightning.qubit binaries are not available""" if CPP_BINARY_AVAILABLE: diff --git a/tests/test_binary_info.py b/tests/test_binary_info.py new file mode 100644 index 0000000000..d0035ac61e --- /dev/null +++ b/tests/test_binary_info.py @@ -0,0 +1,34 @@ +# Copyright 2018-2020 Xanadu Quantum Technologies Inc. + +# 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. +""" +Test binary information of ``lightning.qubit``. +""" +import pytest + +try: + from pennylane_lightning.lightning_qubit_ops import runtime_info, compile_info +except (ImportError, ModuleNotFoundError): + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + + +def test_runtime_info(): + m = runtime_info() + for key in ["AVX", "AVX2", "AVX512F"]: + assert key in m + + +def test_compile_info(): + m = compile_info() + for key in ["cpu.arch", "compiler.name", "compiler.version", "AVX2", "AVX512F"]: + assert key in m diff --git a/tests/test_measures.py b/tests/test_measures.py index a843253faa..067c339540 100644 --- a/tests/test_measures.py +++ b/tests/test_measures.py @@ -16,6 +16,7 @@ """ import numpy as np import pennylane as qml +import math from pennylane.measurements import ( Variance, Expectation, @@ -55,15 +56,20 @@ def dev(self): def test_probs_dtype64(self, dev): """Test if probs changes the state dtype""" - dev._state = np.array([1, 0]).astype(np.complex64) + dev._state = dev._asarray( + np.array([1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0]).astype(np.complex64) + ) p = dev.probability(wires=[0, 1]) assert dev._state.dtype == np.complex64 - assert np.allclose(p, [1, 1, 0, 0]) + assert np.allclose(p, [0.5, 0.5, 0, 0]) + @pytest.mark.skipif( + not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" + ) def test_probs_dtype_error(self, dev): """Test if probs raise error with complex256""" - dev._state = np.array([1, 0]).astype(np.complex256) + dev._state = np.array([1, 0, 0, 0]).astype(np.complex256) with pytest.raises(TypeError, match="Unsupported complex Type:"): dev.probability(wires=[0, 1]) @@ -179,6 +185,9 @@ def test_expval_dtype64(self, dev): assert dev._state.dtype == np.complex64 assert np.allclose(e, 0.0) + @pytest.mark.skipif( + not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" + ) def test_expval_dtype_error(self, dev): """Test if expval raise error with complex256""" dev._state = np.array([1, 0]).astype(np.complex256) @@ -296,6 +305,9 @@ def test_var_dtype64(self, dev): assert dev._state.dtype == np.complex64 assert np.allclose(v, 1.0) + @pytest.mark.skipif( + not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" + ) def test_expval_dtype_error(self, dev): """Test if var raise error with complex256""" dev._state = np.array([1, 0]).astype(np.complex256) @@ -462,6 +474,75 @@ def circuit2(): assert np.allclose(circuit1(), circuit2(), atol=tol) +class TestSample: + """Tests that samples are properly calculated.""" + + @pytest.fixture + def dev(self): + return qml.device("lightning.qubit", wires=2, shots=1000) + + @pytest.mark.parametrize("C", [np.complex64, np.complex128]) + def test_sample_dimensions(self, dev, C): + """Tests if the samples returned by sample have + the correct dimensions + """ + + # Explicitly resetting is necessary as the internal + # state is set to None in __init__ and only properly + # initialized during reset + dev._state = dev._asarray(dev._state, C) + dev.apply([qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])]) + + dev.shots = 10 + dev._wires_measured = {0} + dev._samples = dev.generate_samples() + s1 = dev.sample(qml.PauliZ(wires=[0])) + assert np.array_equal(s1.shape, (10,)) + + dev.reset() + dev.shots = 12 + dev._wires_measured = {1} + dev._samples = dev.generate_samples() + s2 = dev.sample(qml.PauliZ(wires=[1])) + assert np.array_equal(s2.shape, (12,)) + + dev.reset() + dev.shots = 17 + dev._wires_measured = {0, 1} + dev._samples = dev.generate_samples() + s3 = dev.sample(qml.PauliX(0) @ qml.PauliZ(1)) + assert np.array_equal(s3.shape, (17,)) + + @pytest.mark.parametrize("C", [np.complex64, np.complex128]) + def test_sample_values(self, dev, C, tol): + """Tests if the samples returned by sample have + the correct values + """ + + # Explicitly resetting is necessary as the internal + # state is set to None in __init__ and only properly + # initialized during reset + dev._state = dev._asarray(dev._state, C) + + dev.apply([qml.RX(1.5708, wires=[0])]) + dev._wires_measured = {0} + dev._samples = dev.generate_samples() + + s1 = dev.sample(qml.PauliZ(0)) + + # s1 should only contain 1 and -1, which is guaranteed if + # they square to 1 + assert np.allclose(s1**2, 1, atol=tol, rtol=0) + + def test_sample_unsupported_type(self, dev): + """Test if generate_samples raise error with complex256""" + + dev._state = np.array([1, 0]).astype(np.complex256) + + with pytest.raises(TypeError, match="Unsupported complex Type:"): + dev._samples = dev.generate_samples() + + class TestWiresInVar: """Test different Wires settings in Lightning's var.""" diff --git a/tests/test_vjp.py b/tests/test_vjp.py index 078de46ddc..72c98fea07 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -161,7 +161,7 @@ def test_unsupported_complex_type(self, dev): x, y, z = [0.5, 0.3, -0.7] - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) @@ -181,7 +181,7 @@ def test_use_device_state(self, tol, dev, C): x, y, z = [0.5, 0.3, -0.7] - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) @@ -194,7 +194,7 @@ def test_use_device_state(self, tol, dev, C): fn1 = dev.vjp(tape, dy) vjp1 = fn1(tape) - tape.execute(dev) + qml.execute([tape], dev, None) fn2 = dev.vjp(tape, dy, use_device_state=True) vjp2 = fn2(tape) @@ -207,7 +207,7 @@ def test_provide_starting_state(self, tol, dev, C): x, y, z = [0.5, 0.3, -0.7] - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) @@ -220,7 +220,7 @@ def test_provide_starting_state(self, tol, dev, C): fn1 = dev.vjp(tape, dy) vjp1 = fn1(tape) - tape.execute(dev) + qml.execute([tape], dev, None) fn2 = dev.vjp(tape, dy, starting_state=dev._pre_rotated_state) vjp2 = fn2(tape) @@ -232,7 +232,7 @@ def test_not_expval(self, dev, C): expectation values""" dev._state = dev._asarray(dev._state, C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(0.1, wires=0) qml.var(qml.PauliZ(0)) @@ -248,7 +248,7 @@ def test_finite_shots_warns(self, C): dev = qml.device("lightning.qubit", wires=1, shots=1) dev._state = dev._asarray(dev._state, C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.expval(qml.PauliZ(0)) dy = np.array([1.0]) @@ -267,7 +267,7 @@ def test_unsupported_op(self, dev, C): multi-parameter operations that are not qml.Rot""" dev._state = dev._asarray(dev._state, C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.CRot(0.1, 0.2, 0.3, wires=[0, 1]) qml.expval(qml.PauliZ(0)) @@ -278,7 +278,7 @@ def test_unsupported_op(self, dev, C): ): dev.vjp(tape, dy)(tape) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.SingleExcitation(0.1, wires=[0, 1]) qml.expval(qml.PauliZ(0)) @@ -294,7 +294,7 @@ def test_proj_unsupported(self, dev, C): """Test if a QuantumFunctionError is raised for a Projector observable""" dev._state = dev._asarray(dev._state, C) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.CRX(0.1, wires=[0, 1]) qml.expval(qml.Projector([0, 1], wires=[0, 1])) @@ -305,7 +305,7 @@ def test_proj_unsupported(self, dev, C): ): dev.vjp(tape, dy)(tape) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.CRX(0.1, wires=[0, 1]) qml.expval(qml.Projector([0], wires=[0]) @ qml.PauliZ(0)) @@ -321,7 +321,7 @@ def test_unsupported_hermitian_expectation(self, dev, C): obs = np.array([[1, 0], [0, -1]], dtype=np.complex128, requires_grad=False) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RY(0.1, wires=(0,)) qml.expval(qml.Hermitian(obs, wires=(0,))) @@ -332,7 +332,7 @@ def test_unsupported_hermitian_expectation(self, dev, C): ): dev.vjp(tape, dy)(tape) - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RY(0.1, wires=(0,)) qml.expval(qml.Hermitian(obs, wires=(0,)) @ qml.PauliZ(wires=1)) @@ -431,7 +431,7 @@ def test_single_expectation_value(self, tol, dev, C): x = 0.543 y = -0.654 - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) @@ -455,7 +455,7 @@ def test_multiple_expectation_values(self, tol, dev, C): x = 0.543 y = -0.654 - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) @@ -480,7 +480,7 @@ def test_prob_expectation_values(self, dev, C): x = 0.543 y = -0.654 - with qml.tape.JacobianTape() as tape: + with qml.tape.QuantumTape() as tape: qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) @@ -512,7 +512,7 @@ def test_unsupported_complex_type(self, dev): qml.CNOT(wires=[0, 1]) qml.expval(qml.PauliZ(0)) - with qml.tape.JacobianTape() as tape2: + with qml.tape.QuantumTape() as tape2: qml.RX(0.4, wires=0) qml.RX(0.6, wires=0) qml.CNOT(wires=[0, 1]) @@ -537,7 +537,7 @@ def test_one_tape_no_trainable_parameters(self, dev, C): qml.CNOT(wires=[0, 1]) qml.expval(qml.PauliZ(0)) - with qml.tape.JacobianTape() as tape2: + with qml.tape.QuantumTape() as tape2: qml.RX(0.4, wires=0) qml.RX(0.6, wires=0) qml.CNOT(wires=[0, 1]) @@ -593,7 +593,7 @@ def test_zero_dy(self, dev, C): qml.CNOT(wires=[0, 1]) qml.expval(qml.PauliZ(0)) - with qml.tape.JacobianTape() as tape2: + with qml.tape.QuantumTape() as tape2: qml.RX(0.4, wires=0) qml.RX(0.6, wires=0) qml.CNOT(wires=[0, 1]) @@ -615,12 +615,12 @@ def test_reduction_append(self, dev, C): """Test the 'append' reduction strategy""" dev._state = dev._asarray(dev._state, C) - with qml.tape.JacobianTape() as tape1: + with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) qml.CNOT(wires=[0, 1]) qml.expval(qml.PauliZ(0)) - with qml.tape.JacobianTape() as tape2: + with qml.tape.QuantumTape() as tape2: qml.RX(0.4, wires=0) qml.RX(0.6, wires=0) qml.CNOT(wires=[0, 1]) @@ -644,12 +644,12 @@ def test_reduction_append_callable(self, dev, C): """Test the 'append' reduction strategy""" dev._state = dev._asarray(dev._state, C) - with qml.tape.JacobianTape() as tape1: + with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) qml.CNOT(wires=[0, 1]) qml.expval(qml.PauliZ(0)) - with qml.tape.JacobianTape() as tape2: + with qml.tape.QuantumTape() as tape2: qml.RX(0.4, wires=0) qml.RX(0.6, wires=0) qml.CNOT(wires=[0, 1]) @@ -673,12 +673,12 @@ def test_reduction_extend(self, dev, C): """Test the 'extend' reduction strategy""" dev._state = dev._asarray(dev._state, C) - with qml.tape.JacobianTape() as tape1: + with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) qml.CNOT(wires=[0, 1]) qml.expval(qml.PauliZ(0)) - with qml.tape.JacobianTape() as tape2: + with qml.tape.QuantumTape() as tape2: qml.RX(0.4, wires=0) qml.RX(0.6, wires=0) qml.CNOT(wires=[0, 1]) @@ -700,12 +700,12 @@ def test_reduction_extend_callable(self, dev, C): """Test the 'extend' reduction strategy""" dev._state = dev._asarray(dev._state, C) - with qml.tape.JacobianTape() as tape1: + with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) qml.CNOT(wires=[0, 1]) qml.expval(qml.PauliZ(0)) - with qml.tape.JacobianTape() as tape2: + with qml.tape.QuantumTape() as tape2: qml.RX(0.4, wires=0) qml.RX(0.6, wires=0) qml.CNOT(wires=[0, 1])