diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index cd107ec8..e3dd5ec2 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -25,7 +25,7 @@ jobs: - name: Build qermit if: github.event_name == 'pull_request' run: | - pip install -e .[tests] -v + pip install .[tests] -v - name: Test qermit if: github.event_name == 'pull_request' run: | @@ -39,7 +39,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Install Qermit - run: pip install -e .[docs] -v + run: pip install .[docs] -v - name: Build Docs run: | cd docs_src diff --git a/poetry.lock b/poetry.lock index 81c1280a..3787f78d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -455,6 +455,73 @@ files = [ docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] tests = ["pytest", "pytest-cov", "pytest-xdist"] +[[package]] +name = "cython" +version = "3.0.10" +description = "The Cython compiler for writing C extensions in the Python language." +optional = true +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +files = [ + {file = "Cython-3.0.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e876272548d73583e90babda94c1299537006cad7a34e515a06c51b41f8657aa"}, + {file = "Cython-3.0.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adc377aa33c3309191e617bf675fdbb51ca727acb9dc1aa23fc698d8121f7e23"}, + {file = "Cython-3.0.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:401aba1869a57aba2922ccb656a6320447e55ace42709b504c2f8e8b166f46e1"}, + {file = "Cython-3.0.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:541fbe725d6534a90b93f8c577eb70924d664b227a4631b90a6e0506d1469591"}, + {file = "Cython-3.0.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:86998b01f6a6d48398df8467292c7637e57f7e3a2ca68655367f13f66fed7734"}, + {file = "Cython-3.0.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d092c0ddba7e9e530a5c5be4ac06db8360258acc27675d1fc86294a5dc8994c5"}, + {file = "Cython-3.0.10-cp310-cp310-win32.whl", hash = "sha256:3cffb666e649dba23810732497442fb339ee67ba4e0be1f0579991e83fcc2436"}, + {file = "Cython-3.0.10-cp310-cp310-win_amd64.whl", hash = "sha256:9ea31184c7b3a728ef1f81fccb161d8948c05aa86c79f63b74fb6f3ddec860ec"}, + {file = "Cython-3.0.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:051069638abfb076900b0c2bcb6facf545655b3f429e80dd14365192074af5a4"}, + {file = "Cython-3.0.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:712760879600907189c7d0d346851525545484e13cd8b787e94bfd293da8ccf0"}, + {file = "Cython-3.0.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38d40fa1324ac47c04483d151f5e092406a147eac88a18aec789cf01c089c3f2"}, + {file = "Cython-3.0.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bd49a3a9fdff65446a3e1c2bfc0ec85c6ce4c3cad27cd4ad7ba150a62b7fb59"}, + {file = "Cython-3.0.10-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e8df79b596633b8295eaa48b1157d796775c2bb078f32267d32f3001b687f2fd"}, + {file = "Cython-3.0.10-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bcc9795990e525c192bc5c0775e441d7d56d7a7d02210451e9e13c0448dba51b"}, + {file = "Cython-3.0.10-cp311-cp311-win32.whl", hash = "sha256:09f2000041db482cad3bfce94e1fa3a4c82b0e57390a164c02566cbbda8c4f12"}, + {file = "Cython-3.0.10-cp311-cp311-win_amd64.whl", hash = "sha256:3919a55ec9b6c7db6f68a004c21c05ed540c40dbe459ced5d801d5a1f326a053"}, + {file = "Cython-3.0.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8f2864ab5fcd27a346f0b50f901ebeb8f60b25a60a575ccfd982e7f3e9674914"}, + {file = "Cython-3.0.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:407840c56385b9c085826fe300213e0e76ba15d1d47daf4b58569078ecb94446"}, + {file = "Cython-3.0.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a036d00caa73550a3a976432ef21c1e3fa12637e1616aab32caded35331ae96"}, + {file = "Cython-3.0.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc6a0e7e23a96dec3f3c9d39690d4281beabd5297855140d0d30855f950275e"}, + {file = "Cython-3.0.10-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a5e14a8c6a8157d2b0cdc2e8e3444905d20a0e78e19d2a097e89fb8b04b51f6b"}, + {file = "Cython-3.0.10-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f8a2b8fa0fd8358bccb5f3304be563c4750aae175100463d212d5ea0ec74cbe0"}, + {file = "Cython-3.0.10-cp312-cp312-win32.whl", hash = "sha256:2d29e617fd23cf4b83afe8f93f2966566c9f565918ad1e86a4502fe825cc0a79"}, + {file = "Cython-3.0.10-cp312-cp312-win_amd64.whl", hash = "sha256:6c5af936940a38c300977b81598d9c0901158f220a58c177820e17e1774f1cf1"}, + {file = "Cython-3.0.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:5f465443917d5c0f69825fca3b52b64c74ac3de0143b1fff6db8ba5b48c9fb4a"}, + {file = "Cython-3.0.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fadb84193c25641973666e583df8df4e27c52cdc05ddce7c6f6510d690ba34a"}, + {file = "Cython-3.0.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fa9e7786083b6aa61594c16979d621b62e61fcd9c2edd4761641b95c7fb34b2"}, + {file = "Cython-3.0.10-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4780d0f98ce28191c4d841c4358b5d5e79d96520650910cd59904123821c52d"}, + {file = "Cython-3.0.10-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:32fbad02d1189be75eb96456d9c73f5548078e5338d8fa153ecb0115b6ee279f"}, + {file = "Cython-3.0.10-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:90e2f514fc753b55245351305a399463103ec18666150bb1c36779b9862388e9"}, + {file = "Cython-3.0.10-cp36-cp36m-win32.whl", hash = "sha256:a9c976e9ec429539a4367cb4b24d15a1e46b925976f4341143f49f5f161171f5"}, + {file = "Cython-3.0.10-cp36-cp36m-win_amd64.whl", hash = "sha256:a9bb402674788a7f4061aeef8057632ec440123e74ed0fb425308a59afdfa10e"}, + {file = "Cython-3.0.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:206e803598010ecc3813db8748ed685f7beeca6c413f982df9f8a505fce56563"}, + {file = "Cython-3.0.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15b6d397f4ee5ad54e373589522af37935a32863f1b23fa8c6922adf833e28e2"}, + {file = "Cython-3.0.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a181144c2f893ed8e6a994d43d0b96300bc99873f21e3b7334ca26c61c37b680"}, + {file = "Cython-3.0.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74b700d6a793113d03fb54b63bdbadba6365379424bac7c0470605672769260"}, + {file = "Cython-3.0.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:076e9fd4e0ca33c5fa00a7479180dbfb62f17fe928e2909f82da814536e96d2b"}, + {file = "Cython-3.0.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:269f06e6961e8591d56e30b46e1a51b6ccb42cab04c29fa3b30d3e8723485fb4"}, + {file = "Cython-3.0.10-cp37-cp37m-win32.whl", hash = "sha256:d4e83a8ceff7af60064da4ccfce0ac82372544dd5392f1b350c34f1b04d0fae6"}, + {file = "Cython-3.0.10-cp37-cp37m-win_amd64.whl", hash = "sha256:40fac59c3a7fbcd9c25aea64c342c890a5e2270ce64a1525e840807800167799"}, + {file = "Cython-3.0.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f43a58bf2434870d2fc42ac2e9ff8138c9e00c6251468de279d93fa279e9ba3b"}, + {file = "Cython-3.0.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e9a885ec63d3955a08cefc4eec39fefa9fe14989c6e5e2382bd4aeb6bdb9bc3"}, + {file = "Cython-3.0.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acfbe0fff364d54906058fc61f2393f38cd7fa07d344d80923937b87e339adcf"}, + {file = "Cython-3.0.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8adcde00a8a88fab27509b558cd8c2959ab0c70c65d3814cfea8c68b83fa6dcd"}, + {file = "Cython-3.0.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2c9c1e3e78909488f3b16fabae02308423fa6369ed96ab1e250807d344cfffd7"}, + {file = "Cython-3.0.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fc6e0faf5b57523b073f0cdefadcaef3a51235d519a0594865925cadb3aeadf0"}, + {file = "Cython-3.0.10-cp38-cp38-win32.whl", hash = "sha256:35f6ede7c74024ed1982832ae61c9fad7cf60cc3f5b8c6a63bb34e38bc291936"}, + {file = "Cython-3.0.10-cp38-cp38-win_amd64.whl", hash = "sha256:950c0c7b770d2a7cec74fb6f5ccc321d0b51d151f48c075c0d0db635a60ba1b5"}, + {file = "Cython-3.0.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:077b61ee789e48700e25d4a16daa4258b8e65167136e457174df400cf9b4feab"}, + {file = "Cython-3.0.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f1f8bba9d8f37c0cffc934792b4ac7c42d0891077127c11deebe9fa0a0f7e4"}, + {file = "Cython-3.0.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:651a15a8534ebfb9b58cb0b87c269c70984b6f9c88bfe65e4f635f0e3f07dfcd"}, + {file = "Cython-3.0.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d10fc9aa82e5e53a0b7fd118f9771199cddac8feb4a6d8350b7d4109085aa775"}, + {file = "Cython-3.0.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4f610964ab252a83e573a427e28b103e2f1dd3c23bee54f32319f9e73c3c5499"}, + {file = "Cython-3.0.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8c9c4c4f3ab8f8c02817b0e16e8fa7b8cc880f76e9b63fe9c010e60c1a6c2b13"}, + {file = "Cython-3.0.10-cp39-cp39-win32.whl", hash = "sha256:0bac3ccdd4e03924028220c62ae3529e17efa8ca7e9df9330de95de02f582b26"}, + {file = "Cython-3.0.10-cp39-cp39-win_amd64.whl", hash = "sha256:81f356c1c8c0885b8435bfc468025f545c5d764aa9c75ab662616dd1193c331e"}, + {file = "Cython-3.0.10-py2.py3-none-any.whl", hash = "sha256:fcbb679c0b43514d591577fd0d20021c55c240ca9ccafbdb82d3fb95e5edfee2"}, + {file = "Cython-3.0.10.tar.gz", hash = "sha256:dcc96739331fb854dcf503f94607576cfe8488066c61ca50dfd55836f132de99"}, +] + [[package]] name = "dill" version = "0.3.8" @@ -643,6 +710,21 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] +[[package]] +name = "importlib-resources" +version = "6.4.0" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, + {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -800,6 +882,30 @@ atomic-cache = ["atomicwrites"] nearley = ["js2py"] regex = ["regex"] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = true +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "markupsafe" version = "2.1.5" @@ -928,6 +1034,17 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = true +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "mpmath" version = "1.3.0" @@ -1035,21 +1152,21 @@ files = [ [[package]] name = "networkx" -version = "3.3" +version = "2.8.8" description = "Python package for creating and manipulating graphs and networks" optional = false -python-versions = ">=3.10" +python-versions = ">=3.8" files = [ - {file = "networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2"}, - {file = "networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9"}, + {file = "networkx-2.8.8-py3-none-any.whl", hash = "sha256:e435dfa75b1d7195c7b8378c3859f0445cd88c6b0375c181ed66823a9ceb7524"}, + {file = "networkx-2.8.8.tar.gz", hash = "sha256:230d388117af870fce5647a3c52401fcf753e94720e6ea6b4197a5355648885e"}, ] [package.extras] -default = ["matplotlib (>=3.6)", "numpy (>=1.23)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] -developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] -doc = ["myst-nb (>=1.0)", "numpydoc (>=1.7)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] -extra = ["lxml (>=4.6)", "pydot (>=2.0)", "pygraphviz (>=1.12)", "sympy (>=1.10)"] -test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] +default = ["matplotlib (>=3.4)", "numpy (>=1.19)", "pandas (>=1.3)", "scipy (>=1.8)"] +developer = ["mypy (>=0.982)", "pre-commit (>=2.20)"] +doc = ["nb2plots (>=0.6)", "numpydoc (>=1.5)", "pillow (>=9.2)", "pydata-sphinx-theme (>=0.11)", "sphinx (>=5.2)", "sphinx-gallery (>=0.11)", "texext (>=0.6.6)"] +extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.9)", "sympy (>=1.10)"] +test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] [[package]] name = "numpy" @@ -1118,6 +1235,25 @@ files = [ {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, ] +[[package]] +name = "phir" +version = "0.3.2" +description = "A data model and validation tool for PHIR (PECOS High-level Intermediate Representation)." +optional = true +python-versions = ">=3.10" +files = [ + {file = "phir-0.3.2-py3-none-any.whl", hash = "sha256:f5990619ceb6d7349a1d7ad485bb051668b79c8f66d4f5a1885f590ef1babddd"}, + {file = "phir-0.3.2.tar.gz", hash = "sha256:9b14a45d72b27f9471a9df35dd9e74bbc435884f473f6d327106fc253ad8d035"}, +] + +[package.dependencies] +pydantic = "*" +rich = "*" + +[package.extras] +docs = ["autodoc-pydantic", "pydata-sphinx-theme", "sphinx"] +tests = ["pytest"] + [[package]] name = "pillow" version = "10.3.0" @@ -1219,6 +1355,30 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "projectq" +version = "0.8.0" +description = "ProjectQ - An open source software framework for quantum computing" +optional = true +python-versions = ">=3.7" +files = [ + {file = "projectq-0.8.0.tar.gz", hash = "sha256:0bcd242afabe947ac4737dffab1de62628b84125ee6ccb3ec23bd4f1d082ec60"}, +] + +[package.dependencies] +matplotlib = ">=2.2.3" +networkx = ">=2" +numpy = "*" +requests = "*" +scipy = "*" + +[package.extras] +azure-quantum = ["azure-quantum"] +braket = ["boto3"] +docs = ["sphinx", "sphinx_rtd_theme"] +revkit = ["dormouse", "revkit (==3.0a2.dev2)"] +test = ["flaky", "mock", "pytest (>=6.0)", "pytest-cov", "pytest-mock"] + [[package]] name = "psutil" version = "5.9.8" @@ -1247,6 +1407,20 @@ files = [ [package.extras] test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] +[[package]] +name = "pybind11" +version = "2.12.0" +description = "Seamless operability between C++11 and Python" +optional = true +python-versions = ">=3.6" +files = [ + {file = "pybind11-2.12.0-py3-none-any.whl", hash = "sha256:df8d60b94f9e714d81013db233393d430ebf9f3551642b82291cf1b14d1afdbd"}, + {file = "pybind11-2.12.0.tar.gz", hash = "sha256:5e3c557a84b06b969247630407fc4d985bed157b4253b13153b8e8e165e0c3dc"}, +] + +[package.extras] +global = ["pybind11-global (==2.12.0)"] + [[package]] name = "pycodestyle" version = "2.11.1" @@ -1594,6 +1768,43 @@ typing-extensions = ">=4.2,<5.0" [package.extras] zx = ["autoray (>=0.6.1)", "quimb (>=1.5,<2.0)"] +[[package]] +name = "pytket-pecos" +version = "0.1.23" +description = "This package enables emulation of pytket circuits using the PECOS emulator." +optional = true +python-versions = "*" +files = [ + {file = "pytket_pecos-0.1.23-py2.py3-none-any.whl", hash = "sha256:5002a1f9d8c3107f9a2731289659a3aa712806edd9ef66fc216971d977a4d827"}, + {file = "pytket_pecos-0.1.23.tar.gz", hash = "sha256:a3be9d0bb7c3f549c6524ed1778fcf9bd42bac141ecae788d572b16b6b60f96f"}, +] + +[package.dependencies] +pytket = ">=1.26.0" +pytket-phir = ">=0.6.3" +quantum-pecos = {version = ">=0.5.0dev10", extras = ["simulators", "wasmtime"]} + +[[package]] +name = "pytket-phir" +version = "0.6.3" +description = "A circuit analyzer and translator from pytket to PHIR" +optional = true +python-versions = "<3.13,>=3.10" +files = [ + {file = "pytket-phir-0.6.3.tar.gz", hash = "sha256:172bc17dab1db934be875923afc4e59ea9b8785a2394eb7b94ccb576259c1aee"}, + {file = "pytket_phir-0.6.3-py3-none-any.whl", hash = "sha256:a3a706b5d01021decd197d0c820a2ecd62114c253422761dff71fde50023f15c"}, +] + +[package.dependencies] +phir = ">=0.3.2" +pytket = ">=1.21.0" +wasmtime = ">=15.0.0" + +[package.extras] +docs = ["pydata-sphinx-theme", "sphinx"] +phirc = ["projectq", "quantum-pecos (>=0.5.0.dev10)"] +tests = ["pytest"] + [[package]] name = "pytket-qir" version = "0.9.0" @@ -1815,6 +2026,37 @@ requests-ntlm = ">=1.1.0" urllib3 = ">=1.21.1" websocket-client = ">=1.5.1" +[[package]] +name = "quantum-pecos" +version = "0.5.0.dev10" +description = "PECOS is a library/framework for the evaluation, study, and design of QEC protocols. It also provides the ability to study and evaluate the performance advanced hybrid quantum/classical compute execution models for NISQ algorithms and beyond." +optional = true +python-versions = ">=3.10" +files = [ + {file = "quantum-pecos-0.5.0.dev10.tar.gz", hash = "sha256:96e792833fe929df4703abc0fc0d58e9e43dff1451463dfebf3d23586e875ad1"}, + {file = "quantum_pecos-0.5.0.dev10-py2.py3-none-any.whl", hash = "sha256:c01a97704430e9759ccf0bfa31764537b7a57ae948aea214e543ef251c7cb0b6"}, +] + +[package.dependencies] +cython = {version = "*", optional = true, markers = "extra == \"simulators\""} +matplotlib = ">=2.2.0,<4.0" +networkx = ">=2.1.0,<3.0" +numpy = ">=1.15.0,<2.0" +phir = ">=0.3.0,<0.4.0" +projectq = {version = ">=0.5.0,<0.9.0", optional = true, markers = "extra == \"simulators\""} +pybind11 = {version = ">=2.2.3,<3.0", optional = true, markers = "extra == \"simulators\""} +scipy = ">=1.1.0,<2.0" +wasmtime = {version = ">=13.0", optional = true, markers = "extra == \"wasmtime\""} + +[package.extras] +all = ["quantum-pecos[simulators]", "quantum-pecos[tests]", "quantum-pecos[visualization]", "quantum-pecos[wasmer]", "quantum-pecos[wasmtime]"] +cuda = ["cupy (>=10.4.0)", "cuquantum-python (>=23.6.0)"] +simulators = ["cython", "projectq (>=0.5.0,<0.9.0)", "pybind11 (>=2.2.3,<3.0)"] +tests = ["pytest (>=5.0.0)"] +visualization = ["plotly (>=5.9.0,<5.10.0)"] +wasmer = ["wasmer (>=1.1.0,<1.2.0)", "wasmer-compiler-cranelift (>=1.1.0,<1.2.0)"] +wasmtime = ["wasmtime (>=13.0)"] + [[package]] name = "qwasm" version = "1.0.1" @@ -1866,6 +2108,24 @@ cryptography = ">=1.3" pyspnego = ">=0.1.6" requests = ">=2.0.0" +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = true +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "rustworkx" version = "0.14.2" @@ -2358,6 +2618,24 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "wasmtime" +version = "19.0.0" +description = "A WebAssembly runtime powered by Wasmtime" +optional = true +python-versions = ">=3.8" +files = [ + {file = "wasmtime-19.0.0-py3-none-any.whl", hash = "sha256:229075695c97d3220336ab08a0cf6350c6f29b29663b3530d25a9d8aa59db801"}, + {file = "wasmtime-19.0.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:43fb95b952b4387a852834477d8d094f3891cdccd4c0f6c8833cfafb5ec8d35b"}, + {file = "wasmtime-19.0.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6e0625e3d9b5fa850f6098fad3d081f496095f4705c76bf88144d3dec20e6557"}, + {file = "wasmtime-19.0.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:d844128add3ba40d3a667115812ed8064089a76e775c49dc4d43dcf034c15436"}, + {file = "wasmtime-19.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:85890d926adad42dbfe5a772796f452fc42f87a4fda3b982cea842c52dbf734f"}, + {file = "wasmtime-19.0.0-py3-none-win_amd64.whl", hash = "sha256:7b8eb96d270e042442389a3d13c1d51b44c55e5d53c27e33a934d22ef6e2a95f"}, +] + +[package.extras] +testing = ["coverage", "pycparser", "pytest", "pytest-mypy"] + [[package]] name = "websocket-client" version = "1.7.0" @@ -2457,9 +2735,9 @@ files = [ [extras] docs = ["sphinx", "sphinx-book-theme"] -tests = ["flake8", "mypy", "pytest", "pytest-cov", "pytket-quantinuum", "qiskit-ibm-provider"] +tests = ["flake8", "mypy", "pytest", "pytest-cov", "pytket-pecos", "qiskit-ibm-provider"] [metadata] lock-version = "2.0" python-versions = ">=3.10, <3.13" -content-hash = "a68761a5658cd51dc14895ccdc65e026a4a58ba96da017de6187f08f129e9c42" +content-hash = "4b3445859e208601bc5f45fa667814609b151b88a2739d3a1fbd135031ff46c6" diff --git a/pyproject.toml b/pyproject.toml index afd3b2ed..01fe2bf9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,8 +15,10 @@ readme = "README.md" [tool.poetry.dependencies] python = ">=3.10, <3.13" -pytket-qiskit = "^0.50.0" +pytket-qiskit = "0.50" matplotlib = "^3.8.3" +importlib-resources = "^6.4.0" +pytket-quantinuum = "^0.32" pytest = {version="^8.1.1", optional=true} mypy = {version="^1.9.0", optional=true} @@ -25,8 +27,8 @@ sphinx = {version="^7.2.6", optional=true} pytest-cov = {version="^4.1.0", optional=true} sphinx-book-theme = {version="^1.1.2", optional=true} qiskit-ibm-provider = {version="^0.10.0", optional=true} -pytket-quantinuum = "0.32" +pytket-pecos = {version="^0.1.23", optional=true} [tool.poetry.extras] -tests = ["pytest", "mypy", "flake8", "pytest-cov", "qiskit-ibm-provider", "pytket-quantinuum"] -docs = ["sphinx", "sphinx-book-theme", "pytket-quantinuum"] +tests = ["pytest", "mypy", "flake8", "pytest-cov", "qiskit-ibm-provider", "pytket-pecos"] +docs = ["sphinx", "sphinx-book-theme"] diff --git a/qermit/frame_randomisation/frame_randomisation.py b/qermit/frame_randomisation/frame_randomisation.py index 1c2155ed..3184dff2 100644 --- a/qermit/frame_randomisation/frame_randomisation.py +++ b/qermit/frame_randomisation/frame_randomisation.py @@ -33,10 +33,17 @@ from enum import Enum from pytket import OpType from pytket.passes import auto_rebase_pass +from .h_series_randomisation import gen_h_series_randomised_circuit class FrameRandomisation(Enum): + @staticmethod + def HSeriesRandomisation( + circuit: Circuit, shots: int, + ) -> List[CircuitShots]: + return [CircuitShots(Circuit=gen_h_series_randomised_circuit(circuit), Shots=shots)] + @staticmethod def PauliFrameRandomisation( circuit: Circuit, shots: int, samples: int diff --git a/qermit/frame_randomisation/h_series_randomisation.py b/qermit/frame_randomisation/h_series_randomisation.py new file mode 100644 index 00000000..5ed20996 --- /dev/null +++ b/qermit/frame_randomisation/h_series_randomisation.py @@ -0,0 +1,75 @@ +from pytket import Circuit, OpType, wasm +import itertools +from pytket.unit_id import BitRegister, QubitRegister +from pathlib import Path +import importlib_resources + + +def get_wfh(): + + package_path = importlib_resources.files("qermit") + wasm_file = f"{package_path}/frame_randomisation/qermit_rus.wasm" + return wasm.WasmFileHandler(wasm_file) + + +def gen_h_series_randomised_circuit(circuit): + + wfh = get_wfh() + + randomised_circuit = Circuit() + randomisation_reg_dict = {} + for q_register in circuit.q_registers: + randomised_circuit.add_q_register(q_register) + for qubit in q_register: + randomisation_reg_dict[qubit] = randomised_circuit.add_c_register(f"randomisation_{qubit.to_list()}", 4) + for c_register in circuit.c_registers: + randomised_circuit.add_c_register(c_register) + + seed_size = 16 + seed_c_reg = BitRegister(name='seed_c', size=seed_size) + randomised_circuit.add_c_register(seed_c_reg) + + if circuit.n_qubits < seed_size: + seed_q_reg = QubitRegister( + name='seed_q', + size=seed_size-sum(q_register.size for q_register in randomised_circuit.q_registers) + ) + randomised_circuit.add_q_register(seed_q_reg) + + all_qubits = list(itertools.chain.from_iterable([q_register.to_list() for q_register in randomised_circuit.q_registers])) + for qubit, cbit in zip(all_qubits[:seed_size], seed_c_reg.to_list()): + randomised_circuit.H(qubit) + randomised_circuit.Measure(qubit, cbit) + randomised_circuit.Reset(qubit) + randomised_circuit.add_wasm_to_reg("seed_randomisation", wfh, [seed_c_reg], []) + + for command in circuit: + + randomisation_reg = randomisation_reg_dict[command.args[0]] + + if command.op.type == OpType.ZZMax: + randomised_circuit.add_wasm_to_reg("write_randomisation", wfh, [], [randomisation_reg]) + randomised_circuit.Z(command.qubits[0], condition=randomisation_reg[0]) + randomised_circuit.Z(command.qubits[1], condition=randomisation_reg[1]) + randomised_circuit.X(command.qubits[0], condition=randomisation_reg[2]) + randomised_circuit.X(command.qubits[1], condition=randomisation_reg[3]) + + randomised_circuit.add_gate(command.op, command.args) + + if command.op.type == OpType.ZZMax: + randomised_circuit.Z( + command.qubits[0], + condition=randomisation_reg[0] ^ randomisation_reg[2] ^ randomisation_reg[3] + ) + randomised_circuit.Z( + command.qubits[1], + condition=randomisation_reg[1] ^ randomisation_reg[2] ^ randomisation_reg[3] + ) + randomised_circuit.X(command.qubits[0], condition=randomisation_reg[2]) + randomised_circuit.X(command.qubits[1], condition=randomisation_reg[3]) + + randomised_circuit.Phase(0.5, condition=randomisation_reg[2]) + randomised_circuit.Phase(0.5, condition=randomisation_reg[3]) + randomised_circuit.Phase(-1, condition=randomisation_reg[2] & randomisation_reg[3]) + + return randomised_circuit diff --git a/qermit/frame_randomisation/qermit_rus.wasm b/qermit/frame_randomisation/qermit_rus.wasm new file mode 100755 index 00000000..9fc5fb13 Binary files /dev/null and b/qermit/frame_randomisation/qermit_rus.wasm differ diff --git a/qermit/noise_model/noise_model.py b/qermit/noise_model/noise_model.py index 2a4c91a7..e6a79ff1 100644 --- a/qermit/noise_model/noise_model.py +++ b/qermit/noise_model/noise_model.py @@ -141,7 +141,7 @@ def to_ptm(self) -> Tuple[NDArray, Dict[Tuple[Pauli, ...], int]]: return ptm, pauli_index @classmethod - def from_ptm(cls, ptm: NDArray, pauli_index: Dict[Tuple[Pauli, ...], int]) -> ErrorDistribution: + def from_ptm(cls, ptm: NDArray, pauli_index: Dict[Tuple[Pauli, ...], int], rng: Generator = np.random.default_rng()) -> ErrorDistribution: """Convert a Pauli Transfer Matrix (PTM) to an error distribution. :param ptm: Pauli Transfer Matrix to convert. Should be a 4^n by 4^n matrix @@ -201,7 +201,7 @@ def from_ptm(cls, ptm: NDArray, pauli_index: Dict[Tuple[Pauli, ...], int]) -> Er for error, index in pauli_index.items() if (error_rate_list[index] > 10**(-6)) and error != tuple(Pauli.I for _ in range(int(n_qubit))) } - return cls(distribution=distribution) + return cls(distribution=distribution, rng=rng) @property def n_qubits(self) -> int: @@ -376,7 +376,7 @@ def scale(self, scaling_factor: float) -> ErrorDistribution: ptm, pauli_index = self.to_ptm() scaled_ptm = fractional_matrix_power(ptm, scaling_factor) - return ErrorDistribution.from_ptm(ptm=scaled_ptm, pauli_index=pauli_index) + return ErrorDistribution.from_ptm(ptm=scaled_ptm, pauli_index=pauli_index, rng=self.rng) class LogicalErrorDistribution: diff --git a/qermit/noise_model/transpiler_backend.py b/qermit/noise_model/transpiler_backend.py index 9cd3c8e9..b70fa651 100644 --- a/qermit/noise_model/transpiler_backend.py +++ b/qermit/noise_model/transpiler_backend.py @@ -120,7 +120,7 @@ def process_circuit( n_shots: int, **kwargs, ) -> ResultHandle: - """[summary] + """Run a single circuit. :param circuit: Submits circuit to run on noisy backend. :type circuit: Circuit diff --git a/qermit_rus/Cargo.lock b/qermit_rus/Cargo.lock new file mode 100644 index 00000000..21648113 --- /dev/null +++ b/qermit_rus/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "qermit_rus" +version = "0.1.0" +dependencies = [ + "fastrand", +] diff --git a/qermit_rus/Cargo.toml b/qermit_rus/Cargo.toml new file mode 100644 index 00000000..1062814d --- /dev/null +++ b/qermit_rus/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "qermit_rus" +version = "0.1.0" +edition = "2021" + +[lib] +# cdylib specifies a dynamic system library will be produced. This is used when +# compiling a dynamic library to be loaded from another language, as is the +# case for hybrid compute. +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fastrand = "2.0.1" diff --git a/qermit_rus/src/lib.rs b/qermit_rus/src/lib.rs new file mode 100644 index 00000000..1bf77e31 --- /dev/null +++ b/qermit_rus/src/lib.rs @@ -0,0 +1,68 @@ +// For hybrid compute to work, [no_mangle] must be at the top of all functions +// we plan to call from our quantum program +// For more info see: [Calling Rust from Other Languages] +// (https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html?highlight=no_mangle#calling-rust-functions-from-other-languages) +#[no_mangle] +fn init(){ + // This function can have nothing it in, or load some initial function. + // It is needed when passed via the Quantinuum API to warm up the wasm execution environment. + // It can also be used to set up a global state. +} + +// fn get_bit(bit_string: u8, index: u32) -> u8 { +// let base: u8 = 2; +// (bit_string & base.pow(index)) >> index +// } + +// fn get_correction(initial_pauli: u8) -> u8 { + +// // calculate x_1 +// let mut correct_pauli: u8 = get_bit(initial_pauli, 3); +// correct_pauli = correct_pauli << 1; + +// // calculate x_0 +// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 2); +// correct_pauli = correct_pauli << 1; + +// // calculate z_1 +// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 1); +// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 2); +// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 3); +// correct_pauli = correct_pauli << 1; + +// // calculate z_0 +// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 0); +// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 2); +// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 3); + +// correct_pauli +// } + +#[no_mangle] +pub extern "C" fn write_randomisation() -> u8 { + + // let initial_pauli: u8 = fastrand::u8(..); + // let correct_pauli: u8 = get_correction(initial_pauli); + + // initial_pauli | correct_pauli << 4 + fastrand::u8(..) +} + +#[no_mangle] +pub extern "C" fn seed_randomisation(seed: u16) { + fastrand::seed(seed as u64); +} + +// #[cfg(test)] +// mod tests { +// use super::*; + +// #[test] +// fn correct_correction() { +// assert_eq!(get_correction(14), 14); +// assert_eq!(get_correction(9), 10); +// assert_eq!(get_correction(4), 7); +// assert_eq!(get_correction(2), 2); +// assert_eq!(get_correction(6), 5); +// } +// } diff --git a/tests/frame_randomisation_test.py b/tests/frame_randomisation_test.py index e96c889d..f847e39d 100644 --- a/tests/frame_randomisation_test.py +++ b/tests/frame_randomisation_test.py @@ -22,7 +22,133 @@ ) from pytket import Circuit from pytket.extensions.qiskit import AerBackend # type: ignore +from pytket.extensions.quantinuum import QuantinuumBackend, QuantinuumAPIOffline +from qermit.frame_randomisation.h_series_randomisation import gen_h_series_randomised_circuit, get_wfh +from pytket.unit_id import BitRegister +from collections import Counter + + +def test_h_series_randomisation(): + # These tests check that the ideal behaviour of the circuits + # is not altered by adding randomisation. + + api_offline = QuantinuumAPIOffline() + backend = QuantinuumBackend( + device_name="H1-1LE", + api_handler = api_offline, + ) + wasm_file_handler=get_wfh() + + # Small circuit with just one ZZMax. + # The ideal output is 00. + circuit = Circuit(3) + meas_reg = BitRegister(name='measure', size=2) + circuit.add_c_register(meas_reg) + + circuit.ZZMax(0,1) + circuit.Measure( + qubit=circuit.qubits[0], + bit=meas_reg[0] + ) + circuit.Measure( + qubit=circuit.qubits[1], + bit=meas_reg[1] + ) + + randomised_circuit = gen_h_series_randomised_circuit(circuit) + compiled_circuit = backend.get_compiled_circuit(randomised_circuit, optimisation_level=0) + + n_shots = 100 + result = backend.run_circuit( + compiled_circuit, + n_shots=n_shots, + wasm_file_handler=wasm_file_handler, + no_opt=True + ) + assert result.get_counts(cbits=meas_reg) == Counter({(0,0,): n_shots}) + + # To consecutive ZZMax gates. + # Has the effect of acting Z_0 Z_1. + # Checked by applying hadamard rotations. + # I deal outcome in rotate basis is 11. + circuit = Circuit(3) + meas_reg = BitRegister(name='measure', size=2) + circuit.add_c_register(meas_reg) + + circuit.H(0) + circuit.H(1) + + circuit.ZZMax(0,1) + circuit.ZZMax(0,1) + + circuit.H(0) + circuit.H(1) + + circuit.Measure( + qubit=circuit.qubits[0], + bit=meas_reg[0] + ) + circuit.Measure( + qubit=circuit.qubits[1], + bit=meas_reg[1] + ) + + randomised_circuit = gen_h_series_randomised_circuit(circuit) + compiled_circuit = backend.get_compiled_circuit(randomised_circuit, optimisation_level=0) + + n_shots = 100 + result = backend.run_circuit( + compiled_circuit, + n_shots=n_shots, + wasm_file_handler=wasm_file_handler, + no_opt=True + ) + assert result.get_counts(cbits=meas_reg) == Counter({(1,1,): n_shots}) + + # Slightly larger circuit. + # Ideal outcome in rotated basis is 101. + circuit = Circuit(3) + meas_reg = BitRegister(name='measure', size=3) + circuit.add_c_register(meas_reg) + + circuit.H(0) + circuit.H(1) + circuit.H(2) + + circuit.ZZMax(0,1) + circuit.ZZMax(1,2) + circuit.ZZMax(0,1) + circuit.ZZMax(1,2) + + circuit.H(0) + circuit.H(1) + circuit.H(2) + + circuit.Measure( + qubit=circuit.qubits[0], + bit=meas_reg[0] + ) + circuit.Measure( + qubit=circuit.qubits[1], + bit=meas_reg[1] + ) + circuit.Measure( + qubit=circuit.qubits[2], + bit=meas_reg[2] + ) + + randomised_circuit = gen_h_series_randomised_circuit(circuit) + compiled_circuit = backend.get_compiled_circuit(randomised_circuit, optimisation_level=0) + + n_shots = 100 + result = backend.run_circuit( + compiled_circuit, + n_shots=n_shots, + wasm_file_handler=wasm_file_handler, + no_opt=True + ) + assert result.get_counts(cbits=meas_reg) == Counter({(1,0,1,): n_shots}) def test_frame_randomisation_circuits_task_gen(): c = Circuit(2).CX(0, 1).Rx(0.289, 1).CX(0, 1).measure_all() diff --git a/tests/noise_model_test.py b/tests/noise_model_test.py index f6c73658..6c421ae0 100644 --- a/tests/noise_model_test.py +++ b/tests/noise_model_test.py @@ -114,6 +114,31 @@ def test_qermit_pauli_commute_coeff() -> None: assert QermitPauli.commute_coeff(pauli_one=pauli_one, pauli_two=pauli_two) == verified[1] +# Since QermitPauli with ZZMax in the H-series +# WASM randomisation method all input and output Paulis +# are tested here, having been calculated by hand. +def test_qermit_pauli_zzmax(): + + for x_and_z in product([0, 1], repeat=4): + + Z_list = x_and_z[:2] + X_list = x_and_z[2:] + qubit_list = [Qubit(0), Qubit(1)] + + qermit_pauli = QermitPauli( + Z_list=Z_list, + X_list=X_list, + qubit_list=qubit_list, + ) + qermit_pauli.apply_gate( + op_type=OpType.ZZMax, + qubits=qubit_list, + ) + assert list(qermit_pauli.Z_list.values()) == [(Z_list[0] + X_list[0] + X_list[1]) % 2, (Z_list[1] + X_list[0] + X_list[1]) % 2] + assert list(qermit_pauli.X_list.values()) == [X_list[0], X_list[1]] + assert qermit_pauli.phase == (X_list[0] + X_list[1] + 2 * X_list[1] * X_list[0]) % 4 + + def test_noise_model_logical_error_propagation() -> None: pytket_ciruit = Circuit(2).H(0).CX(0, 1).measure_all()