From edefee1df2b4d325d9f46215e57844dcb7e31c6e Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Oct 2020 00:07:04 +0200 Subject: [PATCH] Version 1.5 Release * JKQ QCEC now provides full Python bindings and is available on [PyPI](https://pypi.org/project/jkq.qcec/) (#2) * Configuration options now also include optimization passes applied before equivalence checking * New optimization `removeDiagonalGatesBeforeMeasure` (#4) * removed `--extern` from git submodule update * Updated README.md Signed-off-by: Lukas Burgholzer --- .travis.yml | 2 +- CMakeLists.txt | 8 +- README.md | 173 ++++++++++-------- apps/app.cpp | 49 +++-- extern/qfr | 2 +- include/CompilationFlowEquivalenceChecker.hpp | 6 - include/EquivalenceChecker.hpp | 6 + jkq/qcec/CMakeLists.txt | 2 +- jkq/qcec/__init__.py | 64 ++++++- jkq/qcec/bindings.cpp | 52 +++--- setup.py | 8 +- src/CompilationFlowEquivalenceChecker.cpp | 11 ++ src/EquivalenceChecker.cpp | 15 ++ src/ImprovedDDEquivalenceChecker.cpp | 15 ++ src/PowerOfSimulationEquivalenceChecker.cpp | 16 ++ 15 files changed, 287 insertions(+), 142 deletions(-) diff --git a/.travis.yml b/.travis.yml index bfb011e2..56c1eb8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -137,7 +137,7 @@ jobs: - CIBW_MANYLINUX_X86_64_IMAGE="manylinux2014" - CIBW_MANYLINUX_AARCH64_IMAGE="manylinux2014" - CIBW_BUILD="cp3?-*" - - CIBW_SKIP="*-win32 *-manylinux_i686" + - CIBW_SKIP="*-win32 *-manylinux_i686 cp35-*" - CIBW_BUILD_VERBOSITY=1 - CIBW_TEST_COMMAND="python -c \"from jkq import qcec\"" - CC=gcc CXX=g++ diff --git a/CMakeLists.txt b/CMakeLists.txt index e2332b40..3d43a6a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,10 @@ -cmake_minimum_required(VERSION 3.10...3.16) +cmake_minimum_required(VERSION 3.10...3.19) if(${CMAKE_VERSION} VERSION_LESS 3.12) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) endif() project(qcec - VERSION 1.4.5 + VERSION 1.5.0 DESCRIPTION "A JKQ tool for quantum circuit equivalence checking (JKQ QCEC)" LANGUAGES CXX) @@ -27,11 +27,11 @@ macro(handle_submodule MODULENAME) find_package(Git QUIET) if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git" AND GIT_SUBMODULE) message(STATUS "${MODULENAME} update") - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive --remote -- extern/${MODULENAME} + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive -- extern/${MODULENAME} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE GIT_SUBMOD_RESULT) if(NOT GIT_SUBMOD_RESULT EQUAL "0") - message(FATAL_ERROR "git submodule update --init --recursive --remote -- extern/${MODULENAME} failed with ${GIT_SUBMOD_RESULT}.") + message(FATAL_ERROR "git submodule update --init --recursive -- extern/${MODULENAME} failed with ${GIT_SUBMOD_RESULT}.") endif() endif() diff --git a/README.md b/README.md index a4462073..8bd50c22 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ -[![Build Status](https://travis-ci.com/iic-jku/qcec.svg?branch=master)](https://travis-ci.com/iic-jku/qcec) -[![codecov](https://codecov.io/gh/iic-jku/qcec/branch/master/graph/badge.svg)](https://codecov.io/gh/iic-jku/qcec) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![arXiv](https://img.shields.io/static/v1?label=arXiv&message=2004.08420&color=inactive)](https://arxiv.org/abs/2004.08420) -[![arXiv](https://img.shields.io/static/v1?label=arXiv&message=2009.02376&color=inactive)](https://arxiv.org/abs/2009.02376) -[![toolset: JKQ](https://img.shields.io/badge/toolset-JKQ-blue)](https://github.com/iic-jku/jkq) +[![PyPI](https://img.shields.io/pypi/v/jkq.qcec?logo=pypi&style=plastic)](https://pypi.org/project/jkq.qcec/) +[![Travis (.com) branch](https://img.shields.io/travis/com/iic-jku/qcec/master?logo=travis&style=plastic)](https://travis-ci.com/iic-jku/qcec) +[![Codecov branch](https://img.shields.io/codecov/c/github/iic-jku/qcec/master?label=codecov&logo=codecov&style=plastic)](https://codecov.io/gh/iic-jku/qcec) +![GitHub](https://img.shields.io/github/license/iic-jku/qcec?style=plastic) +[![arXiv](https://img.shields.io/static/v1?label=arXiv&message=2004.08420&color=inactive&style=plastic)](https://arxiv.org/abs/2004.08420) +[![arXiv](https://img.shields.io/static/v1?label=arXiv&message=2009.02376&color=inactive&style=plastic)](https://arxiv.org/abs/2009.02376) +[![toolset: JKQ](https://img.shields.io/badge/toolset-JKQ-blue?style=plastic)](https://github.com/iic-jku/jkq) # JKQ QCEC - A JKQ tool for **Q**uantum **C**ircuit **E**quivalence **C**hecking @@ -34,59 +35,93 @@ If you have any questions, feel free to contact us via [iic-quantum@jku.at](mail ## Usage -This tool can either be used as a **standalone executable** with command-line interface, or as a **library** for the incorporation in other projects. [Python bindings](#python-bindings) are available since version 1.4.5. -- The standalone executable is launched in the following way: - ```commandline - qcec_app (--method ) +JKQ QCEC is mainly developed as a C++ library with a [commandline interface](#command-line-executable). However, using it in Python is as easy as +```bash +pip install jkq.qcec +``` +and then in Python +```python +from jkq import qcec +qcec.verify(...) +``` +where the `verify` function is defined as follows: +```python +""" +Interface to the JKQ QCEC tool for verifying quantum circuits + +Params: + file1 – Path to first file (required) + file2 – Path to second file (required) + method – Equivalence checking method to use (reference | naive | *proportional* | lookahead | simulation | compilation flow) + tolerance – Numerical tolerance used during computation + nsims – Number of simulations to conduct (for simulation method) + fidelity – Fidelity limit for comparison (for simulation method) + csv – Create CSV string for result + statistics – Print statistics + swapGateFusion – Optimization pass reconstructing SWAP operations + singleQubitGateFusion – Optimization pass fusing consecutive single qubit gates + removeDiagonalGatesBeforeMeasure – Optimization pass removing diagonal gates before measurements +Returns: + JSON object containing results +""" +def verify(file1: Union[str, bytes, PathLike], + file2: Union[str, bytes, PathLike], + method: Method = Method.proportional, + tolerance: float = 1e-13, + nsims: int = 16, + fidelity: float = 0.999, + csv: bool = False, + statistics: bool = False, + swapGateFusion: bool = False, + singleQubitGateFusion: bool = False, + removeDiagonalGatesBeforeMeasure: bool = False) -> object +``` +### Command-line Executable +JKQ QCEC also provides a **standalone executable** with command-line interface called `qcec_app`. +It provides the same options as the Python module as flags (e.g., `--ps` for printing statistics, or `--method `for setting the method). Per default, this produces JSON formatted output. +If the `--csv` flag is present, a CSV entry according to the following header is printed +```csv +filename1;nqubits1;ngates1;filename2;nqubits2;ngates2;expectedEquivalent;equivalent;method;time;maxActive;nsims +``` +For a full list of options, call `qcec_app --help`. + +### Library Organisation +Internally the JKQ QCEC library works in the following way +- Import both input files into a `qc::QuantumComputation` object + ```c++ + std::string file1 = ""; + qc::QuantumComputation qc1(file1); + + std::string file2 = ""; + qc::QuantumComputation qc2(file2); ``` - where *\* is one of - - reference - - naive - - proportional (**default**) - - lookahead - - simulation - - compilationflow - - An optional parameter ```--tol e``` allows to specify the numerical tolerance *e* (default: *1e-13*) used during the computation. - The ```simulation``` method has two optional parameters ```--nsims r``` and ```--fid F```, controlling the maximum number of simulations *r* (default: *16*) and the considered fidelity limit *F* (default *0.999*), respectively. - - The executable performs the equivalence check and prints its result to the standard output. Per default, this produces JSON formatted output. Additional statistics (e.g., verification time, maximum number of nodes, required simulations, etc.) can be obtained by additionally providing the `--ps` flag. - If the `--csv` flag is present, a CSV entry according to the following header is printed - ```csv - filename1;nqubits1;ngates1;filename2;nqubits2;ngates2;expectedEquivalent;equivalent;method;time;maxActive;nsims - ``` - -- Internally the library works in the following way - - Import both input files into a `qc::QuantumComputation` object - ```c++ - std::string file1 = ""; - qc::QuantumComputation qc1(file1); - - std::string file2 = ""; - qc::QuantumComputation qc2(file2); - ``` - - Instantiate an `ec::EquivalenceChecker` object with both circuits - ```c++ - ec::Method method = ec::{ Reference | Naive | Proportional | Lookahead }; - auto eq = ec::ImprovedDDEquivalenceChecker(qc1, qc2, method); - ``` - or - ```c++ - auto eq = ec::PowerOfSimulationEquivalenceChecker(qc1, qc2); - ``` - or - ```c++ - auto eq = ec::CompilationFlowEquivalenceChecker(qc1, qc2); - ``` - - Perform the actual equivalence check - ```c++ - eq.check(); - ``` - - Print the results - ```c++ - eq.printResult(); - ``` - or access them through the ```eq.results``` member. +- Instantiate an `ec::EquivalenceChecker` object with both circuits + ```c++ + ec::Method method = ec::{ Reference | Naive | Proportional | Lookahead }; + auto eq = ec::ImprovedDDEquivalenceChecker(qc1, qc2, method); + ``` + or + ```c++ + auto eq = ec::PowerOfSimulationEquivalenceChecker(qc1, qc2); + ``` + or + ```c++ + auto eq = ec::CompilationFlowEquivalenceChecker(qc1, qc2); + ``` +- Set configuration options, e.g., + ```c++ + ec::Configuration config{}; + config.printStatistics = true; + ``` +- Perform the actual equivalence check + ```c++ + eq.check(config); + ``` +- Print the results + ```c++ + ec.printJSONResult(config.printStatistics); + ``` + or access them through the ```eq.results``` member. ### System requirements @@ -132,28 +167,6 @@ In order to build the library execute the following in the project's main direct target_link_libraries(${TARGET_NAME} PRIVATE JKQ::qcec) ``` -### Python Bindings - -Running `pip install .` in the main project directory creates Python bindings for the JKQ QCEC tool. Then, using it in Python is as simple as: -```python -from jkq import qcec -qcec.verify({"file1": "", "file2:": ""}) -``` -The full list of parameters as described in [Usage](#usage) which can be passed to `qcec.verify(...)` as a Python dictionary, are: -```python -instance = { - "file1": "", # required - "file2": "", # required - "method": "proportional", - "tolerance": 1e-13, - "nsims": 16, - "fidelity": 0.999, - "statistics": False, - "csv": False, -} -``` - - ## Reference If you use our tool for your research, we will be thankful if you refer to it by citing the appropriate publication: diff --git a/apps/app.cpp b/apps/app.cpp index a6f7fc8d..d29f8758 100644 --- a/apps/app.cpp +++ b/apps/app.cpp @@ -14,25 +14,30 @@ #include "PowerOfSimulationEquivalenceChecker.hpp" void show_usage(const std::string& name) { - std::cerr << "Usage: " << name << " (--method ) " << std::endl; - std::cerr << "Supported file formats: " << std::endl; - std::cerr << " .real " << std::endl; - std::cerr << " .qasm " << std::endl; - std::cerr << " .tfc " << std::endl; - std::cerr << " .qc " << std::endl; - std::cerr << "Available methods: " << std::endl; - std::cerr << " reference " << std::endl; - std::cerr << " naive " << std::endl; - std::cerr << " proportional (default) " << std::endl; - std::cerr << " lookahead " << std::endl; - std::cerr << " simulation " << std::endl; - std::cerr << " compilationflow " << std::endl; - std::cerr << "Options: " << std::endl; - std::cerr << " --ps: Print statistics " << std::endl; - std::cerr << " --csv: Print results as csv string " << std::endl; - std::cerr << " --tol e (default 1e-13): Numerical tolerance used during computation " << std::endl; - std::cerr << " --nsims r (default 16): Number of simulations to conduct (for simulation method)" << std::endl; - std::cerr << " --fid F (default 0.999): Fidelity limit for comparison (for simulation method) " << std::endl; + std::cerr << "Usage: " << name << " (--method ) " << std::endl; + std::cerr << "Supported file formats: " << std::endl; + std::cerr << " .real " << std::endl; + std::cerr << " .qasm " << std::endl; + std::cerr << " .tfc " << std::endl; + std::cerr << " .qc " << std::endl; + std::cerr << "Available methods: " << std::endl; + std::cerr << " reference " << std::endl; + std::cerr << " naive " << std::endl; + std::cerr << " proportional (default) " << std::endl; + std::cerr << " lookahead " << std::endl; + std::cerr << " simulation " << std::endl; + std::cerr << " compilationflow " << std::endl; + std::cerr << "Result Options: " << std::endl; + std::cerr << " --ps: Print statistics " << std::endl; + std::cerr << " --csv: Print results as csv string " << std::endl; + std::cerr << "Verification Parameters: " << std::endl; + std::cerr << " --tol e (default 1e-13): Numerical tolerance used during computation " << std::endl; + std::cerr << " --nsims r (default 16): Number of simulations to conduct (for simulation method)" << std::endl; + std::cerr << " --fid F (default 0.999): Fidelity limit for comparison (for simulation method) " << std::endl; + std::cerr << "Optimization Options: " << std::endl; + std::cerr << " --swapGateFusion: reconstruct SWAP operations " << std::endl; + std::cerr << " --singleQubitGateFusion: fuse consecutive single qubit gates " << std::endl; + std::cerr << " --removeDiagonalGatesBeforeMeasure: remove diagonal gates before measurements " << std::endl; } int main(int argc, char** argv){ @@ -141,6 +146,12 @@ int main(int argc, char** argv){ show_usage(argv[0]); return 1; } + } else if (cmd == "--swapGateFusion") { + config.swapGateFusion = true; + } else if (cmd == "--singleQubitGateFusion") { + config.singleQubitGateFusion = true; + } else if (cmd == "--removeDiagonalGatesBeforeMeasure") { + config.removeDiagonalGatesBeforeMeasure = true; } else { show_usage(argv[0]); return 1; diff --git a/extern/qfr b/extern/qfr index fb638681..aa978aca 160000 --- a/extern/qfr +++ b/extern/qfr @@ -1 +1 @@ -Subproject commit fb638681163580163f8c93e92bf775c207e49023 +Subproject commit aa978acacc638b4ce9615193669e0d8f42fed4c9 diff --git a/include/CompilationFlowEquivalenceChecker.hpp b/include/CompilationFlowEquivalenceChecker.hpp index 6eccb525..92ae0d4c 100644 --- a/include/CompilationFlowEquivalenceChecker.hpp +++ b/include/CompilationFlowEquivalenceChecker.hpp @@ -11,7 +11,6 @@ #include #include "ImprovedDDEquivalenceChecker.hpp" -#include "CircuitOptimizer.hpp" namespace ec { using CostFunction = std::function; @@ -25,11 +24,6 @@ namespace ec { public: CompilationFlowEquivalenceChecker(qc::QuantumComputation& qc1, qc::QuantumComputation& qc2, CostFunction costFunction = IBMCostFunction): ImprovedDDEquivalenceChecker(qc1, qc2), costFunction(std::move(costFunction)) { method = results.method = CompilationFlow; - qc::CircuitOptimizer::swapGateFusion(qc1); - qc::CircuitOptimizer::singleGateFusion(qc1); - - qc::CircuitOptimizer::swapGateFusion(qc2); - qc::CircuitOptimizer::singleGateFusion(qc2); } void check(const Configuration& config) override; diff --git a/include/EquivalenceChecker.hpp b/include/EquivalenceChecker.hpp index 7d22c469..3e9739a7 100644 --- a/include/EquivalenceChecker.hpp +++ b/include/EquivalenceChecker.hpp @@ -11,6 +11,7 @@ #include #include "QuantumComputation.hpp" +#include "CircuitOptimizer.hpp" #include "EquivalenceCheckingResults.hpp" #define DEBUG_MODE_EC 0 @@ -23,6 +24,11 @@ namespace ec { bool printStatistics = false; fp tolerance = CN::TOLERANCE; + // configuration options for optimizations + bool singleQubitGateFusion = false; + bool swapGateFusion = false; + bool removeDiagonalGatesBeforeMeasure = false; + // configuration options for PowerOfSimulation equivalence checker double fidelity_limit = 0.999; unsigned long long max_sims = 16; diff --git a/jkq/qcec/CMakeLists.txt b/jkq/qcec/CMakeLists.txt index 8ce054c3..af8e1ddb 100644 --- a/jkq/qcec/CMakeLists.txt +++ b/jkq/qcec/CMakeLists.txt @@ -3,5 +3,5 @@ add_library(pybind11 ALIAS pybind11::pybind11) add_subdirectory("${PROJECT_SOURCE_DIR}/extern/pybind11_json" "extern/pybind11_json") -pybind11_add_module(py${PROJECT_NAME} bindings.cpp EXCLUDE_FROM_ALL) +pybind11_add_module(py${PROJECT_NAME} bindings.cpp) target_link_libraries(py${PROJECT_NAME} PUBLIC pybind11_json ${PROJECT_NAME}) diff --git a/jkq/qcec/__init__.py b/jkq/qcec/__init__.py index d46845fd..5eb03496 100644 --- a/jkq/qcec/__init__.py +++ b/jkq/qcec/__init__.py @@ -2,5 +2,67 @@ # This file is part of JKQ QCEC library which is released under the MIT license. # See file README.md or go to http://iic.jku.at/eda/research/quantum_verification/ for more information. # +from pyqcec import Method, _verify +import typing as _typing +import os as _os -from pyqcec import * +__all__ = ['verify', 'Method'] +_Path = _typing.Union[str, bytes, _os.PathLike] + + +def verify(file1: _Path, file2: _Path, + method: Method = Method.proportional, + tolerance: float = 1e-13, + nsims: int = 16, + fidelity: float = 0.999, + csv: bool = False, + statistics: bool = False, + swapGateFusion: bool = False, + singleQubitGateFusion: bool = False, + removeDiagonalGatesBeforeMeasure = False + ) -> object: + """Interface to the JKQ QCEC tool for verifying quantum circuits + + :param file1: Path to first file + :type file1: _Path + :param file2: Path to second file + :type file2: _Path + :param method: Equivalence checking method to use (reference | naive | *proportional* | lookahead | simulation | compilation flow) + :type method: Method + :param tolerance: Numerical tolerance used during computation + :type tolerance: float + :param nsims: Number of simulations to conduct (for simulation method) + :type nsims: int + :param fidelity: Fidelity limit for comparison (for simulation method) + :type fidelity: float + :param csv: Create CSV string for result + :type csv: bool + :param statistics: Print statistics + :type statistics: bool + :param swapGateFusion: Optimization pass reconstructing SWAP operations + :type swapGateFusion: bool + :param singleQubitGateFusion: Optimization pass fusing consecutive single qubit gates + :type singleQubitGateFusion: bool + :param removeDiagonalGatesBeforeMeasure: Optimization pass removing diagonal gates before measurements + :type removeDiagonalGatesBeforeMeasure: bool + :return: JSON object containing results + :rtype: _typing.Dict[str, _typing.Any] + """ + result = _verify({ + "file1": file1, + "file2": file2, + "method": method.name, + "tolerance": tolerance, + "nsims": nsims, + "fidelity": fidelity, + "csv": csv, + "statistics": statistics, + "swapGateFusion": swapGateFusion, + "singleQubitGateFusion": singleQubitGateFusion, + "removeDiagonalGatesBeforeMeasure": removeDiagonalGatesBeforeMeasure + }) + + if "error" in result: + print(result["error"]) + + return result diff --git a/jkq/qcec/bindings.cpp b/jkq/qcec/bindings.cpp index 302bd5e7..59e2d8d2 100644 --- a/jkq/qcec/bindings.cpp +++ b/jkq/qcec/bindings.cpp @@ -14,48 +14,50 @@ namespace py = pybind11; namespace nl = nlohmann; using namespace pybind11::literals; - -nl::json verify(const py::object& instance) { +// private c++ binding function (json) -> json +nl::json _verify(const nl::json& instance) { if (!instance.contains("file1") || !instance.contains("file2")) { - return {"error", R"("file1" and "file2" need to be specified in config)"}; + return {{"error", R"("file1" and "file2" need to be specified in config)"}}; } - std::string file1 = instance["file1"].cast(); - std::string file2 = instance["file2"].cast(); + std::string file1 = instance["file1"].get(); + std::string file2 = instance["file2"].get(); ec::Configuration config{}; ec::Method method = ec::Proportional; if (instance.contains("method")) { - try { - method = instance["method"].cast(); - } catch(std::exception const& e) { - try { - nl::from_json(instance["method"].cast(), method); - } catch(std::exception const& e) { - std::stringstream ss{}; - ss << "Could not parse method: " << e.what(); - return {"error", ss.str()}; - } - } + nl::from_json(instance["method"].get(), method); } if (instance.contains("tolerance")) { - config.tolerance = instance["tolerance"].cast(); + config.tolerance = instance["tolerance"].get(); } if (instance.contains("nsims")) { - config.max_sims = instance["nsims"].cast(); + config.max_sims = instance["nsims"].get(); } if (instance.contains("fidelity")) { - config.fidelity_limit = instance["fidelity"].cast(); + config.fidelity_limit = instance["fidelity"].get(); } if (instance.contains("csv")) { - config.printCSV = instance["csv"].cast(); + config.printCSV = instance["csv"].get(); } if (instance.contains("statistics")) { - config.printStatistics = instance["statistics"].cast(); + config.printStatistics = instance["statistics"].get(); + } + + if (instance.contains("swapGateFusion")) { + config.swapGateFusion = instance["swapGateFusion"].get(); + } + + if (instance.contains("singleQubitGateFusion")) { + config.singleQubitGateFusion = instance["singleQubitGateFusion"].get(); + } + + if (instance.contains("removeDiagonalGatesBeforeMeasure")) { + config.removeDiagonalGatesBeforeMeasure = instance["removeDiagonalGatesBeforeMeasure"].get(); } qc::QuantumComputation qc1; @@ -67,7 +69,7 @@ nl::json verify(const py::object& instance) { } catch (std::exception const& e) { std::stringstream ss{}; ss << "Could not import input files: " << e.what(); - return {"error", ss.str()}; + return {{"error", ss.str()}}; } std::unique_ptr ec; @@ -82,7 +84,7 @@ nl::json verify(const py::object& instance) { } catch (std::exception const& e) { std::stringstream ss{}; ss << "Could not construct equivalence checker: " << e.what(); - return {"error", ss.str()}; + return {{"error", ss.str()}}; } ec->expectNothing(); try { @@ -90,7 +92,7 @@ nl::json verify(const py::object& instance) { } catch (std::exception const& e) { std::stringstream ss{}; ss << "Error during equivalence check: " << e.what(); - return {"error", ss.str()}; + return {{"error", ss.str()}}; } auto result = ec->results.produceJSON(config.printStatistics); @@ -103,7 +105,7 @@ nl::json verify(const py::object& instance) { PYBIND11_MODULE(pyqcec, m) { m.doc() = "pybind11 for the JKQ QCEC quantum circuit equivalence checking tool"; m.attr("__name__") = "jkq.qcec"; - m.def("verify", &verify, "verify the equivalence of two circuits"); + m.def("_verify", &_verify, "verify the equivalence of two circuits"); py::enum_(m, "Method") .value("reference", ec::Method::Reference) diff --git a/setup.py b/setup.py index bae66c49..eaf39c40 100644 --- a/setup.py +++ b/setup.py @@ -63,7 +63,7 @@ def build_extension(self, ext): setup( name='jkq.qcec', - version='1.5.0b1', + version='1.5.0', author='Lukas Burgholzer', author_email='lukas.burgholzer@jku.at', description='JKQ QCEC - A JKQ tool for Quantum Circuit Equivalence Checking', @@ -74,9 +74,9 @@ def build_extension(self, ext): ext_modules=[CMakeExtension('pyqcec')], cmdclass=dict(build_ext=CMakeBuild), zip_safe=False, - packages=find_namespace_packages(), + packages=find_namespace_packages(include=['jkq.*']), classifiers=[ - 'Development Status :: 4 - Beta', + 'Development Status :: 5 - Production/Stable', "Programming Language :: Python :: 3", "Programming Language :: C++", "License :: OSI Approved :: MIT License", @@ -93,6 +93,6 @@ def build_extension(self, ext): 'Tracker': 'https://github.com/iic-jku/qcec/issues', 'Research': 'https://iic.jku.at/eda/research/quantum_verification', }, - python_requires='>=3', + python_requires='>=3.6', setup_requires=['cmake>=3.10'] ) diff --git a/src/CompilationFlowEquivalenceChecker.cpp b/src/CompilationFlowEquivalenceChecker.cpp index de2b592f..ec6e130d 100644 --- a/src/CompilationFlowEquivalenceChecker.cpp +++ b/src/CompilationFlowEquivalenceChecker.cpp @@ -15,6 +15,17 @@ namespace ec { auto start = std::chrono::high_resolution_clock::now(); + qc::CircuitOptimizer::swapGateFusion(qc1); + qc::CircuitOptimizer::swapGateFusion(qc2); + + qc::CircuitOptimizer::singleQubitGateFusion(qc1); + qc::CircuitOptimizer::singleQubitGateFusion(qc2); + + if (config.removeDiagonalGatesBeforeMeasure) { + qc::CircuitOptimizer::removeDiagonalGatesBeforeMeasure(qc1); + qc::CircuitOptimizer::removeDiagonalGatesBeforeMeasure(qc2); + } + #if DEBUG_MODE_EC std::cout << "QC1: "; qc1->printRegisters(); diff --git a/src/EquivalenceChecker.cpp b/src/EquivalenceChecker.cpp index b449b4db..bd4410a7 100644 --- a/src/EquivalenceChecker.cpp +++ b/src/EquivalenceChecker.cpp @@ -235,6 +235,21 @@ namespace ec { auto start = std::chrono::high_resolution_clock::now(); + if (config.swapGateFusion) { + qc::CircuitOptimizer::swapGateFusion(qc1); + qc::CircuitOptimizer::swapGateFusion(qc2); + } + + if (config.singleQubitGateFusion) { + qc::CircuitOptimizer::singleQubitGateFusion(qc1); + qc::CircuitOptimizer::singleQubitGateFusion(qc2); + } + + if (config.removeDiagonalGatesBeforeMeasure) { + qc::CircuitOptimizer::removeDiagonalGatesBeforeMeasure(qc1); + qc::CircuitOptimizer::removeDiagonalGatesBeforeMeasure(qc2); + } + #if DEBUG_MODE_EC std::cout << "QC1: "; qc1->printRegisters(); diff --git a/src/ImprovedDDEquivalenceChecker.cpp b/src/ImprovedDDEquivalenceChecker.cpp index 9896a8d1..3c48051a 100644 --- a/src/ImprovedDDEquivalenceChecker.cpp +++ b/src/ImprovedDDEquivalenceChecker.cpp @@ -66,6 +66,21 @@ namespace ec { auto start = std::chrono::high_resolution_clock::now(); + if (config.swapGateFusion) { + qc::CircuitOptimizer::swapGateFusion(qc1); + qc::CircuitOptimizer::swapGateFusion(qc2); + } + + if (config.singleQubitGateFusion) { + qc::CircuitOptimizer::singleQubitGateFusion(qc1); + qc::CircuitOptimizer::singleQubitGateFusion(qc2); + } + + if (config.removeDiagonalGatesBeforeMeasure) { + qc::CircuitOptimizer::removeDiagonalGatesBeforeMeasure(qc1); + qc::CircuitOptimizer::removeDiagonalGatesBeforeMeasure(qc2); + } + #if DEBUG_MODE_EC std::cout << "QC1: "; qc1->printRegisters(); diff --git a/src/PowerOfSimulationEquivalenceChecker.cpp b/src/PowerOfSimulationEquivalenceChecker.cpp index 92fa46a8..080a9610 100644 --- a/src/PowerOfSimulationEquivalenceChecker.cpp +++ b/src/PowerOfSimulationEquivalenceChecker.cpp @@ -20,6 +20,22 @@ namespace ec { return; auto start = std::chrono::high_resolution_clock::now(); + + if (config.swapGateFusion) { + qc::CircuitOptimizer::swapGateFusion(qc1); + qc::CircuitOptimizer::swapGateFusion(qc2); + } + + if (config.singleQubitGateFusion) { + qc::CircuitOptimizer::singleQubitGateFusion(qc1); + qc::CircuitOptimizer::singleQubitGateFusion(qc2); + } + + if (config.removeDiagonalGatesBeforeMeasure) { + qc::CircuitOptimizer::removeDiagonalGatesBeforeMeasure(qc1); + qc::CircuitOptimizer::removeDiagonalGatesBeforeMeasure(qc2); + } + while (results.nsims < max_sims) { // generate distinct stimulus auto new_stimulus = stimuli.insert(stimuliGenerator());