From ab8fdb808f58ecbdd10a9db1f65fc724398638e7 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:11:28 +0100 Subject: [PATCH 01/76] use poetry for dependency management --- poetry.lock | 3005 ++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 22 +- 2 files changed, 3025 insertions(+), 2 deletions(-) create mode 100644 poetry.lock diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..997a9844 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,3005 @@ +# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. + +[[package]] +name = "accessible-pygments" +version = "0.0.5" +description = "A collection of accessible pygments styles" +optional = false +python-versions = ">=3.9" +files = [ + {file = "accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7"}, + {file = "accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872"}, +] + +[package.dependencies] +pygments = ">=1.5" + +[package.extras] +dev = ["pillow", "pkginfo (>=1.10)", "playwright", "pre-commit", "setuptools", "twine (>=5.0)"] +tests = ["hypothesis", "pytest"] + +[[package]] +name = "alabaster" +version = "0.7.16" +description = "A light, configurable Sphinx theme" +optional = false +python-versions = ">=3.9" +files = [ + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autoray" +version = "0.6.12" +description = "Abstract your array operations." +optional = false +python-versions = ">=3.8" +files = [ + {file = "autoray-0.6.12-py3-none-any.whl", hash = "sha256:3ed7a4abcec052bcbb4f0447c426d0a0b9b9fa03ab71e76eaa77747ca43ac3e2"}, + {file = "autoray-0.6.12.tar.gz", hash = "sha256:721328aa06fc3577155d988052614a7b4bd6e4d01b340695344031ee4abd2a1e"}, +] + +[package.extras] +docs = ["astroid (<3)", "furo", "ipython (!=8.7.0)", "myst-nb", "setuptools-scm", "sphinx (>=2.0)", "sphinx-autoapi", "sphinx-copybutton"] +tests = ["coverage", "numpy", "pytest", "pytest-cov"] + +[[package]] +name = "babel" +version = "2.15.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, + {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, +] + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "certifi" +version = "2024.6.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "cirq-core" +version = "1.4.0" +description = "A framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits." +optional = false +python-versions = ">=3.10.0" +files = [ + {file = "cirq_core-1.4.0-py3-none-any.whl", hash = "sha256:ad68e5a420fc4cf3ad76a0c1bdf3a1fcf21daede8236b850542220cb81504a20"}, +] + +[package.dependencies] +attrs = "*" +duet = ">=0.2.8" +matplotlib = ">=3.0,<4.0" +networkx = ">=2.4" +numpy = ">=1.22,<2.0" +pandas = "*" +scipy = ">=1.0,<2.0" +sortedcontainers = ">=2.0,<3.0" +sympy = "*" +tqdm = "*" +typing-extensions = ">=4.2" + +[package.extras] +contrib = ["opt-einsum", "ply (>=3.6)", "pylatex (>=1.4,<2.0)", "quimb (>=1.7,<2.0)"] + +[[package]] +name = "cirq-google" +version = "1.4.0" +description = "The Cirq module that provides tools and access to the Google Quantum Computing Service" +optional = false +python-versions = ">=3.10.0" +files = [ + {file = "cirq_google-1.4.0-py3-none-any.whl", hash = "sha256:f2121e9273f23c77271e81a429d7461b3690b555aa322da8412a3c97c445008e"}, +] + +[package.dependencies] +cirq-core = "1.4.0" +google-api-core = {version = ">=1.14.0", extras = ["grpc"]} +proto-plus = ">=1.20.0" +protobuf = ">=3.15.0" + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "contourpy" +version = "1.2.1" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = false +python-versions = ">=3.9" +files = [ + {file = "contourpy-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd7c23df857d488f418439686d3b10ae2fbf9bc256cd045b37a8c16575ea1040"}, + {file = "contourpy-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b9eb0ca724a241683c9685a484da9d35c872fd42756574a7cfbf58af26677fd"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c75507d0a55378240f781599c30e7776674dbaf883a46d1c90f37e563453480"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11959f0ce4a6f7b76ec578576a0b61a28bdc0696194b6347ba3f1c53827178b9"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb3315a8a236ee19b6df481fc5f997436e8ade24a9f03dfdc6bd490fea20c6da"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39f3ecaf76cd98e802f094e0d4fbc6dc9c45a8d0c4d185f0f6c2234e14e5f75b"}, + {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94b34f32646ca0414237168d68a9157cb3889f06b096612afdd296003fdd32fd"}, + {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:457499c79fa84593f22454bbd27670227874cd2ff5d6c84e60575c8b50a69619"}, + {file = "contourpy-1.2.1-cp310-cp310-win32.whl", hash = "sha256:ac58bdee53cbeba2ecad824fa8159493f0bf3b8ea4e93feb06c9a465d6c87da8"}, + {file = "contourpy-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9cffe0f850e89d7c0012a1fb8730f75edd4320a0a731ed0c183904fe6ecfc3a9"}, + {file = "contourpy-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6022cecf8f44e36af10bd9118ca71f371078b4c168b6e0fab43d4a889985dbb5"}, + {file = "contourpy-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef5adb9a3b1d0c645ff694f9bca7702ec2c70f4d734f9922ea34de02294fdf72"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6150ffa5c767bc6332df27157d95442c379b7dce3a38dff89c0f39b63275696f"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c863140fafc615c14a4bf4efd0f4425c02230eb8ef02784c9a156461e62c965"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00e5388f71c1a0610e6fe56b5c44ab7ba14165cdd6d695429c5cd94021e390b2"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4492d82b3bc7fbb7e3610747b159869468079fe149ec5c4d771fa1f614a14df"}, + {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49e70d111fee47284d9dd867c9bb9a7058a3c617274900780c43e38d90fe1205"}, + {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b59c0ffceff8d4d3996a45f2bb6f4c207f94684a96bf3d9728dbb77428dd8cb8"}, + {file = "contourpy-1.2.1-cp311-cp311-win32.whl", hash = "sha256:7b4182299f251060996af5249c286bae9361fa8c6a9cda5efc29fe8bfd6062ec"}, + {file = "contourpy-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2855c8b0b55958265e8b5888d6a615ba02883b225f2227461aa9127c578a4922"}, + {file = "contourpy-1.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62828cada4a2b850dbef89c81f5a33741898b305db244904de418cc957ff05dc"}, + {file = "contourpy-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:309be79c0a354afff9ff7da4aaed7c3257e77edf6c1b448a779329431ee79d7e"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e785e0f2ef0d567099b9ff92cbfb958d71c2d5b9259981cd9bee81bd194c9a4"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cac0a8f71a041aa587410424ad46dfa6a11f6149ceb219ce7dd48f6b02b87a7"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af3f4485884750dddd9c25cb7e3915d83c2db92488b38ccb77dd594eac84c4a0"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce6889abac9a42afd07a562c2d6d4b2b7134f83f18571d859b25624a331c90b"}, + {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1eea9aecf761c661d096d39ed9026574de8adb2ae1c5bd7b33558af884fb2ce"}, + {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:187fa1d4c6acc06adb0fae5544c59898ad781409e61a926ac7e84b8f276dcef4"}, + {file = "contourpy-1.2.1-cp312-cp312-win32.whl", hash = "sha256:c2528d60e398c7c4c799d56f907664673a807635b857df18f7ae64d3e6ce2d9f"}, + {file = "contourpy-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a07fc092a4088ee952ddae19a2b2a85757b923217b7eed584fdf25f53a6e7ce"}, + {file = "contourpy-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb6834cbd983b19f06908b45bfc2dad6ac9479ae04abe923a275b5f48f1a186b"}, + {file = "contourpy-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1d59e739ab0e3520e62a26c60707cc3ab0365d2f8fecea74bfe4de72dc56388f"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3db01f59fdcbce5b22afad19e390260d6d0222f35a1023d9adc5690a889364"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a12a813949e5066148712a0626895c26b2578874e4cc63160bb007e6df3436fe"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe0ccca550bb8e5abc22f530ec0466136379c01321fd94f30a22231e8a48d985"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1d59258c3c67c865435d8fbeb35f8c59b8bef3d6f46c1f29f6123556af28445"}, + {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f32c38afb74bd98ce26de7cc74a67b40afb7b05aae7b42924ea990d51e4dac02"}, + {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d31a63bc6e6d87f77d71e1abbd7387ab817a66733734883d1fc0021ed9bfa083"}, + {file = "contourpy-1.2.1-cp39-cp39-win32.whl", hash = "sha256:ddcb8581510311e13421b1f544403c16e901c4e8f09083c881fab2be80ee31ba"}, + {file = "contourpy-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10a37ae557aabf2509c79715cd20b62e4c7c28b8cd62dd7d99e5ed3ce28c3fd9"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a31f94983fecbac95e58388210427d68cd30fe8a36927980fab9c20062645609"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef2b055471c0eb466033760a521efb9d8a32b99ab907fc8358481a1dd29e3bd3"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b33d2bc4f69caedcd0a275329eb2198f560b325605810895627be5d4b876bf7f"}, + {file = "contourpy-1.2.1.tar.gz", hash = "sha256:4d8908b3bee1c889e547867ca4cdc54e5ab6be6d3e078556814a22457f49423c"}, +] + +[package.dependencies] +numpy = ">=1.20" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] + +[[package]] +name = "cotengra" +version = "0.6.2" +description = "Hyper optimized contraction trees for large tensor networks and einsums." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cotengra-0.6.2-py3-none-any.whl", hash = "sha256:524711515cfbaae22ae56a8c6bf3339dfe50485b9b615de52f8f7c330847b196"}, + {file = "cotengra-0.6.2.tar.gz", hash = "sha256:a56b921d0339d1397e925a7b69029d3301636d09b99b509368751753bda39d24"}, +] + +[package.dependencies] +autoray = "*" + +[package.extras] +docs = ["astroid (<3.0.0)", "furo", "ipython (!=8.7.0)", "myst-nb", "setuptools-scm", "sphinx (>=2.0)", "sphinx-autoapi", "sphinx-copybutton", "sphinx-design"] +recommended = ["cotengrust (>=0.1.3)", "cytoolz", "kahypar", "networkx", "numpy", "opt-einsum", "optuna", "ray", "tqdm"] +test = ["altair", "baytune", "chocolate", "dask", "distributed", "kahypar", "matplotlib", "networkx", "nevergrad", "numpy", "opt-einsum", "pytest", "seaborn", "skopt"] + +[[package]] +name = "cryptography" +version = "42.0.8" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, + {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, + {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, + {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, + {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, + {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, + {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "cycler" +version = "0.12.1" +description = "Composable style cycles" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, + {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, +] + +[package.extras] +docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] +tests = ["pytest", "pytest-cov", "pytest-xdist"] + +[[package]] +name = "cytoolz" +version = "0.12.3" +description = "Cython implementation of Toolz: High performance functional utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cytoolz-0.12.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bbe58e26c84b163beba0fbeacf6b065feabc8f75c6d3fe305550d33f24a2d346"}, + {file = "cytoolz-0.12.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c51b66ada9bfdb88cf711bf350fcc46f82b83a4683cf2413e633c31a64df6201"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e70d9c615e5c9dc10d279d1e32e846085fe1fd6f08d623ddd059a92861f4e3dd"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a83f4532707963ae1a5108e51fdfe1278cc8724e3301fee48b9e73e1316de64f"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d028044524ee2e815f36210a793c414551b689d4f4eda28f8bbb0883ad78bf5f"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c2875bcd1397d0627a09a4f9172fa513185ad302c63758efc15b8eb33cc2a98"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:131ff4820e5d64a25d7ad3c3556f2d8aa65c66b3f021b03f8a8e98e4180dd808"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:04afa90d9d9d18394c40d9bed48c51433d08b57c042e0e50c8c0f9799735dcbd"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:dc1ca9c610425f9854323669a671fc163300b873731584e258975adf50931164"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bfa3f8e01bc423a933f2e1c510cbb0632c6787865b5242857cc955cae220d1bf"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:f702e295dddef5f8af4a456db93f114539b8dc2a7a9bc4de7c7e41d169aa6ec3"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0fbad1fb9bb47e827d00e01992a099b0ba79facf5e5aa453be066033232ac4b5"}, + {file = "cytoolz-0.12.3-cp310-cp310-win32.whl", hash = "sha256:8587c3c3dbe78af90c5025288766ac10dc2240c1e76eb0a93a4e244c265ccefd"}, + {file = "cytoolz-0.12.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e45803d9e75ef90a2f859ef8f7f77614730f4a8ce1b9244375734567299d239"}, + {file = "cytoolz-0.12.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3ac4f2fb38bbc67ff1875b7d2f0f162a247f43bd28eb7c9d15e6175a982e558d"}, + {file = "cytoolz-0.12.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0cf1e1e96dd86829a0539baf514a9c8473a58fbb415f92401a68e8e52a34ecd5"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08a438701c6141dd34eaf92e9e9a1f66e23a22f7840ef8a371eba274477de85d"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6b6f11b0d7ed91be53166aeef2a23a799e636625675bb30818f47f41ad31821"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7fde09384d23048a7b4ac889063761e44b89a0b64015393e2d1d21d5c1f534a"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d3bfe45173cc8e6c76206be3a916d8bfd2214fb2965563e288088012f1dabfc"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27513a5d5b6624372d63313574381d3217a66e7a2626b056c695179623a5cb1a"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d294e5e81ff094fe920fd545052ff30838ea49f9e91227a55ecd9f3ca19774a0"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:727b01a2004ddb513496507a695e19b5c0cfebcdfcc68349d3efd92a1c297bf4"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:fe1e1779a39dbe83f13886d2b4b02f8c4b10755e3c8d9a89b630395f49f4f406"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:de74ef266e2679c3bf8b5fc20cee4fc0271ba13ae0d9097b1491c7a9bcadb389"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e04d22049233394e0b08193aca9737200b4a2afa28659d957327aa780ddddf2"}, + {file = "cytoolz-0.12.3-cp311-cp311-win32.whl", hash = "sha256:20d36430d8ac809186736fda735ee7d595b6242bdb35f69b598ef809ebfa5605"}, + {file = "cytoolz-0.12.3-cp311-cp311-win_amd64.whl", hash = "sha256:780c06110f383344d537f48d9010d79fa4f75070d214fc47f389357dd4f010b6"}, + {file = "cytoolz-0.12.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:86923d823bd19ce35805953b018d436f6b862edd6a7c8b747a13d52b39ed5716"}, + {file = "cytoolz-0.12.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3e61acfd029bfb81c2c596249b508dfd2b4f72e31b7b53b62e5fb0507dd7293"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd728f4e6051af6af234651df49319da1d813f47894d4c3c8ab7455e01703a37"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe8c6267caa7ec67bcc37e360f0d8a26bc3bdce510b15b97f2f2e0143bdd3673"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99462abd8323c52204a2a0ce62454ce8fa0f4e94b9af397945c12830de73f27e"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da125221b1fa25c690fcd030a54344cecec80074df018d906fc6a99f46c1e3a6"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c18e351956f70db9e2d04ff02f28e9a41839250d3f936a4c8a1eabd1c3094d2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:921e6d2440ac758c4945c587b1d1d9b781b72737ac0c0ca5d5e02ca1db8bded2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1651a9bd591a8326329ce1d6336f3129161a36d7061a4d5ea9e5377e033364cf"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8893223b87c2782bd59f9c4bd5c7bf733edd8728b523c93efb91d7468b486528"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:e4d2961644153c5ae186db964aa9f6109da81b12df0f1d3494b4e5cf2c332ee2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:71b6eb97f6695f7ba8ce69c49b707a351c5f46fd97f5aeb5f6f2fb0d6e72b887"}, + {file = "cytoolz-0.12.3-cp312-cp312-win32.whl", hash = "sha256:cee3de65584e915053412cd178729ff510ad5f8f585c21c5890e91028283518f"}, + {file = "cytoolz-0.12.3-cp312-cp312-win_amd64.whl", hash = "sha256:9eef0d23035fa4dcfa21e570961e86c375153a7ee605cdd11a8b088c24f707f6"}, + {file = "cytoolz-0.12.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9a38332cfad2a91e89405b7c18b3f00e2edc951c225accbc217597d3e4e9fde"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f501ae1353071fa5d6677437bbeb1aeb5622067dce0977cedc2c5ec5843b202"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56f899758146a52e2f8cfb3fb6f4ca19c1e5814178c3d584de35f9e4d7166d91"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:800f0526adf9e53d3c6acda748f4def1f048adaa780752f154da5cf22aa488a2"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0976a3fcb81d065473173e9005848218ce03ddb2ec7d40dd6a8d2dba7f1c3ae"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c835eab01466cb67d0ce6290601ebef2d82d8d0d0a285ed0d6e46989e4a7a71a"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4fba0616fcd487e34b8beec1ad9911d192c62e758baa12fcb44448b9b6feae22"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6f6e8207d732651e0204779e1ba5a4925c93081834570411f959b80681f8d333"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:8119bf5961091cfe644784d0bae214e273b3b3a479f93ee3baab97bbd995ccfe"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7ad1331cb68afeec58469c31d944a2100cee14eac221553f0d5218ace1a0b25d"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:92c53d508fb8a4463acc85b322fa24734efdc66933a5c8661bdc862103a3373d"}, + {file = "cytoolz-0.12.3-cp37-cp37m-win32.whl", hash = "sha256:2c6dd75dae3d84fa8988861ab8b1189d2488cb8a9b8653828f9cd6126b5e7abd"}, + {file = "cytoolz-0.12.3-cp37-cp37m-win_amd64.whl", hash = "sha256:caf07a97b5220e6334dd32c8b6d8b2bd255ca694eca5dfe914bb5b880ee66cdb"}, + {file = "cytoolz-0.12.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ed0cfb9326747759e2ad81cb6e45f20086a273b67ac3a4c00b19efcbab007c60"}, + {file = "cytoolz-0.12.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:96a5a0292575c3697121f97cc605baf2fd125120c7dcdf39edd1a135798482ca"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b76f2f50a789c44d6fd7f773ec43d2a8686781cd52236da03f7f7d7998989bee"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2905fdccacc64b4beba37f95cab9d792289c80f4d70830b70de2fc66c007ec01"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ebe23028eac51251f22ba01dba6587d30aa9c320372ca0c14eeab67118ec3f"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96c715404a3825e37fe3966fe84c5f8a1f036e7640b2a02dbed96cac0c933451"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bac0adffc1b6b6a4c5f1fd1dd2161afb720bcc771a91016dc6bdba59af0a5d3"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:37441bf4a2a4e2e0fe9c3b0ea5e72db352f5cca03903977ffc42f6f6c5467be9"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f04037302049cb30033f7fa4e1d0e44afe35ed6bfcf9b380fc11f2a27d3ed697"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f37b60e66378e7a116931d7220f5352186abfcc950d64856038aa2c01944929c"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ec9be3e4b6f86ea8b294d34c990c99d2ba6c526ef1e8f46f1d52c263d4f32cd7"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e9199c9e3fbf380a92b8042c677eb9e7ed4bccb126de5e9c0d26f5888d96788"}, + {file = "cytoolz-0.12.3-cp38-cp38-win32.whl", hash = "sha256:18cd61e078bd6bffe088e40f1ed02001387c29174750abce79499d26fa57f5eb"}, + {file = "cytoolz-0.12.3-cp38-cp38-win_amd64.whl", hash = "sha256:765b8381d4003ceb1a07896a854eee2c31ebc950a4ae17d1e7a17c2a8feb2a68"}, + {file = "cytoolz-0.12.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b4a52dd2a36b0a91f7aa50ca6c8509057acc481a24255f6cb07b15d339a34e0f"}, + {file = "cytoolz-0.12.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:581f1ce479769fe7eeb9ae6d87eadb230df8c7c5fff32138162cdd99d7fb8fc3"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46f505d4c6eb79585c8ad0b9dc140ef30a138c880e4e3b40230d642690e36366"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59276021619b432a5c21c01cda8320b9cc7dbc40351ffc478b440bfccd5bbdd3"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e44f4c25e1e7cf6149b499c74945a14649c8866d36371a2c2d2164e4649e7755"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c64f8e60c1dd69e4d5e615481f2d57937746f4a6be2d0f86e9e7e3b9e2243b5e"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33c63186f3bf9d7ef1347bc0537bb9a0b4111a0d7d6e619623cabc18fef0dc3b"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fdddb9d988405f24035234f1e8d1653ab2e48cc2404226d21b49a129aefd1d25"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6986632d8a969ea1e720990c818dace1a24c11015fd7c59b9fea0b65ef71f726"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0ba1cbc4d9cd7571c917f88f4a069568e5121646eb5d82b2393b2cf84712cf2a"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7d267ffc9a36c0a9a58c7e0adc9fa82620f22e4a72533e15dd1361f57fc9accf"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95e878868a172a41fbf6c505a4b967309e6870e22adc7b1c3b19653d062711fa"}, + {file = "cytoolz-0.12.3-cp39-cp39-win32.whl", hash = "sha256:8e21932d6d260996f7109f2a40b2586070cb0a0cf1d65781e156326d5ebcc329"}, + {file = "cytoolz-0.12.3-cp39-cp39-win_amd64.whl", hash = "sha256:0d8edfbc694af6c9bda4db56643fb8ed3d14e47bec358c2f1417de9a12d6d1fb"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:55f9bd1ae6c2a27eda5abe2a0b65a83029d2385c5a1da7b8ef47af5905d7e905"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2d271393c378282727f1231d40391ae93b93ddc0997448acc21dd0cb6a1e56d"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee98968d6a66ee83a8ceabf31182189ab5d8598998c8ce69b6d5843daeb2db60"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01cfb8518828c1189200c02a5010ea404407fb18fd5589e29c126e84bbeadd36"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:456395d7aec01db32bf9e6db191d667347c78d8d48e77234521fa1078f60dabb"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:cd88028bb897fba99ddd84f253ca6bef73ecb7bdf3f3cf25bc493f8f97d3c7c5"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59b19223e7f7bd7a73ec3aa6fdfb73b579ff09c2bc0b7d26857eec2d01a58c76"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a79d72b08048a0980a59457c239555f111ac0c8bdc140c91a025f124104dbb4"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dd70141b32b717696a72b8876e86bc9c6f8eff995c1808e299db3541213ff82"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a1445c91009eb775d479e88954c51d0b4cf9a1e8ce3c503c2672d17252882647"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ca6a9a9300d5bda417d9090107c6d2b007683efc59d63cc09aca0e7930a08a85"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be6feb903d2a08a4ba2e70e950e862fd3be9be9a588b7c38cee4728150a52918"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b6f43f086e5a965d33d62a145ae121b4ccb6e0789ac0acc895ce084fec8c65"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:534fa66db8564d9b13872d81d54b6b09ae592c585eb826aac235bd6f1830f8ad"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:fea649f979def23150680de1bd1d09682da3b54932800a0f90f29fc2a6c98ba8"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a447247ed312dd64e3a8d9483841ecc5338ee26d6e6fbd29cd373ed030db0240"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba3f843aa89f35467b38c398ae5b980a824fdbdb94065adc6ec7c47a0a22f4c7"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:582c22f97a380211fb36a7b65b1beeb84ea11d82015fa84b054be78580390082"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47feb089506fc66e1593cd9ade3945693a9d089a445fbe9a11385cab200b9f22"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ba9002d2f043943744a9dc8e50a47362bcb6e6f360dc0a1abcb19642584d87bb"}, + {file = "cytoolz-0.12.3.tar.gz", hash = "sha256:4503dc59f4ced53a54643272c61dc305d1dbbfbd7d6bdf296948de9f34c3a282"}, +] + +[package.dependencies] +toolz = ">=0.8.0" + +[package.extras] +cython = ["cython"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "docutils" +version = "0.21.2" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.9" +files = [ + {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, + {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, +] + +[[package]] +name = "duet" +version = "0.2.9" +description = "A simple future-based async library for python." +optional = false +python-versions = ">=3.9.0" +files = [ + {file = "duet-0.2.9-py3-none-any.whl", hash = "sha256:a16088b68b0faee8aee12cdf4d0a8af060ed958badb44f3e32f123f13f64119a"}, + {file = "duet-0.2.9.tar.gz", hash = "sha256:d6fa39582e6a3dce1096c47e5fbcbda648a633eed94a38943e68662afa2587f3"}, +] + +[package.extras] +dev-env = ["black (==22.3.0)", "isort (==5.7.*)", "mypy (==0.931.*)", "pylint (==2.10.*)", "pytest (==6.2.*)", "twine (==3.3.*)", "wheel"] + +[[package]] +name = "exceptiongroup" +version = "1.2.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fonttools" +version = "4.53.0" +description = "Tools to manipulate font files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.53.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:52a6e0a7a0bf611c19bc8ec8f7592bdae79c8296c70eb05917fd831354699b20"}, + {file = "fonttools-4.53.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:099634631b9dd271d4a835d2b2a9e042ccc94ecdf7e2dd9f7f34f7daf333358d"}, + {file = "fonttools-4.53.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e40013572bfb843d6794a3ce076c29ef4efd15937ab833f520117f8eccc84fd6"}, + {file = "fonttools-4.53.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:715b41c3e231f7334cbe79dfc698213dcb7211520ec7a3bc2ba20c8515e8a3b5"}, + {file = "fonttools-4.53.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74ae2441731a05b44d5988d3ac2cf784d3ee0a535dbed257cbfff4be8bb49eb9"}, + {file = "fonttools-4.53.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:95db0c6581a54b47c30860d013977b8a14febc206c8b5ff562f9fe32738a8aca"}, + {file = "fonttools-4.53.0-cp310-cp310-win32.whl", hash = "sha256:9cd7a6beec6495d1dffb1033d50a3f82dfece23e9eb3c20cd3c2444d27514068"}, + {file = "fonttools-4.53.0-cp310-cp310-win_amd64.whl", hash = "sha256:daaef7390e632283051e3cf3e16aff2b68b247e99aea916f64e578c0449c9c68"}, + {file = "fonttools-4.53.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a209d2e624ba492df4f3bfad5996d1f76f03069c6133c60cd04f9a9e715595ec"}, + {file = "fonttools-4.53.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f520d9ac5b938e6494f58a25c77564beca7d0199ecf726e1bd3d56872c59749"}, + {file = "fonttools-4.53.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eceef49f457253000e6a2d0f7bd08ff4e9fe96ec4ffce2dbcb32e34d9c1b8161"}, + {file = "fonttools-4.53.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1f3e34373aa16045484b4d9d352d4c6b5f9f77ac77a178252ccbc851e8b2ee"}, + {file = "fonttools-4.53.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:28d072169fe8275fb1a0d35e3233f6df36a7e8474e56cb790a7258ad822b6fd6"}, + {file = "fonttools-4.53.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a2a6ba400d386e904fd05db81f73bee0008af37799a7586deaa4aef8cd5971e"}, + {file = "fonttools-4.53.0-cp311-cp311-win32.whl", hash = "sha256:bb7273789f69b565d88e97e9e1da602b4ee7ba733caf35a6c2affd4334d4f005"}, + {file = "fonttools-4.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:9fe9096a60113e1d755e9e6bda15ef7e03391ee0554d22829aa506cdf946f796"}, + {file = "fonttools-4.53.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d8f191a17369bd53a5557a5ee4bab91d5330ca3aefcdf17fab9a497b0e7cff7a"}, + {file = "fonttools-4.53.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93156dd7f90ae0a1b0e8871032a07ef3178f553f0c70c386025a808f3a63b1f4"}, + {file = "fonttools-4.53.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bff98816cb144fb7b85e4b5ba3888a33b56ecef075b0e95b95bcd0a5fbf20f06"}, + {file = "fonttools-4.53.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:973d030180eca8255b1bce6ffc09ef38a05dcec0e8320cc9b7bcaa65346f341d"}, + {file = "fonttools-4.53.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4ee5a24e281fbd8261c6ab29faa7fd9a87a12e8c0eed485b705236c65999109"}, + {file = "fonttools-4.53.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bd5bc124fae781a4422f61b98d1d7faa47985f663a64770b78f13d2c072410c2"}, + {file = "fonttools-4.53.0-cp312-cp312-win32.whl", hash = "sha256:a239afa1126b6a619130909c8404070e2b473dd2b7fc4aacacd2e763f8597fea"}, + {file = "fonttools-4.53.0-cp312-cp312-win_amd64.whl", hash = "sha256:45b4afb069039f0366a43a5d454bc54eea942bfb66b3fc3e9a2c07ef4d617380"}, + {file = "fonttools-4.53.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:93bc9e5aaa06ff928d751dc6be889ff3e7d2aa393ab873bc7f6396a99f6fbb12"}, + {file = "fonttools-4.53.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2367d47816cc9783a28645bc1dac07f8ffc93e0f015e8c9fc674a5b76a6da6e4"}, + {file = "fonttools-4.53.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:907fa0b662dd8fc1d7c661b90782ce81afb510fc4b7aa6ae7304d6c094b27bce"}, + {file = "fonttools-4.53.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e0ad3c6ea4bd6a289d958a1eb922767233f00982cf0fe42b177657c86c80a8f"}, + {file = "fonttools-4.53.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:73121a9b7ff93ada888aaee3985a88495489cc027894458cb1a736660bdfb206"}, + {file = "fonttools-4.53.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ee595d7ba9bba130b2bec555a40aafa60c26ce68ed0cf509983e0f12d88674fd"}, + {file = "fonttools-4.53.0-cp38-cp38-win32.whl", hash = "sha256:fca66d9ff2ac89b03f5aa17e0b21a97c21f3491c46b583bb131eb32c7bab33af"}, + {file = "fonttools-4.53.0-cp38-cp38-win_amd64.whl", hash = "sha256:31f0e3147375002aae30696dd1dc596636abbd22fca09d2e730ecde0baad1d6b"}, + {file = "fonttools-4.53.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7d6166192dcd925c78a91d599b48960e0a46fe565391c79fe6de481ac44d20ac"}, + {file = "fonttools-4.53.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef50ec31649fbc3acf6afd261ed89d09eb909b97cc289d80476166df8438524d"}, + {file = "fonttools-4.53.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f193f060391a455920d61684a70017ef5284ccbe6023bb056e15e5ac3de11d1"}, + {file = "fonttools-4.53.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba9f09ff17f947392a855e3455a846f9855f6cf6bec33e9a427d3c1d254c712f"}, + {file = "fonttools-4.53.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0c555e039d268445172b909b1b6bdcba42ada1cf4a60e367d68702e3f87e5f64"}, + {file = "fonttools-4.53.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5a4788036201c908079e89ae3f5399b33bf45b9ea4514913f4dbbe4fac08efe0"}, + {file = "fonttools-4.53.0-cp39-cp39-win32.whl", hash = "sha256:d1a24f51a3305362b94681120c508758a88f207fa0a681c16b5a4172e9e6c7a9"}, + {file = "fonttools-4.53.0-cp39-cp39-win_amd64.whl", hash = "sha256:1e677bfb2b4bd0e5e99e0f7283e65e47a9814b0486cb64a41adf9ef110e078f2"}, + {file = "fonttools-4.53.0-py3-none-any.whl", hash = "sha256:6b4f04b1fbc01a3569d63359f2227c89ab294550de277fd09d8fca6185669fa4"}, + {file = "fonttools-4.53.0.tar.gz", hash = "sha256:c93ed66d32de1559b6fc348838c7572d5c0ac1e4a258e76763a5caddd8944002"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "pycairo", "scipy"] +lxml = ["lxml (>=4.0)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.1.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "google-api-core" +version = "2.8.0" +description = "Google API client core library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "google-api-core-2.8.0.tar.gz", hash = "sha256:065bb8e11c605fd232707ae50963dc1c8af5b3c95b4568887515985e6c1156b3"}, + {file = "google_api_core-2.8.0-py3-none-any.whl", hash = "sha256:1b9f59236ce1bae9a687c1d4f22957e79a2669e53d032893f6bf0fca54f6931d"}, +] + +[package.dependencies] +google-auth = ">=1.25.0,<3.0dev" +googleapis-common-protos = ">=1.52.0,<2.0dev" +grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.12.0" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)"] +grpcgcp = ["grpcio-gcp (>=0.2.2)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2)"] + +[[package]] +name = "google-auth" +version = "2.30.0" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.30.0.tar.gz", hash = "sha256:ab630a1320f6720909ad76a7dbdb6841cdf5c66b328d690027e4867bdfb16688"}, + {file = "google_auth-2.30.0-py2.py3-none-any.whl", hash = "sha256:8df7da660f62757388b8a7f249df13549b3373f24388cb5d2f1dd91cc18180b5"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.1" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.1.tar.gz", hash = "sha256:c6442f7a0a6b2a80369457d79e6672bb7dcbaab88e0848302497e3ec80780a6a"}, + {file = "googleapis_common_protos-1.63.1-py2.py3-none-any.whl", hash = "sha256:0e1c2cdfcbc354b76e4a211a35ea35d6926a835cba1377073c4861db904a1877"}, +] + +[package.dependencies] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "graphviz" +version = "0.20.3" +description = "Simple Python interface for Graphviz" +optional = false +python-versions = ">=3.8" +files = [ + {file = "graphviz-0.20.3-py3-none-any.whl", hash = "sha256:81f848f2904515d8cd359cc611faba817598d2feaac4027b266aa3eda7b3dde5"}, + {file = "graphviz-0.20.3.zip", hash = "sha256:09d6bc81e6a9fa392e7ba52135a9d49f1ed62526f96499325930e87ca1b5925d"}, +] + +[package.extras] +dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] +docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] +test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"] + +[[package]] +name = "grpcio" +version = "1.64.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.64.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:55697ecec192bc3f2f3cc13a295ab670f51de29884ca9ae6cd6247df55df2502"}, + {file = "grpcio-1.64.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3b64ae304c175671efdaa7ec9ae2cc36996b681eb63ca39c464958396697daff"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:bac71b4b28bc9af61efcdc7630b166440bbfbaa80940c9a697271b5e1dabbc61"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c024ffc22d6dc59000faf8ad781696d81e8e38f4078cb0f2630b4a3cf231a90"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cd5c1325f6808b8ae31657d281aadb2a51ac11ab081ae335f4f7fc44c1721d"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0a2813093ddb27418a4c99f9b1c223fab0b053157176a64cc9db0f4557b69bd9"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2981c7365a9353f9b5c864595c510c983251b1ab403e05b1ccc70a3d9541a73b"}, + {file = "grpcio-1.64.1-cp310-cp310-win32.whl", hash = "sha256:1262402af5a511c245c3ae918167eca57342c72320dffae5d9b51840c4b2f86d"}, + {file = "grpcio-1.64.1-cp310-cp310-win_amd64.whl", hash = "sha256:19264fc964576ddb065368cae953f8d0514ecc6cb3da8903766d9fb9d4554c33"}, + {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, + {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, + {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, + {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, + {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, + {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, + {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, + {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, + {file = "grpcio-1.64.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:a011ac6c03cfe162ff2b727bcb530567826cec85eb8d4ad2bfb4bd023287a52d"}, + {file = "grpcio-1.64.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4d6dab6124225496010bd22690f2d9bd35c7cbb267b3f14e7a3eb05c911325d4"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a5e771d0252e871ce194d0fdcafd13971f1aae0ddacc5f25615030d5df55c3a2"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3c1b90ab93fed424e454e93c0ed0b9d552bdf1b0929712b094f5ecfe7a23ad"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20405cb8b13fd779135df23fabadc53b86522d0f1cba8cca0e87968587f50650"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cc79c982ccb2feec8aad0e8fb0d168bcbca85bc77b080d0d3c5f2f15c24ea8f"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a3a035c37ce7565b8f4f35ff683a4db34d24e53dc487e47438e434eb3f701b2a"}, + {file = "grpcio-1.64.1-cp38-cp38-win32.whl", hash = "sha256:1257b76748612aca0f89beec7fa0615727fd6f2a1ad580a9638816a4b2eb18fd"}, + {file = "grpcio-1.64.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a12ddb1678ebc6a84ec6b0487feac020ee2b1659cbe69b80f06dbffdb249122"}, + {file = "grpcio-1.64.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:75dbbf415026d2862192fe1b28d71f209e2fd87079d98470db90bebe57b33179"}, + {file = "grpcio-1.64.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e3d9f8d1221baa0ced7ec7322a981e28deb23749c76eeeb3d33e18b72935ab62"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f8b75f64d5d324c565b263c67dbe4f0af595635bbdd93bb1a88189fc62ed2e5"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84ad903d0d94311a2b7eea608da163dace97c5fe9412ea311e72c3684925602"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940e3ec884520155f68a3b712d045e077d61c520a195d1a5932c531f11883489"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f10193c69fc9d3d726e83bbf0f3d316f1847c3071c8c93d8090cf5f326b14309"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac15b6c2c80a4d1338b04d42a02d376a53395ddf0ec9ab157cbaf44191f3ffdd"}, + {file = "grpcio-1.64.1-cp39-cp39-win32.whl", hash = "sha256:03b43d0ccf99c557ec671c7dede64f023c7da9bb632ac65dbc57f166e4970040"}, + {file = "grpcio-1.64.1-cp39-cp39-win_amd64.whl", hash = "sha256:ed6091fa0adcc7e4ff944090cf203a52da35c37a130efa564ded02b7aff63bcd"}, + {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.64.1)"] + +[[package]] +name = "grpcio-status" +version = "1.64.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio_status-1.64.1-py3-none-any.whl", hash = "sha256:2ec6e0777958831484a517e32b6ffe0a4272242eae81bff2f5c3707fa58b40e3"}, + {file = "grpcio_status-1.64.1.tar.gz", hash = "sha256:c50bd14eb6506d8580a6c553bea463d7c08499b2c0e93f6d1864c5e8eabb1066"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.64.1" +protobuf = ">=5.26.1,<6.0dev" + +[[package]] +name = "ibm-cloud-sdk-core" +version = "3.20.0" +description = "Core library used by SDKs for IBM Cloud Services" +optional = false +python-versions = "*" +files = [ + {file = "ibm-cloud-sdk-core-3.20.0.tar.gz", hash = "sha256:0aa6d97043f589a9ef451a71e76eca0634138ef181ce54a6a0a6353dee4c5d10"}, +] + +[package.dependencies] +PyJWT = ">=2.8.0,<3.0.0" +python_dateutil = ">=2.8.2,<3.0.0" +requests = ">=2.31.0,<3.0.0" +urllib3 = ">=2.1.0,<3.0.0" + +[[package]] +name = "ibm-platform-services" +version = "0.53.7" +description = "Python client library for IBM Cloud Platform Services" +optional = false +python-versions = "*" +files = [ + {file = "ibm-platform-services-0.53.7.tar.gz", hash = "sha256:1f1dd0b282757be546eb39c470417b99a9fc3184cdc245872c5b1e8346113e39"}, +] + +[package.dependencies] +ibm_cloud_sdk_core = ">=3.19.2,<4.0.0" + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jax" +version = "0.4.28" +description = "Differentiate, compile, and transform Numpy code." +optional = false +python-versions = ">=3.9" +files = [ + {file = "jax-0.4.28-py3-none-any.whl", hash = "sha256:6a181e6b5a5b1140e19cdd2d5c4aa779e4cb4ec627757b918be322d8e81035ba"}, + {file = "jax-0.4.28.tar.gz", hash = "sha256:dcf0a44aff2e1713f0a2b369281cd5b79d8c18fc1018905c4125897cb06b37e9"}, +] + +[package.dependencies] +ml-dtypes = ">=0.2.0" +numpy = [ + {version = ">=1.22", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +opt-einsum = "*" +scipy = [ + {version = ">=1.9", markers = "python_version < \"3.12\""}, + {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, +] + +[package.extras] +australis = ["protobuf (>=3.13,<4)"] +ci = ["jaxlib (==0.4.27)"] +cpu = ["jaxlib (==0.4.28)"] +cuda = ["jaxlib (==0.4.28+cuda12.cudnn89)"] +cuda12 = ["jax-cuda12-plugin (==0.4.28)", "jaxlib (==0.4.28)", "nvidia-cublas-cu12 (>=12.1.3.1)", "nvidia-cuda-cupti-cu12 (>=12.1.105)", "nvidia-cuda-nvcc-cu12 (>=12.1.105)", "nvidia-cuda-runtime-cu12 (>=12.1.105)", "nvidia-cudnn-cu12 (>=8.9.2.26,<9.0)", "nvidia-cufft-cu12 (>=11.0.2.54)", "nvidia-cusolver-cu12 (>=11.4.5.107)", "nvidia-cusparse-cu12 (>=12.1.0.106)", "nvidia-nccl-cu12 (>=2.18.1)", "nvidia-nvjitlink-cu12 (>=12.1.105)"] +cuda12-cudnn89 = ["jaxlib (==0.4.28+cuda12.cudnn89)"] +cuda12-local = ["jaxlib (==0.4.28+cuda12.cudnn89)"] +cuda12-pip = ["jaxlib (==0.4.28+cuda12.cudnn89)", "nvidia-cublas-cu12 (>=12.1.3.1)", "nvidia-cuda-cupti-cu12 (>=12.1.105)", "nvidia-cuda-nvcc-cu12 (>=12.1.105)", "nvidia-cuda-runtime-cu12 (>=12.1.105)", "nvidia-cudnn-cu12 (>=8.9.2.26,<9.0)", "nvidia-cufft-cu12 (>=11.0.2.54)", "nvidia-cusolver-cu12 (>=11.4.5.107)", "nvidia-cusparse-cu12 (>=12.1.0.106)", "nvidia-nccl-cu12 (>=2.18.1)", "nvidia-nvjitlink-cu12 (>=12.1.105)"] +minimum-jaxlib = ["jaxlib (==0.4.27)"] +tpu = ["jaxlib (==0.4.28)", "libtpu-nightly (==0.1.dev20240508)", "requests"] + +[[package]] +name = "jaxlib" +version = "0.4.28" +description = "XLA library for JAX" +optional = false +python-versions = ">=3.9" +files = [ + {file = "jaxlib-0.4.28-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:a421d237f8c25d2850166d334603c673ddb9b6c26f52bc496704b8782297bd66"}, + {file = "jaxlib-0.4.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f038e68bd10d1a3554722b0bbe36e6a448384437a75aa9d283f696f0ed9f8c09"}, + {file = "jaxlib-0.4.28-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:fabe77c174e9e196e9373097cefbb67e00c7e5f9d864583a7cfcf9dabd2429b6"}, + {file = "jaxlib-0.4.28-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:e3bcdc6f8e60f8554f415c14d930134e602e3ca33c38e546274fd545f875769b"}, + {file = "jaxlib-0.4.28-cp310-cp310-win_amd64.whl", hash = "sha256:a8b31c0e5eea36b7915696b9be40ea8646edc395a3e5437bf7ef26b7239a567a"}, + {file = "jaxlib-0.4.28-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:2ff8290edc7b92c7eae52517f65492633e267b2e9067bad3e4c323d213e77cf5"}, + {file = "jaxlib-0.4.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:793857faf37f371cafe752fea5fc811f435e43b8fb4b502058444a7f5eccf829"}, + {file = "jaxlib-0.4.28-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b41a6b0d506c09f86a18ecc05bd376f072b548af89c333107e49bb0c09c1a3f8"}, + {file = "jaxlib-0.4.28-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:45ce0f3c840cff8236cff26c37f26c9ff078695f93e0c162c320c281f5041275"}, + {file = "jaxlib-0.4.28-cp311-cp311-win_amd64.whl", hash = "sha256:d4d762c3971d74e610a0e85a7ee063cea81a004b365b2a7dc65133f08b04fac5"}, + {file = "jaxlib-0.4.28-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:d6c09a545329722461af056e735146d2c8c74c22ac7426a845eb69f326b4f7a0"}, + {file = "jaxlib-0.4.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8dd8bffe3853702f63cd924da0ee25734a4d19cd5c926be033d772ba7d1c175d"}, + {file = "jaxlib-0.4.28-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:de2e8521eb51e16e85093a42cb51a781773fa1040dcf9245d7ea160a14ee5a5b"}, + {file = "jaxlib-0.4.28-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:46a1aa857f4feee8a43fcba95c0e0ab62d40c26cc9730b6c69655908ba359f8d"}, + {file = "jaxlib-0.4.28-cp312-cp312-win_amd64.whl", hash = "sha256:eee428eac31697a070d655f1f24f6ab39ced76750d93b1de862377a52dcc2401"}, + {file = "jaxlib-0.4.28-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:4f98cc837b2b6c6dcfe0ab7ff9eb109314920946119aa3af9faa139718ff2787"}, + {file = "jaxlib-0.4.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b01562ec8ad75719b7d0389752489e97eb6b4dcb4c8c113be491634d5282ad3c"}, + {file = "jaxlib-0.4.28-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:aa77a9360a395ba9faf6932df637686fb0c14ddcf4fdc1d2febe04bc88a580a6"}, + {file = "jaxlib-0.4.28-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:4a56ebf05b4a4c1791699d874e072f3f808f0986b4010b14fb549a69c90ca9dc"}, + {file = "jaxlib-0.4.28-cp39-cp39-win_amd64.whl", hash = "sha256:459a4ddcc3e120904b9f13a245430d7801d707bca48925981cbdc59628057dc8"}, +] + +[package.dependencies] +ml-dtypes = ">=0.2.0" +numpy = ">=1.22" +scipy = [ + {version = ">=1.9", markers = "python_version < \"3.12\""}, + {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, +] + +[package.extras] +cuda12-pip = ["nvidia-cublas-cu12 (>=12.1.3.1)", "nvidia-cuda-cupti-cu12 (>=12.1.105)", "nvidia-cuda-nvcc-cu12 (>=12.1.105)", "nvidia-cuda-runtime-cu12 (>=12.1.105)", "nvidia-cudnn-cu12 (>=8.9.2.26,<9.0)", "nvidia-cufft-cu12 (>=11.0.2.54)", "nvidia-cusolver-cu12 (>=11.4.5.107)", "nvidia-cusparse-cu12 (>=12.1.0.106)", "nvidia-nccl-cu12 (>=2.18.1)", "nvidia-nvjitlink-cu12 (>=12.1.105)"] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "kiwisolver" +version = "1.4.5" +description = "A fast implementation of the Cassowary constraint solver" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, + {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, +] + +[[package]] +name = "lark" +version = "1.1.9" +description = "a modern parsing library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "lark-1.1.9-py3-none-any.whl", hash = "sha256:a0dd3a87289f8ccbb325901e4222e723e7d745dbfc1803eaf5f3d2ace19cf2db"}, + {file = "lark-1.1.9.tar.gz", hash = "sha256:15fa5236490824c2c4aba0e22d2d6d823575dcaf4cdd1848e34b6ad836240fba"}, +] + +[package.extras] +atomic-cache = ["atomicwrites"] +interegular = ["interegular (>=0.3.1,<0.4.0)"] +nearley = ["js2py"] +regex = ["regex"] + +[[package]] +name = "llvmlite" +version = "0.42.0" +description = "lightweight wrapper around basic LLVM functionality" +optional = false +python-versions = ">=3.9" +files = [ + {file = "llvmlite-0.42.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3366938e1bf63d26c34fbfb4c8e8d2ded57d11e0567d5bb243d89aab1eb56098"}, + {file = "llvmlite-0.42.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c35da49666a21185d21b551fc3caf46a935d54d66969d32d72af109b5e7d2b6f"}, + {file = "llvmlite-0.42.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70f44ccc3c6220bd23e0ba698a63ec2a7d3205da0d848804807f37fc243e3f77"}, + {file = "llvmlite-0.42.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:763f8d8717a9073b9e0246998de89929071d15b47f254c10eef2310b9aac033d"}, + {file = "llvmlite-0.42.0-cp310-cp310-win_amd64.whl", hash = "sha256:8d90edf400b4ceb3a0e776b6c6e4656d05c7187c439587e06f86afceb66d2be5"}, + {file = "llvmlite-0.42.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ae511caed28beaf1252dbaf5f40e663f533b79ceb408c874c01754cafabb9cbf"}, + {file = "llvmlite-0.42.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81e674c2fe85576e6c4474e8c7e7aba7901ac0196e864fe7985492b737dbab65"}, + {file = "llvmlite-0.42.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb3975787f13eb97629052edb5017f6c170eebc1c14a0433e8089e5db43bcce6"}, + {file = "llvmlite-0.42.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5bece0cdf77f22379f19b1959ccd7aee518afa4afbd3656c6365865f84903f9"}, + {file = "llvmlite-0.42.0-cp311-cp311-win_amd64.whl", hash = "sha256:7e0c4c11c8c2aa9b0701f91b799cb9134a6a6de51444eff5a9087fc7c1384275"}, + {file = "llvmlite-0.42.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:08fa9ab02b0d0179c688a4216b8939138266519aaa0aa94f1195a8542faedb56"}, + {file = "llvmlite-0.42.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b2fce7d355068494d1e42202c7aff25d50c462584233013eb4470c33b995e3ee"}, + {file = "llvmlite-0.42.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebe66a86dc44634b59a3bc860c7b20d26d9aaffcd30364ebe8ba79161a9121f4"}, + {file = "llvmlite-0.42.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d47494552559e00d81bfb836cf1c4d5a5062e54102cc5767d5aa1e77ccd2505c"}, + {file = "llvmlite-0.42.0-cp312-cp312-win_amd64.whl", hash = "sha256:05cb7e9b6ce69165ce4d1b994fbdedca0c62492e537b0cc86141b6e2c78d5888"}, + {file = "llvmlite-0.42.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bdd3888544538a94d7ec99e7c62a0cdd8833609c85f0c23fcb6c5c591aec60ad"}, + {file = "llvmlite-0.42.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d0936c2067a67fb8816c908d5457d63eba3e2b17e515c5fe00e5ee2bace06040"}, + {file = "llvmlite-0.42.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a78ab89f1924fc11482209f6799a7a3fc74ddc80425a7a3e0e8174af0e9e2301"}, + {file = "llvmlite-0.42.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7599b65c7af7abbc978dbf345712c60fd596aa5670496561cc10e8a71cebfb2"}, + {file = "llvmlite-0.42.0-cp39-cp39-win_amd64.whl", hash = "sha256:43d65cc4e206c2e902c1004dd5418417c4efa6c1d04df05c6c5675a27e8ca90e"}, + {file = "llvmlite-0.42.0.tar.gz", hash = "sha256:f92b09243c0cc3f457da8b983f67bd8e1295d0f5b3746c7a1861d7a99403854a"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "matplotlib" +version = "3.9.0" +description = "Python plotting package" +optional = false +python-versions = ">=3.9" +files = [ + {file = "matplotlib-3.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2bcee1dffaf60fe7656183ac2190bd630842ff87b3153afb3e384d966b57fe56"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f988bafb0fa39d1074ddd5bacd958c853e11def40800c5824556eb630f94d3b"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe428e191ea016bb278758c8ee82a8129c51d81d8c4bc0846c09e7e8e9057241"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf3978060a106fab40c328778b148f590e27f6fa3cd15a19d6892575bce387d"}, + {file = "matplotlib-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e7f03e5cbbfacdd48c8ea394d365d91ee8f3cae7e6ec611409927b5ed997ee4"}, + {file = "matplotlib-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:13beb4840317d45ffd4183a778685e215939be7b08616f431c7795276e067463"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:063af8587fceeac13b0936c42a2b6c732c2ab1c98d38abc3337e430e1ff75e38"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a2fa6d899e17ddca6d6526cf6e7ba677738bf2a6a9590d702c277204a7c6152"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550cdda3adbd596078cca7d13ed50b77879104e2e46392dcd7c75259d8f00e85"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cce0f31b351e3551d1f3779420cf8f6ec0d4a8cf9c0237a3b549fd28eb4abb"}, + {file = "matplotlib-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c53aeb514ccbbcbab55a27f912d79ea30ab21ee0531ee2c09f13800efb272674"}, + {file = "matplotlib-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5be985db2596d761cdf0c2eaf52396f26e6a64ab46bd8cd810c48972349d1be"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c79f3a585f1368da6049318bdf1f85568d8d04b2e89fc24b7e02cc9b62017382"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdd1ecbe268eb3e7653e04f451635f0fb0f77f07fd070242b44c076c9106da84"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e85a1a6d732f645f1403ce5e6727fd9418cd4574521d5803d3d94911038e5"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a490715b3b9984fa609116481b22178348c1a220a4499cda79132000a79b4db"}, + {file = "matplotlib-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8146ce83cbc5dc71c223a74a1996d446cd35cfb6a04b683e1446b7e6c73603b7"}, + {file = "matplotlib-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:d91a4ffc587bacf5c4ce4ecfe4bcd23a4b675e76315f2866e588686cc97fccdf"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:616fabf4981a3b3c5a15cd95eba359c8489c4e20e03717aea42866d8d0465956"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd53c79fd02f1c1808d2cfc87dd3cf4dbc63c5244a58ee7944497107469c8d8a"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06a478f0d67636554fa78558cfbcd7b9dba85b51f5c3b5a0c9be49010cf5f321"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81c40af649d19c85f8073e25e5806926986806fa6d54be506fbf02aef47d5a89"}, + {file = "matplotlib-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52146fc3bd7813cc784562cb93a15788be0b2875c4655e2cc6ea646bfa30344b"}, + {file = "matplotlib-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:0fc51eaa5262553868461c083d9adadb11a6017315f3a757fc45ec6ec5f02888"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bd4f2831168afac55b881db82a7730992aa41c4f007f1913465fb182d6fb20c0"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:290d304e59be2b33ef5c2d768d0237f5bd132986bdcc66f80bc9bcc300066a03"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff2e239c26be4f24bfa45860c20ffccd118d270c5b5d081fa4ea409b5469fcd"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:af4001b7cae70f7eaacfb063db605280058246de590fa7874f00f62259f2df7e"}, + {file = "matplotlib-3.9.0.tar.gz", hash = "sha256:e6d29ea6c19e34b30fb7d88b7081f869a03014f66fe06d62cc77d5a6ea88ed7a"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.3.1" +numpy = ">=1.23" +packaging = ">=20.0" +pillow = ">=8" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" + +[package.extras] +dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] + +[[package]] +name = "ml-dtypes" +version = "0.4.0" +description = "" +optional = false +python-versions = ">=3.9" +files = [ + {file = "ml_dtypes-0.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:93afe37f3a879d652ec9ef1fc47612388890660a2657fbb5747256c3b818fd81"}, + {file = "ml_dtypes-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bb83fd064db43e67e67d021e547698af4c8d5c6190f2e9b1c53c09f6ff5531d"}, + {file = "ml_dtypes-0.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03e7cda6ef164eed0abb31df69d2c00c3a5ab3e2610b6d4c42183a43329c72a5"}, + {file = "ml_dtypes-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:a15d96d090aebb55ee85173d1775ae325a001aab607a76c8ea0b964ccd6b5364"}, + {file = "ml_dtypes-0.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bdf689be7351cc3c95110c910c1b864002f113e682e44508910c849e144f3df1"}, + {file = "ml_dtypes-0.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c83e4d443962d891d51669ff241d5aaad10a8d3d37a81c5532a45419885d591c"}, + {file = "ml_dtypes-0.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1e2f4237b459a63c97c2c9f449baa637d7e4c20addff6a9bac486f22432f3b6"}, + {file = "ml_dtypes-0.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:75b4faf99d0711b81f393db36d210b4255fd419f6f790bc6c1b461f95ffb7a9e"}, + {file = "ml_dtypes-0.4.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ee9f91d4c4f9959a7e1051c141dc565f39e54435618152219769e24f5e9a4d06"}, + {file = "ml_dtypes-0.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad6849a2db386b38e4d54fe13eb3293464561780531a918f8ef4c8169170dd49"}, + {file = "ml_dtypes-0.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa32979ebfde3a0d7c947cafbf79edc1ec77ac05ad0780ee86c1d8df70f2259"}, + {file = "ml_dtypes-0.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:3b67ec73a697c88c1122038e0de46520e48dc2ec876d42cf61bc5efe3c0b7675"}, + {file = "ml_dtypes-0.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:41affb38fdfe146e3db226cf2953021184d6f0c4ffab52136613e9601706e368"}, + {file = "ml_dtypes-0.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43cf4356a0fe2eeac6d289018d0734e17a403bdf1fd911953c125dd0358edcc0"}, + {file = "ml_dtypes-0.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1724ddcdf5edbaf615a62110af47407f1719b8d02e68ccee60683acb5f74da1"}, + {file = "ml_dtypes-0.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:723af6346447268a3cf0b7356e963d80ecb5732b5279b2aa3fa4b9fc8297c85e"}, + {file = "ml_dtypes-0.4.0.tar.gz", hash = "sha256:eaf197e72f4f7176a19fe3cb8b61846b38c6757607e7bf9cd4b1d84cd3e74deb"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.21.2", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, + {version = ">=1.23.3", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] + +[package.extras] +dev = ["absl-py", "pyink", "pylint (>=2.6.0)", "pytest", "pytest-xdist"] + +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = false +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + +[[package]] +name = "networkx" +version = "3.3" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.10" +files = [ + {file = "networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2"}, + {file = "networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9"}, +] + +[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)"] + +[[package]] +name = "numba" +version = "0.59.1" +description = "compiling Python code using LLVM" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numba-0.59.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97385a7f12212c4f4bc28f648720a92514bee79d7063e40ef66c2d30600fd18e"}, + {file = "numba-0.59.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0b77aecf52040de2a1eb1d7e314497b9e56fba17466c80b457b971a25bb1576d"}, + {file = "numba-0.59.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3476a4f641bfd58f35ead42f4dcaf5f132569c4647c6f1360ccf18ee4cda3990"}, + {file = "numba-0.59.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:525ef3f820931bdae95ee5379c670d5c97289c6520726bc6937a4a7d4230ba24"}, + {file = "numba-0.59.1-cp310-cp310-win_amd64.whl", hash = "sha256:990e395e44d192a12105eca3083b61307db7da10e093972ca285c85bef0963d6"}, + {file = "numba-0.59.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43727e7ad20b3ec23ee4fc642f5b61845c71f75dd2825b3c234390c6d8d64051"}, + {file = "numba-0.59.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:411df625372c77959570050e861981e9d196cc1da9aa62c3d6a836b5cc338966"}, + {file = "numba-0.59.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2801003caa263d1e8497fb84829a7ecfb61738a95f62bc05693fcf1733e978e4"}, + {file = "numba-0.59.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dd2842fac03be4e5324ebbbd4d2d0c8c0fc6e0df75c09477dd45b288a0777389"}, + {file = "numba-0.59.1-cp311-cp311-win_amd64.whl", hash = "sha256:0594b3dfb369fada1f8bb2e3045cd6c61a564c62e50cf1f86b4666bc721b3450"}, + {file = "numba-0.59.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1cce206a3b92836cdf26ef39d3a3242fec25e07f020cc4feec4c4a865e340569"}, + {file = "numba-0.59.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c8b4477763cb1fbd86a3be7050500229417bf60867c93e131fd2626edb02238"}, + {file = "numba-0.59.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d80bce4ef7e65bf895c29e3889ca75a29ee01da80266a01d34815918e365835"}, + {file = "numba-0.59.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7ad1d217773e89a9845886401eaaab0a156a90aa2f179fdc125261fd1105096"}, + {file = "numba-0.59.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bf68f4d69dd3a9f26a9b23548fa23e3bcb9042e2935257b471d2a8d3c424b7f"}, + {file = "numba-0.59.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4e0318ae729de6e5dbe64c75ead1a95eb01fabfe0e2ebed81ebf0344d32db0ae"}, + {file = "numba-0.59.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0f68589740a8c38bb7dc1b938b55d1145244c8353078eea23895d4f82c8b9ec1"}, + {file = "numba-0.59.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:649913a3758891c77c32e2d2a3bcbedf4a69f5fea276d11f9119677c45a422e8"}, + {file = "numba-0.59.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9712808e4545270291d76b9a264839ac878c5eb7d8b6e02c970dc0ac29bc8187"}, + {file = "numba-0.59.1-cp39-cp39-win_amd64.whl", hash = "sha256:8d51ccd7008a83105ad6a0082b6a2b70f1142dc7cfd76deb8c5a862367eb8c86"}, + {file = "numba-0.59.1.tar.gz", hash = "sha256:76f69132b96028d2774ed20415e8c528a34e3299a40581bae178f0994a2f370b"}, +] + +[package.dependencies] +llvmlite = "==0.42.*" +numpy = ">=1.22,<1.27" + +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "opt-einsum" +version = "3.3.0" +description = "Optimizing numpys einsum function" +optional = false +python-versions = ">=3.5" +files = [ + {file = "opt_einsum-3.3.0-py3-none-any.whl", hash = "sha256:2455e59e3947d3c275477df7f5205b30635e266fe6dc300e3d9f9646bfcea147"}, + {file = "opt_einsum-3.3.0.tar.gz", hash = "sha256:59f6475f77bbc37dcf7cd748519c0ec60722e91e63ca114e68821c0c54a46549"}, +] + +[package.dependencies] +numpy = ">=1.7" + +[package.extras] +docs = ["numpydoc", "sphinx (==1.2.3)", "sphinx-rtd-theme", "sphinxcontrib-napoleon"] +tests = ["pytest", "pytest-cov", "pytest-pep8"] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pandas" +version = "2.2.2" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, + {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, + {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, + {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, + {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, + {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "pbr" +version = "6.0.0" +description = "Python Build Reasonableness" +optional = false +python-versions = ">=2.6" +files = [ + {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, + {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, +] + +[[package]] +name = "pillow" +version = "10.3.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.20.4" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.20.4.tar.gz", hash = "sha256:6653541c2f1209e4d5268d3e6302791f72a95cc5f8bdcf3e60d943edc657e70a"}, + {file = "proto_plus-1.20.4-py3-none-any.whl", hash = "sha256:3cfaac30676793d5ee764a0982bc30481beb5059f315e2a2422d7c73ded5b601"}, +] + +[package.dependencies] +protobuf = ">=3.19.0" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.22.2)"] + +[[package]] +name = "protobuf" +version = "5.27.1" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-5.27.1-cp310-abi3-win32.whl", hash = "sha256:3adc15ec0ff35c5b2d0992f9345b04a540c1e73bfee3ff1643db43cc1d734333"}, + {file = "protobuf-5.27.1-cp310-abi3-win_amd64.whl", hash = "sha256:25236b69ab4ce1bec413fd4b68a15ef8141794427e0b4dc173e9d5d9dffc3bcd"}, + {file = "protobuf-5.27.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4e38fc29d7df32e01a41cf118b5a968b1efd46b9c41ff515234e794011c78b17"}, + {file = "protobuf-5.27.1-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:917ed03c3eb8a2d51c3496359f5b53b4e4b7e40edfbdd3d3f34336e0eef6825a"}, + {file = "protobuf-5.27.1-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:ee52874a9e69a30271649be88ecbe69d374232e8fd0b4e4b0aaaa87f429f1631"}, + {file = "protobuf-5.27.1-cp38-cp38-win32.whl", hash = "sha256:7a97b9c5aed86b9ca289eb5148df6c208ab5bb6906930590961e08f097258107"}, + {file = "protobuf-5.27.1-cp38-cp38-win_amd64.whl", hash = "sha256:f6abd0f69968792da7460d3c2cfa7d94fd74e1c21df321eb6345b963f9ec3d8d"}, + {file = "protobuf-5.27.1-cp39-cp39-win32.whl", hash = "sha256:dfddb7537f789002cc4eb00752c92e67885badcc7005566f2c5de9d969d3282d"}, + {file = "protobuf-5.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:39309898b912ca6febb0084ea912e976482834f401be35840a008da12d189340"}, + {file = "protobuf-5.27.1-py3-none-any.whl", hash = "sha256:4ac7249a1530a2ed50e24201d6630125ced04b30619262f06224616e0030b6cf"}, + {file = "protobuf-5.27.1.tar.gz", hash = "sha256:df5e5b8e39b7d1c25b186ffdf9f44f40f810bbcc9d2b71d9d3156fee5a9adf15"}, +] + +[[package]] +name = "psutil" +version = "5.9.8" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "pyasn1" +version = "0.6.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, + {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, + {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.7.0" + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pydantic" +version = "2.7.3" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"}, + {file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.18.4" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.4" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, + {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, + {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, + {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, + {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, + {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, + {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, + {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, + {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, + {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, + {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, + {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, + {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, + {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydata-sphinx-theme" +version = "0.15.3" +description = "Bootstrap-based Sphinx theme from the PyData community" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pydata_sphinx_theme-0.15.3-py3-none-any.whl", hash = "sha256:a48ee049dc9b0f7064dbb8f7064b1cf3ae48aa193faafe14abd403a1b7102810"}, + {file = "pydata_sphinx_theme-0.15.3.tar.gz", hash = "sha256:f26ed9b676f61d1b2ae9289f3d7e496e8678dd56f2568b27a66fa4ad1f164efd"}, +] + +[package.dependencies] +accessible-pygments = "*" +Babel = "*" +beautifulsoup4 = "*" +docutils = "!=0.17.0" +packaging = "*" +pygments = ">=2.7" +sphinx = ">=5" +typing-extensions = "*" + +[package.extras] +a11y = ["pytest-playwright"] +dev = ["pandoc", "pre-commit", "pydata-sphinx-theme[doc,test]", "pyyaml", "sphinx-theme-builder[cli]", "tox"] +doc = ["ablog (>=0.11.8)", "colorama", "ipykernel", "ipyleaflet", "ipywidgets", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (>=3.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap", "sphinx-togglebutton", "sphinxcontrib-youtube (<1.4)", "sphinxext-rediraffe", "xarray"] +i18n = ["Babel", "jinja2"] +test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyjwt" +version = "2.8.0" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, + {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, +] + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + +[[package]] +name = "pyparsing" +version = "3.1.2" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyspnego" +version = "0.10.2" +description = "Windows Negotiate Authentication Client and Server" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyspnego-0.10.2-py3-none-any.whl", hash = "sha256:3d5c5c28dbd0cd6a679acf45219630254db3c0e5ad4a16de521caa0585b088c0"}, + {file = "pyspnego-0.10.2.tar.gz", hash = "sha256:9a22c23aeae7b4424fdb2482450d3f8302ac012e2644e1cfe735cf468fcd12ed"}, +] + +[package.dependencies] +cryptography = "*" +sspilib = {version = ">=0.1.0", markers = "sys_platform == \"win32\""} + +[package.extras] +kerberos = ["gssapi (>=1.6.0)", "krb5 (>=0.3.0)"] +yaml = ["ruamel.yaml"] + +[[package]] +name = "pytest" +version = "8.2.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytket" +version = "1.28.0" +description = "Quantum computing toolkit and interface to the TKET compiler" +optional = false +python-versions = ">=3.10" +files = [ + {file = "pytket-1.28.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5cea8b772c51d3189c353d027b4d2c3dc2f265cca39345fdfaaea2dd6f8c4970"}, + {file = "pytket-1.28.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:1d5c5ab5f3dff39f048cacdc494aa7eea6099d96b0d7455ba411c0f630a46dbe"}, + {file = "pytket-1.28.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91b993a9991050fc8e47279e229cd35fd77acce53f720e8e465a9616ff1727aa"}, + {file = "pytket-1.28.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6276b5735f0c266d18daa7d212dca2cae3fd8cd0697009cd577d01b6fbd2ad35"}, + {file = "pytket-1.28.0-cp310-cp310-win_amd64.whl", hash = "sha256:acad2ddb5f6787e4ad2430c728a30cb77f30c37401c0ca9fea241e3b695241cb"}, + {file = "pytket-1.28.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:01d029c3d6168fea9b2d1c83f346461b393798745b9f6ca6aa05dc59913f21f2"}, + {file = "pytket-1.28.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ef49b12a3bdb5b276da501b70854f0dea26125dfdfe74ad6bdeb041df736f421"}, + {file = "pytket-1.28.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99c2ef5b42a8ab8a11235821b23c98b59fb3dee389f8f26ac9f93977d3cd958d"}, + {file = "pytket-1.28.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e12c24030ee1e94c1f68bed4abbb5ae474a7cdbbe4af7defe2bac0b270e45727"}, + {file = "pytket-1.28.0-cp311-cp311-win_amd64.whl", hash = "sha256:4052e83e252875a1b49a07c59acc61cb3d8e89cc06f9d43ffbbd2d5055455a61"}, + {file = "pytket-1.28.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9b1458433fc3c94ae20ae845ab501ddb692044e1d6d06e05ef0e614e99deff89"}, + {file = "pytket-1.28.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ba0c03a2294c38ed2e75d036f9885755725f0ecc8aa0f285ce9cf3e092414c22"}, + {file = "pytket-1.28.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4628753dacc5e0aa948900510c58c82e50d3efdc59772debc7dcf08e873385b0"}, + {file = "pytket-1.28.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e02fc7362f6ce7aba7a1d94dcc6d89071ce891c0598eec01892352d3046e33f2"}, + {file = "pytket-1.28.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cbe60b5affc5a9fba82639956c465073ccd56e1358fb33a44d195002b9c1975"}, +] + +[package.dependencies] +autoray = {version = ">=0.6.1", optional = true, markers = "extra == \"zx\""} +graphviz = ">=0.14,<1.0" +jinja2 = ">=3.0,<4.0" +lark = ">=1.1,<2.0" +networkx = ">=2.8.8" +numpy = ">=1.21.4,<2.0" +quimb = {version = ">=1.8,<2.0", optional = true, markers = "extra == \"zx\""} +qwasm = ">=1.0,<2.0" +scipy = ">=1.13,<2.0" +sympy = ">=1.6,<2.0" +types-pkg-resources = "*" +typing-extensions = ">=4.2,<5.0" + +[package.extras] +zx = ["autoray (>=0.6.1)", "quimb (>=1.8,<2.0)"] + +[[package]] +name = "pytket-cirq" +version = "0.36.0" +description = "Extension for pytket, providing translation to and from the Cirq framework" +optional = false +python-versions = ">=3.10" +files = [ + {file = "pytket_cirq-0.36.0-py3-none-any.whl", hash = "sha256:154b54f2034a8d61acecf51ccafd53c4ecbb69dd159fcfdf17692de9434380cf"}, +] + +[package.dependencies] +cirq-core = ">=1.0,<2.0" +cirq-google = ">=1.0,<2.0" +protobuf = ">=3.20,<6.0" +pytket = ">=1.27,<2.0" + +[[package]] +name = "pytket-qiskit" +version = "0.53.0" +description = "Extension for pytket, providing translation to and from the Qiskit framework" +optional = false +python-versions = ">=3.10" +files = [ + {file = "pytket_qiskit-0.53.0-py3-none-any.whl", hash = "sha256:d672c2e2d586eb2ed7cd3849d01e6d664c4e1082b45fdedb614def2be84ed22a"}, +] + +[package.dependencies] +numpy = "*" +pytket = ">=1.27,<2.0" +qiskit = ">=1.0,<2.0" +qiskit-aer = ">=0.14.0,<0.15.0" +qiskit-algorithms = ">=0.3.0,<0.4.0" +qiskit-ibm-provider = ">=0.10.0,<0.11.0" +qiskit-ibm-runtime = ">=0.23.0,<0.24.0" + +[[package]] +name = "pytket-qujax" +version = "0.19.0" +description = "Extension for pytket, providing access to qujax functions" +optional = false +python-versions = ">=3.10" +files = [ + {file = "pytket_qujax-0.19.0-py3-none-any.whl", hash = "sha256:a0d4eeed1834c727629e50417ab516ffa277ff35148736e90bd5680f90ca1452"}, +] + +[package.dependencies] +pytket = ">=1.27,<2.0" +qujax = ">=1.0,<2.0" + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "qiskit" +version = "1.1.0" +description = "An open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives." +optional = false +python-versions = ">=3.8" +files = [ + {file = "qiskit-1.1.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:1abad8ac96dceb838e279fecae713f26f5debbd85b476e0a52ea829cd823da95"}, + {file = "qiskit-1.1.0-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c2345eaa770a0112a667c6bcbcd9694dc7eed274f1ceea0732ddb6fb42336ccc"}, + {file = "qiskit-1.1.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:3133d90bda74ec487f205c68ba501cb5f27a23a146ff5f3de70eb93084a906b8"}, + {file = "qiskit-1.1.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:135f8dcd4759ea6d5989abb68d494d47d86a8068deaef4addb365e8fd89d87a8"}, + {file = "qiskit-1.1.0-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06d7081b8c4fb51cd613822d6365643fb0673cdaf4e0fa40b59602f61d9e1229"}, + {file = "qiskit-1.1.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b4f0e946d703f2bfb53d97fe7156444e55ad2d3cfb853a84b756c50877dc368"}, + {file = "qiskit-1.1.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fda2001a3d87c85a3166e660cb8f9f8b2842bd2aa2867e679cc3a8b9ca8785a"}, + {file = "qiskit-1.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01da5ebdcf907435d1ab47ea024bb791b4dea8245c148b89bc68e606c45eb478"}, + {file = "qiskit-1.1.0-cp38-abi3-win32.whl", hash = "sha256:3d9cf7fa7d0a17a500b0181eae07c08d38e9a6a67789e09736b2de334159edfe"}, + {file = "qiskit-1.1.0-cp38-abi3-win_amd64.whl", hash = "sha256:26eb1b391ac36e831bec0f7dd3c78b426ea5b0963b124a749b8da38f03c2e875"}, + {file = "qiskit-1.1.0.tar.gz", hash = "sha256:d4d0310029659711dca6b9c0cc593e1b33daf059e52ba221267f85bb47f1bf4e"}, +] + +[package.dependencies] +dill = ">=0.3" +numpy = ">=1.17,<3" +python-dateutil = ">=2.8.0" +rustworkx = ">=0.14.0" +scipy = ">=1.5" +stevedore = ">=3.0.0" +symengine = ">=0.11" +sympy = ">=1.3" +typing-extensions = "*" + +[package.extras] +all = ["qiskit[crosstalk-pass,csp-layout-pass,qasm3-import,visualization]"] +crosstalk-pass = ["z3-solver (>=4.7)"] +csp-layout-pass = ["python-constraint (>=1.4)"] +qasm3-import = ["qiskit-qasm3-import (>=0.1.0)"] +visualization = ["Pillow (>=4.2.1)", "matplotlib (>=3.3)", "pydot", "pylatexenc (>=1.4)", "seaborn (>=0.9.0)"] + +[[package]] +name = "qiskit-aer" +version = "0.14.2" +description = "Aer - High performance simulators for Qiskit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "qiskit-aer-0.14.2.tar.gz", hash = "sha256:9918916dc25071f4e2ddbe5f5c6114de18738cdb05bc12dc97823a556b3df9c2"}, + {file = "qiskit_aer-0.14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b9385add7fbc0ae790583657c0dc0b564ec95d3285c22fa7d7332a8452f31c5"}, + {file = "qiskit_aer-0.14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7e63523705d27d527d048da9b70bdf398faf728867aa728099f753ea2bea13cd"}, + {file = "qiskit_aer-0.14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c51e85ce3a55d6527f74e1133b045ef7ca23bb11519639ca321249a89fe755b6"}, + {file = "qiskit_aer-0.14.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3691dd8e3afa749822474de355fe813559fb78ab0c7c5ac08f060b8f82bfaeb9"}, + {file = "qiskit_aer-0.14.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b4490ce94d740d6f833776be37b31bf7bb94dc38e8789ab2c27421449cabad2"}, + {file = "qiskit_aer-0.14.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f5f42e6f016bb027079fef73597a3b35b76e2ac398138d83651833e93dc964d"}, + {file = "qiskit_aer-0.14.2-cp310-cp310-win32.whl", hash = "sha256:0bfa5c289f973f081a0874bf79a1087c9d70a052c16f8bbc8ab2ff3e796859c4"}, + {file = "qiskit_aer-0.14.2-cp310-cp310-win_amd64.whl", hash = "sha256:9398730784ded8cdd046596683de7e5fd540f5173c9a3a4e4ccf72d79e47ff58"}, + {file = "qiskit_aer-0.14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b3ec6c470b0ba493703ebc514c1eff2c1cbc204b0795de2f5f2a3bc497edd423"}, + {file = "qiskit_aer-0.14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8dbb6a0ec4ae193cb3649f4400178592ea4ad9714b070bf86222d370bfcb9e15"}, + {file = "qiskit_aer-0.14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c11a3fd727d3075fb4d4ad00cf6670fa944d2df7f4b1b5b109f45bd9156d603"}, + {file = "qiskit_aer-0.14.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:246175da975a80e3999c8b1a375dc7248f59b165b8fcabc3be4dde8d07224ffe"}, + {file = "qiskit_aer-0.14.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7d1526359547c006a7c9ce35441c2a2207ceeb78fc05ac29866d700cf4da175"}, + {file = "qiskit_aer-0.14.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e3bd6a6bc68a8705f3f37e0597c5de91b2ce051b72743028f4a306c3483bd0"}, + {file = "qiskit_aer-0.14.2-cp311-cp311-win32.whl", hash = "sha256:7f2320ea40f0118059acb2345aed03039a453693adc47f8adec079e1d163e3c6"}, + {file = "qiskit_aer-0.14.2-cp311-cp311-win_amd64.whl", hash = "sha256:7b5e91625563a16e2e237d1e0b79996937045c15cd086d2ea265e7b952bea9de"}, + {file = "qiskit_aer-0.14.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:215bc8ad213b8541cbe85b423bfd4ca7464294678a76e65820329f3b92a33478"}, + {file = "qiskit_aer-0.14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4302a11317a46490b06c91d8b70fd1d5895692904a0624c7974d669454ef0876"}, + {file = "qiskit_aer-0.14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e26e59e021376445a0f1673f9da816273d3987fd2270634bcfa6e3c9281b127"}, + {file = "qiskit_aer-0.14.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57d3df63142b17e753f6fbe8ab01038ae45057ba6d897e53f94e78c80960e4f2"}, + {file = "qiskit_aer-0.14.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e734d3afa26919a511085d474b6cfc1fcc44e4c692227e65cd8e09be521a0881"}, + {file = "qiskit_aer-0.14.2-cp312-cp312-win32.whl", hash = "sha256:329e3b8c8ccf6cdfb251ef6cc99249ca424a4193a1b2c6df54d7b66bcdac9566"}, + {file = "qiskit_aer-0.14.2-cp312-cp312-win_amd64.whl", hash = "sha256:4085fdc0a805eddbda12fbcdfde0c69e724c56b50168655778388a95b73c9c12"}, + {file = "qiskit_aer-0.14.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cddf30ee84eb5923c7a9d68074f2ce388790cab10346ad76876d91c6d0a4873c"}, + {file = "qiskit_aer-0.14.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451699ca80b3fef59a7bebac245b3988740dd6095c1fa2959bc474b1fac51187"}, + {file = "qiskit_aer-0.14.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1ba57424a4649288c3c63685b3a2deaf9447e55e4f04d54af623ef981a4853f"}, + {file = "qiskit_aer-0.14.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16ef5ea5fb05c8d66a6911934e3640b0d56ad4842bca41ebf3328387d079c4bd"}, + {file = "qiskit_aer-0.14.2-cp38-cp38-win32.whl", hash = "sha256:c412d866ef50d2813fbefcc093bcb507fcdc1d97d652ea9407e86d13fb25e5df"}, + {file = "qiskit_aer-0.14.2-cp38-cp38-win_amd64.whl", hash = "sha256:7c3cca5a15bd11cc0ff3562a7e39cc99c9069159c609c9633766e0be4ad7789c"}, + {file = "qiskit_aer-0.14.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0aff9888269095fe1fdb4b8a870cf66a9a63c00eaf0296e955e122d8b61e2f3f"}, + {file = "qiskit_aer-0.14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05c826eba94952f0c43e52baefea0801e4eccf9a4886aca484fcfa1381dd5bf3"}, + {file = "qiskit_aer-0.14.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05d19db49fdecd4f71f3a35525c9fcdc32c80e90e502f872536af1e0ffa1f618"}, + {file = "qiskit_aer-0.14.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73a48105df9e1df9284b64ba983cdb6b23eae41090db8c80ff956e78ad91bdac"}, + {file = "qiskit_aer-0.14.2-cp39-cp39-win32.whl", hash = "sha256:8d2fffd42974ed0d2b1c89323c5405aa6cad71316c8e3994aad341e9fb3b3c92"}, + {file = "qiskit_aer-0.14.2-cp39-cp39-win_amd64.whl", hash = "sha256:30ffebbd00860691e7e8802b9aaef2640e691680c3ac2a5b9a454e3b9d336eee"}, +] + +[package.dependencies] +numpy = ">=1.16.3" +psutil = ">=5" +qiskit = ">=0.45.2" +scipy = ">=1.0" + +[package.extras] +dask = ["dask", "distributed"] + +[[package]] +name = "qiskit-algorithms" +version = "0.3.0" +description = "Qiskit Algorithms: A library of quantum computing algorithms" +optional = false +python-versions = ">=3.8" +files = [ + {file = "qiskit-algorithms-0.3.0.tar.gz", hash = "sha256:02eedcbb079c6da371421a50cb296ff1dc6ce4a1c478ec521ff6e62c9bc53e10"}, + {file = "qiskit_algorithms-0.3.0-py3-none-any.whl", hash = "sha256:8ae1aa8aafc32864890a31c06d19f100a79df6412350f1f4b8c124cd17b3f731"}, +] + +[package.dependencies] +numpy = ">=1.17" +qiskit = ">=0.44" +scipy = ">=1.4" + +[[package]] +name = "qiskit-ibm-provider" +version = "0.10.0" +description = "Qiskit IBM Quantum Provider for accessing the quantum devices and simulators at IBM" +optional = false +python-versions = ">=3.8" +files = [ + {file = "qiskit-ibm-provider-0.10.0.tar.gz", hash = "sha256:99dc9fd48bee2e7ed633bc739e0935c91859f1c4b77080a31089ae92e6f110f1"}, + {file = "qiskit_ibm_provider-0.10.0-py3-none-any.whl", hash = "sha256:2e5c6fedeba1308b009d4733a4e8433a5b4d190aed5981dd7f4512ec04433298"}, +] + +[package.dependencies] +numpy = ">=1.13" +python-dateutil = ">=2.8.0" +qiskit = ">=0.45.0" +requests = ">=2.19" +requests-ntlm = ">=1.1.0" +typing-extensions = ">=4.3" +urllib3 = ">=1.21.1" +websocket-client = ">=1.5.1" +websockets = ">=10.0" + +[package.extras] +visualization = ["ipython (>=5.0.0)", "ipyvue (>=1.8.5)", "ipyvuetify (>=1.1)", "ipywidgets (<8.0.0)", "matplotlib (>=2.1)", "plotly (>=4.4)", "pyperclip (>=1.7)", "seaborn (>=0.9.0)", "traitlets (!=5.0.5)"] + +[[package]] +name = "qiskit-ibm-runtime" +version = "0.23.0" +description = "IBM Quantum client for Qiskit Runtime." +optional = false +python-versions = ">=3.8" +files = [ + {file = "qiskit_ibm_runtime-0.23.0-py3-none-any.whl", hash = "sha256:64bc04f71667b3a601b0648be8f09817ea220f2e7d0ca5cde37b75759f25b16c"}, + {file = "qiskit_ibm_runtime-0.23.0.tar.gz", hash = "sha256:ea08147dbc0f71f4a51e45c3426ad9d5f2c43a42f73ef1b932a5c31bb1d6369c"}, +] + +[package.dependencies] +ibm-platform-services = ">=0.22.6" +numpy = ">=1.13" +pydantic = "*" +python-dateutil = ">=2.8.0" +qiskit = ">=1.0.0" +requests = ">=2.19" +requests-ntlm = ">=1.1.0" +urllib3 = ">=1.21.1" +websocket-client = ">=1.5.1" + +[[package]] +name = "quimb" +version = "1.8.1" +description = "Quantum information and many-body library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "quimb-1.8.1-py3-none-any.whl", hash = "sha256:b01c6f1e8470fc76b54216eaa78cd276f394011992d29c780443087f7656a690"}, + {file = "quimb-1.8.1.tar.gz", hash = "sha256:5fea3e07b076c717fa354505a11ccb9b3627712fa3d6e722ee9b293ba8d643ea"}, +] + +[package.dependencies] +autoray = ">=0.6.7" +cotengra = ">=0.5.6" +cytoolz = ">=0.8.0" +numba = ">=0.39" +numpy = ">=1.17" +psutil = ">=4.3.1" +scipy = ">=1.0.0" +tqdm = ">=4" + +[package.extras] +advanced-solvers = ["mpi4py", "petsc4py", "slepc4py"] +docs = ["astroid (<3.0.0)", "autoray (>=0.6.7)", "cotengra (>=0.5.3)", "doc2dash (>=2.4.1)", "furo", "ipython (!=8.7.0)", "myst-nb", "setuptools-scm", "sphinx (>=2.0)", "sphinx-autoapi", "sphinx-copybutton", "sphinx-design"] +tensor = ["matplotlib (>=2.0)", "networkx (>=2.3)"] +tests = ["coverage", "pytest", "pytest-cov"] + +[[package]] +name = "qujax" +version = "1.1.0" +description = "Simulating quantum circuits with JAX" +optional = false +python-versions = ">=3.8" +files = [ + {file = "qujax-1.1.0-py3-none-any.whl", hash = "sha256:8358a3b7a766acda5589c5433b8a7ba974629a7681d16c5f973adae6ee3d075e"}, +] + +[package.dependencies] +jax = "*" +jaxlib = "*" + +[[package]] +name = "qwasm" +version = "1.0.1" +description = "WebAssembly decoder & disassembler" +optional = false +python-versions = "*" +files = [ + {file = "qwasm-1.0.1-py3-none-any.whl", hash = "sha256:c4c82a3f962d29314634868e06375f0cb4676c3d5266fbe137f6cd67321b0ef1"}, + {file = "qwasm-1.0.1.tar.gz", hash = "sha256:01f5dfe27159b7fdd9d02cd299833225d528fa383d1278268e5e1526357950fb"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-ntlm" +version = "1.3.0" +description = "This package allows for HTTP NTLM authentication using the requests library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests_ntlm-1.3.0-py3-none-any.whl", hash = "sha256:4c7534a7d0e482bb0928531d621be4b2c74ace437e88c5a357ceb7452d25a510"}, + {file = "requests_ntlm-1.3.0.tar.gz", hash = "sha256:b29cc2462623dffdf9b88c43e180ccb735b4007228a542220e882c58ae56c668"}, +] + +[package.dependencies] +cryptography = ">=1.3" +pyspnego = ">=0.4.0" +requests = ">=2.0.0" + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "rustworkx" +version = "0.14.2" +description = "A python graph library implemented in Rust" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rustworkx-0.14.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a28a972dc7e0faf03f9f90c5be89328af8a71e609f311840e1a6abc6385edb79"}, + {file = "rustworkx-0.14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:50e682b8fd2f11f9e99c309a01f7ed88a09ad32cda35b92c49835b1c9536ec65"}, + {file = "rustworkx-0.14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6e1c3cf3d265835429074a1ecaa8f9bff327b188e1496a120bf8be8260a46453"}, + {file = "rustworkx-0.14.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a22c02f74bf391b48ae92f633083d068055f3ed85050e35fe6cda967ff8a825"}, + {file = "rustworkx-0.14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:996bad21eacbe124dd1e6abca47dd69ade9db0d4df5dd29197694f5d8e0a8258"}, + {file = "rustworkx-0.14.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95c4647461f05fd9f99bae52002a929e8628d4e5a2e732dbfd7abd00ae5257b7"}, + {file = "rustworkx-0.14.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:829444876bba1940fa3109998f3b6c9184256d91eea5f0e09d9e9f8f26bb4704"}, + {file = "rustworkx-0.14.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:987b430dce1351a0c761bd6eedb8f6999f48983c9d4b06bf4b0b9dc45d08be8d"}, + {file = "rustworkx-0.14.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:18ef16f9b6b4f1c0d458fde3f213b78436ac810d61cae60385696b411aa80e1d"}, + {file = "rustworkx-0.14.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fe30f1e22e69cbab4182d0017e21c345bf75f142a7b66a828227dd3c654d524c"}, + {file = "rustworkx-0.14.2-cp310-cp310-win32.whl", hash = "sha256:c1fe9f9ed18e270074d3632f6c70cc75c461535d9e76db39d1c0ab712bf64a7a"}, + {file = "rustworkx-0.14.2-cp310-cp310-win_amd64.whl", hash = "sha256:271b36412421d622e9e8cd27e2c6e1bd356e452f979edd41bb32d308df936f47"}, + {file = "rustworkx-0.14.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c52e34ff4b08d1eaedd2ec906bca4317f4f852b36e4615d372b1ff2bb435ff26"}, + {file = "rustworkx-0.14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c97dc0cf7efef033ce50fa570887f97896b0f449c841ec3b127ecb70b3c16c84"}, + {file = "rustworkx-0.14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:114bec1606ae31c089ecf52aa511551c545c6ce0746d3e8766082ad450377a2c"}, + {file = "rustworkx-0.14.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:950fa4ffc1691081587c87c4e869a8f5c7d0672d35ce1ba7c69f758f90bfe8c0"}, + {file = "rustworkx-0.14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2134aa9c2065ab6c934017b6909e224e860003eb5dbaa5d2c4e87fff1187459a"}, + {file = "rustworkx-0.14.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bec8f1f1a6fed3ffbf5348a2b9d700f0b840fed2faa6a5198838d0fa9674a781"}, + {file = "rustworkx-0.14.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8046991499df7aa984b3d9092e4f013597901c919aaf6fa43147e8550685734"}, + {file = "rustworkx-0.14.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acb4256fba2c4f5c4ec009f383623b6a7c0a2dbeed1b529d22a193113927364a"}, + {file = "rustworkx-0.14.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:be7be125f9313b58829f7202a66dc166b61bf3c4bbe0c509b8d6902ed0d2da45"}, + {file = "rustworkx-0.14.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5d00f87fce0e48c6d7af4b63ee635188178e91462b52ac900d36ec3184ce92fc"}, + {file = "rustworkx-0.14.2-cp311-cp311-win32.whl", hash = "sha256:521e0f432a94ac9a4c92f30a746b971f7e49476fd128d83d94d4b15a2c17245d"}, + {file = "rustworkx-0.14.2-cp311-cp311-win_amd64.whl", hash = "sha256:8fd20776c0f543340ef96450ba5d9d670b8d74396315f7191303a392844271e0"}, + {file = "rustworkx-0.14.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7bb37e877653ae4b4d505fc7e5f7847ae06e6822b91cec56e9e851941a6a0ae7"}, + {file = "rustworkx-0.14.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:230808e3878236464ac00001d8b440382aa6230f0073554ec627580863e380cc"}, + {file = "rustworkx-0.14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a7cb7103ba88e12e3dd8e3b28365cbe971a8c158c1ee770646b2f3fd5cedab0"}, + {file = "rustworkx-0.14.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2637d0e496f34bac45f926b0aa12fb2e143581208f29a424cfb0eb5a7b5c3bfa"}, + {file = "rustworkx-0.14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692f78ee7f7a60d9c7082a5a26b4eefb697526f195172798389d7009510d84f3"}, + {file = "rustworkx-0.14.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d5513e93b7c10fbce954771a74fc86d551eb33b9eb318eaa35d7668f9929da"}, + {file = "rustworkx-0.14.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26076523a1c43e903c633f2375afac28fdbb83b9668bee00fae24d8c672bf6c9"}, + {file = "rustworkx-0.14.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6de5e2df15c415dfb6e5cb7175239d0862568cb10d028f451d358d101be5d8bf"}, + {file = "rustworkx-0.14.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:21c86c240628abc2123d7d1317647073a738bdfe143c55728261b66bc32806e2"}, + {file = "rustworkx-0.14.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0aa0277b931ca3fdfae07f8999b6a63dc9b89622b2fab820fa6bd95dd1e2e2eb"}, + {file = "rustworkx-0.14.2-cp312-cp312-win32.whl", hash = "sha256:fdc632673d4cd7f1cffe8ce13ea17dc361cf9d0d9f37dfa0888d94bdd5e6c159"}, + {file = "rustworkx-0.14.2-cp312-cp312-win_amd64.whl", hash = "sha256:47768f985f32ac1cd807af816fbd5f6e2433889793afdd838891ae516a95c8a6"}, + {file = "rustworkx-0.14.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:bfeee5a5be9eb71635a7897a6d2c034b1c01bf876fd15007b8bd4c6eaa8921e2"}, + {file = "rustworkx-0.14.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4a20434c77f3daab043ab2f96386b5da871ebf15a5495f9ad5b916c3edf03e5c"}, + {file = "rustworkx-0.14.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d856549e874e064af136f2ce304eb896d32d8865c3e98f8d9e83b577f4c57f1d"}, + {file = "rustworkx-0.14.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2521a223fb5aab2a14351205456d02bd851e0ec6b0c028f5598fe14f292e881b"}, + {file = "rustworkx-0.14.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc9e7718eee8295cd5c11a5cf1c0fc7772e9c1dcc3d110edba4c77aad47e7f07"}, + {file = "rustworkx-0.14.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c23ef82b1d373e07c280b8b6927dbad3953597e34c752e14843ac3df722a621"}, + {file = "rustworkx-0.14.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a46e0d1398138a75fb909369ffe6dfdcec6bab4d21794e80a9abf45fd2823f68"}, + {file = "rustworkx-0.14.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16fb941e8f48aea96ee38471a1ae770ec68623864a9b0e4760aabf82c41fc2b"}, + {file = "rustworkx-0.14.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fa92a97e5d35c6901553a812f31ca18305922c0ef06c2d7a9d20fbcc0769b4d1"}, + {file = "rustworkx-0.14.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2e14b2956f2d06f5bb196bcd95f73008245eb6ffa9ee08f86ec369acf0cc04be"}, + {file = "rustworkx-0.14.2-cp38-cp38-win32.whl", hash = "sha256:816d33f69f4189376e1bb8132dea1deef1cd019b25bd281f01b7f394fcadbdad"}, + {file = "rustworkx-0.14.2-cp38-cp38-win_amd64.whl", hash = "sha256:4163f9c2c2d2158e053b30a39f74b0382b4c5a8a43f192c13b736e200b5e2025"}, + {file = "rustworkx-0.14.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a9b55a8f97799b159da96087176a0e97679dca0b6b5a14b3140aeda7e1050777"}, + {file = "rustworkx-0.14.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:edb2d67870e41d5a1e16288bca0758580fb6961e8b4dfc337557bdaab81ff016"}, + {file = "rustworkx-0.14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f058bdb50c5b0be731b96ffd789c6cec2a99e7f757a57763b2cc56004ed95af6"}, + {file = "rustworkx-0.14.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff900cb6ae2d4028ffe5a3075cfefa21b14929270844b172595e6de0d2f183eb"}, + {file = "rustworkx-0.14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630177a80c68823fb2dd94733298377bd52c2ce3f66758ea0a63966fc2d7c08f"}, + {file = "rustworkx-0.14.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a79c177a9e4f1c623554e01319fcb7b2a062ae26def7b85dc1f0539b7cdd874"}, + {file = "rustworkx-0.14.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff956ee6c8224b8225478bb72103d4fc6dd4a247c066da30927776e1b05690"}, + {file = "rustworkx-0.14.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae61a4c58186b4e428947b92ac2aa0557bcd5071fe8102a542c4337f64091766"}, + {file = "rustworkx-0.14.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5a96b6f96e1bb4e8ee337618d8af0a1aec16c2eda6ffd9968e16d161850d1e77"}, + {file = "rustworkx-0.14.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49bc143729e0d64a51b0ec6d745665f067116db78ce958d5cbe0389e69c6e73c"}, + {file = "rustworkx-0.14.2-cp39-cp39-win32.whl", hash = "sha256:f11e858a1804d5e276d18d6fc197f797adf5da82cd3382550abeef50196c5a7e"}, + {file = "rustworkx-0.14.2-cp39-cp39-win_amd64.whl", hash = "sha256:b55e75ea35a225d6b0afbdd449665e3b907684347be6a38648bdbfd50e177bf0"}, + {file = "rustworkx-0.14.2.tar.gz", hash = "sha256:bd649322c0649b71fa18cc70a9af027b549560415fa860d6894736029c277b13"}, +] + +[package.dependencies] +numpy = ">=1.16.0,<2" + +[package.extras] +all = ["matplotlib (>=3.0)", "pillow (>=5.4)"] +graphviz = ["pillow (>=5.4)"] +mpl = ["matplotlib (>=3.0)"] + +[[package]] +name = "scipy" +version = "1.13.1" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, + {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, + {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, + {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, + {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, + {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, + {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, + {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, + {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, + {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, + {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, +] + +[package.dependencies] +numpy = ">=1.22.4,<2.3" + +[package.extras] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] +test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "setuptools" +version = "70.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + +[[package]] +name = "sphinx" +version = "7.3.7" +description = "Python documentation generator" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinx-7.3.7-py3-none-any.whl", hash = "sha256:413f75440be4cacf328f580b4274ada4565fb2187d696a84970c23f77b64d8c3"}, + {file = "sphinx-7.3.7.tar.gz", hash = "sha256:a4a7db75ed37531c05002d56ed6948d4c42f473a36f46e1382b0bd76ca9627bc"}, +] + +[package.dependencies] +alabaster = ">=0.7.14,<0.8.0" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.18.1,<0.22" +imagesize = ">=1.3" +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.14" +requests = ">=2.25.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.9" +tomli = {version = ">=2", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["flake8 (>=3.5.0)", "importlib_metadata", "mypy (==1.9.0)", "pytest (>=6.0)", "ruff (==0.3.7)", "sphinx-lint", "tomli", "types-docutils", "types-requests"] +test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=6.0)", "setuptools (>=67.0)"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.8" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, + {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.6" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, + {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.5" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, + {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.7" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, + {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.10" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, + {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sspilib" +version = "0.1.0" +description = "SSPI API bindings for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sspilib-0.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5e43f3e684e9d29c80324bd54f52dac65ac4b18d81a2dcd529dce3994369a14d"}, + {file = "sspilib-0.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1eb34eda5d362b6603707a55751f1eff81775709b821e51cb64d1d2fa2bb8b6e"}, + {file = "sspilib-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ffe123f056f78cbe18aaed6b15f06e252020061c3387a72615abd46699a0b24"}, + {file = "sspilib-0.1.0-cp310-cp310-win32.whl", hash = "sha256:a4151072e28ec3b7d785beac9548a3d6a4549c431eb5487a5b8a1de028e9fef0"}, + {file = "sspilib-0.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:2a19696c7b96b6bbef2b2ddf35df5a92f09b268476a348390a2f0da18cf29510"}, + {file = "sspilib-0.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:d2778e5e2881405b4d359a604e2802f5b7a7ed433ff62d6073d04c203af10eb1"}, + {file = "sspilib-0.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:09d7f72ad5e4bbf9a8f1acf0d5f0c3f9fbe500f44c4a45ac24a99ece84f5654f"}, + {file = "sspilib-0.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e5705e11aaa030a61d2b0a2ce09d2b8a1962dd950e55adc7a3c87dd463c6878"}, + {file = "sspilib-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dced8213d311c56f5f38044716ebff5412cc156f19678659e8ffa9bb6a642bd7"}, + {file = "sspilib-0.1.0-cp311-cp311-win32.whl", hash = "sha256:d30d38d52dbd857732224e86ae3627d003cc510451083c69fa481fc7de88a7b6"}, + {file = "sspilib-0.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:61c9067168cce962f7fead42c28804c3a39a164b9a7b660200b8cfe31e3af071"}, + {file = "sspilib-0.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:b526b8e5a236553f5137b951b89a2f108f56138ad05f31fd0a51b10f80b6c3cc"}, + {file = "sspilib-0.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3ff356d40cd34c900f94f1591eaabd458284042af611ebc1dbf609002066dba5"}, + {file = "sspilib-0.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b0fee3a52d0acef090f6c9b49953a8400fdc1c10aca7334319414a3038aa493"}, + {file = "sspilib-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab52d190dad1d578ec40d1fb417a8571954f4e32f35442a14cb709f57d3acbc9"}, + {file = "sspilib-0.1.0-cp312-cp312-win32.whl", hash = "sha256:b3cf819094383ec883e9a63c11b81d622618c815c18a6c9d761d9a14d9f028d1"}, + {file = "sspilib-0.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:b83825a2c43ff84ddff72d09b098057efaabf3841d3c42888078e154cf8e9595"}, + {file = "sspilib-0.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:9aa6ab4c3fc1057251cf1f3f199daf90b99599cdfafc9eade8fdf0c01526dec8"}, + {file = "sspilib-0.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:82bff5df178386027d0112458b6971bbd18c76eb9e7be53fd61dab33d7bf8417"}, + {file = "sspilib-0.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:18393a9e6e0447cb7f319d361b65e9a0eaa5484705f16787133ffc49ad364c28"}, + {file = "sspilib-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a423fbca206ba0ca811dc995d8c3af045402b7d330f033e938b24f3a1d93fc"}, + {file = "sspilib-0.1.0-cp38-cp38-win32.whl", hash = "sha256:86bd936b1ef0aa63c6d9623ad08473e74ceb15f342f6e92cbade15ed9574cd33"}, + {file = "sspilib-0.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f688b94f0a64128444063e1d3d59152614175999222f6e2920681faea833f4"}, + {file = "sspilib-0.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2acef24e13e40d9dd8697eaae84ead9f417528ff741d087ec4eb4260518f4dc7"}, + {file = "sspilib-0.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b625802d80144d856d5eb6e8f4412f186565758da4493c7ad1b88e3d6d353de"}, + {file = "sspilib-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06ca1e34702bca1c750dcb5133b716f316b38dccb28d55a1a44d9842bc3f391"}, + {file = "sspilib-0.1.0-cp39-cp39-win32.whl", hash = "sha256:68496c9bd52b57a1b6d2e5529b43c30060249b8db901127b8343c4ad8cd93670"}, + {file = "sspilib-0.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:369727097f07a440099882580e284e137d9c27b7de354d63b65e327a454e7bee"}, + {file = "sspilib-0.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:87d8268c0517149c51a53b3888961ebf66826bb3dbb82c4e5cf10108f5456104"}, + {file = "sspilib-0.1.0.tar.gz", hash = "sha256:58b5291553cf6220549c0f855e0e6973f4977375d8236ce47bb581efb3e9b1cf"}, +] + +[[package]] +name = "stevedore" +version = "5.2.0" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.8" +files = [ + {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, + {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, +] + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +name = "symengine" +version = "0.11.0" +description = "Python library providing wrappers to SymEngine" +optional = false +python-versions = ">=3.8,<4" +files = [ + {file = "symengine-0.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a5ab2281fe5b3cbe8fbe4411c4e2e4ce6788e4d653b5d31b002d05f4af13b7c7"}, + {file = "symengine-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1f0c83e91c90a0344fdb9f1d71970f09d4fad5715110c9651d6ebc9ac995f97e"}, + {file = "symengine-0.11.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:94c3e143bad732f858f5a8740bda640e24b491aa8cea30b2a68517a552060c84"}, + {file = "symengine-0.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8994e8e9b7273ce48e045d2a97e88b034bb99566bb3d0dd2a2ff79f2dccd4765"}, + {file = "symengine-0.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36c96584b5f852990e09904bb890623666cdc0bb83c5758f00939b540fcb2dc9"}, + {file = "symengine-0.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:fcfaf43781ae6e3e0b6b2f1da700e8ee6a0389426d3300925f4611323eb9b616"}, + {file = "symengine-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:292efdba67ce3465c99ff1e35338857d982583bbf505ae79f111f8f4fa670367"}, + {file = "symengine-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e935e0f5274d1ab456f3ba75a03dea1ee8a422dbd4d5d4f316653310a177962"}, + {file = "symengine-0.11.0-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f588199cb79b1c365005d52a3a8bc0967487dce5db77b864ee73735a5f4d2ec"}, + {file = "symengine-0.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb419847f24944fd29e834a342c6d05577dd66b34942f65131d417d70c2e7926"}, + {file = "symengine-0.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10fe4f99d95dc222fd7c37217f80f06f15b9e9b78b951de3c5dc0648cbd1572e"}, + {file = "symengine-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:468b42d2626495808b21cc3b73a162d43c757cc97e4549b1911ec40331d3d491"}, + {file = "symengine-0.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:07acd4ab7b147b913fdb62444ced626a9b66b9eefef5d3abfc4f65dccc411403"}, + {file = "symengine-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5fb1d62479c18cc00f025b53ba5f72a7bce92eabee9fec44256bb08d3c28d851"}, + {file = "symengine-0.11.0-cp312-cp312-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ccd28886f3eea78a1b9150b636ae40acf7d27ac79b4cc9161ec46f592e366a47"}, + {file = "symengine-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:517fec27be3741f89e1eea92b9e2f0cdca70fcd77d3fd5c4f8e4f13e6f04e8e4"}, + {file = "symengine-0.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54c5bcca900ec3bc6a20c749957310d52132a815bb3fca614fd66a7881626814"}, + {file = "symengine-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:6123f7e287cebeac9168e60eb6a9e9641f457617dbeee7b117fa7606fde84151"}, + {file = "symengine-0.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:116e1f644b3505608b55e745d71b80521b2e72042f421cb70c91dbe7c6710655"}, + {file = "symengine-0.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1c7913e264c95f44a52f181e4fff98afd9640651de149d0aee42d73b8b89860b"}, + {file = "symengine-0.11.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ce5e0c14e925fb3600d6955b07314e9981ed389240e437f5dd42f08f70a18af8"}, + {file = "symengine-0.11.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6323c43a703fefb0a50d42430e63a590c18b2955cfa8022cf7c0f6d0f32b86"}, + {file = "symengine-0.11.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d20163ad6c266e67fb9ad27970c7939bc3d5627487c8f0dc24cd26b9028e3f"}, + {file = "symengine-0.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:1f79feeeb1af286c29fb791537f243798229020a1ae6340b3d21eece3992783d"}, + {file = "symengine-0.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a78f905944ea9b9ef97e1afb355de4972fe84ed4cceadfc84b01366ce1f50d5b"}, + {file = "symengine-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33d2dfea163b77b41fa4c56ade5c6ac4a91693502bc282ba2c645591ef94775d"}, + {file = "symengine-0.11.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a4412da691328a8e6b0622067807ec61ea97b87dee8d94b1eea2fab2eab8d91"}, + {file = "symengine-0.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c42229ba2fbf70975ebebb0ca5114f4633ba4158246bd7ce14ba9cf4a19828f8"}, + {file = "symengine-0.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66def111281fed93da17afd0c787abca48f651078bd3f7987ee4bfb997e26a15"}, + {file = "symengine-0.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:76b3fceb99d6967d207fbaa93a9cd477949093a63bdb4652cb3c601de85d5229"}, + {file = "symengine-0.11.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7cde52f8c3e8eaa1bf7347a20e50445206cfe058815c61a2a7b86701b1690133"}, + {file = "symengine-0.11.0-pp39-pypy39_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:83da94f8487b54e391367d66cfdd268636811c4c660daa7b7e90ef9d7b659467"}, + {file = "symengine-0.11.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ae18741ee870ad897f4b1e45ad37587ab78c90a195083d87eb3db2ba66b12c"}, + {file = "symengine-0.11.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb8cc1314312af70b8a148611680f4cd91f8ef971c4fd4befa968cf7d6a2bd19"}, + {file = "symengine-0.11.0.tar.gz", hash = "sha256:0dd30d29b804ebb7251bddec29c38c3b1fc15ea6953a2c57ee758d5f6fcba458"}, +] + +[[package]] +name = "sympy" +version = "1.12.1" +description = "Computer algebra system (CAS) in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sympy-1.12.1-py3-none-any.whl", hash = "sha256:9b2cbc7f1a640289430e13d2a56f02f867a1da0190f2f99d8968c2f74da0e515"}, + {file = "sympy-1.12.1.tar.gz", hash = "sha256:2877b03f998cd8c08f07cd0de5b767119cd3ef40d09f41c30d722f6686b0fb88"}, +] + +[package.dependencies] +mpmath = ">=1.1.0,<1.4.0" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "toolz" +version = "0.12.1" +description = "List processing tools and functional utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "toolz-0.12.1-py3-none-any.whl", hash = "sha256:d22731364c07d72eea0a0ad45bafb2c2937ab6fd38a3507bf55eae8744aa7d85"}, + {file = "toolz-0.12.1.tar.gz", hash = "sha256:ecca342664893f177a13dac0e6b41cbd8ac25a358e5f215316d43e2100224f4d"}, +] + +[[package]] +name = "tqdm" +version = "4.66.4" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, + {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "types-pkg-resources" +version = "0.1.3" +description = "Typing stubs for pkg_resources" +optional = false +python-versions = "*" +files = [ + {file = "types-pkg_resources-0.1.3.tar.gz", hash = "sha256:834a9b8d3dbea343562fd99d5d3359a726f6bf9d3733bccd2b4f3096fbab9dae"}, + {file = "types_pkg_resources-0.1.3-py2.py3-none-any.whl", hash = "sha256:0cb9972cee992249f93fff1a491bf2dc3ce674e5a1926e27d4f0866f7d9b6d9c"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "websocket-client" +version = "1.8.0" +description = "WebSocket client for Python with low level API options" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, + {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, +] + +[package.extras] +docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "b8e37a5c25c1e411f4a77d6f1aba62cc831cd254d5a1d020c54617f5bb2de07f" diff --git a/pyproject.toml b/pyproject.toml index 01eaa902..539022eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,20 @@ -[tool.black] -extend-exclude = '^/examples/python/pytket-qujax_heisenberg_vqe.py' +[tool.poetry] +name = "pytket-docs" +version = "0.1.0" +description = "" +authors = ["CalMacCQ <93673602+CalMacCQ@users.noreply.github.com>"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" +pytket = { extras = ["zx"], version = "^1.28.0" } +pytket-qiskit = "^0.53.0" +pytket-cirq = "^0.36.0" +pytket-qujax = "0.19.0" +pytest = "^8.2.2" +pydata-sphinx-theme = "^0.15.3" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" From 3e6d7e1f87681d6e3a215822c08cc3dfa797b184 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:13:49 +0100 Subject: [PATCH 02/76] add additonal manual dependencies --- poetry.lock | 998 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 6 + 2 files changed, 995 insertions(+), 9 deletions(-) diff --git a/poetry.lock b/poetry.lock index 997a9844..7c6cb116 100644 --- a/poetry.lock +++ b/poetry.lock @@ -40,6 +40,35 @@ files = [ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] +[[package]] +name = "appnope" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = ">=3.6" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] + [[package]] name = "attrs" version = "23.2.0" @@ -109,6 +138,24 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +name = "bleach" +version = "6.1.0" +description = "An easy safelist-based HTML-sanitizing tool." +optional = false +python-versions = ">=3.8" +files = [ + {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, + {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, +] + +[package.dependencies] +six = ">=1.9.0" +webencodings = "*" + +[package.extras] +css = ["tinycss2 (>=1.1.0,<1.3)"] + [[package]] name = "cachetools" version = "5.3.3" @@ -347,6 +394,23 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "comm" +version = "0.2.2" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +files = [ + {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, + {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +test = ["pytest"] + [[package]] name = "contourpy" version = "1.2.1" @@ -617,6 +681,59 @@ toolz = ">=0.8.0" [package.extras] cython = ["cython"] +[[package]] +name = "debugpy" +version = "1.8.1" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, + {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, + {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, + {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, + {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, + {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, + {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, + {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, + {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, + {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, + {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, + {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, + {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, + {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, + {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, + {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, + {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, + {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, + {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, + {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, + {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, + {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, +] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + [[package]] name = "dill" version = "0.3.8" @@ -671,6 +788,34 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.5" +files = [ + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + +[[package]] +name = "fastjsonschema" +version = "2.19.1" +description = "Fastest Python implementation of JSON schema" +optional = false +python-versions = "*" +files = [ + {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, + {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + [[package]] name = "fonttools" version = "4.53.0" @@ -952,6 +1097,98 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "ipykernel" +version = "6.29.4" +description = "IPython Kernel for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.29.4-py3-none-any.whl", hash = "sha256:1181e653d95c6808039c509ef8e67c4126b3b3af7781496c7cbfb5ed938a27da"}, + {file = "ipykernel-6.29.4.tar.gz", hash = "sha256:3d44070060f9475ac2092b760123fadf105d2e2493c24848b6691a7c4f42af5c"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=24" +tornado = ">=6.1" +traitlets = ">=5.4.0" + +[package.extras] +cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "8.25.0" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.10" +files = [ + {file = "ipython-8.25.0-py3-none-any.whl", hash = "sha256:53eee7ad44df903a06655871cbab66d156a051fd86f3ec6750470ac9604ac1ab"}, + {file = "ipython-8.25.0.tar.gz", hash = "sha256:c6ed726a140b6e725b911528f80439c534fac915246af3efc39440a6b0f9d716"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} +prompt-toolkit = ">=3.0.41,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5.13.0" +typing-extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} + +[package.extras] +all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"] +kernel = ["ipykernel"] +matplotlib = ["matplotlib"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] + +[[package]] +name = "ipywidgets" +version = "8.1.3" +description = "Jupyter interactive widgets" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ipywidgets-8.1.3-py3-none-any.whl", hash = "sha256:efafd18f7a142248f7cb0ba890a68b96abd4d6e88ddbda483c9130d12667eaf2"}, + {file = "ipywidgets-8.1.3.tar.gz", hash = "sha256:f5f9eeaae082b1823ce9eac2575272952f40d748893972956dc09700a6392d9c"}, +] + +[package.dependencies] +comm = ">=0.1.3" +ipython = ">=6.1.0" +jupyterlab-widgets = ">=3.0.11,<3.1.0" +traitlets = ">=4.3.1" +widgetsnbextension = ">=4.0.11,<4.1.0" + +[package.extras] +test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] + [[package]] name = "jax" version = "0.4.28" @@ -966,14 +1203,14 @@ files = [ [package.dependencies] ml-dtypes = ">=0.2.0" numpy = [ - {version = ">=1.22", markers = "python_version < \"3.11\""}, - {version = ">=1.23.2", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.22", markers = "python_version < \"3.11\""}, ] opt-einsum = "*" scipy = [ - {version = ">=1.9", markers = "python_version < \"3.12\""}, {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, + {version = ">=1.9", markers = "python_version < \"3.12\""}, ] [package.extras] @@ -1021,13 +1258,32 @@ files = [ ml-dtypes = ">=0.2.0" numpy = ">=1.22" scipy = [ - {version = ">=1.9", markers = "python_version < \"3.12\""}, {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, + {version = ">=1.9", markers = "python_version < \"3.12\""}, ] [package.extras] cuda12-pip = ["nvidia-cublas-cu12 (>=12.1.3.1)", "nvidia-cuda-cupti-cu12 (>=12.1.105)", "nvidia-cuda-nvcc-cu12 (>=12.1.105)", "nvidia-cuda-runtime-cu12 (>=12.1.105)", "nvidia-cudnn-cu12 (>=8.9.2.26,<9.0)", "nvidia-cufft-cu12 (>=11.0.2.54)", "nvidia-cusolver-cu12 (>=11.4.5.107)", "nvidia-cusparse-cu12 (>=12.1.0.106)", "nvidia-nccl-cu12 (>=2.18.1)", "nvidia-nvjitlink-cu12 (>=12.1.105)"] +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + [[package]] name = "jinja2" version = "3.1.4" @@ -1045,6 +1301,150 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jsonschema" +version = "4.22.0" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.22.0-py3-none-any.whl", hash = "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802"}, + {file = "jsonschema-4.22.0.tar.gz", hash = "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "jupyter-client" +version = "8.6.2" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.6.2-py3-none-any.whl", hash = "sha256:50cbc5c66fd1b8f65ecb66bc490ab73217993632809b6e505687de18e9dea39f"}, + {file = "jupyter_client-8.6.2.tar.gz", hash = "sha256:2bda14d55ee5ba58552a8c53ae43d215ad9868853489213f37da060ced54d8df"}, +] + +[package.dependencies] +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = ">=5.3" + +[package.extras] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "jupyter-sphinx" +version = "0.5.3" +description = "Jupyter Sphinx Extensions" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_sphinx-0.5.3-py3-none-any.whl", hash = "sha256:a67b3208d4da5b3508dbb8260d3b359ae476c36c6c642747b78a2520e5be0b05"}, + {file = "jupyter_sphinx-0.5.3.tar.gz", hash = "sha256:2e23699a3a1cf5db31b10981da5aa32606ee730f6b73a844d1e76d800756af56"}, +] + +[package.dependencies] +ipykernel = ">=4.5.1" +ipython = "*" +ipywidgets = ">=7.0.0" +nbconvert = ">=5.5" +nbformat = "*" +sphinx = ">=7" + +[package.extras] +doc = ["matplotlib"] +test = ["bash-kernel", "pytest"] + +[[package]] +name = "jupyterlab-pygments" +version = "0.3.0" +description = "Pygments theme using JupyterLab CSS variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, + {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, +] + +[[package]] +name = "jupyterlab-widgets" +version = "3.0.11" +description = "Jupyter interactive widgets for JupyterLab" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jupyterlab_widgets-3.0.11-py3-none-any.whl", hash = "sha256:78287fd86d20744ace330a61625024cf5521e1c012a352ddc0a3cdc2348becd0"}, + {file = "jupyterlab_widgets-3.0.11.tar.gz", hash = "sha256:dd5ac679593c969af29c9bed054c24f26842baa51352114736756bc035deee27"}, +] + +[[package]] +name = "kahypar" +version = "1.3.5" +description = "Python Inferface for the Karlsruhe Hypergraph Partitioning Framework (KaHyPar)" +optional = false +python-versions = "*" +files = [ + {file = "kahypar-1.3.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ee3520d83c7e808134041e13218f0507882203063e18cc72d8d82531561bc56"}, + {file = "kahypar-1.3.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2218296b4197e1ee9b7be386e6d681cf6bf8f0b2d4d36971bb7c8c5561108b46"}, + {file = "kahypar-1.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:da02aca5d72c91cf270af15c7990922170c400996cfab77d13a1003cf0638de8"}, + {file = "kahypar-1.3.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc0c30462d63be436d3c4275dcd8a12224b6e121411fe4eecb5306c1160185"}, + {file = "kahypar-1.3.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a12e4b1471b7ca66626d79ad9369efd33cfe1b45d7354854066a926737364f1"}, + {file = "kahypar-1.3.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6753893082338c93df8361a61b74b47c51805ec08d7ba816877f00aea7d046f0"}, + {file = "kahypar-1.3.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d9e39aca3e90e20b83ba7926089377cf4cbebe085ccf380b398a322a8ff20250"}, + {file = "kahypar-1.3.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb463e5aaa676bea3eec661fa6ebe371513ef1050018bedb4bc1f1e08708"}, + {file = "kahypar-1.3.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:90c1719ca7b5599bb6f2dc933806f9cc3eb05a5ec61d8c47b39a41a235ac1799"}, + {file = "kahypar-1.3.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aea9d7633c1a5e94a62d5c9f6df1567164eff053fde8dc25432008dec222ae4c"}, + {file = "kahypar-1.3.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cef4997a480d4e25e155755ed1f56db217b0733b884541af58dd15daa9eefd78"}, + {file = "kahypar-1.3.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24ac274107e71b4da883fcd8bff4d734a8d663b9e7961b697573bed12ad50d33"}, + {file = "kahypar-1.3.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a361871d8427cb2512eab647ff40066ea2f848920af5d4408bda8aaf3edab7a"}, +] + [[package]] name = "kiwisolver" version = "1.4.5" @@ -1326,6 +1726,31 @@ python-dateutil = ">=2.7" [package.extras] dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, + {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mistune" +version = "3.0.2" +description = "A sane and fast Markdown parser with useful plugins and renderers" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mistune-3.0.2-py3-none-any.whl", hash = "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205"}, + {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, +] + [[package]] name = "ml-dtypes" version = "0.4.0" @@ -1354,9 +1779,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.21.2", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, - {version = ">=1.23.3", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.23.3", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.21.2", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, ] [package.extras] @@ -1379,6 +1804,97 @@ docs = ["sphinx"] gmpy = ["gmpy2 (>=2.1.0a4)"] tests = ["pytest (>=4.6)"] +[[package]] +name = "nbclient" +version = "0.10.0" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, + {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, +] + +[package.dependencies] +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +nbformat = ">=5.1" +traitlets = ">=5.4" + +[package.extras] +dev = ["pre-commit"] +docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] + +[[package]] +name = "nbconvert" +version = "7.16.4" +description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbconvert-7.16.4-py3-none-any.whl", hash = "sha256:05873c620fe520b6322bf8a5ad562692343fe3452abda5765c7a34b7d1aa3eb3"}, + {file = "nbconvert-7.16.4.tar.gz", hash = "sha256:86ca91ba266b0a448dc96fa6c5b9d98affabde2867b363258703536807f9f7f4"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +bleach = "!=5.0.0" +defusedxml = "*" +jinja2 = ">=3.0" +jupyter-core = ">=4.7" +jupyterlab-pygments = "*" +markupsafe = ">=2.0" +mistune = ">=2.0.3,<4" +nbclient = ">=0.5.0" +nbformat = ">=5.7" +packaging = "*" +pandocfilters = ">=1.4.1" +pygments = ">=2.4.1" +tinycss2 = "*" +traitlets = ">=5.1" + +[package.extras] +all = ["flaky", "ipykernel", "ipython", "ipywidgets (>=7.5)", "myst-parser", "nbsphinx (>=0.2.12)", "playwright", "pydata-sphinx-theme", "pyqtwebengine (>=5.15)", "pytest (>=7)", "sphinx (==5.0.2)", "sphinxcontrib-spelling", "tornado (>=6.1)"] +docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"] +qtpdf = ["pyqtwebengine (>=5.15)"] +qtpng = ["pyqtwebengine (>=5.15)"] +serve = ["tornado (>=6.1)"] +test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"] +webpdf = ["playwright"] + +[[package]] +name = "nbformat" +version = "5.10.4" +description = "The Jupyter Notebook format" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, + {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, +] + +[package.dependencies] +fastjsonschema = ">=2.15" +jsonschema = ">=2.6" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +traitlets = ">=5.1" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["pep440", "pre-commit", "pytest", "testpath"] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + [[package]] name = "networkx" version = "3.3" @@ -1545,9 +2061,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.22.4", markers = "python_version < \"3.11\""}, - {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -1578,6 +2094,32 @@ sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-d test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] xml = ["lxml (>=4.9.2)"] +[[package]] +name = "pandocfilters" +version = "1.5.1" +description = "Utilities for writing pandoc filters in python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, + {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, +] + +[[package]] +name = "parso" +version = "0.8.4" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, +] + +[package.extras] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] + [[package]] name = "pbr" version = "6.0.0" @@ -1589,6 +2131,20 @@ files = [ {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, ] +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + [[package]] name = "pillow" version = "10.3.0" @@ -1675,6 +2231,22 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa typing = ["typing-extensions"] xmp = ["defusedxml"] +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + [[package]] name = "pluggy" version = "1.5.0" @@ -1690,6 +2262,20 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "prompt-toolkit" +version = "3.0.46" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.46-py3-none-any.whl", hash = "sha256:45abe60a8300f3c618b23c16c4bb98c6fc80af8ce8b17c7ae92db48db3ee63c1"}, + {file = "prompt_toolkit-3.0.46.tar.gz", hash = "sha256:869c50d682152336e23c4db7f74667639b5047494202ffe7670817053fd57795"}, +] + +[package.dependencies] +wcwidth = "*" + [[package]] name = "proto-plus" version = "1.20.4" @@ -1755,6 +2341,31 @@ files = [ [package.extras] test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] + [[package]] name = "pyasn1" version = "0.6.0" @@ -2130,6 +2741,129 @@ files = [ {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "pyzmq" +version = "26.0.3" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:44dd6fc3034f1eaa72ece33588867df9e006a7303725a12d64c3dff92330f625"}, + {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:acb704195a71ac5ea5ecf2811c9ee19ecdc62b91878528302dd0be1b9451cc90"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbb9c997932473a27afa93954bb77a9f9b786b4ccf718d903f35da3232317de"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bcb34f869d431799c3ee7d516554797f7760cb2198ecaa89c3f176f72d062be"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ece17ec5f20d7d9b442e5174ae9f020365d01ba7c112205a4d59cf19dc38ee"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ba6e5e6588e49139a0979d03a7deb9c734bde647b9a8808f26acf9c547cab1bf"}, + {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3bf8b000a4e2967e6dfdd8656cd0757d18c7e5ce3d16339e550bd462f4857e59"}, + {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2136f64fbb86451dbbf70223635a468272dd20075f988a102bf8a3f194a411dc"}, + {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e8918973fbd34e7814f59143c5f600ecd38b8038161239fd1a3d33d5817a38b8"}, + {file = "pyzmq-26.0.3-cp310-cp310-win32.whl", hash = "sha256:0aaf982e68a7ac284377d051c742610220fd06d330dcd4c4dbb4cdd77c22a537"}, + {file = "pyzmq-26.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f1a9b7d00fdf60b4039f4455afd031fe85ee8305b019334b72dcf73c567edc47"}, + {file = "pyzmq-26.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:80b12f25d805a919d53efc0a5ad7c0c0326f13b4eae981a5d7b7cc343318ebb7"}, + {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:a72a84570f84c374b4c287183debc776dc319d3e8ce6b6a0041ce2e400de3f32"}, + {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ca684ee649b55fd8f378127ac8462fb6c85f251c2fb027eb3c887e8ee347bcd"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e222562dc0f38571c8b1ffdae9d7adb866363134299264a1958d077800b193b7"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f17cde1db0754c35a91ac00b22b25c11da6eec5746431d6e5092f0cd31a3fea9"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7c0c0b3244bb2275abe255d4a30c050d541c6cb18b870975553f1fb6f37527"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac97a21de3712afe6a6c071abfad40a6224fd14fa6ff0ff8d0c6e6cd4e2f807a"}, + {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:88b88282e55fa39dd556d7fc04160bcf39dea015f78e0cecec8ff4f06c1fc2b5"}, + {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:72b67f966b57dbd18dcc7efbc1c7fc9f5f983e572db1877081f075004614fcdd"}, + {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4b6cecbbf3b7380f3b61de3a7b93cb721125dc125c854c14ddc91225ba52f83"}, + {file = "pyzmq-26.0.3-cp311-cp311-win32.whl", hash = "sha256:eed56b6a39216d31ff8cd2f1d048b5bf1700e4b32a01b14379c3b6dde9ce3aa3"}, + {file = "pyzmq-26.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:3191d312c73e3cfd0f0afdf51df8405aafeb0bad71e7ed8f68b24b63c4f36500"}, + {file = "pyzmq-26.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:b6907da3017ef55139cf0e417c5123a84c7332520e73a6902ff1f79046cd3b94"}, + {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:068ca17214038ae986d68f4a7021f97e187ed278ab6dccb79f837d765a54d753"}, + {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7821d44fe07335bea256b9f1f41474a642ca55fa671dfd9f00af8d68a920c2d4"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb438a26d87c123bb318e5f2b3d86a36060b01f22fbdffd8cf247d52f7c9a2b"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69ea9d6d9baa25a4dc9cef5e2b77b8537827b122214f210dd925132e34ae9b12"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7daa3e1369355766dea11f1d8ef829905c3b9da886ea3152788dc25ee6079e02"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6ca7a9a06b52d0e38ccf6bca1aeff7be178917893f3883f37b75589d42c4ac20"}, + {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1b7d0e124948daa4d9686d421ef5087c0516bc6179fdcf8828b8444f8e461a77"}, + {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e746524418b70f38550f2190eeee834db8850088c834d4c8406fbb9bc1ae10b2"}, + {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6b3146f9ae6af82c47a5282ac8803523d381b3b21caeae0327ed2f7ecb718798"}, + {file = "pyzmq-26.0.3-cp312-cp312-win32.whl", hash = "sha256:2b291d1230845871c00c8462c50565a9cd6026fe1228e77ca934470bb7d70ea0"}, + {file = "pyzmq-26.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:926838a535c2c1ea21c903f909a9a54e675c2126728c21381a94ddf37c3cbddf"}, + {file = "pyzmq-26.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:5bf6c237f8c681dfb91b17f8435b2735951f0d1fad10cc5dfd96db110243370b"}, + {file = "pyzmq-26.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c0991f5a96a8e620f7691e61178cd8f457b49e17b7d9cfa2067e2a0a89fc1d5"}, + {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dbf012d8fcb9f2cf0643b65df3b355fdd74fc0035d70bb5c845e9e30a3a4654b"}, + {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:01fbfbeb8249a68d257f601deb50c70c929dc2dfe683b754659569e502fbd3aa"}, + {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c8eb19abe87029c18f226d42b8a2c9efdd139d08f8bf6e085dd9075446db450"}, + {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5344b896e79800af86ad643408ca9aa303a017f6ebff8cee5a3163c1e9aec987"}, + {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:204e0f176fd1d067671157d049466869b3ae1fc51e354708b0dc41cf94e23a3a"}, + {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a42db008d58530efa3b881eeee4991146de0b790e095f7ae43ba5cc612decbc5"}, + {file = "pyzmq-26.0.3-cp37-cp37m-win32.whl", hash = "sha256:8d7a498671ca87e32b54cb47c82a92b40130a26c5197d392720a1bce1b3c77cf"}, + {file = "pyzmq-26.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:3b4032a96410bdc760061b14ed6a33613ffb7f702181ba999df5d16fb96ba16a"}, + {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2cc4e280098c1b192c42a849de8de2c8e0f3a84086a76ec5b07bfee29bda7d18"}, + {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bde86a2ed3ce587fa2b207424ce15b9a83a9fa14422dcc1c5356a13aed3df9d"}, + {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:34106f68e20e6ff253c9f596ea50397dbd8699828d55e8fa18bd4323d8d966e6"}, + {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ebbbd0e728af5db9b04e56389e2299a57ea8b9dd15c9759153ee2455b32be6ad"}, + {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b1d1c631e5940cac5a0b22c5379c86e8df6a4ec277c7a856b714021ab6cfad"}, + {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e891ce81edd463b3b4c3b885c5603c00141151dd9c6936d98a680c8c72fe5c67"}, + {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9b273ecfbc590a1b98f014ae41e5cf723932f3b53ba9367cfb676f838038b32c"}, + {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b32bff85fb02a75ea0b68f21e2412255b5731f3f389ed9aecc13a6752f58ac97"}, + {file = "pyzmq-26.0.3-cp38-cp38-win32.whl", hash = "sha256:f6c21c00478a7bea93caaaef9e7629145d4153b15a8653e8bb4609d4bc70dbfc"}, + {file = "pyzmq-26.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:3401613148d93ef0fd9aabdbddb212de3db7a4475367f49f590c837355343972"}, + {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:2ed8357f4c6e0daa4f3baf31832df8a33334e0fe5b020a61bc8b345a3db7a606"}, + {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1c8f2a2ca45292084c75bb6d3a25545cff0ed931ed228d3a1810ae3758f975f"}, + {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b63731993cdddcc8e087c64e9cf003f909262b359110070183d7f3025d1c56b5"}, + {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b3cd31f859b662ac5d7f4226ec7d8bd60384fa037fc02aee6ff0b53ba29a3ba8"}, + {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115f8359402fa527cf47708d6f8a0f8234f0e9ca0cab7c18c9c189c194dbf620"}, + {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:715bdf952b9533ba13dfcf1f431a8f49e63cecc31d91d007bc1deb914f47d0e4"}, + {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e1258c639e00bf5e8a522fec6c3eaa3e30cf1c23a2f21a586be7e04d50c9acab"}, + {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:15c59e780be8f30a60816a9adab900c12a58d79c1ac742b4a8df044ab2a6d920"}, + {file = "pyzmq-26.0.3-cp39-cp39-win32.whl", hash = "sha256:d0cdde3c78d8ab5b46595054e5def32a755fc028685add5ddc7403e9f6de9879"}, + {file = "pyzmq-26.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ce828058d482ef860746bf532822842e0ff484e27f540ef5c813d516dd8896d2"}, + {file = "pyzmq-26.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:788f15721c64109cf720791714dc14afd0f449d63f3a5487724f024345067381"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c18645ef6294d99b256806e34653e86236eb266278c8ec8112622b61db255de"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e6bc96ebe49604df3ec2c6389cc3876cabe475e6bfc84ced1bf4e630662cb35"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:971e8990c5cc4ddcff26e149398fc7b0f6a042306e82500f5e8db3b10ce69f84"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8416c23161abd94cc7da80c734ad7c9f5dbebdadfdaa77dad78244457448223"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:082a2988364b60bb5de809373098361cf1dbb239623e39e46cb18bc035ed9c0c"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d57dfbf9737763b3a60d26e6800e02e04284926329aee8fb01049635e957fe81"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:77a85dca4c2430ac04dc2a2185c2deb3858a34fe7f403d0a946fa56970cf60a1"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c82a6d952a1d555bf4be42b6532927d2a5686dd3c3e280e5f63225ab47ac1f5"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4496b1282c70c442809fc1b151977c3d967bfb33e4e17cedbf226d97de18f709"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e4946d6bdb7ba972dfda282f9127e5756d4f299028b1566d1245fa0d438847e6"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:03c0ae165e700364b266876d712acb1ac02693acd920afa67da2ebb91a0b3c09"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3e3070e680f79887d60feeda051a58d0ac36622e1759f305a41059eff62c6da7"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6ca08b840fe95d1c2bd9ab92dac5685f949fc6f9ae820ec16193e5ddf603c3b2"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e76654e9dbfb835b3518f9938e565c7806976c07b37c33526b574cc1a1050480"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:871587bdadd1075b112e697173e946a07d722459d20716ceb3d1bd6c64bd08ce"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d0a2d1bd63a4ad79483049b26514e70fa618ce6115220da9efdff63688808b17"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0270b49b6847f0d106d64b5086e9ad5dc8a902413b5dbbb15d12b60f9c1747a4"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:703c60b9910488d3d0954ca585c34f541e506a091a41930e663a098d3b794c67"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74423631b6be371edfbf7eabb02ab995c2563fee60a80a30829176842e71722a"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4adfbb5451196842a88fda3612e2c0414134874bffb1c2ce83ab4242ec9e027d"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3516119f4f9b8671083a70b6afaa0a070f5683e431ab3dc26e9215620d7ca1ad"}, + {file = "pyzmq-26.0.3.tar.gz", hash = "sha256:dba7d9f2e047dfa2bca3b01f4f84aa5246725203d6284e3790f2ca15fba6b40a"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + [[package]] name = "qiskit" version = "1.1.0" @@ -2340,6 +3074,21 @@ files = [ [package.dependencies] setuptools = "*" +[[package]] +name = "referencing" +version = "0.35.1" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + [[package]] name = "requests" version = "2.32.3" @@ -2377,6 +3126,114 @@ cryptography = ">=1.3" pyspnego = ">=0.4.0" requests = ">=2.0.0" +[[package]] +name = "rpds-py" +version = "0.18.1" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53"}, + {file = "rpds_py-0.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1"}, + {file = "rpds_py-0.18.1-cp310-none-win32.whl", hash = "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333"}, + {file = "rpds_py-0.18.1-cp310-none-win_amd64.whl", hash = "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88"}, + {file = "rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb"}, + {file = "rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a"}, + {file = "rpds_py-0.18.1-cp312-none-win32.whl", hash = "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6"}, + {file = "rpds_py-0.18.1-cp312-none-win_amd64.whl", hash = "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc"}, + {file = "rpds_py-0.18.1-cp38-none-win32.whl", hash = "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9"}, + {file = "rpds_py-0.18.1-cp38-none-win_amd64.whl", hash = "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26"}, + {file = "rpds_py-0.18.1-cp39-none-win32.whl", hash = "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360"}, + {file = "rpds_py-0.18.1-cp39-none-win_amd64.whl", hash = "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e"}, + {file = "rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f"}, +] + [[package]] name = "rsa" version = "4.9" @@ -2605,6 +3462,24 @@ docs = ["sphinxcontrib-websupport"] lint = ["flake8 (>=3.5.0)", "importlib_metadata", "mypy (==1.9.0)", "pytest (>=6.0)", "ruff (==0.3.7)", "sphinx-lint", "tomli", "types-docutils", "types-requests"] test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=6.0)", "setuptools (>=67.0)"] +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +description = "Add a copy button to each of your code cells." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, + {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, +] + +[package.dependencies] +sphinx = ">=1.8" + +[package.extras] +code-style = ["pre-commit (==2.12.1)"] +rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] + [[package]] name = "sphinxcontrib-applehelp" version = "1.0.8" @@ -2738,6 +3613,25 @@ files = [ {file = "sspilib-0.1.0.tar.gz", hash = "sha256:58b5291553cf6220549c0f855e0e6973f4977375d8236ce47bb581efb3e9b1cf"}, ] +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + [[package]] name = "stevedore" version = "5.2.0" @@ -2810,6 +3704,24 @@ files = [ [package.dependencies] mpmath = ">=1.1.0,<1.4.0" +[[package]] +name = "tinycss2" +version = "1.3.0" +description = "A tiny CSS parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tinycss2-1.3.0-py3-none-any.whl", hash = "sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7"}, + {file = "tinycss2-1.3.0.tar.gz", hash = "sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d"}, +] + +[package.dependencies] +webencodings = ">=0.4" + +[package.extras] +doc = ["sphinx", "sphinx_rtd_theme"] +test = ["pytest", "ruff"] + [[package]] name = "tomli" version = "2.0.1" @@ -2832,6 +3744,26 @@ files = [ {file = "toolz-0.12.1.tar.gz", hash = "sha256:ecca342664893f177a13dac0e6b41cbd8ac25a358e5f215316d43e2100224f4d"}, ] +[[package]] +name = "tornado" +version = "6.4.1" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">=3.8" +files = [ + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, + {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, + {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, + {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, +] + [[package]] name = "tqdm" version = "4.66.4" @@ -2852,6 +3784,21 @@ notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] +[[package]] +name = "traitlets" +version = "5.14.3" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, + {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] + [[package]] name = "types-pkg-resources" version = "0.1.3" @@ -2902,6 +3849,28 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + [[package]] name = "websocket-client" version = "1.8.0" @@ -2999,7 +3968,18 @@ files = [ {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, ] +[[package]] +name = "widgetsnbextension" +version = "4.0.11" +description = "Jupyter interactive widgets for Jupyter Notebook" +optional = false +python-versions = ">=3.7" +files = [ + {file = "widgetsnbextension-4.0.11-py3-none-any.whl", hash = "sha256:55d4d6949d100e0d08b94948a42efc3ed6dfdc0e9468b2c4b128c9a2ce3a7a36"}, + {file = "widgetsnbextension-4.0.11.tar.gz", hash = "sha256:8b22a8f1910bfd188e596fe7fc05dcbd87e810c8a4ba010bdb3da86637398474"}, +] + [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "b8e37a5c25c1e411f4a77d6f1aba62cc831cd254d5a1d020c54617f5bb2de07f" +content-hash = "6d3befebd955b1f27fc8c9580d04a6eb2265fe7d62e46c36f0618ab54d5e4e59" diff --git a/pyproject.toml b/pyproject.toml index 539022eb..61500fa9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,12 @@ pytket-cirq = "^0.36.0" pytket-qujax = "0.19.0" pytest = "^8.2.2" pydata-sphinx-theme = "^0.15.3" +qiskit-algorithms = "^0.3.0" +ipykernel = "^6.29.4" +kahypar = "^1.3.5" +sphinx-copybutton = "^0.5.2" +jupyter-sphinx = "^0.5.3" +quimb = "^1.8.1" [build-system] From 64e9f8e21a23153ab4c0c19676aa56c42334ca3a Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:14:36 +0100 Subject: [PATCH 03/76] delete legacy example --- legacy_examples/QSE_pytket.ipynb | 944 ------------------------------- legacy_examples/README.md | 3 - 2 files changed, 947 deletions(-) delete mode 100644 legacy_examples/QSE_pytket.ipynb delete mode 100644 legacy_examples/README.md diff --git a/legacy_examples/QSE_pytket.ipynb b/legacy_examples/QSE_pytket.ipynb deleted file mode 100644 index a57569c2..00000000 --- a/legacy_examples/QSE_pytket.ipynb +++ /dev/null @@ -1,944 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# LEGACY EXAMPLE: THIS NOTEBOOK IS NO LONGER MAINTAINED AND IS NOT EXECUTABLE WITH UP TO DATE VERSIONS OF THE PACKAGES IT DEPENDS ON\n", - "\n", - "# _*Quantum chemistry with Qiskit `Terra`, `Aqua` and CQC's `t|ket〉` compiler*_ \n", - "\n", - "In this tutorial, we discuss how to use IBM's Qiskit `Terra` and `Aqua` packages, and the `t|ket〉` compiler by Cambridge Quantum Computing (CQC), to calculate the excited state energies of simple molecules using the quantum subspace expansion (QSE) technique. By the end of this tutorial, you will\n", - "\n", - "* Understand why optimizing circuits for quantum chemistry is necessary\n", - "* Learn about the quantum subspace expansion technique for computing excited state energies\n", - "* See how Qiskit `Terra` enables 3rd-party passes in its transpiler architecture\n", - "* Use `pytket` to perform native circuit optimization and circuit routing\n", - "\n", - "NOTE: Throughout this tutorial, we assume the reader has some familiarity with quantum chemistry methods, though an extensive knowledge is not necessary.\n", - "\n", - "**Code setup**\n", - "\n", - "This tutorial makes use of Qiskit `Terra` and `Aqua`, as well as `pytket` (the Python interface to `t|ket〉`). To install `Terra` and `Aqua`, follow instructions available [here](https://qiskit.org/). To install `pytket`, follow the instructions at [this Github repository](https://github.com/CQCL/pytket)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Quantum chemistry in the NISQ era, and the need for circuit optimization techniques\n", - "\n", - "One of the expected applications of noisy, intemediate-scale quantum (NISQ) devices is simulating quantum chemistry. A simple quantum chemistry question is \"Given how the electrons and nuclei of a particular molecule are arranged, what is the _energy_ of that configuration?\". By examining how the energy of the configuration changes as the nuclei and electrons are moved relative to one another, we can map out an _energy surface_. The configuration that minimizes the energy is a stable, equilibrium point for the molecule. Knowing this configuration (and its associated energy), we can deduce a variety of molecular properties, such as reaction rates.\n", - "\n", - "Most techniques for computing molecular energies on near term quantum devices rely on the [variational quantum eigensolver (VQE) algorithm](https://doi.org/10.1038/ncomms5213). This algorithm uses a parameterized _ansatz_ $|{\\psi}(\\boldsymbol{\\theta})\\rangle$ to describe the ground state energy of the Hamiltonian $H$ for a given configuration. By examining how the expected energy $\\langle \\psi(\\boldsymbol{\\theta})|H| \\psi(\\boldsymbol{\\theta})\\rangle/\\langle \\psi(\\boldsymbol{\\theta})|\\psi(\\boldsymbol{\\theta})\\rangle$ changes as $\\boldsymbol{\\theta}$ is varied, we can optimize the parameters to find an estimate for the ground state and its corresponding energy.\n", - "\n", - "Running the VQE algorithm on actual hardware has two complications:\n", - "\n", - "* Efficiently preparing the trial state $|\\psi(\\boldsymbol{\\theta})\\rangle$.\n", - "\n", - "* Efficiently measuring the terms in the Hamiltonian $H$ that describe the configuration.\n", - "\n", - "In this tutorial, we'll focus on the problem of efficiently preparing the trial state. We do so for two reasons:\n", - "\n", - "* An accurate estimate of the ground state is all that's necessary to do the quantum subspace expansion technique to estimate excited state energies\n", - "\n", - "* To demonstrate the capabilies of Qiskit `Terra` and CQC's `t|ket〉` compiler to reduce the circuit _resources_ (number of gates, depth, etc.) for the state preparation. In principle, these capabilities can be deployed for other problems.\n", - "\n", - "Reducing the resources is crucial in the NISQ era, because the noise present in NISQ devices dominates more for larger circuits (with more operations) and reduces the accuracy of the results. Therefore techniques which reduce circuit requirements can significantly improve the results on real hardware. Note that there are techniques, such as [qubit tapering](https://arxiv.org/abs/1701.08213), or ['gate-efficient' circuits](https://arxiv.org/abs/1809.05057), which are useful for constructing a lower-resource circuit that could be run on _ideal_ hardware. In practice, even these \"resource-efficient\" circuits can be further optimized, especially when the imperfections and constraints of a real piece of hardware are taken into account.\n", - "\n", - "At a high level, our approach utilizes Qiskit `Aqua` to generate a parameterized ansatz based on the [_Unitary Coupled Cluster, Single-Double_ (UCCSD) ansatz](https://en.wikipedia.org/wiki/Coupled_cluster). We then use Qiskit `Terra` and the `t|ket〉` compiler (as implemented in `pytket`) to optimize the circuit for that ansatz. We will show that the optimized ansatz requires substantially fewer circuit resources than the naive UCCSD ansatz. An application of our approach, we show how to use the quantum subspace expansion (QSE) technique to compute excited state energies for gaseous hydrogen (H$_{2}$) and lithium hydride (LiH)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Computing excited state energies using the Quantum Subspace Expansion (QSE) technique\n", - "\n", - "The VQE algorithm is often used to compute the _ground state_ energy of a given molecular configuration. However, knowing the energies of _excited states_ of the system is also useful. The energies of electronic excited states prove, in general, challenging to compute. Excited states are usually more _entangled_ than ground states and so require more computational resources to compute. While there exists techniques for efficiently representing certain classes of entangled states, in general, the complexity of the simulation will be non-trivial. This is a problem as it makes classical computation of the energy, to an appropriate accuracy, of many molecules impossible. \n", - "\n", - "This notebook demonstrates calculation of excited states of molecules using the [quantum subspace expanansion (QSE) technique](https://doi.org/10.1103/PhysRevA.95.042308). The QSE technique uses an accurate estimate of the ground state energy of a given molecular configuration to estimate the energies of excited states. Consider a fixed molecule (represented by a given Hamiltonian $H$), and suppose $\\left|\\Psi_{0}\\right\\rangle$ is the output of the VQE algorithm for estimating the ground state energy of $H$.\n", - "\n", - "The QSE technique constructs a subspace of state vectors $\\left|\\Psi_j^k\\right\\rangle$ formed by one-electron excitations of the ground state wavefunction:\n", - "\n", - "\\begin{equation}\n", - "\\left|\\Psi_{j}^{k}\\right\\rangle = c_k^{\\dagger}c_{j}\\left|\\Psi_0\\right\\rangle.\n", - "\\end{equation}\n", - "\n", - "where $c_k^{\\dagger}, c_{j}$ are the fermionic creation and annihilation operators over spin orbitals $k$ and $j$, respectively. That is, these vectors are formed by reducing the occupation of spin orbital $j$ by one, and increasing the occupation of spin orbital $k$ by one. The vectors are not in general orthogonal to $\\Psi_{0}$ hence we will need to calculate an overlap matrix.\n", - "\n", - "\n", - "Within this subspace, we solve a generalized eigenvalue problem. Consider the operator $H'$ with matrix elements given by\n", - "$$(H')_{jk}^{lm} = \\langle\\Psi_j^l \\left| H \\right| \\Psi_k^m\\rangle,$$\n", - "\n", - "and define an overlap matrix $S$ whose matrix elements are given by\n", - "\n", - "$$S_{jk}^{lm} = \\langle \\Psi_j^l \\left|\\Psi_k^m\\right\\rangle.$$\n", - "\n", - "The generalized eigenvalue equation to be solved is \n", - "\n", - "\\begin{equation}\n", - "H'C=SCE,\n", - "\\end{equation}\n", - "\n", - "where $C$ is the matrix of eigenvectors, and $E$ is the vector of eigenvalues. Crucially, _the energy eigenvalues $E$ provide an estimate of the excited state energies of $H$_ as well as a refined value of the ground state energy.\n", - "\n", - "Notice that the solution to the generalized eigenvalue equation can be done on a classical computer, provided $H'$ and $S$ have been calculated. The matrix elements of both of these matrices can be constructed using a quantum computer, in the following way. First, re-write the matrix elements in terms of $\\left | \\Psi_{0}\\right \\rangle$:\n", - "\n", - "\\begin{align}\n", - "(H')_{jk}^{lm} =& \\langle\\Psi_j^l \\left| H \\right| \\Psi_k^m\\rangle = \\langle \\Psi_{0} | c_{j}^\\dagger c_{l} Hc_{m}^{\\dagger}c_{k}|\\Psi_{0}\\rangle\\\\\n", - "S_{jk}^{lm} &= \\langle \\Psi_j^l \\left|\\Psi_k^m\\right\\rangle = \\langle \\Psi_{0} | c_{j}^\\dagger c_{l} c_{m}^{\\dagger}c_{k}|\\Psi_{0}\\rangle.\n", - "\\end{align}\n", - "\n", - "The matrix elements can be calculated using a quantum computer or simluator. How? By transforming the operators \n", - "$c_{j}^\\dagger c_{l} c_{k}^{\\dagger}c_{m}$ and $c_{l}^\\dagger c_{j} H c_{m}^{\\dagger}c_{k}$ to a set of Pauli quantum gates according to an appropriate scheme such as Jordan-Wigner or Bravyi-Kitaev, apply this gate set to the ground state wavefunction (constructed with the coefficients obtained from the VQE calculation) and perform a measurement to obtain the expected value. By measuring these expectation values, we obtain estimates of $S_{jk}^{lm}$ and $(H')_{jk}^{lm}$, respectively.\n", - "\n", - "Therefore, to use the QSE technique, we need to use the quantum computer to calculate 2 quantities:\n", - "\n", - "* An estimate of the ground state $\\left |\\Psi_{0}\\right\\rangle$ (by running the VQE algorithm)\n", - "* The matrix elements of $H'$ and $S$.\n", - "\n", - "For the first quantity, we want to have an efficient representation of the trial state $|\\psi(\\boldsymbol{\\theta})\\rangle$. And for the second, we need to be able to take the estimate of the ground state and efficiently compute matrix elements. In both of these cases, being able to optimize the circuit for preparing would be useful. Thankfully, Qiskit `Terra`, in conjunction with `t|ket〉`, provides us tools for doing so. We discuss the problem of _circuit compilation/optimization_ in the next section." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Circuit compilation with Qiskit `Terra` and `t|ket〉`\n", - "\n", - "In the remainder of this tutorial, we show how to use Qiskit `Terra` and `t|ket〉` to optimize the circuits for preparing VQE trial states, and simulate the QSE technique." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Application I: Reducing circuit resources for trial state preparation\n", - "\n", - "In this tutorial, we'll focus on two simple molecules: hydrogen (H$_{2}$) and lithium hydride (LiH). NOTE: the code is much slower for LiH, which is why here, we'll demonstrate H$_{2}$.\n", - "\n", - "\n", - "As a first application of the pipelines provided by `Terra`, `Aqua`, and `t|ket〉`, we first show how to configure a VQE experiment using `Aqua`, and then use `Terra` and `t|ket〉` to compile the trial state preparation circuit.\n", - "\n", - "Let's start by importing the necessary packages." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# numpy, for random number generation\n", - "import numpy as np\n", - "\n", - "# Qiskit, for transpiler-related functions, the IBMQ provider, and the Aer simulator\n", - "from qiskit import IBMQ, Aer, QuantumRegister\n", - "from qiskit.transpiler import transpile, transpile_dag, PassManager\n", - "from qiskit.converters import circuit_to_dag, dag_to_circuit\n", - "\n", - "# pytket, for optimization\n", - "import pytket\n", - "from pytket.qiskit import TketPass\n", - "\n", - "# Qiskit Aqua, for chemistry\n", - "from qiskit.chemistry.drivers import PySCFDriver, UnitsType\n", - "from qiskit.chemistry import FermionicOperator\n", - "from qiskit.chemistry.components.initial_states import HartreeFock\n", - "from qiskit.chemistry.components.variational_forms import UCCSD" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 0: Enable IBMQ account" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2018-09-29T00:04:16.313210Z", - "start_time": "2018-09-29T00:04:14.460647Z" - } - }, - "outputs": [], - "source": [ - "provider = IBMQ.load_account()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 1: setting up the molecule\n", - "\n", - "We choose the basis set spanning the molecular wavefunction, the molecular geometry, the chemical identity of each atom, the charge and spin quantum number.\n", - "\n", - "To calculate results for LiH (slower), just comment out the H$_{2}$ string and replace it with the LiH one.\n", - "\n", - "NOTE: Here, we focus only on one particular value for the `bond_length`. If you wanted to replicate the final plot for the excited state energies of LiH as a function of bond length, you'd need to sweep `bond_length` over several values." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2018-09-29T00:04:20.069592Z", - "start_time": "2018-09-29T00:04:20.065489Z" - } - }, - "outputs": [], - "source": [ - "# Choose a particular bond length\n", - "# NOTE: Units are in Angstroms\n", - "\n", - "bond_length = 0.7\n", - "\n", - "# Set up molecule\n", - "\n", - "# base_molecule_str = 'Li .0 .0 .0; H .0 .0 {}'\n", - "base_molecule_str = 'H .0 .0 .0; H .0 .0 {}'\n", - "\n", - "# Specify other molecular properties\n", - "charge = 0\n", - "spin = 0\n", - "basis = 'sto3g'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Having set up the molecule, we now execute our classical chemistry driver to obtain the integrals that define the terms in the molecule's Hamiltonian. In this case, we choose `PYSCF` as our driver, so make sure you have that installed.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Molecular repulsion energy: 0.7559674441714287\n" - ] - } - ], - "source": [ - "# Using driver to get fermionic Hamiltonian\n", - "# PySCF example\n", - "\n", - "driver = PySCFDriver(atom=base_molecule_str.format(bond_length),\n", - " unit=UnitsType.ANGSTROM,\n", - " charge=charge,\n", - " spin=spin,\n", - " basis=basis)\n", - "\n", - "molecule = driver.run()\n", - "\n", - "print(\"Molecular repulsion energy: \", molecule.nuclear_repulsion_energy)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The molecular repulsion energy calculated by the driver corresponds to the coulombic repulsion between the nuclei in the molecule, and we can add it to our electron structure calculation at the end to get the total energy." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 2: Set up a variational form for VQE\n", - "\n", - "To run the VQE algorithm, we need to specify a mapping from the molecular Hamiltonian to qubits, an initial state, and the ansatz we use for the trial state. Here, we use the Jordan-Wigner transform to map the molecular Hamiltonian onto qubits (Pauli operators). We choose the initial state to be a Hartree-Fock state, and we take the variational ansatz to be the unitary coupled cluster with single and double excitations (UCCSD)." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Number of qubits: 4\n" - ] - } - ], - "source": [ - "n_qubits = molecule.one_body_integrals.shape[0]\n", - "n_electrons = molecule.num_alpha + molecule.num_beta - molecule.molecular_charge\n", - "\n", - "# get fermionic operator and mapping to qubit operator\n", - "ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals)\n", - "\n", - "qubitOp = ferOp.mapping(map_type='JORDAN_WIGNER', threshold=0.00000001)\n", - "qubitOp.chop(10**-10)\n", - "\n", - "# Instantiate the initial state as a Hartree-Fock state\n", - "initial_hf = HartreeFock(num_qubits=n_qubits, num_orbitals=n_qubits, \n", - " qubit_mapping='jordan_wigner', two_qubit_reduction=False, num_particles= n_electrons)\n", - "\n", - "# Create the variational form\n", - "var_form = UCCSD(num_qubits=n_qubits, num_orbitals=n_qubits, \n", - " num_particles=n_electrons, depth=1, initial_state=initial_hf, qubit_mapping='jordan_wigner')\n", - "\n", - "# How many qubits do we need?\n", - "print('Number of qubits: {0}'.format(n_qubits))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 3: Transpile circuit and examine circuit properties" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At this point, we have instantiated a variational form for the molecule according to the UCCSD formulation. Aqua's `UCCSD` method has returned back to us an abstraction of the VQE variational form. Let's query some of its properties." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(3, (-3.141592653589793, 3.141592653589793))" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Query the variational form for the number of parameters, and the parameter bounds.\n", - "var_form.num_parameters, var_form.parameter_bounds[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As a VQE circuit, `var_form` has some number of parameters, and each parameter can take values in $[-\\pi, \\pi]$.\n", - "These parameters control the angles of rotation in the quantum circuit that represents the variational anatz.\n", - "\n", - "For a particular set of parameters, there is an assocated quantum circuit. Let's input some fiducial parameter values and query properties of the resulting circuit to introduce some nomenclature for describing circuits." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'size': 150,\n", - " 'depth': 83,\n", - " 'width': 4,\n", - " 'bits': 0,\n", - " 'factors': 1,\n", - " 'operations': {'u3': 42, 'u2': 40, 'cx': 56, 'u1': 12}}" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Instantiate a concrete instance of the VQE ansatz by setting all the parameters to the\n", - "# arbitrarily-chosen value of 0.\n", - "var_circ = var_form.construct_circuit(np.zeros(var_form.num_parameters))\n", - "\n", - "# Use Terra to convert the circuit to its directed, acyclic graph (DAG) representation.\n", - "var_circ_dag = circuit_to_dag(var_circ)\n", - "\n", - "# The .properties() method of the DAG to get circuit properties.\n", - "var_circ_dag.properties()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These circuit properties are:\n", - "\n", - "\n", - "* `size`: The total number of gates in the circuit\n", - "* `depth`: The total number of _layers_ in the circuit\n", - "* `width`: The number of qubits in the circuit\n", - "* `bits`: The number of classical bits in the circuit. (NOTE: Because the circuit prepares a VQE trial state, and does not have any measurements, `bits` will be 0.)\n", - "* `factors`: The number of tensor factors the circuit could be decomposed into (by looking at the number of weakly connected components of the DAG)\n", - "\n", - "The `.properties()` method of the DAG representation also breaks down the total number of gates (`size`) by the individual gates themselves. These are the $u1,u2,u3$ and CNOT gates described [here](https://qiskit.org/documentation/terra/summary_of_quantum_operations.html). You can verify that the total number of gates is in fact equal to `size`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 4: Transpile the circuit using Terra and `t|ket〉`\n", - "\n", - "Having instantiated a VQE variational form and examined some of its properties, we'd like to optimize those properties so that the circuit could be run on a near-term device. Terra provides a framework (the [_transpiler_](https://qiskit.org/documentation/terra/overview.html#transpiler)) for manipulating circuits according to certain _passes_, and where the execution of the passes is orchestrated by a _PassManager_, which we can use to do this optimization. Importantly, _the transpiler manipulates the circuit to change circuit properties, without actually changing the input-output relationship the circuit defines_. That is, the transpiler takes a circuit and re-writes it, but doesn't change what the circuit actually does.\n", - "\n", - "CQC has written several passes for manipulating quantum circuits which are available via `pytket`. For an extensive discussion of the framework `t|ket〉` uses to manipulate circuits, see **[TODO: include link]**. Here, we'll demonstrate using Terra and `pytket` to optimize a randomly-chosen realization of the VQE variational form. Currently, passes in the transpiler require a backend in order to run. For simplicity, we start by using a simulator backend." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "# Grab an Aer backend\n", - "aer_backend = Aer.get_backend('qasm_simulator')" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Choose a random set of parameters\n", - "seed = 0\n", - "np.random.seed(seed)\n", - "params = np.random.uniform(low=-3.1, high=3.1, size=var_form.num_parameters)\n", - "\n", - "# Construct a random instance of the variational circuit\n", - "var_circuit = var_form.construct_circuit(params)\n", - "\n", - "# Turn the circuit into a DAG\n", - "var_dag = circuit_to_dag(var_circuit)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This randomly-chosen realization of the variational form has the same circuit properties as the circuit we instantiated in Step 3." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'size': 150,\n", - " 'depth': 83,\n", - " 'width': 4,\n", - " 'bits': 0,\n", - " 'factors': 1,\n", - " 'operations': {'u3': 42, 'u2': 40, 'cx': 56, 'u1': 12}}" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "var_dag.properties()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, we set up a transpiler using Terra and the `TketPass` from `pytket`." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "# Create a Terra PassManager object\n", - "tk_pass_manager = PassManager()\n", - "\n", - "# Set up the TketPass\n", - "tk_pass = TketPass(aer_backend)\n", - "\n", - "# Add the TketPass to the PassManager\n", - "tk_pass_manager.append(tk_pass)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With the transpiler set up and the realization of the variational form put into a DAG, we can now use the `transpile_dag` function to run the PassManger." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "var_dag_transpiled = transpile_dag(var_dag, pass_manager=tk_pass_manager)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's check the properties of this circuit to see how the transpiled circuit differs from the original one." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'size': 95,\n", - " 'depth': 57,\n", - " 'width': 4,\n", - " 'bits': 0,\n", - " 'factors': 1,\n", - " 'operations': {'u3': 30, 'cx': 52, 'u1': 12, 'u2': 1}}" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "var_dag_transpiled.properties()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `t|ket〉` compiler has optimized the circuit, and as we might hope for, _the transpiled circuit has a lower `size` and `depth` than the original circuit._\n", - "\n", - "\n", - "The table below gives properties of the transpiled circuit for H$_{2}$ and LiH (with `seed=0`, corresponding to the worst-case and also most common performance of the transpiler).\n", - "\n", - "\n", - "| Molecule: H$_2$ | Total Gates | Depth | CNOT Count |\n", - "|--------------------------------------|-------------|---------------|--------------------|\n", - "| Input circuit | 150 | 83 | 56 |\n", - "| `tket` circuit optimization (Aer backend) | 95 | 57 | 52 |\n", - "\n", - "\n", - "| Molecule: LiH | Total Gates | Depth | CNOT Count |\n", - "|--------------------------------------|-------------|---------------|--------------------|\n", - "| Input circuit | 13700 | 9342 | 8064 |\n", - "| `tket` circuit optimization (Aer backend) | 7411 | 4416 | 5096 |\n", - "\n", - "For both H$_2$ and LiH, circuit optimization using `t|ket〉` reduces the number of gates necessary to prepare the trial state. The depth also decreases, which makes running the circuit more feasible on near-term devices." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 5: Route the circuit onto real hardware" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Even though we've optimized the circuit, there's no guarantee that it can run, as written, on a real backend. This is because the directionaly of the CNOT gates on the backend may not be respected by the circuit. For this reason, we need to re-write the circuit in such as way that the CNOT gates respect the _coupling map_ of the backend. `t|ket〉` knows how to do this, and handles this problem (called circuit \"routing\") when a real backend is put into the `TketPass` object. Routing refers to the process of making quantum circuits hardware compliant by the addition of SWAP gates such that all multi-qubit interactions occur on adjacent physical qubits.\n", - "\n", - "Because we know _a priori_ that the H$_{2}$ and LiH molecules requires at most 12 qubits, we make sure to use an IBMQ backend with no less than 12 qubits." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[,\n", - " ]" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Grab only backends that have at least 12 qubits\n", - "provider.backends(filters=lambda x: x.configuration().n_qubits >= 12)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll use the `ibmq_16_melbourne` backend." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "real_backend = provider.get_backend('ibmq_16_melbourne')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To route the variational circuit onto real hardware, we simply need to change the backend." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# Create a Terra PassManager object\n", - "tk_pass_manager = PassManager()\n", - "\n", - "# Set up the TketPass\n", - "tk_pass = TketPass(real_backend)\n", - "\n", - "# Add the TketPass to the PassManager\n", - "tk_pass_manager.append(tk_pass)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Again, we can transpile the DAG representation of the variational circuit to a backend-compliant circuit using the `tranpsile_dag` function. Here, because the backend has a non-trivial coupling map, the `TketPass` will perform both circuit optimization and optimal routing calculations. We need to add a register containing the ancilla qubits on the architecture that `TketPass` can use in routing." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "blank_qubits = QuantumRegister(len(real_backend.properties().qubits) - var_dag.width())\n", - "var_dag.add_qreg(blank_qubits)\n", - "var_dag_transpiled = transpile_dag(var_dag, pass_manager=tk_pass_manager)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'size': 108,\n", - " 'depth': 69,\n", - " 'width': 4,\n", - " 'bits': 0,\n", - " 'factors': 1,\n", - " 'operations': {'u3': 30, 'cx': 52, 'u1': 12, 'u2': 14}}" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "var_dag_transpiled.properties()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can compare these results to Qiskit's own default transpilation by passing in the backend's `coupling_map` to the `transpile_dag` function." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'size': 119,\n", - " 'depth': 89,\n", - " 'width': 14,\n", - " 'bits': 0,\n", - " 'factors': 11,\n", - " 'operations': {'u3': 15, 'cx': 56, 'u1': 14, 'u2': 34}}" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "transpile_dag(var_dag, coupling_map=real_backend.configuration().coupling_map).properties()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The table below shows circuit properties for H$_{2}$ (with `seed=0`).\n", - "\n", - "| Molecule: H$_2$ | Total Gates | Overall Depth | Overall CNOT Count |\n", - "|--------------------------------------|-------------|---------------|--------------------|\n", - "| Input circuit | 150 | 83 | 56 |\n", - "| `tket` circuit optimization (Aer backend) | 95 | 57 | 52 |\n", - "| `Qiskit ` default routing (real backend) | 119 | 89 | 56 |\n", - "| `tket` circuit optimzation + routing (real backend) | 108 | 69 | 52 |\n", - "\n", - "The table below shows circuit properties for LiH (with `seed=0`).\n", - "\n", - "| Molecule: LiH | Total Gates | Overall Depth | Overall CNOT Count |\n", - "|--------------------------------------|-------------|---------------|--------------------|\n", - "| Input circuit | 13700 | 9342 | 8064 |\n", - "| `tket` circuit optimization (Aer backend) | 7411 | 4416 | 5096 |\n", - "| `Qiskit ` default routing (real backend) | 42178 | 23367 | 17977 |\n", - "| `tket` circuit optimzation + routing (real backend) | 19256 | 10022 | 8711 |" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Application II: using QSE to compute excited state energies" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The previous application showed us how to use `t|ket〉` and Terra to optimally route circuits onto real hardware. In the introduction, we observed that in order to use the quantum subspace expansion to compute excited state energies, we need to first come up with an estimate of the ground state. For this, we use VQE.\n", - "\n", - "In this application, we'll instantiate a VQE circuit, run it, and use the estimated ground state as input to `pytket`'s quantum subspace expansion function(s). NOTE: We'll use a simulator backend, and will not run the VQE algorithm on real hardware." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "# Code imports\n", - "\n", - "# From Aqua, we need \n", - "from qiskit.aqua import QuantumInstance\n", - "\n", - "from qiskit.aqua.algorithms.adaptive import VQE\n", - "from qiskit.aqua.components.optimizers import L_BFGS_B\n", - "\n", - "# From pytket, we need QSE functions\n", - "\n", - "from pytket.chemistry import QseMatrices, QSE" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "backend = Aer.get_backend('statevector_simulator')" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "pass_manager = PassManager()\n", - "tk_pass = TketPass(backend)\n", - "pass_manager.append(tk_pass)\n", - "\n", - "quantum_instance = QuantumInstance(backend, pass_manager=pass_manager)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 1: Use VQE to estimate ground state\n", - "\n", - "First, we'll use VQE to estimate the ground state and its energy." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "# Temporary code for Aer on Macbook\n", - "import os\n", - "os.environ['KMP_DUPLICATE_LIB_OK']='True'" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GS Minimum value: -1.1361894540653963\n", - "GS Parameters: [ 5.06008657e-07 5.12457730e-07 -1.04867316e-01]\n" - ] - } - ], - "source": [ - "# Set initial values of parameters\n", - "number_amplitudes = len(var_form._single_excitations)+ len(var_form._double_excitations)\n", - "\n", - "amplitudes_0 = []\n", - "for i in range(number_amplitudes):\n", - " amplitudes_0.append(0.00001)\n", - "\n", - "optimizer = L_BFGS_B()\n", - "optimizer.set_options(maxfun=1000, factr=10, iprint=10)\n", - "\n", - "# setup VQE with operator, variation form, and optimzer\n", - "vqe_algorithm = VQE(operator=qubitOp, \n", - " var_form=var_form, optimizer=optimizer, initial_point=amplitudes_0)\n", - "\n", - "results = vqe_algorithm.run(quantum_instance)\n", - "\n", - "eigval = results['eigvals'][0]\n", - "gs_energy = eigval.real + molecule.nuclear_repulsion_energy\n", - "\n", - "print(\"GS Minimum value: {}\".format(gs_energy))\n", - "print(\"GS Parameters: {}\".format(results['opt_params']))\n", - "\n", - "# store ground state amplitudes for subsequent steps\n", - "opti_amplitudes = results['opt_params']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 2: Use QSE to find excited states from ground state \n", - "\n", - "We now have the main ingredients to perform a QSE calculation: the molecular Hamiltonian and the optimized parameters to reconstruct the ground state wavefunction. We build our excitation hamiltonian and overlap operators, and measure the elements that compose $H$ and $S$. After we have obtained these arrays, we perform a diagonalization to obtain the excited state energies and vectors.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Excited State Energies: [-1.13618945 -0.47845306 -0.47845306 -0.47845306 -0.1204519 0.5833141\n", - " 0.75596744 0.75596744 0.75596744 0.75596744 0.75596744 0.75596744\n", - " 0.75596744 0.75596744 0.75596744 0.75596744]\n" - ] - } - ], - "source": [ - "qubitOp = ferOp.mapping(map_type='JORDAN_WIGNER', threshold=0.00000001)\n", - "n_qubits = qubitOp.num_qubits\n", - "qubitOp.chop(10**-10)\n", - "\n", - "# Use matrix term helper class\n", - "matrix_terms = QseMatrices(qubitOp, n_qubits)\n", - "\n", - "# Instantiate an instance of the QSE algorithm\n", - "qse_algorithm = QSE(matrix_terms, 'matrix', var_form, opt_init_point=opti_amplitudes)\n", - "\n", - "# Run the algorithm\n", - "energies = qse_algorithm.run(quantum_instance)['eigvals']\n", - "\n", - "# The excited state energies are the energies from above,\n", - "# plus the nuclear repulsion energy.\n", - "print(\"Excited State Energies: \", energies+molecule.nuclear_repulsion_energy)\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The calculation provides a refined value of the ground state and a series of excited state energies, whose number depends on the size of the basis set chosen for the molecule, as well as its nature and symmetry. Some of the energies obtained are repeated several times, signaling that some of the states obtained are degenerate. This result can be improved by either increasing the basis set or considering higher-order excitations in the subspace expansion.\n", - "\n", - "The following graph shows us the excited states of LiH at a range of bond distances calculated via our method, compared to values computed using the classical [EOM-CCSD method](https://aip.scitation.org/doi/10.1063/1.464746). To generate this data yourself, you can scan the bond length parameter we set at the start of the calculation.\n", - "\n", - "![alt text](LiH.png \"Title\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using the QSE technique, and our simulated VQE ground state, we find the ground state curve has a minimum at a separation of about 1.5 Å, which is in reasonable agreement with experimental data. The calculation also finds a number of excited states. Looking at the first three of these, we find that at the equilibrium distance, these states are 0.11, 0.12 and 0.17 Ha higher in energy than the ground state, which is again in reasonable agreement with experimental data. Note the small kink in one of the excited states energy curve at a distance of approximately 1.2 Å. This indicates that our restriction to single electron excitations is not enough to provide an accurate description at this distance. Overall, the comparison with classically computed EOM-CCSD curves shows that this method reproduces excited state energies with good accuracy at most distances." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} \ No newline at end of file diff --git a/legacy_examples/README.md b/legacy_examples/README.md deleted file mode 100644 index 431b159a..00000000 --- a/legacy_examples/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# pytket Legacy Examples - -The examples in this directory are no longer maintained and are likely not executable with up to date versions of pytket or other dependant packages, they are included for reference only. From 706209c790356da08e3892ee5aec6e609c474bc3 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:14:59 +0100 Subject: [PATCH 04/76] update manual script --- build-manual | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/build-manual b/build-manual index 891522f1..6f3f3560 100755 --- a/build-manual +++ b/build-manual @@ -1,17 +1,7 @@ #!/bin/sh -python -m pip install -r manual_requirements.txt -python -m pip install -c manual_constraints.txt sphinx sphinx-book-theme sphinx-copybutton jupyter-sphinx quimb -python -m pip install qiskit-algorithms -python -m pip install ipykernel -python -m pip install kahypar - cd manual/ -ESCAPED_REQS=$(awk '{printf "%s%s",sep,$0; sep="\\\n"} END{print ""}' ../manual_requirements.txt) -sed "s/REQUIREMENTS/$ESCAPED_REQS/" index-rst-template > index.rst - rm -rf build/ sphinx-build -b html . build -W -rm index.rst From 62623063913137e86ca3f72f7a9f1f0812da664a Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:23:22 +0100 Subject: [PATCH 05/76] use pydata theme and clean index page --- manual/conf.py | 12 ++++++++---- manual/{index-rst-template => index.rst} | 6 ------ 2 files changed, 8 insertions(+), 10 deletions(-) rename manual/{index-rst-template => index.rst} (86%) diff --git a/manual/conf.py b/manual/conf.py index f0932abe..7c65a4a6 100644 --- a/manual/conf.py +++ b/manual/conf.py @@ -16,19 +16,23 @@ "sphinx.ext.autosectionlabel", ] -html_theme = "sphinx_book_theme" +html_theme = "pydata_sphinx_theme" html_title = "pytket user manual" html_theme_options = { - "repository_url": "https://github.com/CQCL/tket", - "use_repository_button": True, "navigation_with_keys": True, - "use_issues_button": True, "logo": { "image_light": "_static/Quantinuum_logo_black.png", "image_dark": "_static/Quantinuum_logo_white.png", }, + "icon_links": [ + { + "name": "GitHub", + "url": "https://github.com/CQCL/tket", + "icon": "fa-brands fa-github", + } + ], } html_static_path = ["_static"] diff --git a/manual/index-rst-template b/manual/index.rst similarity index 86% rename from manual/index-rst-template rename to manual/index.rst index 12006f4c..ff515ba0 100644 --- a/manual/index-rst-template +++ b/manual/index.rst @@ -1,12 +1,6 @@ Pytket User Manual ================== -This manual refers to the following versions of pytket and extensions: - -.. code-block:: text - -REQUIREMENTS - .. toctree:: :caption: Manual Sections: :maxdepth: 2 From 19b25f6e6d661a820e0e71e003816384f200f55a Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:27:47 +0100 Subject: [PATCH 06/76] remove legacy python -> jupyter conversion --- examples/gen-nb | 8 - examples/python/Forest_portability_example.py | 95 --- examples/python/ansatz_sequence_example.py | 112 --- examples/python/backends_example.py | 246 ------ examples/python/circuit_analysis_example.py | 112 --- examples/python/circuit_generation_example.py | 354 -------- examples/python/comparing_simulators.py | 286 ------- examples/python/compilation_example.py | 272 ------ examples/python/conditional_gate_example.py | 115 --- examples/python/contextual_optimization.py | 69 -- examples/python/creating_backends.py | 777 ------------------ examples/python/entanglement_swapping.py | 361 -------- examples/python/expectation_value_example.py | 255 ------ examples/python/mapping_example.py | 504 ------------ .../python/measurement_reduction_example.py | 96 --- examples/python/phase_estimation.py | 350 -------- .../python/pytket-qujax-classification.py | 182 ---- .../python/pytket-qujax_heisenberg_vqe.py | 192 ----- examples/python/pytket-qujax_qaoa.py | 149 ---- examples/python/qasm | 1 - examples/python/qiskit_integration.py | 81 -- examples/python/spam_example.py | 164 ---- examples/python/symbolics_example.py | 77 -- examples/python/ucc_vqe.py | 515 ------------ manual_constraints.txt | 2 - manual_requirements.txt | 3 - 26 files changed, 5378 deletions(-) delete mode 100755 examples/gen-nb delete mode 100644 examples/python/Forest_portability_example.py delete mode 100644 examples/python/ansatz_sequence_example.py delete mode 100644 examples/python/backends_example.py delete mode 100644 examples/python/circuit_analysis_example.py delete mode 100644 examples/python/circuit_generation_example.py delete mode 100644 examples/python/comparing_simulators.py delete mode 100644 examples/python/compilation_example.py delete mode 100644 examples/python/conditional_gate_example.py delete mode 100644 examples/python/contextual_optimization.py delete mode 100644 examples/python/creating_backends.py delete mode 100644 examples/python/entanglement_swapping.py delete mode 100644 examples/python/expectation_value_example.py delete mode 100644 examples/python/mapping_example.py delete mode 100644 examples/python/measurement_reduction_example.py delete mode 100644 examples/python/phase_estimation.py delete mode 100644 examples/python/pytket-qujax-classification.py delete mode 100644 examples/python/pytket-qujax_heisenberg_vqe.py delete mode 100644 examples/python/pytket-qujax_qaoa.py delete mode 120000 examples/python/qasm delete mode 100644 examples/python/qiskit_integration.py delete mode 100644 examples/python/spam_example.py delete mode 100644 examples/python/symbolics_example.py delete mode 100644 examples/python/ucc_vqe.py delete mode 100644 manual_constraints.txt delete mode 100644 manual_requirements.txt diff --git a/examples/gen-nb b/examples/gen-nb deleted file mode 100755 index 984340be..00000000 --- a/examples/gen-nb +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# Convert the scripts in the `python` directory to notebooks. - -for name in `cat maintained-notebooks.txt` -do - p2j -o -t ${name}.ipynb python/${name}.py -done diff --git a/examples/python/Forest_portability_example.py b/examples/python/Forest_portability_example.py deleted file mode 100644 index af8def7f..00000000 --- a/examples/python/Forest_portability_example.py +++ /dev/null @@ -1,95 +0,0 @@ -# # Code portability and intro to forest - -# The quantum hardware landscape is incredibly competitive and rapidly changing. Many full-stack quantum software platforms lock users into them in order to use the associated devices and simulators. This notebook demonstrates how `pytket` can free up your existing high-level code to be used on devices from other providers. We will take a state-preparation and evolution circuit generated using `qiskit`, and enable it to be run on several Rigetti backends. -# -# To use a real hardware device, this notebook should be run from a Rigetti QMI instance. Look [here](https://www.rigetti.com/qcs/docs/intro-to-qcs) for information on how to set this up. Otherwise, make sure you have QuilC and QVM running in server mode. You will need to have `pytket`, `pytket_pyquil`, and `pytket_qiskit` installed, which are all available from PyPI. - -# We will start by using `qiskit` to build a random initial state over some qubits. (We remove the initial "reset" gates from the circuit since these are not recognized by the Forest backends, which assume an all-zero initial state.) - -from qiskit import QuantumCircuit -from qiskit.quantum_info.states.random import random_statevector - -n_qubits = 3 -state = random_statevector((1 << n_qubits, 1)).data -state_prep_circ = QuantumCircuit(n_qubits) -state_prep_circ.initialize(state) -state_prep_circ = state_prep_circ.decompose().decompose() -state_prep_circ.data = [ - datum for datum in state_prep_circ.data if datum[0].name != "reset" -] - -print(state_prep_circ) - -# We can now evolve this state under an operator for a given duration. - -from qiskit.opflow import PauliTrotterEvolution -from qiskit.opflow.primitive_ops import PauliSumOp -from qiskit.quantum_info import Pauli - -duration = 1.2 -op = PauliSumOp.from_list([("XXI", 0.3), ("YYI", 0.5), ("ZZZ", -0.4)]) -evolved_op = (duration * op).exp_i() -evolution_circ = PauliTrotterEvolution(reps=1).convert(evolved_op).to_circuit() -print(evolution_circ) - -for op in evolution_circ: - state_prep_circ.append(op) - -# Now that we have a circuit, `pytket` can take this and start operating on it directly. For example, we can apply some basic compilation passes to simplify it. - -from pytket.extensions.qiskit import qiskit_to_tk - -tk_circ = qiskit_to_tk(state_prep_circ) - -from pytket.passes import ( - SequencePass, - CliffordSimp, - DecomposeBoxes, - KAKDecomposition, - SynthesiseTket, -) - -DecomposeBoxes().apply(tk_circ) -optimise = SequencePass([KAKDecomposition(), CliffordSimp(False), SynthesiseTket()]) -optimise.apply(tk_circ) - -# Display the optimised circuit: - -from pytket.circuit.display import render_circuit_jupyter - -render_circuit_jupyter(tk_circ) - -# The Backends in `pytket` abstract away the differences between different devices and simulators as much as possible, allowing painless switching between them. The `pytket_pyquil` package provides two Backends: `ForestBackend` encapsulates both running on physical devices via Rigetti QCS and simulating those devices on the QVM, and `ForestStateBackend` acts as a wrapper to the pyQuil Wavefunction Simulator. -# -# Both of these still have a few restrictions on the circuits that can be run. Each only supports a subset of the gate types available in `pytket`, and a real device or associated simulation will have restricted qubit connectivity. The Backend objects will contain a default compilation pass that will statisfy these constraints as much as possible, with minimal or no optimisation. -# -# The `ForestStateBackend` will allow us to view the full statevector (wavefunction) expected from a perfect execution of the circuit. - -from pytket.extensions.pyquil import ForestStateBackend - -state_backend = ForestStateBackend() -tk_circ = state_backend.get_compiled_circuit(tk_circ) - -handle = state_backend.process_circuit(tk_circ) -state = state_backend.get_result(handle).get_state() -print(state) - -# For users who are familiar with the Forest SDK, the association of qubits to indices of bitstrings (and consequently the ordering of statevectors) used by default in `pytket` Backends differs from that described in the [Forest docs](http://docs.rigetti.com/en/stable/wavefunction_simulator.html#multi-qubit-basis-enumeration). You can recover the ordering used by the Forest systems with `BackendResult.get_state(tk_circ, basis:pytket.BasisOrder.dlo)` (see our docs on the `BasisOrder` enum for more details). - -# Connecting to real devices works very similarly. Instead of obtaining the full statevector, we are only able to measure the quantum state and sample from the resulting distribution. Beyond that, the process is pretty much the same. -# -# The following shows how to run the circuit on the "9q-square" lattice. The `as_qvm` switch on the `get_qc` method will switch between connecting to the real Aspen device and the QVM, allowing you to test your code with a simulator before you reserve your slot with the device. - -tk_circ.measure_all() - -from pyquil import get_qc -from pytket.extensions.pyquil import ForestBackend - -aspen_qc = get_qc("9q-square", as_qvm=True) -aspen_backend = ForestBackend(aspen_qc) -tk_circ = aspen_backend.get_compiled_circuit(tk_circ) - -counts = aspen_backend.run_circuit(tk_circ, 2000).get_counts() -print(counts) - -# Note that attempting to connect to a live quantum device (using a `QuantumComputer` constructed with `as_qvm=False`) will fail unless it is running from a QMI instance during a reservation for the named lattice. diff --git a/examples/python/ansatz_sequence_example.py b/examples/python/ansatz_sequence_example.py deleted file mode 100644 index b6b34b91..00000000 --- a/examples/python/ansatz_sequence_example.py +++ /dev/null @@ -1,112 +0,0 @@ -# # Ansatz sequencing - -# When performing variational algorithms like VQE, one common approach to generating circuit ansätze is to take an operator $U$ representing excitations and use this to act on a reference state $\lvert \phi_0 \rangle$. One such ansatz is the Unitary Coupled Cluster ansatz. Each excitation, indexed by $j$, within $U$ is given a real coefficient $a_j$ and a parameter $t_j$, such that $U = e^{i \sum_j \sum_k a_j t_j P_{jk}}$, where $P_{jk} \in \{I, X, Y, Z \}^{\otimes n}$. The exact form is dependent on the chosen qubit encoding. This excitation gives us a variational state $\lvert \psi (t) \rangle = U(t) \lvert \phi_0 \rangle$. The operator $U$ must be Trotterised, to give a product of Pauli exponentials, and converted into native quantum gates to create the ansatz circuit. -# -# This notebook will describe how to use an advanced feature of `pytket` to enable automated circuit synthesis for $U$ and reduce circuit depth dramatically. -# -# We must create a `pytket` `QubitPauliOperator`, which represents such an operator $U$, and contains a dictionary from Pauli string $P_{jk}$ to symbolic expression. Here, we make a mock operator ourselves, which resembles the UCCSD excitation operator for the $\mathrm{H}_2$ molecule using the Jordan-Wigner qubit encoding. -# -# First, we create a series of `QubitPauliString` objects, which represent each $P_{jk}$. - -from pytket.pauli import Pauli, QubitPauliString -from pytket.circuit import Qubit - -q = [Qubit(i) for i in range(4)] -qps0 = QubitPauliString([q[0], q[1], q[2]], [Pauli.Y, Pauli.Z, Pauli.X]) -qps1 = QubitPauliString([q[0], q[1], q[2]], [Pauli.X, Pauli.Z, Pauli.Y]) -qps2 = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.X, Pauli.Y]) -qps3 = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.Y, Pauli.X]) -qps4 = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.X, Pauli.X]) -qps5 = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.X, Pauli.X]) - -# Now, create some symbolic expressions for the $a_j t_j$ terms. - -from pytket.circuit import fresh_symbol - -symbol1 = fresh_symbol("s0") -expr1 = 1.2 * symbol1 -symbol2 = fresh_symbol("s1") -expr2 = -0.3 * symbol2 - -# We can now create our `QubitPauliOperator`. - -from pytket.utils import QubitPauliOperator - -dict1 = dict((string, expr1) for string in (qps0, qps1)) -dict2 = dict((string, expr2) for string in (qps2, qps3, qps4, qps5)) -operator = QubitPauliOperator({**dict1, **dict2}) -print(operator) - -# Now we can let `pytket` sequence the terms in this operator for us, using a selection of strategies. First, we will create a `Circuit` to generate an example reference state, and then use the `gen_term_sequence_circuit` method to append the Pauli exponentials. - -from pytket.circuit import Circuit -from pytket.utils import gen_term_sequence_circuit -from pytket.partition import PauliPartitionStrat, GraphColourMethod - -reference_circ = Circuit(4).X(1).X(3) -ansatz_circuit = gen_term_sequence_circuit( - operator, reference_circ, PauliPartitionStrat.CommutingSets, GraphColourMethod.Lazy -) - -# This method works by generating a graph of Pauli exponentials and performing graph colouring. Here we have chosen to partition the terms so that exponentials which commute are gathered together, and we have done so using a lazy, greedy graph colouring method. -# -# Alternatively, we could have used the `PauliPartitionStrat.NonConflictingSets`, which puts Pauli exponentials together so that they only require single-qubit gates to be converted into the form $e^{i \alpha Z \otimes Z \otimes ... \otimes Z}$. This strategy is primarily useful for measurement reduction, a different problem. -# -# We could also have used the `GraphColourMethod.LargestFirst`, which still uses a greedy method, but builds the full graph and iterates through the vertices in descending order of arity. We recommend playing around with the options, but we typically find that the combination of `CommutingSets` and `Lazy` allows the best optimisation. -# -# In general, not all of our exponentials will commute, so the semantics of our circuit depend on the order of our sequencing. As a result, it is important for us to be able to inspect the order we have produced. `pytket` provides functionality to enable this. Each set of commuting exponentials is put into a `CircBox`, which lets us inspect the partitoning. - -from pytket.circuit import OpType - -for command in ansatz_circuit: - if command.op.type == OpType.CircBox: - print("New CircBox:") - for pauli_exp in command.op.get_circuit(): - print( - " {} {} {}".format( - pauli_exp, pauli_exp.op.get_paulis(), pauli_exp.op.get_phase() - ) - ) - else: - print("Native gate: {}".format(command)) - -# We can convert this circuit into basic gates using a `pytket` `Transform`. This acts in place on the circuit to do rewriting, for gate translation and optimisation. We will start off with a naive decomposition. - -from pytket.transform import Transform - -naive_circuit = ansatz_circuit.copy() -Transform.DecomposeBoxes().apply(naive_circuit) -print(naive_circuit.get_commands()) - -# This is a jumble of one- and two-qubit gates. We can get some relevant circuit metrics out: - -print("Naive CX Depth: {}".format(naive_circuit.depth_by_type(OpType.CX))) -print("Naive CX Count: {}".format(naive_circuit.n_gates_of_type(OpType.CX))) - -# These metrics can be improved upon significantly by smart compilation. A `Transform` exists precisely for this purpose: - -from pytket.transform import PauliSynthStrat, CXConfigType - -smart_circuit = ansatz_circuit.copy() -Transform.UCCSynthesis(PauliSynthStrat.Sets, CXConfigType.Tree).apply(smart_circuit) -print("Smart CX Depth: {}".format(smart_circuit.depth_by_type(OpType.CX))) -print("Smart CX Count: {}".format(smart_circuit.n_gates_of_type(OpType.CX))) - -# This `Transform` takes in a `Circuit` with the structure specified above: some arbitrary gates for the reference state, along with several `CircBox` gates containing `PauliExpBox` gates. -# -# We have chosen `PauliSynthStrat.Sets` and `CXConfigType.Tree`. The `PauliSynthStrat` dictates the method for decomposing multiple adjacent Pauli exponentials into basic gates, while the `CXConfigType` dictates the structure of adjacent CX gates. -# -# If we choose a different combination of strategies, we can produce a different output circuit: - -last_circuit = ansatz_circuit.copy() -Transform.UCCSynthesis(PauliSynthStrat.Individual, CXConfigType.Snake).apply( - last_circuit -) -print(last_circuit.get_commands()) - -print("Last CX Depth: {}".format(last_circuit.depth_by_type(OpType.CX))) -print("Last CX Count: {}".format(last_circuit.n_gates_of_type(OpType.CX))) - -# Other than some single-qubit Cliffords we acquired via synthesis, you can check that this gives us the same circuit structure as our `Transform.DecomposeBoxes` method! It is a suboptimal synthesis method. -# -# As with the `gen_term_sequence` method, we recommend playing around with the arguments and seeing what circuits come out. Typically we find that `PauliSynthStrat.Sets` and `CXConfigType.Tree` work the best, although routing can affect this somewhat. diff --git a/examples/python/backends_example.py b/examples/python/backends_example.py deleted file mode 100644 index 82513e23..00000000 --- a/examples/python/backends_example.py +++ /dev/null @@ -1,246 +0,0 @@ -# # TKET `Backend` tutorial - -# This example shows how to use `pytket` to execute quantum circuits on both simulators and real devices, and how to interpret the results. As tket is designed to be platform-agnostic, we have unified the interfaces of different providers as much as possible into the `Backend` class for maximum portability of code. - -# For the full list of supported backends see the pytket [extensions index page](https://tket.quantinuum.com/api-docs/extensions). - -# In this notebook we will focus on the Aer, IBMQ and ProjectQ backends. -# -# To get started, we must install the core pytket package and the subpackages required to interface with the desired providers. We will also need the `QubitOperator` class from `openfermion` to construct operators for a later example. To get everything run the following in shell: -# -# `pip install pytket pytket-qiskit pytket-projectq openfermion` -# -# First, import the backends that we will be demonstrating. - -from pytket.extensions.qiskit import ( - AerStateBackend, - AerBackend, - AerUnitaryBackend, - IBMQBackend, - IBMQEmulatorBackend, -) -from pytket.extensions.projectq import ProjectQBackend - -# We are also going to be making a circuit to run on these backends, so import the `Circuit` class. - -from pytket import Circuit, Qubit - -# Below we generate a circuit which will produce a Bell state, assuming the qubits are all initialised in the |0> state: - -circ = Circuit(2) -circ.H(0) -circ.CX(0, 1) - -# As a sanity check, we will use the `AerStateBackend` to verify that `circ` does actually produce a Bell state. -# -# To submit a circuit for excution on a backend we can use `process_circuit` with appropriate arguments. If we have multiple circuits to excecute, we can use `process_circuits` (note the plural), which will attempt to batch up the circuits if possible. Both methods return a `ResultHandle` object per submitted `Circuit` which you can use with result retrieval methods to get the result type you want (as long as that result type is supported by the backend). -# -# Calling `get_state` will return a `numpy` array corresponding to the statevector. -# -# This style of usage is used consistently in the `pytket` backends. - -aer_state_b = AerStateBackend() -state_handle = aer_state_b.process_circuit(circ) -statevector = aer_state_b.get_result(state_handle).get_state() -print(statevector) - -# As we can see, the output state vector $\lvert \psi_{\mathrm{circ}}\rangle$ is $(\lvert00\rangle + \lvert11\rangle)/\sqrt2$. -# -# This is a symmetric state. For non-symmetric states, we default to an ILO-BE format (increasing lexicographic order of (qu)bit ids, big-endian), but an alternative convention can be specified when retrieving results from backends. See the docs for the `BasisOrder` enum for more information. - -# A lesser-used simulator available through Qiskit Aer is their unitary simulator. This will be somewhat more expensive to run, but returns the full unitary matrix for the provided circuit. This is useful in the design of small subcircuits that will be used multiple times within other larger circuits - statevector simulators will only test that they act correctly on the $\lvert 0 \rangle^{\otimes n}$ state, which is not enough to guarantee the circuit's correctness. -# -# The `AerUnitaryBackend` provides a convenient access point for this simulator for use with `pytket` circuits. The unitary of the circuit can be retrieved from backends that support it using the `BackendResult.get_unitary` interface. In this example, we chose to use `Backend.run_circuit`, which is equivalent to calling `process_circuit` followed by `get_result`. - -aer_unitary_b = AerUnitaryBackend() -result = aer_unitary_b.run_circuit(circ) -print(result.get_unitary()) - -# Note that state vector and unitary simulations are also available in pytket directly. In general, we recommend you use these unless you require another Backend explicitly. - -statevector = circ.get_statevector() -unitary = circ.get_unitary() - -# Now suppose we want to measure this Bell state to get some actual results out, so let's append some `Measure` gates to the circuit. The `Circuit` class has the `measure_all` utility function which appends `Measure` gates on every qubit. All of these results will be written to the default classical register ('c'). This function will automatically add the classical bits to the circuit if they are not already there. - -circ.measure_all() - -# We can get some measured counts out from the `AerBackend`, which is an interface to the Qiskit Aer QASM simulator. Suppose we would like to get 10 shots out (10 repeats of the circuit and measurement). We can seed the simulator's random-number generator in order to make the results reproducible, using an optional keyword argument to `process_circuit`. - -aer_b = AerBackend() -handle = aer_b.process_circuit(circ, n_shots=10, seed=1) - -counts = aer_b.get_result(handle).get_counts() -print(counts) - -# What happens if we simulate some noise in our imagined device, using the Qiskit Aer noise model? - -# To investigate this, we will require an import from Qiskit. For more information about noise modelling using Qiskit Aer, see the [Qiskit device noise](https://qiskit.org/documentation/apidoc/aer_noise.html) documentation. - -from qiskit_aer.noise import NoiseModel - -my_noise_model = NoiseModel() -readout_error = 0.2 -for q in range(2): - my_noise_model.add_readout_error( - [[1 - readout_error, readout_error], [readout_error, 1 - readout_error]], [q] - ) - -# This simple noise model gives a 20% chance that, upon measurement, a qubit that would otherwise have been measured as $0$ would instead be measured as $1$, and vice versa. Let's see what our shot table looks like with this model: - -noisy_aer_b = AerBackend(my_noise_model) -noisy_handle = noisy_aer_b.process_circuit(circ, n_shots=10, seed=1, valid_check=False) -noisy_counts = noisy_aer_b.get_result(noisy_handle).get_counts() -print(noisy_counts) - -# We now have some spurious $01$ and $10$ measurements, which could never happen when measuring a Bell state on a noiseless device. -# -# The `AerBackend` class can accept any Qiskit noise model. -# -# All backends expose a generic `get_result` method which takes a `ResultHandle` and returns the respective result in the form of a `BackendResult` object. This object may hold measured results in the form of shots or counts, or an exact statevector from simulation. Measured results are stored as `OutcomeArray` objects, which compresses measured bit values into 8-bit integers. We can extract the bitwise values using `to_readouts`. -# -# Instead of an assumed ILO or DLO convention, we can use this object to request only the `Bit` measurements we want, in the order we want. Let's try reversing the bits of the noisy results. - -backend_result = noisy_aer_b.get_result(noisy_handle) -bits = circ.bits - -outcomes = backend_result.get_counts([bits[1], bits[0]]) -print(outcomes) - -# `BackendResult` objects can be natively serialized to and deserialized from a dictionary. This dictionary can be immediately dumped to `json` for storing results. - -from pytket.backends.backendresult import BackendResult - -result_dict = backend_result.to_dict() -print(result_dict) -print(BackendResult.from_dict(result_dict).get_counts()) - -# The last simulator we will demonstrate is the `ProjectQBackend`. ProjectQ offers fast simulation of quantum circuits with built-in support for fast expectation values from operators. The `ProjectQBackend` exposes this functionality to take in OpenFermion `QubitOperator` instances. These are convertible to and from `QubitPauliOperator` instances in Pytket. -# -# Note: ProjectQ can also produce statevectors in the style of `AerStateBackend`, and similarly Aer backends can calculate expectation values directly, consult the relevant documentation to see more. -# -# Let's create an OpenFermion `QubitOperator` object and a new circuit: - -import openfermion as of - -hamiltonian = 0.5 * of.QubitOperator("X0 X2") + 0.3 * of.QubitOperator("Z0") - -circ2 = Circuit(3) -circ2.Y(0) -circ2.H(1) -circ2.Rx(0.3, 2) - -# We convert the OpenFermion Hamiltonian into a pytket QubitPauliOperator: -from pytket.pauli import Pauli, QubitPauliString -from pytket.utils.operators import QubitPauliOperator - - -pauli_sym = {"I": Pauli.I, "X": Pauli.X, "Y": Pauli.Y, "Z": Pauli.Z} - - -def qps_from_openfermion(paulis): - """Convert OpenFermion tensor of Paulis to pytket QubitPauliString.""" - qlist = [] - plist = [] - for q, p in paulis: - qlist.append(Qubit(q)) - plist.append(pauli_sym[p]) - return QubitPauliString(qlist, plist) - - -def qpo_from_openfermion(openf_op): - """Convert OpenFermion QubitOperator to pytket QubitPauliOperator.""" - tk_op = dict() - for term, coeff in openf_op.terms.items(): - string = qps_from_openfermion(term) - tk_op[string] = coeff - return QubitPauliOperator(tk_op) - - -hamiltonian_op = qpo_from_openfermion(hamiltonian) - -# Now we can create a `ProjectQBackend` instance and feed it our circuit and `QubitOperator`: - -from pytket.utils.operators import QubitPauliOperator - -projectq_b = ProjectQBackend() -expectation = projectq_b.get_operator_expectation_value(circ2, hamiltonian_op) -print(expectation) - -# The last leg of this tour includes running a pytket circuit on an actual quantum computer. To do this, you will need an IBM quantum experience account and have your credentials stored on your computer. See https://quantum-computing.ibm.com to make an account and view available devices and their specs. -# -# Physical devices have much stronger constraints on the form of admissible circuits than simulators. They tend to support a minimal gate set, have restricted connectivity between qubits for two-qubit gates, and can have limited support for classical control flow or conditional gates. This is where we can invoke the tket compiler passes to transform our desired circuit into one that is suitable for the backend. -# -# To check our code works correctly, we can use the `IBMQEmulatorBackend` to run our code exactly as if it were going to run on a real device, but just execute on a simulator (with a basic noise model adapted from the reported device properties). - - -# Let's create an `IBMQEmulatorBackend` for the `ibmq_manila` device and check if our circuit is valid to be run. - -ibmq_b_emu = IBMQEmulatorBackend("ibmq_manila") -ibmq_b_emu.valid_circuit(circ) - -# It looks like we need to compile this circuit to be compatible with the device. To simplify this procedure, we provide minimal compilation passes designed for each backend (the `default_compilation_pass()` method) which will guarantee compatibility with the device. These may still fail if the input circuit has too many qubits or unsupported usage of conditional gates. The default passes can have their degree of optimisation by changing an integer parameter (optimisation levels 0, 1, 2), and they can be easily composed with any of tket's other optimisation passes for better performance. -# -# For convenience, we also wrap up this pass into the `get_compiled_circuit` method if you just want to compile a single circuit. - -compiled_circ = ibmq_b_emu.get_compiled_circuit(circ) - - -# Let's create a backend for running on the actual device and check our compiled circuit is valid for this backend too. - -ibmq_b = IBMQBackend("ibmq_manila") -ibmq_b.valid_circuit(compiled_circ) - -# We are now good to run this circuit on the device. After submitting, we can use the handle to check on the status of the job, so that we know when results are ready to be retrieved. The `circuit_status` method works for all backends, and returns a `CircuitStatus` object. If we just run `get_result` straight away, the backend will wait for results to complete, blocking any other code from running. -# -# In this notebook we will use the emulated backend `ibmq_b_emu` to illustrate, but the workflow is the same as for the real backend `ibmq_b` (except that the latter will typically take much longer because of the size of the queue). - -quantum_handle = ibmq_b_emu.process_circuit(compiled_circ, n_shots=10) - -print(ibmq_b_emu.circuit_status(quantum_handle)) - -quantum_counts = ibmq_b_emu.get_result(quantum_handle).get_counts() -print(quantum_counts) - -# These are from an actual device, so it's impossible to perfectly predict what the results will be. However, because of the problem of noise, it would be unsurprising to find a few $01$ or $10$ results in the table. The circuit is very short, so it should be fairly close to the ideal result. -# -# The devices available through the IBM Q Experience serve jobs one at a time from their respective queues, so a large amount of experiment time can be taken up by waiting for your jobs to reach the front of the queue. `pytket` allows circuits to be submitted to any backend in a single batch using the `process_circuits` method. For the `IBMQBackend`, this will collate the circuits into as few jobs as possible which will all be sent off into the queue for the device. The method returns a `ResultHandle` per submitted circuit, in the order of submission. - -circuits = [] -for i in range(5): - c = Circuit(2) - c.Rx(0.2 * i, 0).CX(0, 1) - c.measure_all() - circuits.append(ibmq_b_emu.get_compiled_circuit(c)) -handles = ibmq_b_emu.process_circuits(circuits, n_shots=100) -print(handles) - -# We can now retrieve the results and process them. As we measured each circuit in the $Z$-basis, we can obtain the expectation value for the $ZZ$ operator immediately from these measurement results. We can calculate this using the `expectation_from_counts` utility method in `pytket`. - -from pytket.utils import expectation_from_counts - -for handle in handles: - counts = ibmq_b_emu.get_result(handle).get_counts() - exp_val = expectation_from_counts(counts) - print(exp_val) - -# A `ResultHandle` can be easily stored in its string representaton and later reconstructed using the `from_str` method. For example, we could do something like this: - -from pytket.backends import ResultHandle - -c = Circuit(2).Rx(0.5, 0).CX(0, 1).measure_all() -c = ibmq_b_emu.get_compiled_circuit(c) -handle = ibmq_b_emu.process_circuit(c, n_shots=10) -handlestring = str(handle) -print(handlestring) -# ... later ... -oldhandle = ResultHandle.from_str(handlestring) -print(ibmq_b_emu.get_result(oldhandle).get_counts()) - -# For backends which support persistent handles (e.g. `IBMQBackend`, `QuantinuumBackend`, `BraketBackend` and `AQTBackend`) you can even stop your python session and use your result handles in a separate script to retrive results when they are ready, by storing the handle strings. For experiments with long queue times, this enables separate job submission and retrieval. Use `Backend.persistent_handles` to check whether a backend supports this feature. -# -# All backends will also cache all results obtained in the current python session, so you can use the `ResultHandle` to retrieve the results many times if you need to reuse the results. Over a long experiment, this can consume a large amount of RAM, so we recommend removing results from the cache when you are done with them. A simple way to achieve this is by calling `Backend.empty_cache` (e.g. at the end of each loop of a variational algorithm), or removing individual results with `Backend.pop_result`. - -# The backends in `pytket` are designed to be as similar to one another as possible. The example above using physical devices can be run entirely on a simulator by swapping out the `IBMQBackend` constructor for any other backend supporting shot outputs (e.g. `AerBackend`, `ProjectQBackend`, `ForestBackend`), or passing it the name of a different device. Furthermore, using pytket it is simple to convert between handling shot tables, counts maps and statevectors. -# -# For more information on backends and other `pytket` features, read our [documentation](https://tket.quantinuum.com/api-docs/) or see the other pytket examples. diff --git a/examples/python/circuit_analysis_example.py b/examples/python/circuit_analysis_example.py deleted file mode 100644 index c4ec0ca1..00000000 --- a/examples/python/circuit_analysis_example.py +++ /dev/null @@ -1,112 +0,0 @@ -# # Circuit analysis - -# This notebook will introduce the basic methods of analysis and visualization of circuits available in `pytket`. -# -# It makes use of the modules `pytket_qiskit` and `pytket_cirq` for visualization; these need to be installed (with `pip`) in addition to `pytket`. -# -# We'll start by generating a small circuit to use as an example, and give it a name. - -from pytket.circuit import Circuit, OpType - -c = Circuit(4, name="example") -c.add_gate(OpType.CU1, 0.5, [0, 1]) -c.H(0).X(1).Y(2).Z(3) -c.X(0).CX(1, 2).Y(1).Z(2).H(3) -c.Y(0).Z(1) -c.add_gate(OpType.CU1, 0.5, [2, 3]) -c.H(2).X(3) -c.Z(0).H(1).X(2).Y(3).CX(3, 0) - -# ## Basic statistics - -# From the circuit we can easily read off the number of qubits ... - -c.n_qubits - -# ... the name ... - -c.name - -# ... the overall depth of the circuit ... - -c.depth() - -# ... and the depth by type of gate: - -from pytket.circuit import OpType - -c.depth_by_type(OpType.CU1), c.depth_by_type(OpType.H) - -# This last method counts the number of instances of the specified gate type that cannot be parallelized. Notice that although there are 4 H gates in the circuit, the H-depth is 2 because pairs of them can be parallelized (as will be clear from the visualizations below). - -# ## Visualization - -# There are several ways to produce useful visualizations of circuits from `pytket`: we can use the methods in the `pytket.circuit.display` class; we can use the built-in `Graph` class to visualize the circuit as a directed acyclic graph (DAG); we can convert the circuit to either Qiskit or Cirq and use the tools provided by those modules; or we can export to Latex. - -# ### Via the `render_circuit_jupyter` method - -from pytket.circuit.display import render_circuit_jupyter - -render_circuit_jupyter(c) - -# Notice that although the `render_circuit_jupyter` method is the recommended way to render a circuit as jupyter cell output, one of the other methods should be used when working with scripts or python shells. - -# ### Via the `Graph` class - -from pytket.utils import Graph - -G = Graph(c) -G.get_DAG() - -# The small numbers (0 and 1) shown at the entry to and exit from the two-qubit gates represent "port numbers" on the gates; these allow us to track individual qubits, which may be placed in a different order on entry and exit in order to simplify the layout. -# -# The `Graph` class also has methods to save this image to a file and to open it in a PDF viewer. -# -# We can also view the qubit connectivity graph of a circuit: - -G.get_qubit_graph() - -# ### Via Qiskit - -from pytket.extensions.qiskit import tk_to_qiskit - -print(tk_to_qiskit(c)) - -# ### Via Cirq - -from pytket.extensions.cirq import tk_to_cirq - -print(tk_to_cirq(c)) - -# (Note that Cirq cannot represent all gates diagrammatically.) - -# ### Via Latex - -# We can create a Latex document containing a diagram of the circuit using the `to_latex_file()` method. This uses the `quantikz` library. The document can be viewed on its own or the Latex can easily be copied and pasted into a larger document. - -c.to_latex_file("c.tex") - -# ## Commands - -# We can retrieve a list of operations comprising a circuit, each represented as a `Command` object: - -cmds = c.get_commands() -print(cmds) - -# Each `Command` is defined by an operation and the qubits it operates on (as well as the classical bits and conditions, if any). For example: - -cmd0 = cmds[0] -op0 = cmd0.op -print(op0) -qubits0 = cmd0.args -print(qubits0) - -# From the `Op` we can read off the string representation (in normal or Latex form), the parameters and the type: - -op0.get_name() # normal form - -op0.get_name(latex=True) # Latex form - -op0.type, op0.params - -# Note that some compilation passes introduce implicit wire swaps at the end of the circuit, which are not represented in the command list. (The internal representation of the circuit as a directed acyclic graph reduces explicit permutations of qubits to implicit features of the graph.) diff --git a/examples/python/circuit_generation_example.py b/examples/python/circuit_generation_example.py deleted file mode 100644 index 886afa41..00000000 --- a/examples/python/circuit_generation_example.py +++ /dev/null @@ -1,354 +0,0 @@ -# # Circuit generation - -# This notebook will provide a brief introduction to some of the more advanced methods of circuit generation available in `pytket`, including: -# * how to address wires and registers; -# * reading in circuits from QASM and Quipper ASCII files; -# * various types of 'boxes'; -# * composition of circuits (both 'horizontally' and 'vertically'); -# * use of symbolic gate parameters; -# * representation of classically controlled gates. - -# ## Wires, unit IDs and registers - -# Let's get started by constructing a circuit with 3 qubits and 2 classical bits: - -from pytket.circuit import Circuit -from pytket.circuit.display import render_circuit_jupyter as draw - -circ = Circuit(1, 2) -print(circ.qubits) -print(circ.bits) - -# The qubits have automatically been assigned to a register with name `q` and indices 0, 1 and 2, while the bits have been assigned to a register with name `c` and indices 0 and 1. -# -# We can give these units arbitrary names and indices of arbitrary dimension: - -from pytket.circuit import Qubit - -new_q1 = Qubit("alpha", 0) -new_q2 = Qubit("beta", 2, 1) -circ.add_qubit(new_q1) -circ.add_qubit(new_q2) -print(circ.qubits) - -# We can also add a new register of qubits in one go: - -delta_reg = circ.add_q_register("delta", 2) -print(circ.qubits) - -# Similar commands are available for classical bits. -# -# We can add gates to the circuit as follows: - -circ.CX(delta_reg[0], delta_reg[1]) - -# This command appends a CX gate with control `q[0]` and target `q[1]`. Note that the integer arguments are automatically converted to the default unit IDs. For simple circuits it is often easiest to stick to the default register and refer to the qubits by integers. To add gates to our own named units, we simply pass the `Qubit` (or classical `Bit`) as an argument. (We can't mix the two conventions in one command, however.) - -circ.H(new_q1) -circ.CX(Qubit("q", 0), new_q2) -circ.Rz(0.5, new_q2) - -# Let's have a look at our circuit using the interactive circuit renderer: -from pytket.circuit.display import render_circuit_jupyter as draw - -draw(circ) - -# ## Exporting to and importing from standard formats - -# We can export a `Circuit` to a file in QASM format. Conversely, if we have such a file we can import it into `pytket`. There are some limitations on the circuits that can be converted: for example, multi-dimensional indices (as in `beta` and `gamma` above) are not allowed. -# -# Here is a simple example: - -from pytket.qasm import circuit_from_qasm, circuit_to_qasm - -circ = Circuit(3, 1) -circ.H(0) -circ.CX(0, 1) -circ.CX(1, 2) -circ.Rz(0.25, 2) -circ.Measure(2, 0) -draw(circ) - -qasmfile = "c.qasm" -circuit_to_qasm(circ, qasmfile) - -with open(qasmfile, encoding="utf-8") as f: - print(f.read()) - -c1 = circuit_from_qasm(qasmfile) -circ == c1 - -# We can also import files in the Quipper ASCII format: - -from pytket.quipper import circuit_from_quipper - -quipfile = "c.quip" -with open(quipfile, "w", encoding="utf-8") as f: - f.write( - """Inputs: 0:Qbit, 1:Qbit -QGate["W"](0,1) -QGate["omega"](1) -QGate["swap"](0,1) -QGate["W"]*(1,0) -Outputs: 0:Qbit, 1:Qbit -""" - ) - -c = circuit_from_quipper(quipfile) -draw(c) - -# Note that the Quipper gates that are not supported directly in `pytket` (`W` and `omega`) are translated into equivalent sequences of `pytket` gates. See the [pytket.quipper](https://tket.quantinuum.com/api-docs/quipper.html) docs for more. -# -# Quipper subroutines are also supported, corresponding to `CircBox` operations in `pytket`: - -with open(quipfile, "w", encoding="utf-8") as f: - f.write( - """Inputs: 0:Qbit, 1:Qbit, 2:Qbit -QGate["H"](0) -Subroutine(x2)["sub", shape "([Q,Q],())"] (2,1) -> (2,1) -QGate["H"](1) -Outputs: 0:Qbit, 1:Qbit, 2:Qbit \n -Subroutine: "sub" -Shape: "([Q,Q],())" -Controllable: no -Inputs: 0:Qbit, 1:Qbit -QGate["Y"](0) -QGate["not"](1) with controls=[+0] -QGate["Z"](1) -Outputs: 0:Qbit, 1:Qbit -""" - ) - -c = circuit_from_quipper(quipfile) - -draw(c) - -# ## Boxes - -# The `CircBox` is an example of a `pytket` 'box', which is a reusable encapsulation of a circuit inside another. We can recover the circuit 'inside' the box using the `get_circuit()` method: -cmds = c.get_commands() -boxed_circuit = cmds[1].op.get_circuit() -draw(boxed_circuit) - -# The `CircBox` is the most general type of box, implementing an arbitrary circuit. But `pytket` supports several other useful box types: -# * [Unitary1qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary1qBox) (implementing an arbitrary $2 \times 2$ unitary matrix); -# * [Unitary2qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary2qBox) (implementing an arbitrary $4 \times 4$ unitary matrix); -# * [ExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.ExpBox) (implementing $e^{itA}$ for an arbitrary $4 \times 4$ hermitian matrix $A$ and parameter $t$); -# * [PauliExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.PauliExpBox) (implementing $e^{-\frac{1}{2} i \pi t (\sigma_0 \otimes \sigma_1 \otimes \cdots)}$ for arbitrary Pauli operators $\sigma_i \in \{\mathrm{I}, \mathrm{X}, \mathrm{Y}, \mathrm{Z}\}$ and parameter $t$). - -# An example will illustrate how these various box types are added to a circuit: - -from math import sqrt - -import numpy as np -from pytket.circuit import CircBox, ExpBox, PauliExpBox, Unitary1qBox, Unitary2qBox -from pytket.pauli import Pauli - -boxycirc = Circuit(3) - -# Add a `CircBox`: - -subcirc = Circuit(2, name="MY BOX") -subcirc.X(0).Y(1).CZ(0, 1) -cbox = CircBox(subcirc) -boxycirc.add_gate(cbox, args=[Qubit(0), Qubit(1)]) - -# Add a `Unitary1qBox`: - -m1 = np.asarray([[1 / 2, sqrt(3) / 2], [sqrt(3) / 2, -1 / 2]]) -m1box = Unitary1qBox(m1) -boxycirc.add_unitary1qbox(m1box, 2) - -# Add a `Unitary2qBox`: - -m2 = np.asarray([[0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0]]) -m2box = Unitary2qBox(m2) -boxycirc.add_unitary2qbox(m2box, 1, 2) - -# Add an `ExpBox`: - -A = np.asarray( - [[1, 2, 3, 4 + 1j], [2, 0, 1j, -1], [3, -1j, 2, 1j], [4 - 1j, -1, -1j, 1]] -) -ebox = ExpBox(A, 0.5) -boxycirc.add_expbox(ebox, 0, 1) - -# Add a `PauliExpBox`: - -pbox = PauliExpBox([Pauli.X, Pauli.Z, Pauli.X], 0.75) -boxycirc.add_gate(pbox, [0, 1, 2]) - -draw(boxycirc) - -# Try clicking on boxes in the diagram above to get information about the underlying subroutine. - -# The `get_circuit()` method is available for all box types, and returns a `Circuit` object. For example if we look inside the `ExpBox`: - -draw(ebox.get_circuit()) - -# ## Circuit composition - -# For more discussion of circuit composition see the corresponding section of the [manual](https://tket.quantinuum.com/user-manual/manual_circuit.html#composing-circuits). - -# Circuits can be composed either serially, whereby wires are joined together, or in parallel, using the `append()` command. -# -# For a simple illustration of serial composition, let's create two circuits with compatible set of wires, and append the second to the first: - -c = Circuit(2) -c.CX(0, 1) - -c1 = Circuit(2) -c1.CZ(1, 0) - -c.append(c1) - -draw(c) - -# In the above example, there was a one-to-one match between the unit IDs in the two circuits, and they were matched up accordingly. The same applied with named unit IDs: - -x, y = Qubit("x"), Qubit("y") - -c = Circuit() -c.add_qubit(x) -c.add_qubit(y) -c.CX(x, y) - -c1 = Circuit() -c1.add_qubit(x) -c1.add_qubit(y) -c1.CZ(y, x) - -c.append(c1) - -draw(c) - -# If either circuit contains wires not matching any wires in the other, those are added to the other circuit before composition: - -z = Qubit("z") -c1.add_qubit(z) -c1.CY(y, z) -c.append(c1) -print(c.qubits) -draw(c) - -# If the sets of unit IDs for the two circuits are disjoint, then the composition is entirely parallel. -# -# What if we want to serially compose two circuits having different sets of `Qubit`? In that case, we can use the `rename_units()` method on one or other of them to bring them into line. This method takes a dictionary mapping current unit IDs to new one: - -c2 = Circuit() -c2.add_q_register("w", 3) -w = [Qubit("w", i) for i in range(3)] -c2.H(w[0]).CX(w[0], w[1]).CRz(0.25, w[1], w[2]) - -c.rename_units({x: w[0], y: w[1], z: w[2]}) - -c.append(c2) - -draw(c) - -# ## Symbolic parameters - -# Many of the gates supported by `pytket` are parametrized by one or more phase parameters, which represent rotations in multiples of $\pi$. For example, $\mathrm{Rz}(\frac{1}{2})$ represents a quarter turn, i.e. a rotation of $\pi/2$, about the Z axis. If we know the values of these parameters we can add the gates directly to our circuit: - -c = Circuit(1) -c.Rz(0.5, 0) - -# However, we may wish to construct and manipulate circuits containing such parametrized gates without specifying the values. This allows us to do calculations in a general setting, only later substituting values for the parameters. -# -# Thus `pytket` allows us to specify any of the parameters as symbols. All manipulations (such as combination and cancellation of gates) are performed on the symbolic representation: - -from sympy import Symbol - -a = Symbol("a") -c.Rz(a, 0) - -draw(c) - -# When we apply any transformation to this circuit, the symbolic parameter is preserved in the result: - -from pytket.transform import Transform - -Transform.RemoveRedundancies().apply(c) - -draw(c) - -# To substitute values for symbols, we use the `symbol_substitution()` method, supplying a dictionary from symbols to values: - -c.symbol_substitution({a: 0.75}) - -draw(c) - -# We can also substitute symbols for other symbols: - -b = Symbol("b") -c = Circuit(1) -c.Rz(a + b, 0) -c.symbol_substitution({b: 2 * a}) -draw(c) - -# ## Custom gates - -# We can define custom parametrized gates in `pytket` by first setting up a circuit containing symbolic parameters and then converting this to a parametrized operation type: - -from pytket.circuit import CustomGateDef - -a = Symbol("a") -b = Symbol("b") -setup = Circuit(3) -setup.CX(0, 1) -setup.Rz(a + 0.5, 2) -setup.CRz(b, 0, 2) -my_gate = CustomGateDef.define("g", setup, [a, b]) -c = Circuit(4) -c.add_custom_gate(my_gate, [0.2, 1.3], [0, 3, 1]) -draw(c) - -# Custom gates can also receive symbolic parameters: - -x = Symbol("x") -c.add_custom_gate(my_gate, [x, 1.0], [0, 1, 2]) -draw(c) - -# ## Decomposing boxes and custom gates - -# Having defined a circuit containing custom gates, we may now want to decompose it into elementary gates. The `DecomposeBoxes()` transform allows us to do this: - -Transform.DecomposeBoxes().apply(c) -draw(c) - -# The same transform works on circuits composed of arbitrary boxes. Let's try it on a copy of the circuit we built up earlier out of various box types. - -c = boxycirc.copy() -Transform.DecomposeBoxes().apply(c) -draw(c) - -# Note that the unitaries have been decomposed into elementary gates. - -# ## Classical controls - -# Most of the examples above involve only pure quantum gates. However, `pytket` can also represent gates whose operation is conditional on one or more classical inputs. -# -# For example, suppose we want to run the complex circuit `c` we've just constructed, then measure qubits 0 and 1, and finally apply an $\mathrm{Rz}(\frac{1}{2})$ rotation to qubit 2 if and only if the measurements were 0 and 1 respectively. -# -# First, we'll add two classical wires to the circuit to store the measurement results: - -from pytket.circuit import Bit - -c.add_c_register("m", 2) -m = [Bit("m", i) for i in range(2)] - -# Classically conditioned operations depend on all their inputs being 1. Since we want to condition on `m[0]` being 0, we must first apply an X gate to its qubit, and then measure: - -q = [Qubit("q", i) for i in range(3)] -c.X(q[0]) -c.Measure(q[0], m[0]) -c.Measure(q[1], m[1]) - -# Finally we add the classically conditioned Rz operation, using the `add_gate()` method: - -from pytket.circuit import OpType - -c.add_gate(OpType.Rz, [0.5], [q[2]], condition_bits=[m[0], m[1]], condition_value=3) -draw(c) - -# Note that many of the transforms and compilation passes will not accept circuits that contain classical controls. diff --git a/examples/python/comparing_simulators.py b/examples/python/comparing_simulators.py deleted file mode 100644 index 909ae078..00000000 --- a/examples/python/comparing_simulators.py +++ /dev/null @@ -1,286 +0,0 @@ -# # Comparison of the simulators available through TKET - -# In this tutorial, we will focus on: -# - exploring the wide array of simulators available through the extension modules for `pytket`; -# - comparing their unique features and capabilities. - -# This example assumes the reader is familiar with the basics of circuit construction and evaluation. -# -# To run every option in this example, you will need `pytket`, `pytket-qiskit`, `pytket-pyquil`, `pytket-qsharp`, `pytket-qulacs`, and `pytket-projectq`. -# -# With the number of simulator `Backend`s available across the `pytket` extension modules, we are often asked why to use one over another. Surely, any two simulators are equivalent if they are able to sample the circuits in the same way, right? Not quite. In this notebook we go through each of the simulators in turn and describe what sets them apart from others and how to make use of any unique features. -# -# But first, to demonstrate the significant overlap in functionality, we'll just give some examples of common usage for different types of backends. - -# ## Sampling simulator usage - -from pytket import Circuit -from pytket.extensions.qiskit import AerBackend - -# Define a circuit: - -c = Circuit(3, 3) -c.Ry(0.7, 0) -c.CX(0, 1) -c.X(2) -c.measure_all() - -# Run on the backend: - -backend = AerBackend() -c = backend.get_compiled_circuit(c) -handle = backend.process_circuit(c, n_shots=2000) -counts = backend.get_result(handle).get_counts() -print(counts) - -# ## Statevector simulator usage - -from pytket import Circuit -from pytket.extensions.qiskit import AerStateBackend - -# Build a quantum state: - -c = Circuit(3) -c.H(0).CX(0, 1) -c.Rz(0.3, 0) -c.Rz(-0.3, 1) -c.Ry(0.8, 2) - -# Examine the statevector: - -backend = AerStateBackend() -c = backend.get_compiled_circuit(c) -handle = backend.process_circuit(c) -state = backend.get_result(handle).get_state() -print(state) - -# ## Expectation value usage - -from pytket import Circuit, Qubit -from pytket.extensions.qiskit import AerBackend, AerStateBackend -from pytket.pauli import Pauli, QubitPauliString -from pytket.utils.operators import QubitPauliOperator - -# Build a quantum state: - -c = Circuit(3) -c.H(0).CX(0, 1) -c.Rz(0.3, 0) -c.Rz(-0.3, 1) -c.Ry(0.8, 2) - -# Define the measurement operator: - -xxi = QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X}) -zzz = QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z, Qubit(2): Pauli.Z}) -op = QubitPauliOperator({xxi: -1.8, zzz: 0.7}) - -# Run on the backend: - -backend = AerBackend() -c = backend.get_compiled_circuit(c) -exp = backend.get_operator_expectation_value(c, op) -print(exp) - -# ## `AerBackend` - -# `AerBackend` wraps up the `qasm_simulator` from the Qiskit Aer package. It supports an extremely flexible set of circuits and uses many effective simulation methods making it a great all-purpose sampling simulator. -# -# Unique features: -# - supports mid-circuit measurement and OpenQASM-style conditional gates; -# - encompasses a variety of underlying simulation methods and automatically selects the best one for each circuit (including statevector, density matrix, (extended) stabilizer and matrix product state); -# - can be provided with a `qiskit.providers.Aer.noise.NoiseModel` on instantiation to perform a noisy simulation. - -# Useful features: -# - support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s. - -from pytket import Circuit -from pytket.extensions.qiskit import AerBackend -from itertools import combinations -from qiskit_aer.noise import NoiseModel, depolarizing_error - -# Quantum teleportation circuit: - -c = Circuit() -alice = c.add_q_register("a", 2) -bob = c.add_q_register("b", 1) -data = c.add_c_register("d", 2) -final = c.add_c_register("f", 1) - -# Start in an interesting state: - -c.Rx(0.3, alice[0]) - -# Set up a Bell state between Alice and Bob: - -c.H(alice[1]).CX(alice[1], bob[0]) - -# Measure Alice's qubits in the Bell basis: - -c.CX(alice[0], alice[1]).H(alice[0]) -c.Measure(alice[0], data[0]) -c.Measure(alice[1], data[1]) - -# Correct Bob's qubit: - -c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=1) -c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3) -c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=2) -c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3) - -# Measure Bob's qubit to observe the interesting state: - -c.Measure(bob[0], final[0]) - -# Set up a noisy simulator: - -model = NoiseModel() -dep_err = depolarizing_error(0.04, 2) -for i, j in combinations(range(3), r=2): - model.add_quantum_error(dep_err, ["cx"], [i, j]) - model.add_quantum_error(dep_err, ["cx"], [j, i]) -backend = AerBackend(noise_model=model) - -# Run circuit: - -c = backend.get_compiled_circuit(c) -handle = backend.process_circuit(c, n_shots=2000) -result = backend.get_result(handle) -counts = result.get_counts([final[0]]) -print(counts) - -# ## `AerStateBackend` - -# `AerStateBackend` provides access to Qiskit Aer's `statevector_simulator`. It supports a similarly large gate set and has competitive speed for statevector simulations. -# -# Useful features: -# - no dependency on external executables, making it easy to install and run on any computer; -# - support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s. - -# ## `AerUnitaryBackend` - -# Finishing the set of simulators from Qiskit Aer, `AerUnitaryBackend` captures the `unitary_simulator`, allowing for the entire unitary of a pure quantum process to be calculated. This is especially useful for testing small subcircuits that will be used many times in a larger computation. -# -# Unique features: -# - provides the full unitary matrix for a pure quantum circuit. - -from pytket import Circuit -from pytket.extensions.qiskit import AerUnitaryBackend -from pytket.predicates import NoClassicalControlPredicate - -# Define a simple quantum incrementer: - -c = Circuit(3) -c.CCX(2, 1, 0) -c.CX(2, 1) -c.X(2) - -# Examine the unitary: - -backend = AerUnitaryBackend() -c = backend.get_compiled_circuit(c) -result = backend.run_circuit(c) -unitary = result.get_unitary() -print(unitary.round(1).real) - -# ## `ForestBackend` - -# Whilst it can, with suitable credentials, be used to access the Rigetti QPUs, the `ForestBackend` also features a simulator mode which turns it into a noiseless sampling simulator that matches the constraints of the simulated device (e.g. the same gate set, restricted connectivity, measurement model, etc.). This is useful when playing around with custom compilation strategies to ensure that your final circuits are suitable to run on the device and for checking that your overall program works fine before you invest in reserving a QPU. -# -# Unique features: -# - faithful recreation of the circuit constraints of Rigetti QPUs. - -# If trying to use the `ForestBackend` locally (i.e. not on a Rigetti QMI), you will need to have `quilc` and `qvm` running as separate processes in server mode. One easy way of doing this is with `docker` (see the `quilc` and `qvm` documentation for alternative methods of running them): -# `docker run --rm -it -p 5555:5555 rigetti/quilc -R` -# `docker run --rm -it -p 5000:5000 rigetti/qvm -S` - -# ## `ForestStateBackend` - -# The Rigetti `pyquil` package also provides the `WavefunctionSimulator`, which we present as the `ForestStateBackend`. Functionally, it is very similar to the `AerStateBackend` so can be used interchangeably. It does require that `quilc` and `qvm` are running as separate processes when not running on a Rigetti QMI. -# -# Useful features: -# - support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s. - -# ## `QsharpSimulatorBackend` - -# The `QsharpSimulatorBackend` is another basic sampling simulator that is interchangeable with others, using the Microsoft QDK simulator. Note that the `pytket-qsharp` package is dependent on the `dotnet` SDK and `iqsharp` tool. Please consult the `pytket-qsharp` installation instructions for recommendations. - -# ## `QsharpToffoliSimulatorBackend` - -# Toffoli circuits form a strict fragment of quantum circuits and can be efficiently simulated. The `QsharpToffoliSimulatorBackend` can only operate on these circuits, but scales much better with system size than regular simulators. -# -# Unique features: -# - efficient simulation of Toffoli circuits. - -from pytket import Circuit -from pytket.extensions.qsharp import QsharpToffoliSimulatorBackend - -# Define a circuit - start in a basis state: - -c = Circuit(3) -c.X(0).X(2) -# Define a circuit - incrementer -c.CCX(2, 1, 0) -c.CX(2, 1) -c.X(2) - -# Run on the backend: - -backend = QsharpToffoliSimulatorBackend() -c = backend.get_compiled_circuit(c) -handle = backend.process_circuit(c, n_shots=10) -counts = backend.get_result(handle).get_counts() -print(counts) - -# ## `QsharpEstimatorBackend` - -# The `QsharpEstimatorBackend` is not strictly a simulator, as it doesn't model the state of the quantum system and try to identify the final state, but instead analyses the circuit to estimate the required resources to run it. It does not support any of the regular outcome types (e.g. shots, counts, statevector), just the summary of the estimated resources. -# -# Unique features: -# - estimates resources to perform the circuit, without actually simulating/running it. - -from pytket import Circuit - -# from pytket.extensions.qsharp import QsharpEstimatorBackend - -# Define a circuit - start in a basis state: - -c = Circuit(3) -c.X(0).X(2) -# Define a circuit - incrementer -c.CCX(2, 1, 0) -c.CX(2, 1) -c.X(2) - -# Run on the backend: - -# (disabled because of https://github.com/CQCL/pytket-qsharp/issues/37) -# backend = QsharpEstimatorBackend() -# c = backend.get_compiled_circuit(c) -# handle = backend.process_circuit(c, n_shots=10) -# resources = backend.get_resources(handle) -# print(resources) - -# ## `QulacsBackend` - -# The `QulacsBackend` is an all-purpose simulator with both sampling and statevector modes, using the basic CPU simulator from Qulacs. -# -# Unique features: -# - supports both sampling (shots/counts) and complete statevector outputs. - -# Useful features: -# - support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s. - -# ## `QulacsGPUBackend` - -# If the GPU version of Qulacs is installed, the `QulacsGPUBackend` will use that to benefit from even faster speeds. It is very easy to get started with using a GPU, as it only requires a CUDA installation and the `qulacs-gpu` package from `pip`. Functionally, it is identical to the `QulacsBackend`, but potentially faster if you have GPU resources available. -# -# Unique features: -# - GPU support for very fast simulation. - -# ## `ProjectQBackend` - -# ProjectQ is a popular quantum circuit simulator, thanks to its availability and ease of use. It provides a similar level of performance and features to `AerStateBackend`. -# -# Useful features: -# - support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s. diff --git a/examples/python/compilation_example.py b/examples/python/compilation_example.py deleted file mode 100644 index 42ce105c..00000000 --- a/examples/python/compilation_example.py +++ /dev/null @@ -1,272 +0,0 @@ -# # Compilation passes - -# There are numerous ways to optimize circuits in `pytket`. In this notebook we will introduce the basics of compilation passes and how to combine and apply them. -# -# We assume familiarity with the `pytket` `Circuit` class. The objective is to transform one `Circuit` into another, equivalent, `Circuit`, that: -# * satisfies the connectivity constraints of a given architecture; -# * satisfies some further user-defined constraints (such as restricted gate sets); -# * minimizes some cost function (such as CX count). - -# ## Passes - -# The basic mechanism of compilation is the 'pass', which is a transform that can be applied to a circuit. There is an extensive library of passes in `pytket`, and several standard ways in which they can be combined to form new passes. For example: - -from pytket.passes import DecomposeMultiQubitsCX - -pass1 = DecomposeMultiQubitsCX() - -# This pass converts all multi-qubit gates into CX and single-qubit gates. So let's create a circuit containing some non-CX multi-qubit gates: - -from pytket.circuit import Circuit - -circ = Circuit(3) -circ.CRz(0.5, 0, 1) -circ.T(2) -circ.CSWAP(2, 0, 1) - -# In order to apply a pass to a circuit, we must first create a `CompilationUnit` from it. We can think of this as a 'bridge' between the circuit and the pass. The `CompilationUnit` is constructed from the circuit; the pass is applied to the `CompilationUnit`; and the transformed circuit is extracted from the `CompilationUnit`: - -from pytket.predicates import CompilationUnit - -cu = CompilationUnit(circ) -pass1.apply(cu) -circ1 = cu.circuit - -# Let's have a look at the result of the transformation: - -print(circ1.get_commands()) - -# ## Predicates - -# Every `CompilationUnit` has associated with it a set of 'predicates', which describe target properties that can be checked against the circuit. There are many types of predicates available in `pytket`. For example, the `GateSetPredicate` checks whether all gates in a circuit belong to a particular set: - -from pytket.predicates import GateSetPredicate -from pytket.circuit import OpType - -pred1 = GateSetPredicate({OpType.Rz, OpType.T, OpType.Tdg, OpType.H, OpType.CX}) - -# When we construct a `CompilationUnit`, we may pass a list of target predicates as well as the circuit: - -cu = CompilationUnit(circ, [pred1]) - -# To check whether the circuit associated to a `CompilationUnit` satisfies its target predicates, we can call the `check_all_predicates()` method: - -cu.check_all_predicates() - -pass1.apply(cu) -cu.check_all_predicates() - -# We can also directly check whether a given circuit satisfies a given predicate, using the predicate's `verify()` method: - -pred1.verify(circ1) - -# ### In-place compilation - -# The example above produced a new circuit, leaving the original circuit untouched. It is also possible to apply a pass to a circuit in-place: - -DecomposeMultiQubitsCX().apply(circ) -print(circ.get_commands()) - -# ## Combining passes - -# There are various ways to combine the elementary passes into more complex ones. -# -# To combine several passes in sequence, we use a `SequencePass`: - -from pytket.passes import SequencePass, OptimisePhaseGadgets - -seqpass = SequencePass([DecomposeMultiQubitsCX(), OptimisePhaseGadgets()]) - -# This pass will apply the two transforms in succession: - -cu = CompilationUnit(circ) -seqpass.apply(cu) -circ1 = cu.circuit -print(circ1.get_commands()) - -# The `apply()` method for an elementary pass returns a boolean indicating whether or not the pass had any effect on the circuit. For a `SequencePass`, the return value indicates whether _any_ of the constituent passes had some effect. -# -# A `RepeatPass` repeatedly calls `apply()` on a pass until it returns `False`, indicating that there was no effect: - -from pytket.passes import CommuteThroughMultis, RemoveRedundancies, RepeatPass - -seqpass = SequencePass([CommuteThroughMultis(), RemoveRedundancies()]) -reppass = RepeatPass(seqpass) - -# This pass will repeatedly apply `CommuteThroughMultis` (which commutes single-qubit operations through multi-qubit operations where possible towards the start of the circuit) and `RemoveRedundancies` (which cancels inverse pairs, merges coaxial rotations and removes redundant gates before measurement) until neither pass has any effect on the circuit. -# -# Let's use `pytket`'s built-in visualizer to see the effect on a circuit: - -from pytket.circuit.display import render_circuit_jupyter - -circ = Circuit(3) -circ.X(0).Y(1).CX(0, 1).Z(0).Rx(1.3, 1).CX(0, 1).Rz(0.4, 0).Ry(0.53, 0).H(1).H(2).Rx( - 1.5, 2 -).Rx(0.5, 2).H(2) - -render_circuit_jupyter(circ) - -cu = CompilationUnit(circ) -reppass.apply(cu) -circ1 = cu.circuit - -render_circuit_jupyter(circ1) - -# If we want to repeat a pass until the circuit satisfies some desired property, we first define a boolean function to test for that property, and then pass this function to the constructor of a `RepeatUntilSatisfied` pass: - -from pytket.passes import RepeatUntilSatisfiedPass - - -def no_CX(circ): - return circ.n_gates_of_type(OpType.CX) == 0 - - -circ = ( - Circuit(2) - .CX(0, 1) - .X(1) - .CX(0, 1) - .X(1) - .CX(0, 1) - .X(1) - .CX(0, 1) - .Z(1) - .CX(1, 0) - .Z(1) - .CX(1, 0) -) - -custom_pass = RepeatUntilSatisfiedPass(seqpass, no_CX) -cu = CompilationUnit(circ) -custom_pass.apply(cu) -circ1 = cu.circuit - -render_circuit_jupyter(circ1) - -# The `RepeatWithMetricPass` provides another way of generating more sophisticated passes. This is defined in terms of a cost function and another pass type; the pass is applied repeatedly until the cost function stops decreasing. -# -# For example, suppose we wish to associate a cost to each gate in out circuit, with $n$-qubit gates having a cost of $n^2$: - - -def cost(circ): - return sum(pow(len(x.args), 2) for x in circ) - - -# Let's construct a new circuit: - -circ = Circuit(2) -circ.CX(0, 1).X(1).Y(0).CX(0, 1).X(1).Z(0).CX(0, 1).X(1).Y(0).CX(0, 1).Z(1).CX(1, 0).Z( - 1 -).X(0).CX(1, 0) - -# We will repeatedly apply `CommuteThroughMultis`, `DecomposeMultiQubitsCX` and `RemoveRedundancies` until the `cost` function stops decreasing: - -from pytket.passes import RepeatWithMetricPass - -pass1 = SequencePass( - [CommuteThroughMultis(), DecomposeMultiQubitsCX(), RemoveRedundancies()] -) -pass2 = RepeatWithMetricPass(pass1, cost) - -cu = CompilationUnit(circ) -pass2.apply(cu) -print(cu.circuit.get_commands()) - -# ## Targeting architectures - -# If we are given a target architecture, we can generate passes tailored to it. -# -# In `pytket` an architecture is defined by a connectivity graph, i.e. a list of pairs of qubits capable of executing two-qubit operations. For example, we can represent a 5-qubit linear architecture, with qubits labelled `n[i]`, as follows: - -from pytket.architecture import Architecture -from pytket.circuit import Node - -n = [Node("n", i) for i in range(5)] - -arc = Architecture([[n[0], n[1]], [n[1], n[2]], [n[2], n[3]], [n[3], n[4]]]) - -# Suppose we have a circuit that we wish to run on this architecture: - -circ = Circuit(5) -circ.CX(0, 1) -circ.H(0) -circ.Z(1) -circ.CX(0, 3) -circ.Rx(1.5, 3) -circ.CX(2, 4) -circ.X(2) -circ.CX(1, 4) -circ.CX(0, 4) - -render_circuit_jupyter(circ) - -# A mapping pass lets us rewrite this circuit for our architecture: - -from pytket.passes import DefaultMappingPass - -mapper = DefaultMappingPass(arc) -cu = CompilationUnit(circ) -mapper.apply(cu) -circ1 = cu.circuit - -render_circuit_jupyter(circ1) - -# If we want to decompose all SWAP and BRIDGE gates to CX gates in the final circuit, we can use another pass: - -from pytket.passes import DecomposeSwapsToCXs - -pass1 = DecomposeSwapsToCXs(arc) -pass1.apply(cu) -circ2 = cu.circuit - -render_circuit_jupyter(circ2) - -# Note that the pass we just ran also performed some clean-up: the SWAP gate was decomposed into three CX gates, one of which was cancelled by a preceding CX gate; the cancelling gates were removed from the circuit. -# -# Every compilation pass has associated sets of preconditions and postconditions on the circuit. If all preconditions are satisfied before the pass, all postconditions are guaranteed to be satisfied afterwards. When we apply a pass to a circuit, we can optionally pass `SafetyMode.Audit` as the second parameter; this will tell the pass to check all preconditions explicitly. By default, there is only limited checking of preconditions and `pytket` relies on the programmer assuring these. -# -# For example, the `NoClassicalControl` predicate is a precondition of the `PauliSimp` pass. Let's add a classically controlled gate to our circuit: - -from pytket.passes import PauliSimp, SafetyMode -from pytket.circuit import Qubit, Bit - -q = [Qubit("q", i) for i in range(5)] -c = Bit("c") -circ.add_bit(c) -circ.Measure(q[3], c) -circ.CY(q[0], q[1], condition_bits=[c], condition_value=1) -cu = CompilationUnit(circ) -try: - PauliSimp().apply(cu, safety_mode=SafetyMode.Audit) -except RuntimeError as e: - print("Error:", str(e)) - - -# The preconditions and postconditions of all the elementary predicates are documented in their string representations: - -PauliSimp() - -# ## Backends and default passes - -# A `pytket` `Backend` may have a default compilation pass, which will guarantee that the circuit can run on it. This is given by the `default_compilation_pass` property. For example, the default pass for Qiskit's `AerBackend` just converts all gates to U1, U2, U3 and CX: - -from pytket.extensions.qiskit import AerBackend - -b = AerBackend() -b.default_compilation_pass - -# To compile a circuit using the default pass of a `Backend` we can simply use the `get_compiled_circuit()` method: - -circ = Circuit(2).X(0).Y(1).CRz(0.5, 1, 0) -circ1 = b.get_compiled_circuit(circ) -render_circuit_jupyter(circ1) - -# Every `Backend` will have a certain set of requirements that must be met by any circuit in order to run. These are exposed via the `required_predicates` property: - -b.required_predicates - -# We can test whether a given circuit satisfies these requirements using the `valid_circuit()` method: - -b.valid_circuit(circ) - -b.valid_circuit(circ1) diff --git a/examples/python/conditional_gate_example.py b/examples/python/conditional_gate_example.py deleted file mode 100644 index 580176bb..00000000 --- a/examples/python/conditional_gate_example.py +++ /dev/null @@ -1,115 +0,0 @@ -# # Conditional gates - -# Whilst any quantum process can be created by performing "pure" operations delaying all measurements to the end, this is not always practical and can greatly increase the resource requirements. It is much more convenient to alternate quantum gates and measurements, especially if we can use the measurement results to determine which gates to apply (we refer to this more generic circuit model as "mixed" circuits, against the usual "pure" circuits). This is especially crucial for error correcting codes, where the correction gates are applied only if an error is detected. -# -# Measurements on many NISQ devices are often slow and it is hard to maintain other qubits in a quantum state during the measurement operation. Hence they may only support a single round of measurements at the end of the circuit, removing the need for conditional gate support. However, the ability to work with mid-circuit measurement and conditional gates is a feature in high demand for the future, and tket is ready for it. -# -# Not every circuit language specification supports conditional gates in the same way. The most popular circuit model at the moment is that provided by the OpenQASM language. This permits a very restricted model of classical logic, where we can apply a gate conditionally on the exact value of a classical register. There is no facility in the current spec for Boolean logic or classical operations to apply any function to the value prior to the equality check. -# -# For example, quantum teleportation can be performed by the following QASM: -# `OPENQASM 2.0;` -# `include "qelib1.inc";` -# `qreg a[2];` -# `qreg b[1];` -# `creg c[2];` -# `// Bell state between Alice and Bob` -# `h a[1];` -# `cx a[1],b[0];` -# `// Bell measurement of Alice's qubits` -# `cx a[0],a[1];` -# `h a[0];` -# `measure a[0] -> c[0];` -# `measure a[1] -> c[1];` -# `// Correction of Bob's qubit` -# `if(c==1) z b[0];` -# `if(c==3) z b[0];` -# `if(c==2) x b[0];` -# `if(c==3) x b[0];` - -# tket supports a slightly more general form of conditional gates, where the gate is applied conditionally on the exact value of any list of bits. When adding a gate to a `Circuit` object, pass in the kwargs `condition_bits` and `condition_value` and the gate will only be applied if the state of the bits yields the binary representation of the value. - -from pytket import Circuit -from pytket.circuit.display import render_circuit_jupyter - -circ = Circuit() -alice = circ.add_q_register("a", 2) -bob = circ.add_q_register("b", 1) -cr = circ.add_c_register("c", 2) -# Bell state between Alice and Bob: -circ.H(alice[1]) -circ.CX(alice[1], bob[0]) -# Bell measurement of Alice's qubits: -circ.CX(alice[0], alice[1]) -circ.H(alice[0]) -circ.Measure(alice[0], cr[0]) -circ.Measure(alice[1], cr[1]) -# Correction of Bob's qubit: -circ.Z(bob[0], condition_bits=[cr[0]], condition_value=1) -circ.X(bob[0], condition_bits=[cr[1]], condition_value=1) -render_circuit_jupyter(circ) - - -# Performing individual gates conditionally is sufficient, but can get cumbersome for larger circuits. Fortunately, tket's Box structures can also be performed conditionally, enabling this to be applied to large circuits with ease. -# -# For the sake of example, assume our device struggles to perform $X$ gates. We can surround it by $CX$ gates onto an ancilla, so measuring the ancilla will either result in the identity or $X$ being applied to the target qubit. If we detect that the $X$ fails, we can retry. - -from pytket.circuit import CircBox, Qubit, Bit - -checked_x = Circuit(2, 1) -checked_x.CX(0, 1) -checked_x.X(0) -checked_x.CX(0, 1) -checked_x.Measure(1, 0) -x_box = CircBox(checked_x) - -circ2 = Circuit() -target = Qubit("t", 0) -ancilla = Qubit("a", 0) -success = Bit("s", 0) -circ2.add_qubit(target) -circ2.add_qubit(ancilla) -circ2.add_bit(success) - -# Try the X gate: - -circ2.add_gate(x_box, args=[target, ancilla, success]) -# Try again if the X failed -circ2.add_gate( - x_box, args=[target, ancilla, success], condition_bits=[success], condition_value=0 -) -render_circuit_jupyter(circ2) - -# tket is able to apply essential compilation passes on circuits containing conditional gates. This includes decomposing any boxes into primitive gates and rebasing to other gatesets whilst preserving the conditional data. - -from pytket.passes import DecomposeBoxes, RebaseTket, SequencePass - -comp_pass = SequencePass([DecomposeBoxes(), RebaseTket()]) -comp_pass.apply(circ2) -render_circuit_jupyter(circ2) - - -# A tket circuit can be converted to OpenQASM or other languages following the same classical model (e.g. Qiskit) when all conditional gates are dependent on the exact state of a single, whole classical register. - -from pytket.extensions.qiskit import tk_to_qiskit - -qc = tk_to_qiskit(circ2) -print(qc) - -# This allows us to test our mixed programs using the `AerBackend`. - -from pytket.extensions.qiskit import AerBackend - -circ3 = Circuit(2, 1) -circ3.Rx(0.3, 0) -circ3.Measure(0, 0) -# Set qubit 1 to be the opposite result and measure -circ3.X(1, condition_bits=[0], condition_value=0) -circ3.Measure(1, 0) -backend = AerBackend() -compiled_circ = backend.get_compiled_circuit(circ3) -render_circuit_jupyter(compiled_circ) - -counts = backend.run_circuit(compiled_circ, 1024).get_counts() -print(counts) - -# Try out mid-circuit measurement and conditional gate support on the `AerBackend` simulator, or ask about accessing the `QuantinuumBackend` to try on a hardware device. diff --git a/examples/python/contextual_optimization.py b/examples/python/contextual_optimization.py deleted file mode 100644 index 173bdb69..00000000 --- a/examples/python/contextual_optimization.py +++ /dev/null @@ -1,69 +0,0 @@ -# # Contextual optimisation - -# This notebook will illustrate the techniques of "contextual optimisation" available in TKET. - -# See the user manaul for an introduction to the concept and methods. Here we will present an example showing how we can save some gates at the beginnning and end of a circuit, making no assumptions about the structure of the circuit. - -# We will take as an example an ansatz circuit consisting of alternating layers of Ry and CX gates, where some proportion of the Ry angles are zero. This is a typical ansatz for variational algorithms, used for solving diagonal Hamiltonians for combinatorial optimisation. - -from pytket.circuit import Circuit -from random import random, randrange, seed - - -def random_sparse_ansatz(n_qubits, n_layers, p, rng_seed=None): - seed(rng_seed) - circ = Circuit(n_qubits) - for q in range(n_qubits): - if random() < p: - circ.Ry(0.1 * randrange(20), q) - for l in range(n_layers): - for q in range(0, n_qubits - 1, 2): - circ.CX(q, q + 1) - for q in range(2 * (n_qubits // 2)): - if random() < p: - circ.Ry(0.1 * randrange(20), q) - for q in range(1, n_qubits - 1, 2): - circ.CX(q, q + 1) - for q in range(2 * ((n_qubits - 1) // 2)): - if random() < p: - circ.Ry(0.1 * randrange(20), q + 1) - circ.measure_all() - return circ - - -# Let's examine a smallish example: - -from pytket.circuit import OpType -from pytket.circuit.display import render_circuit_jupyter - -c = random_sparse_ansatz(4, 3, 0.5, rng_seed=0) -render_circuit_jupyter(c) -print("Number of CX:", c.n_gates_of_type(OpType.CX)) - -# Contextual optimizations allow us to shave some gates from the beginning and end of the circuit. Those at the end get commuted through the Measure gates into a classical post-processing circuit, which we can then pass to `BackendResult` methods to have the postprocessing performed automatically. - -# The `prepare_circuit()` method returns a pair of circuits, the first of which is what we actually run and the second of specifies the required postprocessing. - -from pytket.utils import prepare_circuit - -c0, ppcirc = prepare_circuit(c) -render_circuit_jupyter(c0) -print("Number of CX:", c0.n_gates_of_type(OpType.CX)) - -# In this case, one CX has been shaved from the beginning of the circuit and two from the end. - -# We can run the processed circuit on our backend: - -from pytket.extensions.qiskit import AerBackend - -b = AerBackend() -c1 = b.get_compiled_circuit(c0) -h = b.process_circuit(c1, n_shots=10) -r = b.get_result(h) - -# And finally get the counts or shots, accounting for the classical postprocessing: - -counts = r.get_counts(ppcirc=ppcirc) -print(counts) - -# See the [pytket user manual](https://tket.quantinuum.com/user-manual/manual_compiler.html#contextual-optimisations) for more details about contextual optimisations and how to apply them in TKET. diff --git a/examples/python/creating_backends.py b/examples/python/creating_backends.py deleted file mode 100644 index 319376d6..00000000 --- a/examples/python/creating_backends.py +++ /dev/null @@ -1,777 +0,0 @@ -# # How to create your own `Backend` using `pytket` - -# In this tutorial, we will focus on: -# - the components of the abstract `Backend` class; -# - adaptations for statevector simulation versus measurement sampling. - -# To run this example, you will only need the core `pytket` package. -# -# The `pytket` framework currently has direct integration with the largest variety of devices and simulators out of any quantum software platform, but this is certainly not a complete collection. New quantum backends are frequently being rolled out as more device manufacturers bring their machines online and advanced in simulation research give rise to many purpose-built simulators for fast execution of specific circuit fragments. -# -# If you have something that can take circuits (i.e. a sequence of gates) and run/simulate them, adding integration with `pytket` connects it to a great number of users and enables existing software solutions to immediately take advantage of your new backend. This reach is further extended beyond just software written with `pytket` by exploiting its integration with the rest of the quantum software ecosystem, such as via the `TketBackend` wrapper to use the new backend within Qiskit projects. -# -# This notebook will take a toy simulator and demonstrate how to write each component of the `Backend` class to make it work with the rest of `pytket`. We'll start by defining the internal representation of a circuit that our simulator will use. Rather than common gates, this example will use exponentiated Pauli tensors ($e^{-i \theta P}$ for $P \in \{I, X, Y, Z\}^n$) as its basic operation, which are universal for unitary circuits. To keep it simple, we will ignore measurements for now and just consider unitaries. - -from pytket import Qubit -from pytket.pauli import QubitPauliString -from typing import List - - -class MyCircuit: - """A minimal representation of a unitary circuit""" - - def __init__(self, qubits: List[Qubit]): - """Creates a circuit over some set of qubits - - :param qubits: The list of qubits in the circuit - :type qubits: List[Qubit] - """ - self.qubits = sorted(qubits, reverse=True) - self.gates = list() - - def add_gate(self, qps: QubitPauliString, angle: float): - """Adds a gate to the end of the circuit e^{-0.5i * qps * angle} - - :param qps: Pauli string to rotate around - :type qps: QubitPauliString - :param angle: Angle of rotation in radians - :type angle: float - """ - self.gates.append((qps, angle)) - - -# To simulate these, it is enough to generate the matrix of these exponentials and apply them in sequence to our initial state. Calculating these matrix exponentials is easy since we can exploit the following property: if an operator $A$ satisfies $A^2 = I$, then $e^{i\theta A} = \mathrm{cos}(\theta)I + i \mathrm{sin}(\theta) A$. This works for any tensor of Pauli matrices. Furthermore, since each Pauli matrix is some combination of a diagonal matrix and a permutation matrix, they benefit greatly from a sparse matrix representation, which we can obtain from the `QubitPauliString`. - -import numpy as np - - -class MySimulator: - """A minimal statevector simulator for unitary circuits""" - - def __init__(self, qubits: List[Qubit]): - """Initialise a statevector, setting all qubits to the |0❭ state. - We treat qubits[0] as the most-significant qubit - - :param qubits: The list of qubits in the circuit - :type qubits: List[Qubit] - """ - self._qubits = qubits - self._qstate = np.zeros((2 ** len(qubits),), dtype=complex) - self._qstate[0] = 1.0 - - def apply_Pauli_rot(self, qps: QubitPauliString, angle: float): - """Applies e^{-0.5i * qps * angle} to the state - - :param qps: Pauli to rotate around - :type qps: QubitPauliString - :param angle: Angle of rotation in radians - :type angle: float - """ - pauli_tensor = qps.to_sparse_matrix(self._qubits) - exponent = -0.5 * angle - self._qstate = np.cos(exponent) * self._qstate + 1j * np.sin( - exponent - ) * pauli_tensor.dot(self._qstate) - - -def run_mycircuit(circ: MyCircuit) -> np.ndarray: - """Gives the state after applying the circuit to the all-|0❭ state - - :param circ: The circuit to simulate - :type circ: MyCircuit - :return: The final statevector - :rtype: np.ndarray - """ - sim = MySimulator(circ.qubits) - for qps, angle in circ.gates: - sim.apply_Pauli_rot(qps, angle) - return sim._qstate - - -# And that's all we need for a basic simulator! We can check that this works by trying to generate a Bell state (up to global phase). - -from pytket.pauli import Pauli - -q = [Qubit(0), Qubit(1)] -circ = MyCircuit(q) -# Hadamard on Qubit(0) -circ.add_gate(QubitPauliString(Qubit(0), Pauli.Z), np.pi / 2) -circ.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2) -circ.add_gate(QubitPauliString(Qubit(0), Pauli.Z), np.pi / 2) -# CX with control Qubit(0) and target Qubit(1) -circ.add_gate(QubitPauliString(Qubit(0), Pauli.Z), -np.pi / 2) -circ.add_gate(QubitPauliString(Qubit(1), Pauli.X), -np.pi / 2) -circ.add_gate(QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.X}), np.pi / 2) -print(run_mycircuit(circ)) - -# A useful first step to integrating this is to define a conversion from the `pytket.Circuit` class to the `MyCircuit` class. In most cases, this will just amount to converting one gate at a time by a simple syntax map. We need not specify how to convert every possible `OpType`, since we can rely on the compilation passes in `pytket` to map the circuit into the required gate set as long as it is universal. For this example, the definitions of `OpType.Rx`, `OpType.Ry`, `OpType.Rz`, and `OpType.ZZMax` all match the form of a single Pauli exponential. - -from pytket import Circuit, OpType - - -def tk_to_mycircuit(tkc: Circuit) -> MyCircuit: - """Convert a pytket Circuit to a MyCircuit object. - Supports Rz, Rx, Ry, and ZZMax gates. - - :param tkc: The Circuit to convert - :type tkc: Circuit - :return: An equivalent MyCircuit object - :rtype: MyCircuit - """ - circ = MyCircuit(tkc.qubits) - for command in tkc: - optype = command.op.type - if optype == OpType.Rx: - circ.add_gate( - QubitPauliString(command.args[0], Pauli.X), np.pi * command.op.params[0] - ) - elif optype == OpType.Ry: - circ.add_gate( - QubitPauliString(command.args[0], Pauli.Y), np.pi * command.op.params[0] - ) - elif optype == OpType.Rz: - circ.add_gate( - QubitPauliString(command.args[0], Pauli.Z), np.pi * command.op.params[0] - ) - elif optype == OpType.ZZMax: - circ.add_gate( - QubitPauliString(command.args, [Pauli.Z, Pauli.Z]), np.pi * 0.5 - ) - else: - raise ValueError("Cannot convert optype to MyCircuit: ", optype) - return circ - - -# Now we turn to the `Backend` class. This provides a uniform API to submit `Circuit` objects for evaluation, typically returning either a statevector or a set of measurement shots. It also captures all of the information needed for compilation and asynchronous job management. -# -# We will make a subclass of `Backend` for our statevector simulator. The `_supports_state` flag lets the methods of the abstract `Backend` class know that this implementation supports statevector simulation. We also set `_persistent_handles` to `False` since this `Backend` will not be able to retrieve results from a previous Python session. -# -# Since we do not need to connect to a remote process for the simulator, the constructor doesn't need to set anything up. The base `Backend` constructor will initialise the `_cache` field for storing job data. - -from pytket.backends import Backend - - -class MyBackend(Backend): - """A pytket Backend wrapping around the MySimulator statevector simulator""" - - _supports_state = True - _persistent_handles = False - - def __init__(self): - """Create a new instance of the MyBackend class""" - super().__init__() - - -# Most `Backend`s will only support a small fragment of the `Circuit` language, either through implementation limitations or since a specific presentation is universal. It is helpful to keep this information in the `Backend` object itself so that users can clearly see how a `Circuit` needs to look before it can be successfully run. The `Predicate` classes in `pytket` can capture many common restrictions. The idea behind the `required_predicates` list is that any `Circuit` satisfying every `Predicate` in the list can be run on the `Backend` successfully as it is. -# -# However, a typical high-level user will not be writing `Circuit`s that satisfies all of the `required_predicates`, preferring instead to use the model that is most natural for the algorithm they are implementing. Providing a `default_compilation_pass` gives users an easy starting point for compiling an arbitrary `Circuit` into a form that can be executed (when not blocked by paradigm constraints like `NoMidMeasurePredicate` or `NoClassicalControlPredicate` that cannot easily be solved by compilation). -# -# You can provide several options using the `optimisation_level` argument. We tend to use `0` for very basic compilation with no optimisation applied, `1` for the inclusion of fast optimisations (e.g. `SynthesiseIBM` is a pre-defined sequence of optimisation passes that scales well with circuit size), and `2` for heavier optimisation (e.g. `FullPeepholeOptimise` incorporates `SynthesiseTket` alongside some extra passes that may take longer for large circuits). -# -# When designing these compilation pass sequences for a given `Backend`, it can be a good idea to start with the passes that solve individual constraints from `required_predicates` (like `FullMappingPass` for `ConnectivityPredicate` or `RebaseX` for `GateSetPredicate`), and find an ordering such that no later pass invalidates the work of an earlier one. -# -# For `MyBackend`, we will need to enforce that our circuits are expressed entirely in terms of `OpType.Rx`, `OpType.Ry`, `OpType.Rz`, and `OpType.ZZMax` gates which we can solve using `RebaseCustom`. Note that we omit `OpType.Measure` since we can only run pure quantum circuits. -# -# The standard docstrings for these and other abstract methods can be seen in the abstract `Backend` [API reference](https://cqcl.github.io/tket/pytket/api/backends.html#pytket.backends.Backend). - -from pytket.predicates import Predicate, GateSetPredicate, NoClassicalBitsPredicate -from pytket.passes import ( - BasePass, - SequencePass, - DecomposeBoxes, - SynthesiseTket, - FullPeepholeOptimise, - RebaseCustom, - SquashCustom, -) - - -@property -def required_predicates(self) -> List[Predicate]: - """ - The minimum set of predicates that a circuit must satisfy before it can - be successfully run on this backend. - - :return: Required predicates. - :rtype: List[Predicate] - """ - preds = [ - NoClassicalBitsPredicate(), - GateSetPredicate( - { - OpType.Rx, - OpType.Ry, - OpType.Rz, - OpType.ZZMax, - } - ), - ] - return preds - - -# Every `Backend` must define a rebasing method, which will normally be called from its default compilation passes (see below), but which may also be called independently. Given the target gate set, it is usually straightforward to define this using the `RebaseCustom` pass, with a couple of helpers defining rebase of an `OpType.CX` and a general `OpType.TK1` gate: - -cx_circ = Circuit(2) -cx_circ.Sdg(0) -cx_circ.V(1) -cx_circ.Sdg(1) -cx_circ.Vdg(1) -cx_circ.ZZMax(0, 1) -cx_circ.Vdg(1) -cx_circ.Sdg(1) -cx_circ.add_phase(0.5) - - -def sq(a, b, c): - circ = Circuit(1) - if c != 0: - circ.Rz(c, 0) - if b != 0: - circ.Rx(b, 0) - if a != 0: - circ.Rz(a, 0) - return circ - - -rebase = RebaseCustom({OpType.Rx, OpType.Ry, OpType.Rz, OpType.ZZMax}, cx_circ, sq) - - -def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: - """ - A suggested compilation pass that will guarantee the resulting circuit - will be suitable to run on this backend with as few preconditions as - possible. - - :param optimisation_level: The level of optimisation to perform during - compilation. Level 0 just solves the device constraints without - optimising. Level 1 additionally performs some light optimisations. - Level 2 adds more intensive optimisations that can increase compilation - time for large circuits. Defaults to 1. - :type optimisation_level: int, optional - :return: Compilation pass guaranteeing required predicates. - :rtype: BasePass - """ - assert optimisation_level in range(3) - - squash = SquashCustom({OpType.Rz, OpType.Rx, OpType.Ry}, sq) - seq = [DecomposeBoxes()] # Decompose boxes into basic gates - if optimisation_level == 1: - seq.append(SynthesiseTket()) # Optional fast optimisation - elif optimisation_level == 2: - seq.append(FullPeepholeOptimise()) # Optional heavy optimisation - seq.append(rebase) # Map to target gate set - if optimisation_level != 0: - seq.append(squash) # Optionally simplify 1qb gate chains within this gate set - return SequencePass(seq) - - -# The `backend_info` property is used for storing various properties of a backend. By default it provides all device information useful for compilation. Typically we would make it return a class attribute `self._backend_info` that we initialise on construction, but we will define it at point of request here. We use a `FullyConnected` architecture producing an `Architecture` object with couplings between 4 qubits. - - -from pytket.backends.backendinfo import BackendInfo -from pytket.architecture import FullyConnected - - -@property -def backend_info(self) -> BackendInfo: - return BackendInfo( - "MyBackend", - "MySimulator", - "1.0", - FullyConnected(4), - { - OpType.Rx, - OpType.Ry, - OpType.Rz, - OpType.ZZMax, - OpType.Measure, - }, - supports_midcircuit_measurement=False, - misc={"characterisation": None}, - ) - - -# Asynchronous job management is all managed through the `ResultHandle` associated with a particular `Circuit` that has been submitted. We can use it to inspect the status of the job to see if it has completed, or to look up the results if they are available. -# -# For devices, `circuit_status` should query the job to see if it is in a queue, currently being executed, completed successfully, etc. The `CircuitStatus` class is mostly driven by the `StatusEnum` values, but can also contain messages to give more detailed feedback if available. For our simulator, we are not running things asynchronously, so a `Circuit` has either not been run or it will have been completed. -# -# Since a device API will probably define its own data type for job handles, the `ResultHandle` definition is flexible enough to cover many possible data types so you can likely use the underlying job handle as the `ResultHandle`. The `_result_id_type` property specifies what data type a `ResultHandle` for this `Backend` should look like. Since our simulator has no underlying job handle, we can just use a UUID string. - -from pytket.backends import ResultHandle, CircuitStatus, StatusEnum, CircuitNotRunError -from pytket.backends.resulthandle import _ResultIdTuple - - -@property -def _result_id_type(self) -> _ResultIdTuple: - """Identifier type signature for ResultHandle for this backend. - - :return: Type signature (tuple of hashable types) - :rtype: _ResultIdTuple - """ - return (str,) - - -def circuit_status(self, handle: ResultHandle) -> CircuitStatus: - """ - Return a CircuitStatus reporting the status of the circuit execution - corresponding to the ResultHandle - """ - if handle in self._cache: - return CircuitStatus(StatusEnum.COMPLETED) - raise CircuitNotRunError(handle) - - -# And finally, we have the method that actually submits a job for execution. `process_circuits` should take a collection of (compiled) `Circuit` objects, process them and return a `ResultHandle` for each `Circuit`. If execution is synchronous, then this can simply wait until it is finished, store the result in `_cache` and return. For backends that support asynchronous jobs, you will need to set up an event to format and store the result on completion. -# -# It is recommended to use the `valid_check` parameter to control a call to `Backend._check_all_circuits()`, which will raise an exception if any of the circuits do not satisfy everything in `required_predicates`. -# -# The `_cache` fields stores all of the information about current jobs that have been run. When a job has finished execution, the results are expected to be stored in `_cache[handle]["result"]`, though it can also be used to store other data about the job such as some information about the `Circuit` required to properly format the results. Methods like `Backend.get_result()` and `Backend.empty_cache()` expect to interact with the results of a given job in this way. -# -# The final output of the execution is stored in a `BackendResult` object. This captures enough information about the results to reinterpret it in numerous ways, such as requesting the statevector in a specific qubit ordering or converting a complete shot table to a summary of the counts. If we create a `BackendResult` with quantum data (e.g. a statevector or unitary), we must provide the `Qubit` ids in order from most-significant to least-significant with regards to indexing the state. Similarly, creating one with classical readouts (e.g. a shot table or counts summary), we give the `Bit` ids in the order they appear in a readout (left-to-right). -# -# For a statevector simulation, we should also take into account the global phase stored in the `Circuit` object and any implicit qubit permutations, since these become observable when inspecting the quantum state. We can handle the qubit permutation by changing the order in which we pass the `Qubit` ids into the `BackendResult` object. - -from pytket.backends.backendresult import BackendResult -from pytket.utils.results import KwargTypes -from typing import Iterable, Optional -from uuid import uuid4 - - -def process_circuits( - self, - circuits: Iterable[Circuit], - n_shots: Optional[int] = None, - valid_check: bool = True, - **kwargs: KwargTypes, -) -> List[ResultHandle]: - """ - Submit circuits to the backend for running. The results will be stored - in the backend's result cache to be retrieved by the corresponding - get_ method. - - Use keyword arguments to specify parameters to be used in submitting circuits - See specific Backend derived class for available parameters, from the following - list: - - * `seed`: RNG seed for simulators - - :param circuits: Circuits to process on the backend. - :type circuits: Iterable[Circuit] - :param n_shots: Number of shots to run per circuit. None is to be used - for state/unitary simulators. Defaults to None. - :type n_shots: Optional[int], optional - :param valid_check: Explicitly check that all circuits satisfy all required - predicates to run on the backend. Defaults to True - :type valid_check: bool, optional - :return: Handles to results for each input circuit, as an interable in - the same order as the circuits. - :rtype: List[ResultHandle] - """ - - circuit_list = list(circuits) - if valid_check: - self._check_all_circuits(circuit_list) - - handle_list = [] - for circuit in circuit_list: - handle = ResultHandle(str(uuid4())) - mycirc = tk_to_mycircuit(circuit) - state = run_mycircuit(mycirc) - state *= np.exp(1j * np.pi * circuit.phase) - implicit_perm = circuit.implicit_qubit_permutation() - res_qubits = [implicit_perm[qb] for qb in sorted(circuit.qubits, reverse=True)] - res = BackendResult(q_bits=res_qubits, state=state) - self._cache[handle] = {"result": res} - handle_list.append(handle) - return handle_list - - -# Let's redefine our `MyBackend` class to use these methods to finish it off. - - -class MyBackend(Backend): - """A pytket Backend wrapping around the MySimulator statevector simulator""" - - _supports_state = True - _persistent_handles = False - - def __init__(self): - """Create a new instance of the MyBackend class""" - super().__init__() - - required_predicates = required_predicates - rebase_pass = rebase - default_compilation_pass = default_compilation_pass - _result_id_type = _result_id_type - circuit_status = circuit_status - process_circuits = process_circuits - - -# Our new `Backend` subclass is now complete, so let's test it out. If you are planning on maintaining a backend class, it is recommended to set up some unit tests. The following tests will cover basic operation and integration with `pytket` utilities. - -from pytket.circuit import BasisOrder, Unitary1qBox -from pytket.passes import CliffordSimp -from pytket.utils import get_operator_expectation_value -from pytket.utils.operators import QubitPauliOperator -import pytest - - -def test_bell() -> None: - c = Circuit(2) - c.H(0) - c.CX(0, 1) - b = MyBackend() - c = b.get_compiled_circuit(c) - h = b.process_circuit(c) - assert np.allclose( - b.get_result(h).get_state(), np.asarray([1, 0, 0, 1]) * 1 / np.sqrt(2) - ) - - -def test_basisorder() -> None: - c = Circuit(2) - c.X(1) - b = MyBackend() - c = b.get_compiled_circuit(c) - h = b.process_circuit(c) - r = b.get_result(h) - assert np.allclose(r.get_state(), np.asarray([0, 1, 0, 0])) - assert np.allclose(r.get_state(basis=BasisOrder.dlo), np.asarray([0, 0, 1, 0])) - - -def test_implicit_perm() -> None: - c = Circuit(2) - c.CX(0, 1) - c.CX(1, 0) - c.Ry(0.1, 1) - c1 = c.copy() - CliffordSimp().apply(c1) - b = MyBackend() - c = b.get_compiled_circuit(c, optimisation_level=1) - c1 = b.get_compiled_circuit(c1, optimisation_level=1) - assert c.implicit_qubit_permutation() != c1.implicit_qubit_permutation() - h, h1 = b.process_circuits([c, c1]) - r, r1 = b.get_results([h, h1]) - for bo in [BasisOrder.ilo, BasisOrder.dlo]: - s = r.get_state(basis=bo) - s1 = r1.get_state(basis=bo) - assert np.allclose(s, s1) - - -def test_compilation_pass() -> None: - b = MyBackend() - for opt_level in range(3): - c = Circuit(2) - c.CX(0, 1) - u = np.asarray([[0, 1], [-1j, 0]]) - c.add_unitary1qbox(Unitary1qBox(u), 1) - c.CX(0, 1) - c.add_gate(OpType.CRz, 0.35, [1, 0]) - assert not (b.valid_circuit(c)) - c = b.get_compiled_circuit(c, optimisation_level=opt_level) - assert b.valid_circuit(c) - - -def test_invalid_measures() -> None: - c = Circuit(2) - c.H(0).CX(0, 1).measure_all() - b = MyBackend() - c = b.get_compiled_circuit(c) - assert not (b.valid_circuit(c)) - - -def test_expectation_value() -> None: - c = Circuit(2) - c.H(0) - c.CX(0, 1) - op = QubitPauliOperator( - { - QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z}): 1.0, - QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X}): 0.3, - QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Y}): 0.8j, - QubitPauliString({Qubit(0): Pauli.Y}): -0.4j, - } - ) - b = MyBackend() - c = b.get_compiled_circuit(c) - assert get_operator_expectation_value(c, op, b) == pytest.approx(1.3) - - -# Explicit calls are needed for this notebook. Normally pytest will just find these "test_X" methods when run from the command line: - -test_bell() -test_basisorder() -test_implicit_perm() -test_compilation_pass() -test_invalid_measures() -test_expectation_value() - -# To show how this compares to a sampling simulator, let's extend our simulator to handle end-of-circuit measurements. - -from typing import Set - - -def sample_mycircuit( - circ: MyCircuit, qubits: Set[Qubit], n_shots: int, seed: Optional[int] = None -) -> np.ndarray: - """Run the circuit on the all-|0❭ state and measures a set of qubits - - :param circ: The circuit to simulate - :type circ: MyCircuit - :param qubits: The set of qubits to measure - :type qubits: Set[Qubit] - :param n_shots: The number of samples to take - :type n_shots: int - :param seed: Seed for the random number generator, defaults to no seed - :type seed: Optional[int], optional - :return: Table of shots; each row is a shot, columns are qubit readouts in ascending Qubit order - :rtype: np.ndarray - """ - state = run_mycircuit(circ) - cumulative_probs = (state * state.conjugate()).cumsum() - if seed is not None: - np.random.seed(seed) - shots = np.zeros((n_shots, len(circ.qubits))) - for s in range(n_shots): - # Pick a random point in the distribution - point = np.random.uniform(0.0, 1.0) - # Find the corresponding readout - index = np.searchsorted(cumulative_probs, point) - # Convert index to a binary array - # `bin` maps e.g. index 6 to '0b110' - # So we ignore the first two symbols and add leading 0s to make it a fixed length - bitstring = bin(index)[2:].zfill(len(circ.qubits)) - shots[s] = np.asarray([int(b) for b in bitstring]) - filtered = np.zeros((n_shots, len(qubits))) - target = 0 - for col, q in enumerate(circ.qubits): - if q in qubits: - filtered[:, target] = shots[:, col] - target += 1 - return filtered - - -# Since `MyCircuit` doesn't have a representation for measurement gates, our converter must return both the `MyCircuit` object and some way of capturing the measurements. Since we will also want to know how they map into our `Bit` ids, the simplest option is just a dictionary from `Qubit` to `Bit`. - -from pytket import Bit -from typing import Dict, Tuple - - -def tk_to_mymeasures(tkc: Circuit) -> Tuple[MyCircuit, Dict[Qubit, Bit]]: - """Convert a pytket Circuit to a MyCircuit object and a measurement map. - Supports Rz, Rx, Ry, and ZZMax gates, as well as end-of-circuit measurements. - - :param tkc: The Circuit to convert - :type tkc: Circuit - :return: An equivalent MyCircuit object and a map from measured Qubit to the Bit containing the result - :rtype: Tuple[MyCircuit, Dict[Qubit, Bit]] - """ - circ = MyCircuit(tkc.qubits) - measure_map = dict() - measured_units = ( - set() - ) # Track measured Qubits/used Bits to identify mid-circuit measurement - for command in tkc: - for u in command.args: - if u in measured_units: - raise ValueError("Circuit contains a mid-circuit measurement") - optype = command.op.type - if optype == OpType.Rx: - circ.add_gate( - QubitPauliString(command.args[0], Pauli.X), np.pi * command.op.params[0] - ) - elif optype == OpType.Ry: - circ.add_gate( - QubitPauliString(command.args[0], Pauli.Y), np.pi * command.op.params[0] - ) - elif optype == OpType.Rz: - circ.add_gate( - QubitPauliString(command.args[0], Pauli.Z), np.pi * command.op.params[0] - ) - elif optype == OpType.ZZMax: - circ.add_gate( - QubitPauliString(command.args, [Pauli.Z, Pauli.Z]), np.pi * 0.5 - ) - elif optype == OpType.Measure: - measure_map[command.args[0]] = command.args[1] - measured_units.add(command.args[0]) - measured_units.add(command.args[1]) - else: - raise ValueError("Cannot convert optype to MyCircuit: ", optype) - return circ, measure_map - - -# To build a `Backend` subclass for this sampling simulator, we only need to change how we write `required_predicates` and `process_circuits`. - -from pytket.predicates import NoMidMeasurePredicate, NoClassicalControlPredicate -from pytket.utils.outcomearray import OutcomeArray - - -class MySampler(Backend): - """A pytket Backend wrapping around the MySimulator simulator with readout sampling""" - - _supports_shots = True - _supports_counts = True - _persistent_handles = False - - def __init__(self): - """Create a new instance of the MySampler class""" - super().__init__() - - rebase_pass = rebase - default_compilation_pass = default_compilation_pass - _result_id_type = _result_id_type - circuit_status = circuit_status - - @property - def required_predicates(self) -> List[Predicate]: - """ - The minimum set of predicates that a circuit must satisfy before it can - be successfully run on this backend. - - :return: Required predicates. - :rtype: List[Predicate] - """ - preds = [ - NoClassicalControlPredicate(), - NoMidMeasurePredicate(), - GateSetPredicate( - { - OpType.Rx, - OpType.Ry, - OpType.Rz, - OpType.ZZMax, - OpType.Measure, - } - ), - ] - return preds - - def process_circuits( - self, - circuits: Iterable[Circuit], - n_shots: Optional[int] = None, - valid_check: bool = True, - **kwargs: KwargTypes, - ) -> List[ResultHandle]: - """ - Submit circuits to the backend for running. The results will be stored - in the backend's result cache to be retrieved by the corresponding - get_ method. - - Use keyword arguments to specify parameters to be used in submitting circuits - See specific Backend derived class for available parameters, from the following - list: - - * `seed`: RNG seed for simulators - - :param circuits: Circuits to process on the backend. - :type circuits: Iterable[Circuit] - :param n_shots: Number of shots to run per circuit. None is to be used - for state/unitary simulators. Defaults to None. - :type n_shots: Optional[int], optional - :param valid_check: Explicitly check that all circuits satisfy all required - predicates to run on the backend. Defaults to True - :type valid_check: bool, optional - :return: Handles to results for each input circuit, as an interable in - the same order as the circuits. - :rtype: List[ResultHandle] - """ - - circuit_list = list(circuits) - if valid_check: - self._check_all_circuits(circuit_list) - - handle_list = [] - for circuit in circuit_list: - handle = ResultHandle(str(uuid4())) - mycirc, measure_map = tk_to_mymeasures(circuit) - qubit_list, bit_list = zip(*measure_map.items()) - qubit_shots = sample_mycircuit( - mycirc, set(qubit_list), n_shots, kwargs.get("seed") - ) - # Pad shot table with 0 columns for unused bits - all_shots = np.zeros((n_shots, len(circuit.bits)), dtype=int) - all_shots[:, : len(qubit_list)] = qubit_shots - res_bits = [measure_map[q] for q in sorted(qubit_list, reverse=True)] - for b in circuit.bits: - if b not in bit_list: - res_bits.append(b) - res = BackendResult( - c_bits=res_bits, shots=OutcomeArray.from_readouts(all_shots) - ) - self._cache[handle] = {"result": res} - handle_list.append(handle) - return handle_list - - -# Likewise, we run some basic tests to make sure it works. - - -def test_sampler_bell() -> None: - c = Circuit(2, 2) - c.H(0) - c.CX(0, 1) - c.measure_all() - b = MySampler() - c = b.get_compiled_circuit(c) - res = b.run_circuit(c, n_shots=10, seed=3) - assert res.get_shots().shape == (10, 2) - assert res.get_counts() == {(0, 0): 5, (1, 1): 5} - - -def test_sampler_basisorder() -> None: - c = Circuit(2, 2) - c.X(1) - c.measure_all() - b = MySampler() - c = b.get_compiled_circuit(c) - res = b.run_circuit(c, n_shots=10, seed=0) - assert res.get_counts() == {(0, 1): 10} - assert res.get_counts(basis=BasisOrder.dlo) == {(1, 0): 10} - - -def test_sampler_compilation_pass() -> None: - b = MySampler() - for opt_level in range(3): - c = Circuit(2) - c.CX(0, 1) - u = np.asarray([[0, 1], [-1j, 0]]) - c.add_unitary1qbox(Unitary1qBox(u), 1) - c.CX(0, 1) - c.add_gate(OpType.CRz, 0.35, [1, 0]) - c.measure_all() - assert not (b.valid_circuit(c)) - c = b.get_compiled_circuit(c, optimisation_level=opt_level) - assert b.valid_circuit(c) - - -def test_sampler_expectation_value() -> None: - c = Circuit(2) - c.H(0) - c.CX(0, 1) - op = QubitPauliOperator( - { - QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z}): 1.0, - QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X}): 0.3, - QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Y}): 0.8j, - QubitPauliString({Qubit(0): Pauli.Y}): -0.4j, - } - ) - b = MySampler() - c = b.get_compiled_circuit(c) - expectation = get_operator_expectation_value(c, op, b, n_shots=2000, seed=0) - assert (np.real(expectation), np.imag(expectation)) == pytest.approx( - (1.3, 0.0), abs=0.1 - ) - - -test_sampler_bell() -test_sampler_basisorder() -test_sampler_compilation_pass() -test_sampler_expectation_value() - -# Exercises: -# - Add some extra gate definitions to the simulator and expand the accepted gate set of the backends. Start with some that are easily represented as exponentiated Pauli tensors like `OpType.YYPhase`. For a challenge, try adding `OpType.CCX` efficiently (it is possible to encode it using seven Pauli rotations). -# - Restrict the simulator to a limited qubit connectivity. Express this in the backends by modifying the `Architecture` property of the `BackendInfo` attribute object and adding to the `required_predicates`. Adjust the `default_compilation_pass` to solve for the connectivity. -# - The file `creating_backends_exercise.py` extends the simulators above to allow for mid-circuit measurement and conditional gates using a binary decision tree. Implement an appropriate converter and `Backend` class for this simulator. diff --git a/examples/python/entanglement_swapping.py b/examples/python/entanglement_swapping.py deleted file mode 100644 index 354fb882..00000000 --- a/examples/python/entanglement_swapping.py +++ /dev/null @@ -1,361 +0,0 @@ -# # Iterated entanglement swapping - -# In this tutorial, we will focus on: -# - designing circuits with mid-circuit measurement and conditional gates; -# - utilising noise models in supported simulators. - -# This example assumes the reader is familiar with the Qubit Teleportation and Entanglement Swapping protocols, and basic models of noise in quantum devices. -# -# To run this example, you will need `pytket`, `pytket-qiskit`, and `plotly` (installed via `pip`). To view the graphs, you will need an intallation of `plotly-orca`. -# -# Current quantum hardware fits into the NISQ (Noisy, Intermediate-Scale Quantum) regime. This noise cannot realistically be combatted using conventional error correcting codes, because of the lack of available qubits, noise levels exceeding the code thresholds, and very few devices available that can perform measurements and corrections mid-circuit. Analysis of how quantum algorithms perform under noisy conditions is a very active research area, as is finding ways to cope with it. Here, we will look at how well we can perform the Entanglement Swapping protocol with different noise levels. -# -# The Entanglement Swapping protocol requires two parties to share Bell pairs with a third party, who applies the Qubit Teleportation protocol to generate a Bell pair between the two parties. The Qubit Teleportation step requires us to be able to measure some qubits and make subsequent corrections to the remaining qubits. There are only a handful of simulators and devices that currently support this, with others restricted to only measuring the qubits at the end of the circuit. -# -# The most popular circuit model with conditional gates at the moment is that provided by the OpenQASM language. This permits a very restricted model of classical logic, where we can apply a gate conditionally on the exact value of a classical register. There is no facility in the current spec for Boolean logic or classical operations to apply any function to the value prior to the equality check. For example, Qubit Teleportation can be performed by the following QASM: -# `OPENQASM 2.0;` -# `include "qelib1.inc";` -# `qreg a[2];` -# `qreg b[1];` -# `creg c[2];` -# `// Bell state between Alice and Bob` -# `h a[1];` -# `cx a[1],b[0];` -# `// Bell measurement of Alice's qubits` -# `cx a[0],a[1];` -# `h a[0];` -# `measure a[0] -> c[0];` -# `measure a[1] -> c[1];` -# `// Correction of Bob's qubit` -# `if(c==1) z b[0];` -# `if(c==3) z b[0];` -# `if(c==2) x b[0];` -# `if(c==3) x b[0];` -# -# This corresponds to the following `pytket` code: - -from pytket import Circuit - -qtel = Circuit() -alice = qtel.add_q_register("a", 2) -bob = qtel.add_q_register("b", 1) -data = qtel.add_c_register("d", 2) - -# Bell state between Alice and Bob: - -qtel.H(alice[1]) -qtel.CX(alice[1], bob[0]) - -# Bell measurement of Alice's qubits: - -qtel.CX(alice[0], alice[1]) -qtel.H(alice[0]) -qtel.Measure(alice[0], data[0]) -qtel.Measure(alice[1], data[1]) - -# Correction of Bob's qubit: - -qtel.X(bob[0], condition_bits=[data[0], data[1]], condition_value=2) -qtel.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3) -qtel.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=1) -qtel.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3) - -# So to demonstrate the Entanglement Swapping protocol, we just need to run this on one side of a Bell pair. - -es = Circuit() -ava = es.add_q_register("a", 1) -bella = es.add_q_register("b", 2) -charlie = es.add_q_register("c", 1) -data = es.add_c_register("d", 2) - -# Bell state between Ava and Bella: - -es.H(ava[0]) -es.CX(ava[0], bella[0]) - -# Teleport `bella[0]` to `charlie[0]`: - -tel_to_c = qtel.copy() -tel_to_c.rename_units({alice[0]: bella[0], alice[1]: bella[1], bob[0]: charlie[0]}) -es.append(tel_to_c) - -print(es.get_commands()) - -# Let's start by running a noiseless simulation of this to verify that what we get looks like a Bell pair. - -from pytket.extensions.qiskit import AerBackend - -# Connect to a simulator: - -backend = AerBackend() - -# Make a ZZ measurement of the Bell pair: - -bell_test = es.copy() -bell_test.Measure(ava[0], data[0]) -bell_test.Measure(charlie[0], data[1]) - -# Run the experiment: - -bell_test = backend.get_compiled_circuit(bell_test) -from pytket.circuit.display import render_circuit_jupyter - -render_circuit_jupyter(bell_test) -handle = backend.process_circuit(bell_test, n_shots=2000) -counts = backend.get_result(handle).get_counts() - -print(counts) - -# This is good, we have got roughly 50/50 measurement results of 00 and 11 under the ZZ operator. But there are many other states beyond the Bell state that also generate this distribution, so to gain more confidence in our claim about the state we should make more measurements that also characterise it, i.e. perform state tomography. -# -# Here, we will demonstrate a naive approach to tomography that makes 3^n measurement circuits for an n-qubit state. More elaborate methods also exist. - -from pytket.pauli import Pauli, QubitPauliString -from pytket.utils import append_pauli_measurement, probs_from_counts -from itertools import product -from scipy.linalg import lstsq, eigh -import numpy as np - - -def gen_tomography_circuits(state, qubits, bits): - # Yields {X, Y, Z}^n measurements in lexicographical order - # Only measures qubits, storing the result in bits - # (since we don't care about the ancilla qubits) - assert len(qubits) == len(bits) - for paulis in product([Pauli.X, Pauli.Y, Pauli.Z], repeat=len(qubits)): - circ = state.copy() - for qb, b, p in zip(qubits, bits, paulis): - if p == Pauli.X: - circ.H(qb) - elif p == Pauli.Y: - circ.V(qb) - circ.Measure(qb, b) - yield circ - - -def run_tomography_circuits(state, qubits, bits, backend): - circs = list(gen_tomography_circuits(state, qubits, bits)) - # Compile and run each circuit - circs = backend.get_compiled_circuits(circs) - handles = backend.process_circuits(circs, n_shots=2000) - # Get the observed measurement probabilities - probs_list = [] - for result in backend.get_results(handles): - counts = result.get_counts() - probs = probs_from_counts(counts) - probs_list.append(probs) - return probs_list - - -def fit_tomography_outcomes(probs_list, n_qbs): - # Define the density matrices for the basis states - basis = dict() - basis[(Pauli.X, 0)] = np.asarray([[0.5, 0.5], [0.5, 0.5]]) - basis[(Pauli.X, 1)] = np.asarray([[0.5, -0.5], [-0.5, 0.5]]) - basis[(Pauli.Y, 0)] = np.asarray([[0.5, -0.5j], [0.5j, 0.5]]) - basis[(Pauli.Y, 1)] = np.asarray([[0.5, 0.5j], [-0.5j, 0.5]]) - basis[(Pauli.Z, 0)] = np.asarray([[1, 0], [0, 0]]) - basis[(Pauli.Z, 1)] = np.asarray([[0, 0], [0, 1]]) - dim = 2**n_qbs - # Define vector all_probs as a concatenation of probability vectors for each measurement (2**n x 3**n, 1) - # Define matrix all_ops mapping a (vectorised) density matrix to a vector of probabilities for each measurement - # (2**n x 3**n, 2**n x 2**n) - all_probs = [] - all_ops = [] - for paulis, probs in zip( - product([Pauli.X, Pauli.Y, Pauli.Z], repeat=n_qbs), probs_list - ): - prob_vec = [] - meas_ops = [] - for outcome in product([0, 1], repeat=n_qbs): - prob_vec.append(probs.get(outcome, 0)) - op = np.eye(1, dtype=complex) - for p, o in zip(paulis, outcome): - op = np.kron(op, basis[(p, o)]) - meas_ops.append(op.reshape(1, dim * dim).conj()) - all_probs.append(np.vstack(prob_vec)) - all_ops.append(np.vstack(meas_ops)) - # Solve for density matrix by minimising || all_ops * dm - all_probs || - dm, _, _, _ = lstsq(np.vstack(all_ops), np.vstack(all_probs)) - dm = dm.reshape(dim, dim) - # Make density matrix positive semi-definite - v, w = eigh(dm) - for i in range(dim): - if v[i] < 0: - for j in range(i + 1, dim): - v[j] += v[i] / (dim - (i + 1)) - v[i] = 0 - dm = np.zeros([dim, dim], dtype=complex) - for j in range(dim): - dm += v[j] * np.outer(w[:, j], np.conj(w[:, j])) - # Normalise trace of density matrix - dm /= np.trace(dm) - return dm - - -probs_list = run_tomography_circuits( - es, [ava[0], charlie[0]], [data[0], data[1]], backend -) -dm = fit_tomography_outcomes(probs_list, 2) -print(dm.round(3)) - -# This is very close to the true density matrix for a pure Bell state. We can attribute the error here to the sampling error since we only take 2000 samples of each measurement circuit. -# -# To quantify exactly how similar it is to the correct density matrix, we can calculate the fidelity. - -from scipy.linalg import sqrtm - - -def fidelity(dm0, dm1): - # Calculate the fidelity between two density matrices - sq0 = sqrtm(dm0) - sq1 = sqrtm(dm1) - return np.linalg.norm(sq0.dot(sq1)) ** 2 - - -bell_state = np.asarray( - [ - [0.5, 0, 0, 0.5], - [0, 0, 0, 0], - [0, 0, 0, 0], - [0.5, 0, 0, 0.5], - ] -) -print(fidelity(dm, bell_state)) - -# This high fidelity is unsurprising since we have a completely noiseless simulation. So the next step is to add some noise to the simulation and observe how the overall fidelity is affected. The `AerBackend` wraps around the Qiskit Aer simulator and can pass on any `qiskit.providers.aer.noise.NoiseModel` to the simulator. Let's start by adding some uniform depolarising noise to each CX gate and some uniform measurement error. - -from qiskit_aer.noise import NoiseModel, depolarizing_error, ReadoutError - - -def make_noise_model(dep_err_rate, ro_err_rate, qubits): - # Define a noise model that applies uniformly to the given qubits - model = NoiseModel() - dep_err = depolarizing_error(dep_err_rate, 2) - ro_err = ReadoutError( - [[1 - ro_err_rate, ro_err_rate], [ro_err_rate, 1 - ro_err_rate]] - ) - # Add depolarising error to CX gates between any qubits (implying full connectivity) - for i, j in product(qubits, repeat=2): - if i != j: - model.add_quantum_error(dep_err, ["cx"], [i, j]) - # Add readout error for each qubit - for i in qubits: - model.add_readout_error(ro_err, qubits=[i]) - return model - - -test_model = make_noise_model(0.03, 0.05, range(4)) -backend = AerBackend(noise_model=test_model) -probs_list = run_tomography_circuits( - es, [ava[0], charlie[0]], [data[0], data[1]], backend -) -dm = fit_tomography_outcomes(probs_list, 2) -print(dm.round(3)) -print(fidelity(dm, bell_state)) - -# Despite the very small circuit and the relatively small error rates, the fidelity of the final state has reduced considerably. -# -# As far as circuits go, the entanglement swapping protocol is little more than a toy example and is nothing close to the scale of circuits for most interesting quantum computational problems. However, it is possible to iterate the protocol many times to build up a larger computation, allowing us to see the impact of the noise at different scales. - -from pytket import OpType -from plotly.graph_objects import Scatter, Figure - - -def iterated_entanglement_swap(n_iter): - # Iterate the entanglement swapping protocol n_iter times - it_es = Circuit() - ava = it_es.add_q_register("a", 1) - bella = it_es.add_q_register("b", 2) - charlie = it_es.add_q_register("c", 1) - data = it_es.add_c_register("d", 2) - - # Start with an initial Bell state - it_es.H(ava[0]) - it_es.CX(ava[0], bella[0]) - - for i in range(n_iter): - if i % 2 == 0: - # Teleport bella[0] to charlie[0] to give a Bell pair between ava[0] and charlier[0] - tel_to_c = qtel.copy() - tel_to_c.rename_units( - {alice[0]: bella[0], alice[1]: bella[1], bob[0]: charlie[0]} - ) - it_es.append(tel_to_c) - it_es.Reset(bella[0]) - it_es.Reset(bella[1]) - else: - # Teleport charlie[0] to bella[0] to give a Bell pair between ava[0] and bella[0] - tel_to_b = qtel.copy() - tel_to_b.rename_units( - {alice[0]: charlie[0], alice[1]: bella[1], bob[0]: bella[0]} - ) - it_es.append(tel_to_b) - it_es.Reset(bella[1]) - it_es.Reset(charlie[0]) - # Return the circuit and the qubits expected to share a Bell pair - if n_iter % 2 == 0: - return it_es, [ava[0], bella[0]] - else: - return it_es, [ava[0], charlie[0]] - - -def iterated_noisy_experiment(dep_err_rate, ro_err_rate, max_iter): - # Set up the noisy simulator with the given error rates - test_model = make_noise_model(dep_err_rate, ro_err_rate, range(4)) - backend = AerBackend(noise_model=test_model) - # Estimate the fidelity after n iterations, from 0 to max_iter (inclusive) - fid_list = [] - for i in range(max_iter + 1): - it_es, qubits = iterated_entanglement_swap(i) - probs_list = run_tomography_circuits(it_es, qubits, [data[0], data[1]], backend) - dm = fit_tomography_outcomes(probs_list, 2) - fid = fidelity(dm, bell_state) - fid_list.append(fid) - return fid_list - - -fig = Figure() -fig.update_layout( - title="Iterated Entanglement Swapping under Noise (dep_err = 0.03)", - xaxis_title="Iterations", - xaxis=dict(range=[0, 10]), - yaxis_title="Fidelity", -) -iter_range = np.arange(11) -for i in range(7): - fids = iterated_noisy_experiment(0.03, 0.025 * i, 10) - plot_data = Scatter( - x=iter_range, y=fids, name="ro_err=" + str(np.round(0.025 * i, 3)) - ) - fig.add_trace(plot_data) -try: - fig.show(renderer="svg") -except ValueError as e: - print(e) # requires plotly-orca - -fig = Figure() -fig.update_layout( - title="Iterated Entanglement Swapping under Noise (ro_err = 0.05)", - xaxis_title="Iterations", - xaxis=dict(range=[0, 10]), - yaxis_title="Fidelity", -) -iter_range = np.arange(11) -for i in range(9): - fids = iterated_noisy_experiment(0.01 * i, 0.05, 10) - plot_data = Scatter( - x=iter_range, y=fids, name="dep_err=" + str(np.round(0.01 * i, 3)) - ) - fig.add_trace(plot_data) -try: - fig.show(renderer="svg") -except ValueError as e: - print(e) # requires plotly-orca - -# These graphs are not very surprising, but are still important for seeing that the current error rates of typical NISQ devices become crippling for fidelities very quickly after repeated mid-circuit measurements and corrections (even with this overly-simplified model with uniform noise and no crosstalk or higher error modes). This provides good motivation for the adoption of error mitigation techniques, and for the development of new techniques that are robust to errors in mid-circuit measurements. - -# Exercises: -# - Vary the fixed noise levels to compare how impactful the depolarising and measurement errors are. -# - Add extra noise characteristics to the noise model to obtain something that more resembles a real device. Possible options include adding error during the reset operations, extending the errors to be non-local, or constructing the noise model from a device's calibration data. -# - Change the circuit from iterated entanglement swapping to iterated applications of a correction circuit from a simple error-correcting code. Do you expect this to be more sensitive to depolarising errors from unitary gates or measurement errors? diff --git a/examples/python/expectation_value_example.py b/examples/python/expectation_value_example.py deleted file mode 100644 index 6c5aa573..00000000 --- a/examples/python/expectation_value_example.py +++ /dev/null @@ -1,255 +0,0 @@ -# # Expectation values - -# Given a circuit generating a quantum state $\lvert \psi \rangle$, it is very common to have an operator $H$ and ask for the expectation value $\langle \psi \vert H \vert \psi \rangle$. A notable example is in quantum computational chemistry, where $\lvert \psi \rangle$ encodes the wavefunction for the electronic state of a small molecule, and the energy of the molecule can be derived from the expectation value with respect to the molecule's Hamiltonian operator $H$. -# -# This example uses this chemistry scenario to demonstrate the overall procedure for using `pytket` to perform advanced high-level procedures. We build on top of topics covered by several other example notebooks, including circuit generation, optimisation, and using different backends. -# -# There is limited built-in functionality in `pytket` for obtaining expectation values from circuits. This is designed to encourage users to consider their needs for parallelising the processing of circuits, manipulating results (e.g. filtering, adjusting counts to mitigate errors, and other forms of data processing), or more advanced schemes for grouping the terms of the operator into measurement circuits. For this example, suppose that we want to focus on reducing the queueing time for IBM device backends, and filter our shots to eliminate some detected errors. -# -# This notebook makes use of the Qiskit and ProjectQ backend modules `pytket_qiskit` and `pytket_projectq`, as well as the electronic structure module `openfermion`, all three of which should first be installed via `pip`. -# -# We will start by generating an ansatz and Hamiltonian for the chemical of interest. Here, we are just using a simple model of $\mathrm{H}_2$ with four qubits representing the occupation of four spin orbitals. - -from pytket import Circuit, Qubit, Bit -from sympy import symbols - -# Generate ansatz and Hamiltonian: - -ansatz = Circuit() -qubits = ansatz.add_q_register("q", 4) -args = symbols("a0 a1 a2 a3 a4 a5 a6 a7") -for i in range(4): - ansatz.Ry(args[i], qubits[i]) -for i in range(3): - ansatz.CX(qubits[i], qubits[i + 1]) -for i in range(4): - ansatz.Ry(args[4 + i], qubits[i]) -ansatz.measure_all() - -for command in ansatz: - print(command) - -# In reality, you would use an expectation value calculation as the objective function for a classical optimisation routine to determine the parameter values for the ground state. For the purposes of this notebook, we will use some predetermined values for the ansatz, already optimised for $\mathrm{H}_2$. - -arg_values = [ - 7.17996183e-02, - 2.95442468e-08, - 1.00000015e00, - 1.00000086e00, - 9.99999826e-01, - 1.00000002e00, - 9.99999954e-01, - 1.13489747e-06, -] - -ansatz.symbol_substitution(dict(zip(args, arg_values))) - -# We can use for example the openfermion library to express an Hamiltonian as a sum of tensors of paulis. -import openfermion as of - -hamiltonian = ( - -0.0970662681676282 * of.QubitOperator("") - + -0.045302615503799284 * of.QubitOperator("X0 X1 Y2 Y3") - + 0.045302615503799284 * of.QubitOperator("X0 Y1 Y2 X3") - + 0.045302615503799284 * of.QubitOperator("Y0 X1 X2 Y3") - + -0.045302615503799284 * of.QubitOperator("Y0 Y1 X2 X3") - + 0.17141282644776884 * of.QubitOperator("Z0") - + 0.16868898170361213 * of.QubitOperator("Z0 Z1") - + 0.12062523483390425 * of.QubitOperator("Z0 Z2") - + 0.16592785033770352 * of.QubitOperator("Z0 Z3") - + 0.17141282644776884 * of.QubitOperator("Z1") - + 0.16592785033770352 * of.QubitOperator("Z1 Z2") - + 0.12062523483390425 * of.QubitOperator("Z1 Z3") - + -0.22343153690813597 * of.QubitOperator("Z2") - + 0.17441287612261608 * of.QubitOperator("Z2 Z3") - + -0.22343153690813597 * of.QubitOperator("Z3") -) - -# This can be converted into pytket's QubitPauliOperator type. -# -# The OpenFermion `QubitOperator` class represents the operator by its decomposition into a linear combination of Pauli operators (tensor products of the $I$, $X$, $Y$, and $Z$ matrices). -# -# A `QubitPauliString` is a sparse representation of a Pauli operator with support over some subset of qubits. - -from pytket.pauli import Pauli, QubitPauliString -from pytket.utils.operators import QubitPauliOperator - -pauli_sym = {"I": Pauli.I, "X": Pauli.X, "Y": Pauli.Y, "Z": Pauli.Z} - - -def qps_from_openfermion(paulis): - """Convert OpenFermion tensor of Paulis to pytket QubitPauliString.""" - qlist = [] - plist = [] - for q, p in paulis: - qlist.append(Qubit(q)) - plist.append(pauli_sym[p]) - return QubitPauliString(qlist, plist) - - -def qpo_from_openfermion(openf_op): - """Convert OpenFermion QubitOperator to pytket QubitPauliOperator.""" - tk_op = dict() - for term, coeff in openf_op.terms.items(): - string = qps_from_openfermion(term) - tk_op[string] = coeff - return QubitPauliOperator(tk_op) - - -hamiltonian_op = qpo_from_openfermion(hamiltonian) - -# We can simulate this exactly using a statevector simulator like ProjectQ. This has a built-in method for fast calculations of expectation values that works well for small examples like this. - -from pytket.extensions.projectq import ProjectQBackend - -backend = ProjectQBackend() -ideal_energy = backend.get_operator_expectation_value(ansatz, hamiltonian_op) -print(ideal_energy) - -# Ideally the state generated by this ansatz will only span the computational basis states with exactly two of the four qubits in state $\lvert 1 \rangle$. This is because these basis states correspond to two electrons being present in the molecule. -# -# This ansatz is a hardware-efficient model that is designed to explore a large portion of the Hilbert space with relatively few entangling gates. Unfortunately, with this much freedom, it will regularly generate states that have no physical interpretation such as states spanning multiple basis states corresponding to different numbers of electrons in the system (which we assume is fixed and conserved). -# -# We can mitigate this by using a syndrome qubit that calculates the parity of the other qubits. Post-selecting this syndrome with $\langle 0 \rvert$ will project the remaining state onto the subspace of basis states with even parity, increasing the likelihood the observed state will be a physically admissible state. -# -# Even if the ansatz parameters are tuned to give a physical state, real devices have noise and imperfect gates, so in practice we may also measure bad states with a small probability. If this syndrome qubit is measured as 1, it means an error has definitely occurred, so we should discard the shot. - -syn = Qubit("synq", 0) -syn_res = Bit("synres", 0) -ansatz.add_qubit(syn) -ansatz.add_bit(syn_res) -for qb in qubits: - ansatz.CX(qb, syn) -ansatz.Measure(syn, syn_res) - -# Using this, we can define a filter function which removes the shots which the syndrome qubit detected as erroneous. `BackendResult` objects allow retrieval of shots in any bit order, so we can retrieve the `synres` results separately and use them to filter the shots from the remaining bits. The Backends example notebook describes this in more detail. - -from collections import Counter - - -def filter_shots(backend_result, syn_res_bit): - bits = sorted(backend_result.get_bitlist()) - bits.remove(syn_res_bit) - syn_shots = backend_result.get_shots([syn_res])[:, 0] - main_shots = backend_result.get_shots(bits) - return main_shots[syn_shots == 0] - - -def filter_counts(backend_result, syn_res_bit): - bits = sorted(backend_result.get_bitlist()) - syn_index = bits.index(syn_res_bit) - counts = backend_result.get_counts() - filtered_counts = Counter() - for readout, count in counts.items(): - if readout[syn_index] == 0: - filtered_readout = tuple(v for i, v in enumerate(readout) if i != syn_index) - filtered_counts[filtered_readout] += count - return filtered_counts - - -# Depending on which backend we will be using, we will need to compile each circuit we run to conform to the gate set and connectivity constraints. We can define a compilation pass for each backend that optimises the circuit and maps it onto the backend's gate set and connectivity constraints. We don't expect this to change our circuit too much as it is already near-optimal. - -from pytket.passes import OptimisePhaseGadgets, SequencePass - - -def compiler_pass(backend): - return SequencePass([OptimisePhaseGadgets(), backend.default_compilation_pass()]) - - -# Given the full statevector, the expectation value can be calculated simply by matrix multiplication. However, with a real quantum system, we cannot observe the full statevector directly. Fortunately, the Pauli decomposition of the operator gives us a sequence of measurements we should apply to obtain the relevant information to reconstruct the expectation value. -# -# The utility method `append_pauli_measurement` takes a single term of a `QubitPauliOperator` (a `QubitPauliString`) and appends measurements in the corresponding bases to obtain the expectation value for that particular Pauli operator. We will want to make a new `Circuit` object for each of the measurements we wish to observe. -# - -from pytket.predicates import CompilationUnit -from pytket.utils import append_pauli_measurement - - -def gen_pauli_measurement_circuits(state_circuit, compiler_pass, operator): - # compile main circuit once - state_cu = CompilationUnit(state_circuit) - compiler_pass.apply(state_cu) - compiled_state = state_cu.circuit - final_map = state_cu.final_map - # make a measurement circuit for each pauli - pauli_circuits = [] - coeffs = [] - energy = 0 - for p, c in operator.terms.items(): - if p == (): - # constant term - energy += c - else: - # make measurement circuits and compile them - pauli_circ = Circuit(state_circuit.n_qubits - 1) # ignore syndrome qubit - append_pauli_measurement(qps_from_openfermion(p), pauli_circ) - pauli_cu = CompilationUnit(pauli_circ) - compiler_pass.apply(pauli_cu) - pauli_circ = pauli_cu.circuit - init_map = pauli_cu.initial_map - # map measurements onto the placed qubits from the state - rename_map = { - i: final_map[o] for o, i in init_map.items() if o in final_map - } - pauli_circ.rename_units(rename_map) - state_and_measure = compiled_state.copy() - state_and_measure.append(pauli_circ) - pauli_circuits.append(state_and_measure) - coeffs.append(c) - return pauli_circuits, coeffs, energy - - -# We can now start composing these together to get our generalisable expectation value function. Passing all of our circuits to `process_circuits` allows them to be submitted to IBM Quantum devices at the same time, giving substantial savings in overall queueing time. Since the backend will cache any results from `Backend.process_circuits`, we will remove the results when we are done with them to prevent memory bloating when this method is called many times. - -from pytket.utils import expectation_from_shots, expectation_from_counts - - -def expectation_value(state_circuit, operator, backend, n_shots): - if backend.supports_expectation: - circuit = state_circuit.copy() - compiled_circuit = backend.get_compiled_circuit(circuit) - return backend.get_operator_expectation_value( - compiled_circuit, qpo_from_openfermion(operator) - ) - elif backend.supports_shots: - syn_res_index = state_circuit.bit_readout[syn_res] - pauli_circuits, coeffs, energy = gen_pauli_measurement_circuits( - state_circuit, compiler_pass(backend), operator - ) - handles = backend.process_circuits(pauli_circuits, n_shots=n_shots) - for handle, coeff in zip(handles, coeffs): - res = backend.get_result(handle) - filtered = filter_shots(res, syn_res) - energy += coeff * expectation_from_shots(filtered) - backend.pop_result(handle) - return energy - elif backend.supports_counts: - syn_res_index = state_circuit.bit_readout[syn_res] - pauli_circuits, coeffs, energy = gen_pauli_measurement_circuits( - state_circuit, compiler_pass(backend), operator - ) - handles = backend.process_circuits(pauli_circuits, n_shots=n_shots) - for handle, coeff in zip(handles, coeffs): - res = backend.get_result(handle) - filtered = filter_counts(res, syn_res) - energy += coeff * expectation_from_counts(filtered) - backend.pop_result(handle) - return energy - else: - raise NotImplementedError("Implementation for state to be written") - - -# ...and then run it for our ansatz. `AerBackend` supports faster expectation value from snapshopts (using the `AerBackend.get_operator_expectation_value` method), but this only works when all the qubits in the circuit are default register qubits that go up from 0. So we will need to rename `synq`. - -from pytket.extensions.qiskit import IBMQEmulatorBackend, AerBackend - -ansatz.rename_units({Qubit("synq", 0): Qubit("q", 4)}) - -print(expectation_value(ansatz, hamiltonian, AerBackend(), 8000)) -# Try replacing IBMQEmulatorBackend with IBMQBackend to submit the circuits to a real IBM Quantum device. -print(expectation_value(ansatz, hamiltonian, IBMQEmulatorBackend("ibmq_manila"), 8000)) - -# For basic practice with using pytket backends and their results, try editing the code here to: -# * Extend `expectation_value` to work with statevector backends (e.g. `AerStateBackend`) -# * Remove the row filtering from `filter_shots` and see the effect on the expectation value on a noisy simulation/device -# * Adapt `filter_shots` to be able to filter a counts dictionary and adapt `expectation_value` to calulate the result using the counts summary from the backend (`pytket.utils.expectation_from_counts` will be useful here) diff --git a/examples/python/mapping_example.py b/examples/python/mapping_example.py deleted file mode 100644 index 7f82fb1e..00000000 --- a/examples/python/mapping_example.py +++ /dev/null @@ -1,504 +0,0 @@ -# # Qubit mapping and routing - - -# In this tutorial we will show how the problem of mapping from logical quantum circuits to physically permitted circuits is solved automatically in TKET. The basic examples require only the installation of pytket, ```pip install pytket```. - -# There is a wide variety of different blueprints for realising quantum computers, including the well known superconducting and ion trap devices. Different devices come with different constraints, such as a limited primitive gate set for universal quantum computing. Often this limited gate set accommodates an additional constraint, that two-qubit gates can not be executed between all pairs of qubits. - - -# In software, typically this constraint is presented as a "connectivity" graph where vertices connected by an edge represents pairs of physical qubits which two-qubit gates can be executed on. As programmers usually write logical quantum circuits with no sense of architecture (or may want to run their circuit on a range of hardware with different connectivity constraints), most quantum software development kits offer the means to automatically solve this constraint. One common way is to automatically add logical ```SWAP``` gates to a Circuit, changing the position of logical qubits on physical qubits until a two-qubit gate can be realised. This is an active area of research in quantum computing and a problem we discuss in our paper "On The Qubit Routing Problem" - arXiv:1902.08091. - -# In TKET this constraint is represented by the ```Architecture``` class. An Architecture object requires a coupling map to be created, a list of pairs of qubits which defines where two-qubit primitives may be executed. A coupling map can be produced naively by the integer indexing of nodes and edges in some architecture. - -from pytket.architecture import Architecture -from pytket.circuit import Node - -import networkx as nx -from typing import List, Union, Tuple - - -def draw_graph(coupling_map: List[Union[Tuple[int, int], Tuple[Node, Node]]]): - coupling_graph = nx.Graph(coupling_map) - nx.draw(coupling_graph, labels={node: node for node in coupling_graph.nodes()}) - - -simple_coupling_map = [(0, 1), (1, 2), (2, 3)] -simple_architecture = Architecture(simple_coupling_map) -draw_graph(simple_coupling_map) - -# Alternatively we could use the `Node` class to assign our nodes - you will see why this can be helpful later. Lets create an Architecture with an identical graph: - -node_0 = Node("e0", 0) -node_1 = Node("e1", 1) -node_2 = Node("e2", 2) -node_3 = Node("e3", 3) - -id_coupling_map = [(node_0, node_1), (node_1, node_2), (node_2, node_3)] -id_architecture = Architecture(id_coupling_map) -draw_graph(id_coupling_map) - -# We can also create an ID with an arbitrary-dimensional index. Let us make a 2x2x2 cube: - -node_000 = Node("cube", [0, 0, 0]) -node_001 = Node("cube", [0, 0, 1]) -node_010 = Node("cube", [0, 1, 0]) -node_011 = Node("cube", [0, 1, 1]) -node_100 = Node("cube", [1, 0, 0]) -node_101 = Node("cube", [1, 0, 1]) -node_110 = Node("cube", [1, 1, 0]) -node_111 = Node("cube", [1, 1, 1]) - -cube_coupling_map = [ - (node_000, node_001), - (node_000, node_010), - (node_010, node_011), - (node_001, node_011), - (node_000, node_100), - (node_001, node_101), - (node_010, node_110), - (node_011, node_111), - (node_100, node_101), - (node_100, node_110), - (node_110, node_111), - (node_101, node_111), -] - -cube_architecture = Architecture(cube_coupling_map) -draw_graph(cube_coupling_map) - -# To avoid that tedium though we could just use our SquareGrid Architecture: - -from pytket.architecture import SquareGrid - -alternative_cube_architecture = SquareGrid(2, 2, 2) -draw_graph(alternative_cube_architecture.coupling) - -# The current range of quantum computers are commonly referred to as Noisy-Intermediate-Scale-Quantum devices i.e. NISQ devices. The impact of noise is a primary concern during compilation and incentivizes producing physically permitted circuits that have a minimal number of gates. For this reason benchmarking in this area is often completed by comparing the final number of two-qubit (or particularly SWAP gates) in compiled circuits. - -# However it is important to remember that adding logical SWAP gates to minimise gate count is not the only way this constraint can be met, with large scale architecture-aware synthesis methods and fidelity aware methods amongst other approaches producing viable physically permitted circuits. It is likely that no SINGLE approach is better for all circuits, but the ability to use different approaches where best fitted will give the best results during compilation. - - -# Producing physically valid circuits is completed via the `MappingManager` class, which aims to accomodate a wide range of approaches. - -from pytket.mapping import MappingManager - -# A `MappingManager` object requires an `Architecture` object at construction. - -mapping_manager = MappingManager(id_architecture) - -# All mapping is done through the `MappingManager.route_circuit` method. The `MappingManager.route_circuit` method has two arguments, the first a Circuit to be routed (which is mutated), the second a `List[RoutingMethodCircuit]` object that defines how the mapping is completed. - -# Later we will look at defining our own `RoutingMethodCircuit` objects, but initially lets consider one thats already available. - -from pytket.mapping import LexiLabellingMethod, LexiRouteRoutingMethod - -lexi_label = LexiLabellingMethod() -lexi_route = LexiRouteRoutingMethod(10) - -# The `lexi_route` object here is of little use outside `MappingManager`. Note that it takes a lookahead parameter, which will affect the performance of the method, defining the number of two-qubit gates it considers when finding `SWAP` gates to add. - -from pytket import Circuit, OpType -from pytket.circuit import display - -c = ( - Circuit(4) - .CX(0, 1) - .CX(1, 2) - .CX(0, 2) - .CX(0, 3) - .CX(2, 3) - .CX(1, 3) - .CX(0, 1) - .measure_all() -) -display.render_circuit_jupyter(c) - -# We can also look at which logical qubits are interacting. - -from pytket.utils import Graph - -Graph(c).get_qubit_graph() - -# By running the `MappingManager.route_circuit` method on our circuit `c` with the `LexiLabellingMethod` and `LexiRouteRoutingMethod` objects as an argument, qubits in `c` with some physical requirements will be relabelled and the qubit graph modified (by the addition of SWAP gates and relabelling some CX as BRIDGE gates) such that the qubit graph is isomorphic to some subgraph of the full architecture. - -mapping_manager.route_circuit(c, [lexi_label, lexi_route]) -display.render_circuit_jupyter(c) - -# The graph: - -Graph(c).get_qubit_graph() - -# The resulting circuit may also change if we reduce the lookahead parameter. - -c = ( - Circuit(4) - .CX(0, 1) - .CX(1, 2) - .CX(0, 2) - .CX(0, 3) - .CX(2, 3) - .CX(1, 3) - .CX(0, 1) - .measure_all() -) -mapping_manager.route_circuit(c, [lexi_label, LexiRouteRoutingMethod(1)]) -display.render_circuit_jupyter(c) - -# We can also pass multiple `RoutingMethod` options for Routing in a ranked List. Each `RoutingMethod` option has a function for checking whether it can usefully modify a subcircuit at a stage in Routing. To choose, each method in the List is checked in order until one returns True. This will be discussed more later. - -# We can aid the mapping procedure by relabelling qubits in advance. This can be completed using the `Placement` class. - -from pytket.placement import Placement, LinePlacement, GraphPlacement - -# The default ```Placement``` assigns logical qubits to physical qubits as they are encountered during routing. ```LinePlacement``` uses a strategy described in https://arxiv.org/abs/1902.08091. ```GraphPlacement``` is described in Section 7.1 of https://arxiv.org/abs/2003.10611. Lets look at how we can use the ```LinePlacement``` class.` - -line_placement = LinePlacement(id_architecture) - -c = ( - Circuit(4) - .CX(0, 1) - .CX(1, 2) - .CX(0, 2) - .CX(0, 3) - .CX(2, 3) - .CX(1, 3) - .CX(0, 1) - .measure_all() -) -line_placement.place(c) - -display.render_circuit_jupyter(c) - -# Note that one qubit remains unplaced in this example. `LexiRouteRoutingMethod` will dynamically assign it during mapping. - -# Different placements will lead to different selections of SWAP gates being added. However each different routed circuit will preserve the original unitary action of the full circuit while respecting connectivity constraints. - -mapping_manager.route_circuit(c, [lexi_label, lexi_route]) -display.render_circuit_jupyter(c) - -# The graph: - -Graph(c).get_qubit_graph() - -# However, small changes to the depth of lookahead or the original assignment of `Architecture` `Node` can greatly affect the resulting physical circuit for the `LexiRouteRoutingMethod` method. Considering this variance, it should be possible to easily throw additional computational resources at the problem if necessary, which is something TKET is leaning towards with the ability to define custom `RoutingCircuitMethod` objects. - -# To define a new `RoutingMethodCircuit` method though, we first need to understand how it is used in `MappingManager` and routing. The `MappingManager.route_circuit` method treats the global problem of mapping to physical circuits as many sequential sub-problems. Consider the following problem. - -from pytket import Circuit - -from pytket.placement import place_with_map - -circ = Circuit(4).CX(0, 1).CX(1, 2).CX(0, 2).CX(0, 3).CX(2, 3).CX(1, 3).CX(0, 1) -naive_map = { - circ.qubits[0]: node_0, - circ.qubits[1]: node_1, - circ.qubits[2]: node_2, - circ.qubits[3]: node_3, -} -place_with_map(circ, naive_map) -Graph(circ).get_DAG() - -# So what happens when we run the following? - -mapping_manager.route_circuit(circ, [lexi_route]) -Graph(circ).get_DAG() - -# Sequential mapping typically works by partitioning the circuit into two, a first partition comprising a connected subcircuit that is physically permitted, a second partition that is not. Therefore, the first thing `MappingManager.route_circuit` does is find this partition for the passed circuit, by iterating through gates in the circuit. - -# We will construct the partitions ourselves for illustrative purposes. Lets assume we are routing for the four qubit line architecture (qubits are connected to adjacent indices) "simple_architecture" we constructed earlier. - -circ_first_partition = Circuit(4).CX(0, 1).CX(1, 2) -place_with_map(circ_first_partition, naive_map) -Graph(circ_first_partition).get_DAG() - -circ_second_partition = Circuit(4).CX(0, 2).CX(0, 3).CX(2, 3).CX(1, 3).CX(0, 1) -place_with_map(circ_second_partition, naive_map) -Graph(circ_second_partition).get_DAG() - -# Note that there are gates in the second partition that would be physically permitted, if they were not dependent on other gates that are not. - -# The next step is to modify the second partition circuit to move it closer being physically permitted. Here the `LexiRouteRoutingMethod` as before will either insert a SWAP gate at the start of the partition, or will substitute a CX gate in the first slice of the partition with a BRIDGE gate. - -# The option taken by `LexiRouteRoutingethod(1)` is to insert a SWAP gate between the first two nodes of the architecture, swapping their logical states. How does this change the second partition circuit? - -circ_second_partition = ( - Circuit(4).SWAP(0, 1).CX(1, 2).CX(1, 3).CX(2, 3).CX(0, 3).CX(1, 0) -) -place_with_map(circ_second_partition, naive_map) -Graph(circ_second_partition).get_DAG() - -# Leaving the full circuit as: - -full_circuit = ( - Circuit(4).CX(0, 1).CX(1, 2).SWAP(0, 1).CX(1, 2).CX(1, 3).CX(2, 3).CX(0, 3).CX(1, 0) -) -place_with_map(full_circuit, naive_map) -Graph(full_circuit).get_DAG() - -# After a modification is made the partition is updated. - -# The first partition: - -circ_first_partition = Circuit(4).CX(0, 1).CX(1, 2).SWAP(0, 1).CX(1, 2) -place_with_map(circ_first_partition, naive_map) -Graph(circ_first_partition).get_DAG() - -# The second partition: - -circ_second_partition = Circuit(4).CX(1, 3).CX(2, 3).CX(0, 3).CX(1, 0) -place_with_map(circ_second_partition, naive_map) -Graph(circ_second_partition).get_DAG() - -# This pattern of modification and upating the partition is repeated until the partition has reached the end of the circuit, i.e. the back side of the partition has no gates in it. Also note that the process of updating the partition has been simplified for this example with "physically permitted" encapsulating two-qubit gate constraints only - in the future we expect other arity gates to provide constraints that need to be met. Also note that any modification to the second circuit can willfully modify the qubit labelling and a token swapping network will be automatically added to conform to the new labelling. - -# We now enough about how `MappingManager` works to add our own `RoutingMethodCircuit`. While `LexiRouteRoutingMethod` is implemented in c++ TKET, giving it some advantages, via lambda functions we can define our own `RoutingMethodCircuit` in python. - -# A python defined `RoutingMethodCircuit` requires three arguments. The first is a function that given a Circuit (the circuit after the partition) and an Architecture, returns a bool (determining whether the new circuit should be substitued in a full routing process), a new Circuit (a modification of the original circuit such as an added SWAP) a Dict between qubits reflecting any relabelling done in the method, and a Dict between qubits giving any implicit permutation of qubits (such as by adding a SWAP). For some clarity (we will write an example later), lets look at an example function declaration. - -from typing import Dict - - -def route_subcircuit_func( - circuit: Circuit, architecture: Architecture -) -> Tuple[bool, Circuit, Dict[Node, Node], Dict[Node, Node]]: - return () - - -# The first return is a bool which detemrines if a given `RoutingMethodCircuit` is suitable for providing a solution at a given partition. `MappingManager.route_circuit` accepts a List of of `RoutingMethod` defining how solutions are found. At the point the partition circuit is modified, the circuit is passed to `RoutingMethodCircuit.routing_method` which additionally to finding a subcircuit substitution, should determine whether it can or can't helpfully modify the partition boundary circuit, and return True if it can. The first `RoutingMethodCircuit` to return True is then used for modification - meaning the ordering of List elements is important. - -# The third argument sets the maximum number of gates given in the passed Circuit and the fourth argument sets the maximum depth in the passed Circuit. - -# `LexiRouteRoutingMethod` will always return True, because it can always find some helpful SWAP to insert, and it can dynamically assign logical to physical qubits. Given this, lets construct a more specialised modification - an architecture-aware decomposition of a distance-2 CRy gate. Lets write our function type declarations for each method: - - -def distance2_CRy_decomp( - circuit: Circuit, architecture: Architecture -) -> Tuple[bool, Circuit, Dict[Node, Node], Dict[Node, Node]]: - return (False, Circuit(), {}, {}) - - -# Where do we start? Lets define a simple scope for our solution: for a single gate in the passed circuit (the circuit after the partition) that has OpType CRy, if the two qubits it's acting on are at distance 2 on the architecture, decompose the gate using BRIDGE gates. - -# The first restriction is to only have a single gate from the first slice - we can achieve this by setting both the maximum depth and size parameters to 1. - -# The second restriction is for the gate to have OpType CRy and for the qubits to be at distance 2 - we can check this restriction in a `distance2_CRy_check` method. - - -def distance2_CRy_check(circuit: Circuit, architecture: Architecture) -> bool: - if circuit.n_gates != 1: - raise ValueError( - "Circuit for CRy check should only have 1 gate, please change parameters of method declaration." - ) - command = circuit.get_commands()[0] - if command.op.type == OpType.CRy: - # Architecture stores qubits under `Node` identifier - n0 = Node(command.qubits[0].reg_name, command.qubits[0].index) - n1 = Node(command.qubits[1].reg_name, command.qubits[1].index) - # qubits could not be placed in circuit, so check before finding distance - if n0 in architecture.nodes and n1 in architecture.nodes: - # means we can run the decomposition - if architecture.get_distance(n0, n1) == 2: - return True - return False - - -# The `distance2_CRy_check` confirms whether the required restrictions are respected. Given this, if the `distance2_CRy_decomp` method is called we know where to add the decomposition. - - -def distance2_CRy_decomp( - circuit: Circuit, architecture: Architecture -) -> Tuple[bool, Circuit, Dict[Node, Node], Dict[Node, Node]]: - worthwhile_substitution = distance2_CRy_check(circuit, architecture) - if worthwhile_substitution == False: - return (False, Circuit(), {}, {}) - - command = circuit.get_commands()[0] - qubits = command.qubits - # Architecture stores qubits under `Node` identifier - n0 = Node(qubits[0].reg_name, qubits[0].index) - n1 = Node(qubits[1].reg_name, qubits[1].index) - - # need to find connecting node for decomposition - adjacent_nodes_0 = architecture.get_adjacent_nodes(n0) - adjacent_nodes_1 = architecture.get_adjacent_nodes(n1) - connecting_nodes = adjacent_nodes_0.intersection(adjacent_nodes_1) - - if len(connecting_nodes) == 0: - raise ValueError("Qubits for distance-2 CRy decomp are not at distance 2.") - - connecting_node = connecting_nodes.pop() - - c = Circuit() - - # the "relabelling map" empty, and the permutation map is qubit to qubit, so add here - permutation_map = dict() - for q in circuit.qubits: - permutation_map[q] = q - c.add_qubit(q) - # rotation, can assume only parameter as CRy - angle = command.op.params[0] - c.Ry(angle, qubits[1]) - # distance-2 CX decomp - c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1]) - c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1]) - # rotation - c.Ry(-1 * angle, qubits[1]) - # distance-2 CX decomp - c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1]) - c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1]) - - # the "relabelling map" is just qubit to qubit - return (True, c, {}, permutation_map) - - -# Before turning this into a `RoutingMethod` we can try it ourselves. - -test_c = Circuit(4) -test_c.CRy(0.6, 0, 2) -place_with_map(test_c, naive_map) -Graph(test_c).get_DAG() - -# As we can see, our circuit has one CRy gate at distance two away. - -print(distance2_CRy_check(test_c, id_architecture)) - -# Our method returns True, as expected! We should also test cases where it returns errors or False. - -test_c_false = Circuit(4) -test_c_false.CRy(0.4, 0, 1) -place_with_map(test_c_false, naive_map) -print(distance2_CRy_check(test_c_false, id_architecture)) - -test_c_error = Circuit(4) -test_c_error.CRy(0.6, 0, 2) -test_c_error.CRy(0.4, 0, 1) -place_with_map(test_c_error, naive_map) -try: - distance2_CRy_check(test_c_error, id_architecture) -except ValueError: - print("Error reached!") - -# Does the decomposition work? - -test_c = Circuit(4) -test_c.CRy(0.6, 0, 2) -place_with_map(test_c, naive_map) -decomp = distance2_CRy_decomp(test_c, id_architecture) -display.render_circuit_jupyter(decomp[1]) - -# Great! Our check function and decomposition method are both working. Lets wrap them into a `RoutingMethodCircuit` and try them out. - -from pytket.mapping import RoutingMethodCircuit - -cry_rmc = RoutingMethodCircuit(distance2_CRy_decomp, 1, 1) - -# We can use our original `MappingManager` object as it is defined for the same architecture. Lets try it out on a range of circumstances. - -# If we pass it a full CX circuit without `LexiRouteRoutingMethod`, we should find that `MappingManager` throws an error, as none of the passed methods can route for the given circuit. - -c = ( - Circuit(4) - .CX(0, 1) - .CX(1, 2) - .CX(0, 2) - .CX(0, 3) - .CX(2, 3) - .CX(1, 3) - .CX(0, 1) - .measure_all() -) -place_with_map(c, naive_map) -try: - mapping_manager.route_circuit(c, [cry_rmc]) -except RuntimeError: - print("Error reached!") - -# Alternatively, we can add `LexiRouteRoutingMethod` on top: - -c = ( - Circuit(4) - .CX(0, 1) - .CX(1, 2) - .CX(0, 2) - .CX(0, 3) - .CX(2, 3) - .CX(1, 3) - .CX(0, 1) - .measure_all() -) -place_with_map(c, naive_map) -mapping_manager.route_circuit(c, [cry_rmc, LexiRouteRoutingMethod(10)]) -display.render_circuit_jupyter(c) - -# However as there are no CRy gates our new method is unused. We can add one: - -c = ( - Circuit(4) - .CRy(0.6, 0, 2) - .CX(0, 1) - .CX(1, 2) - .CX(0, 2) - .CX(0, 3) - .CX(2, 3) - .CX(1, 3) - .CX(0, 1) - .measure_all() -) -mapping_manager.route_circuit(c, [lexi_label, cry_rmc, LexiRouteRoutingMethod(10)]) -display.render_circuit_jupyter(c) - -# This time we can see our decomposition! If we reorder the methods though `LexiRouteRoutingMethod` is checked first (and returns True), so our new method is unused. The order is important! - -# Finally, lets see what happens if the gate is not at the right distance initially. - -c = ( - Circuit(4) - .CRy(0.6, 0, 3) - .CX(0, 1) - .CX(1, 2) - .CX(0, 2) - .CX(0, 3) - .CX(2, 3) - .CX(1, 3) - .CX(0, 1) - .measure_all() -) -mapping_manager.route_circuit(c, [lexi_label, cry_rmc, LexiRouteRoutingMethod(10)]) -display.render_circuit_jupyter(c) - -# Above a SWAP gate is inserted by `LexiRouteRoutingMethod` before anything else. - -# For anyone interested, a simple extension exercise could be to extend this to additionally work for distance-2 CRx and CRz. Alternatively one could improve on the method itself - this approach always decomposes a CRy at distance-2, but is this a good idea? - -# Also note that higher performance solutions are coded straight into the TKET c++ codebase. This provides advantages, including that Circuit construction and substitution is unncessary (as with python) as the circuit can be directly modified, however the ability to produce prototypes at the python level is very helpful. If you have a great python implementation but are finding some runtime bottlenecks, why not try implementing it straight into TKET (the code is open source at https://github.com/CQCL/tket). - -# Besides the `LexiRouteRoutingMethod()` and the `LexiLabellingMethod()` there are other routing methods in pytket, such as the `AASRouteRoutingMethod()` and the corresponding `AASLabellingMethod()`, which are used to route phase-polynomial boxes using architecture-aware synthesis. Usually circuits contain non-phase-polynomial operations as well, so it is a good idea to combine them with the `LexiRouteRoutingMethod()`, as in the following example: - -from pytket.mapping import AASRouteRoutingMethod, AASLabellingMethod -from pytket.circuit import PhasePolyBox, Qubit -import numpy as np - -c = Circuit(3, 3) -n_qb = 3 -qubit_indices = {Qubit(0): 0, Qubit(1): 1, Qubit(2): 2} -phase_polynomial = {(True, False, True): 0.333, (False, False, True): 0.05} -linear_transformation = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) -p_box = PhasePolyBox(n_qb, qubit_indices, phase_polynomial, linear_transformation) -c.add_phasepolybox(p_box, [0, 1, 2]) -c.CX(0, 1).CX(0, 2).CX(1, 2) -display.render_circuit_jupyter(c) -nodes = [Node("test", 0), Node("test", 1), Node("test", 2)] -arch = Architecture([[nodes[0], nodes[1]], [nodes[1], nodes[2]]]) -mm = MappingManager(arch) -mm.route_circuit( - c, - [ - AASRouteRoutingMethod(1), - LexiLabellingMethod(), - LexiRouteRoutingMethod(), - AASLabellingMethod(), - ], -) -display.render_circuit_jupyter(c) - -# In this case the order of the methods is not very relevant, because in each step of the routing only one of the methods is suitable. In the first part of the circuit the mapping is done without inserting swaps by the AAS method; in the second part one swap gate is added to the circuit. diff --git a/examples/python/measurement_reduction_example.py b/examples/python/measurement_reduction_example.py deleted file mode 100644 index 8385741c..00000000 --- a/examples/python/measurement_reduction_example.py +++ /dev/null @@ -1,96 +0,0 @@ -# # Advanced expectation values and measurement reduction - -# This notebook is an advanced follow-up to the "expectation_value_example" notebook, focussing on reducing the number of circuits required for measurement. -# -# When calculating the expectation value $\langle \psi \vert H \vert \psi \rangle$ of some operator $H$ on a quantum computer, we prepare $\vert \psi \rangle$ using a circuit, and the operator $H$ is first decomposed into a sum of smaller, tractable operators of the form $\alpha P$, where $P \in \mathcal{G}_n$, the multi-qubit Pauli group. Naively, one would obtain the expectation value of each of these smaller operators individually by doing shots on the quantum computer and measuring in the correct Pauli bases. Assuming the device measures only single qubits in the $Z$-basis, this basis change requires single-qubit Clifford gates, which are "cheaper" (less noisy and quicker) than entangling gates. The sum of these smaller operator expectation values is then used to obtain the desired $\langle \psi \vert H \vert \psi \rangle$. -# -# However, the scaling of this process can be poor, meaning that many shots are required. Instead, several of these smaller operators can be measured simultaneously, reducing the total number of measurements. For some sets of measurements, it can be done "for free", meaning that no extra entangling gates are required to perform simultaneous measurement. For general commuting sets of Pauli measurements, Clifford gates are required for simultaneous measurement, including entangling gates. - -# There are several strategies for measurement reduction throughout the literature. Examples include https://arxiv.org/abs/1908.06942, https://arxiv.org/abs/1908.08067 and https://arxiv.org/abs/1907.07859. - -# In `pytket`, we provide tools to perform measurement reduction. The most accessible way is to use the utils method, `get_operator_expectation_value`. This method wraps up some under-the-hood processes to allow users to calculate expectation values, agnostic to the backend, operator, or circuit. In this tutorial we will use the Qiskit Aer simulators via the `AerBackend`, for shots, and the `AerStateBackend`, for statevector simulation. -# -# We use the `QubitPauliOperator` class to represent the operator $H$. - -from pytket.circuit import Circuit, Qubit -from pytket.pauli import Pauli, QubitPauliString -from pytket.utils import QubitPauliOperator -from pytket.utils.expectations import get_operator_expectation_value -from pytket.extensions.qiskit import AerBackend, AerStateBackend - -# First, let's get some results on a toy circuit without using any measurement reduction: - -shots_backend = AerBackend() -n_shots = 10000 - -c = Circuit(5) -c.H(4) -c.V(2) - -c = shots_backend.get_compiled_circuit(c) -op = QubitPauliOperator( - { - QubitPauliString([Qubit(0)], [Pauli.Z]): 0.1, - QubitPauliString( - [Qubit(0), Qubit(1), Qubit(2), Qubit(3), Qubit(4)], - [Pauli.Y, Pauli.Z, Pauli.X, Pauli.X, Pauli.Y], - ): 0.4, - QubitPauliString([Qubit(0), Qubit(1)], [Pauli.X, Pauli.X]): 0.2, - } -) - -shots_result = get_operator_expectation_value(c, op, shots_backend, n_shots) -print(shots_result) - -# The result should be around 0.1, although as the shot simulator is stochastic this will be inexact. Let's test to check what the exact result should be using the statevector simulator: - -state_backend = AerStateBackend() -state_result = get_operator_expectation_value(c, op, state_backend) -print(state_result) - -# Now we can introduce measurement reduction. First we need to choose a strategy: - -from pytket.partition import PauliPartitionStrat - -# This first one only performs measurements on simultaneous Pauli operators when there is no cost incurred to do so. - -strat = PauliPartitionStrat.NonConflictingSets -shots_result = get_operator_expectation_value(c, op, shots_backend, n_shots, strat) -print(shots_result) - -# The other strategy we use groups together arbitrary Pauli operators, with the condition that all Pauli operators within a group commute. For an input circuit with $n$ qubits, our method requires the addition of up to $\frac{n(n-1)}{2}$ $CX$ gates to "diagonalise" the Pauli operators, although in practice we find that our techniques tend to give far lower gate overhead than this bound. We describe the procedure in an upcoming paper. - -strat = PauliPartitionStrat.CommutingSets -shots_result = get_operator_expectation_value(c, op, shots_backend, n_shots, strat) -print(shots_result) - -# Obviously, the `AerBackend` can be swapped out for the backend of a real machine. - -# We will now demonstrate how to manually use the methods that are being called by `get_operator_expectation_value`. These methods are primarily intended for internal use, but we show them here for advanced users who may wish to have more information about the number of CX gates being added to each circuit, the number of circuits being run and other diagnostics. - -from pytket.circuit import OpType -from pytket.partition import measurement_reduction - -id_string = QubitPauliString() -qpt_list = [p for p in op._dict.keys() if (p != id_string)] -setup_1 = measurement_reduction(qpt_list, PauliPartitionStrat.NonConflictingSets) -print("Circuits required for measurement: {}".format(len(setup_1.measurement_circs))) - -# This produced a `MeasurementSetup` object using the `NonConflictingSets` strategy of measurement reduction. This object holds a set of circuits which perform different basis changes, and the measurements associated with these circuits. -# -# There are 3 circuits held within the `MeasurementSetup` object, meaning that our original `QubitOperator` has been reduced from the 5 originally required measurements to 3. - -for circ in setup_1.measurement_circs: - print("CX gates for measurement: {}".format(circ.n_gates_of_type(OpType.CX))) - -# No CX gates have been added for any of the required measurements. Now, we will change to the `CommutingSets` strategy. - -setup_2 = measurement_reduction(qpt_list, PauliPartitionStrat.CommutingSets) -print("Circuits required for measurement: {}".format(len(setup_2.measurement_circs))) - -# There are only 2 circuits required when expanding the scope of allowed simultaneous measurements. However, this comes at a cost: - -for circ in setup_2.measurement_circs: - print("CX gates for measurement: {}".format(circ.n_gates_of_type(OpType.CX))) - -# A CX gate has been introduced to one of the measurement circuits, to convert to the correct Pauli basis set. On current devices which are extremely constrained in the number of entangling gates, the reduction in number of shots may not be worth the gate overhead. diff --git a/examples/python/phase_estimation.py b/examples/python/phase_estimation.py deleted file mode 100644 index 16b9b7cc..00000000 --- a/examples/python/phase_estimation.py +++ /dev/null @@ -1,350 +0,0 @@ -# # Quantum Phase Estimation -# -# When constructing circuits for quantum algorithms it is useful to think of higher level operations than just individual quantum gates. In `pytket` we can construct circuits using box structures which abstract away the complexity of the underlying circuit. This notebook is intended to complement the [boxes section](https://tket.quantinuum.com/user-manual/manual_circuit.html#boxes) of the user manual which introduces the different box types. -# -# To demonstrate boxes in `pytket` we will consider the Quantum Phase Estimation algorithm (QPE). This is an important subroutine in several quantum algorithms including Shor's algorithm and fault-tolerant approaches to quantum chemistry. -# -# ## Overview of Phase Estimation -# -# The Quantum Phase Estimation algorithm can be used to estimate the eigenvalues of some unitary operator $U$ to some desired precision. -# -# The eigenvalues of $U$ lie on the unit circle, giving us the following eigenvalue equation -# -# $$ -# \begin{equation} -# U |\psi \rangle = e^{2 \pi i \theta} |\psi\rangle\,, \quad 0 \leq \theta \leq 1 -# \end{equation} -# $$ -# -# Here $|\psi \rangle$ is an eigenstate of the operator $U$. In phase estimation we estimate the eigenvalue $e^{2 \pi i \theta}$ by approximating $\theta$. -# -# -# The circuit for Quantum phase estimation is itself composed of several subroutines which we can realise as boxes. -# -# ![](images/phase_est.png "Quantum Phase Estimation Circuit") - -# QPE is generally split up into three stages -# -# 1. Firstly we prepare an initial state in one register. In parallel we prepare a uniform superposition state using Hadamard gates on some ancilla (measurement) qubits. The number of ancilla qubits determines how precisely we can estimate the phase $\theta$. -# -# 2. Secondly we apply successive controlled $U$ gates. This has the effect of "kicking back" phases onto the ancilla qubits according to the eigenvalue equation above. -# -# 3. Finally we apply the inverse Quantum Fourier Transform (QFT). This essentially plays the role of destructive interference, suppressing amplitudes from "undesirable states" and hopefully allowing us to measure a single outcome (or a small number of outcomes) with high probability. -# -# -# There is some subtlety around the first point. The initial state used can be an exact eigenstate of $U$ however this may be difficult to prepare if we don't know the eigenvalues of $U$ in advance. Alternatively we could use an initial state that is a linear combination of eigenstates, as the phase estimation will project into the eigenspace of $U$. - -# We also assume that we can implement $U$ with a quantum circuit. In chemistry applications $U$ could be of the form $U=e^{-iHt}$ where $H$ is the Hamiltonian of some system of interest. In the textbook algorithm, the number of controlled unitaries we apply scales exponentially with the number of measurement qubits. This allows more precision at the expense of a larger quantum circuit. - -# ## The Quantum Fourier Transform - -# Before considering the other parts of the QPE algorithm, lets focus on the Quantum Fourier Transform (QFT) subroutine. -# -# Mathematically, the QFT has the following action. -# -# $$ -# \begin{equation} -# QFT : |j\rangle\ \longmapsto \frac{1}{\sqrt{N}} \sum_{k=0}^{N - 1} e^{2 \pi ijk/N}|k\rangle, \quad N= 2^n -# \end{equation} -# $$ -# -# This is essentially the Discrete Fourier transform except the input is a quantum state $|j\rangle$. -# -# We can build the circuit for the $n$ qubit QFT using $n$ Hadamard gates $\lfloor{\frac{n}{2}}\rfloor$ swap gates and $\frac{n(n-1)}{2}$ controlled unitary rotations $\text{CU1}$. -# -# $$ -# \begin{equation} -# U1(\phi) = -# \begin{pmatrix} -# 1 & 0 \\ -# 0 & e^{i \pi \phi} -# \end{pmatrix}\, , \quad -# CU1(\phi) = -# \begin{pmatrix} -# 1 & 0 & 0 & 0 \\ -# 0 & 1 & 0 & 0 \\ -# 0 & 0 & 1 & 0 \\ -# 0 & 0 & 0 & e^{i \pi \phi} -# \end{pmatrix} -# \end{equation} -# $$ -# -# The circuit for the Quantum Fourier transform on three qubits is the following -# -# ![](images/qft.png "QFT Circuit") -# -# We can build this circuit in `pytket` by adding gate operations manually: - -# lets build the QFT for three qubits -from pytket.circuit import Circuit -from pytket.circuit.display import render_circuit_jupyter - -qft3_circ = Circuit(3) -qft3_circ.H(0) -qft3_circ.CU1(0.5, 1, 0) -qft3_circ.CU1(0.25, 2, 0) -qft3_circ.H(1) -qft3_circ.CU1(0.5, 2, 1) -qft3_circ.H(2) -qft3_circ.SWAP(0, 2) -render_circuit_jupyter(qft3_circ) - - -# We can generalise the quantum Fourier transform to $n$ qubits by iterating over the qubits as follows - - -def build_qft_circuit(n_qubits: int) -> Circuit: - circ = Circuit(n_qubits, name="QFT") - for i in range(n_qubits): - circ.H(i) - for j in range(i + 1, n_qubits): - circ.CU1(1 / 2 ** (j - i), j, i) - - for k in range(0, n_qubits // 2): - circ.SWAP(k, n_qubits - k - 1) - - return circ - - -qft4_circ: Circuit = build_qft_circuit(4) -render_circuit_jupyter(qft4_circ) - - -# Now that we have the generalised circuit we can wrap it up in a `CircBox` which can then be added to another circuit as a subroutine. - -from pytket.circuit import CircBox - -qft4_box: CircBox = CircBox(qft4_circ) -qft_circ = Circuit(4).add_gate(qft4_box, [0, 1, 2, 3]) -render_circuit_jupyter(qft_circ) - - -# Note how the `CircBox` inherits the name `QFT` from the underlying circuit. - -# Recall that in our phase estimation algorithm we need to use the inverse QFT. -# -# $$ -# \begin{equation} -# \text{QFT}^† : \frac{1}{\sqrt{N}} \sum_{k=0}^{N - 1} e^{2 \pi ijk/N}|k\rangle \longmapsto |j\rangle\,, \quad N= 2^n -# \end{equation} -# $$ -# -# -# Now that we have the QFT circuit we can obtain the inverse by using `CircBox.dagger`. We can also verify that this is correct by inspecting the circuit inside with `CircBox.get_circuit()`. - - -inv_qft4_box = qft4_box.dagger -# Explicitly set the name of the `CircBox` to "QFT†" -inv_qft4_box.circuit_name = "QFT†" -qft_inv_circ = Circuit(4) -qft_inv_circ.add_gate(inv_qft4_box, [0, 1, 2, 3]) -render_circuit_jupyter(qft_inv_circ) - - -# ## Building the Phase Estimation Circuit - -# We can now define a function to build our entire QPE circuit. We can make this function take a state preparation circuit and a unitary circuit as input as well. The function also has the number of measurement qubits as input which will determine the precision of our phase estimate. - - -from pytket.circuit import QControlBox - - -def build_phase_estimation_circuit( - n_measurement_qubits: int, state_prep_circuit: Circuit, unitary_circuit: Circuit -) -> Circuit: - # Define a Circuit with a measurement and prep register - qpe_circ: Circuit = Circuit() - n_state_prep_qubits = state_prep_circuit.n_qubits - measurement_register = qpe_circ.add_q_register("m", n_measurement_qubits) - state_prep_register = qpe_circ.add_q_register("p", n_state_prep_qubits) - - qpe_circ.add_circuit(state_prep_circuit, list(state_prep_register)) - - # Create a controlled unitary with a single control qubit - unitary_circuit.name = "U" - controlled_u_gate = QControlBox(CircBox(unitary_circuit), 1) - - # Add Hadamard gates to every qubit in the measurement register - for m_qubit in measurement_register: - qpe_circ.H(m_qubit) - - # Add all (2**n_measurement_qubits - 1) of the controlled unitaries sequentially - for m_qubit in range(n_measurement_qubits): - control_index = n_measurement_qubits - m_qubit - 1 - control_qubit = [measurement_register[control_index]] - for _ in range(2**m_qubit): - qpe_circ.add_gate( - controlled_u_gate, control_qubit + list(state_prep_register) - ) - - # Finally, append the inverse qft and measure the qubits - qft_box = CircBox(build_qft_circuit(n_measurement_qubits)) - inverse_qft_box = qft_box.dagger - inverse_qft_box.circuit_name = "QFT†" - - qpe_circ.add_gate(inverse_qft_box, list(measurement_register)) - - qpe_circ.measure_register(measurement_register, "c") - - return qpe_circ - - -# ## Phase Estimation with a Trivial Eigenstate -# -# Lets test our circuit construction by preparing a trivial $|1\rangle$ eigenstate of the $\text{U1}$ gate. We can then see if our phase estimation circuit returns the expected eigenvalue. - -# $$ -# \begin{equation} -# U1(\phi)|1\rangle = e^{i \pi \phi}|1\rangle = e^{2 \pi i \theta} |1\rangle \implies \theta = \frac{\phi}{2} -# \end{equation} -# $$ -# -# So we expect that our ideal phase $\theta$ will be half the input angle $\phi$ to our $U1$ gate. - - -prep_circuit = Circuit(1).X(0) # prepare the |1> eigenstate of U1 - -input_angle = 0.73 # angle as number of half turns - -unitary_circuit = Circuit(1).U1(input_angle, 0) # Base unitary for controlled U ops - - -qpe_circ_trivial = build_phase_estimation_circuit( - 4, state_prep_circuit=prep_circuit, unitary_circuit=unitary_circuit -) - - -render_circuit_jupyter(qpe_circ_trivial) - - -# Lets use the noiseless `AerBackend` simulator to run our phase estimation circuit. - - -from pytket.extensions.qiskit import AerBackend - -backend = AerBackend() - -compiled_circ = backend.get_compiled_circuit(qpe_circ_trivial) - - -n_shots = 1000 -result = backend.run_circuit(compiled_circ, n_shots) - -print(result.get_counts()) - - -from pytket.backends.backendresult import BackendResult -import matplotlib.pyplot as plt - - -# plotting function for QPE Notebook -def plot_qpe_results( - sim_result: BackendResult, - n_strings: int = 4, - dark_mode: bool = False, - y_limit: int = 1000, -) -> None: - """ - Plots results in a barchart given a BackendResult. the number of stings displayed - can be specified with the n_strings argument. - """ - counts_dict = sim_result.get_counts() - sorted_shots = counts_dict.most_common() - - n_most_common_strings = sorted_shots[:n_strings] - x_axis_values = [str(entry[0]) for entry in n_most_common_strings] # basis states - y_axis_values = [entry[1] for entry in n_most_common_strings] # counts - - if dark_mode: - plt.style.use("dark_background") - - fig = plt.figure() - ax = fig.add_axes((0, 0, 0.75, 0.5)) - color_list = ["orange"] * (len(x_axis_values)) - ax.bar( - x=x_axis_values, - height=y_axis_values, - color=color_list, - ) - ax.set_title(label="Results") - plt.ylim([0, y_limit]) - plt.xlabel("Basis State") - plt.ylabel("Number of Shots") - plt.show() - - -plot_qpe_results(result, y_limit=int(1.2 * n_shots)) - - -# As expected we see one outcome with high probability. Lets now extract our approximation of $\theta$ from our output bitstrings. -# -# suppose the $j$ is an integer representation of our most commonly measured bitstring. - -# $$ -# \begin{equation} -# \theta_{estimate} = \frac{j}{N} -# \end{equation} -# $$ - -# Here $N = 2 ^m$ where $m$ is the number of measurement qubits. - - -from pytket.backends.backendresult import BackendResult - - -def single_phase_from_backendresult(result: BackendResult) -> float: - # Extract most common measurement outcome - basis_state = result.get_counts().most_common()[0][0] - bitstring = "".join([str(bit) for bit in basis_state]) - integer_j = int(bitstring, 2) - - # Calculate theta estimate - return integer_j / (2 ** len(bitstring)) - - -theta = single_phase_from_backendresult(result) - -print(theta) - - -print(input_angle / 2) - - -# Our output is close to half our input angle $\phi$ as expected. Lets calculate our error $E$ to three decimal places. - -# $$ -# \begin{equation} -# E = |\phi - 2 \, \theta_{estimate}| -# \end{equation} -# $$ - -error = round(abs(input_angle - (2 * theta)), 3) -print(error) - -# ## Phase Estimation with Time Evolution - -# In the phase estimation algorithm we repeatedly perform controlled unitary operations. In the textbook variant of QPE presented here, the number of controlled unitaries will be $2^m - 1$ where $m$ is the number of measurement qubits. - -# In the example above we've shown a trivial instance of QPE where we know the exact phase in advance. For more realistic applications of QPE we will have some non-trivial state preparation required. -# -# For chemistry or condensed matter physics $U$ typically be the time evolution operator $U(t) = e^{- i H t}$ where $H$ is the problem Hamiltonian. -# Suppose that we had the following decomposition for $H$ in terms of Pauli strings $P_j$ and complex coefficients $\alpha_j$. -# -# $$ -# \begin{equation} -# H = \sum_j \alpha_j P_j\,, \quad \, P_j \in \{I, \,X, \,Y, \,Z\}^{\otimes n} -# \end{equation} -# $$ -# -# Here the term Pauli strings refers to tensor products of Pauli operators. These strings form an orthonormal basis for $2^n \times 2^n$ matrices. - -# If we have a Hamiltonian in the form above, we can then implement $U(t)$ as a sequence of Pauli gadget circuits. We can do this with the [PauliExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.PauliExpBox) construct in pytket. For more on `PauliExpBox` see the [user manual](https://tket.quantinuum.com/user-manual/manual_circuit.html#pauli-exponential-boxes). - -# Once we have a circuit to implement our time evolution operator $U(t)$, we can construct the controlled $U(t)$ operations using [QControlBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.QControlBox). If our base unitary is a sequence of `PauliExpBox`(es) then there is some structure we can exploit to simplify our circuit. See this [blog post](https://tket.quantinuum.com/blog/posts/controlled_gates/) on [ConjugationBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.ConjugationBox) for more. - -# As an exercise, try to use phase estimation to calculate the ground state of diatomic hydrogen $H_2$. - -# ## Suggestions for further reading -# -# * Quantinuum paper on Bayesian phase estimation -> https://arxiv.org/pdf/2306.16608.pdf -# * Blog post on `ConjugationBox` (efficient circuits for controlled gates) -> https://tket.quantinuum.com/blog/posts/controlled_gates/ diff --git a/examples/python/pytket-qujax-classification.py b/examples/python/pytket-qujax-classification.py deleted file mode 100644 index 589d6c93..00000000 --- a/examples/python/pytket-qujax-classification.py +++ /dev/null @@ -1,182 +0,0 @@ -# # Binary classification using `pytket-qujax` - -# See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html). - -from jax import numpy as jnp, random, vmap, value_and_grad, jit -from pytket import Circuit -from pytket.circuit.display import render_circuit_jupyter -from pytket.extensions.qujax.qujax_convert import tk_to_qujax -import matplotlib.pyplot as plt - -# # Define the classification task -# We'll try and learn a _donut_ binary classification function (i.e. a bivariate coordinate is labelled 1 if it is inside the donut and 0 if it is outside) - -inner_rad = 0.25 -outer_rad = 0.75 - - -def classification_function(x, y): - r = jnp.sqrt(x**2 + y**2) - return jnp.where((r > inner_rad) * (r < outer_rad), 1, 0) - - -linsp = jnp.linspace(-1, 1, 1000) -Z = vmap(lambda x: vmap(lambda y: classification_function(x, y))(linsp))(linsp) - -plt.contourf(linsp, linsp, Z, cmap="Purples") - -# Now let's generate some data for our quantum circuit to learn from - -n_data = 1000 -x = random.uniform(random.PRNGKey(0), shape=(n_data, 2), minval=-1, maxval=1) -y = classification_function(x[:, 0], x[:, 1]) - -plt.scatter(x[:, 0], x[:, 1], alpha=jnp.where(y, 1, 0.2), s=10) - -# # Quantum circuit time -# We'll use a variant of data re-uploading [Pérez-Salinas et al](https://doi.org/10.22331/q-2020-02-06-226) to encode the input data, alongside some variational parameters within a quantum circuit classifier - -n_qubits = 3 -depth = 5 - -c = Circuit(n_qubits) - -for layer in range(depth): - for qi in range(n_qubits): - c.Rz(0.0, qi) - c.Ry(0.0, qi) - c.Rz(0.0, qi) - - if layer < (depth - 1): - for qi in range(layer, layer + n_qubits - 1, 2): - c.CZ(qi % n_qubits, (qi + 1) % n_qubits) - c.add_barrier(range(n_qubits)) - -render_circuit_jupyter(c) - -# We can use `pytket-qujax` to generate our angles-to-statetensor function. - -angles_to_st = tk_to_qujax(c) - -# We'll parameterise each angle as -# -# $$ -# \begin{equation} -# \theta_k = b_k + w_k \, x_k -# \end{equation} -# $$ -# -# where $b_k, w_k$ are variational parameters to be learnt and $x_k = x_0$ if $k$ even, $x_k = x_1$ if $k$ odd for a single bivariate input point $(x_0, x_1)$. - -n_angles = 3 * n_qubits * depth -n_params = 2 * n_angles - - -def param_and_x_to_angles(param, x_single): - biases = param[:n_angles] - weights = param[n_angles:] - - weights_times_data = jnp.where( - jnp.arange(n_angles) % 2 == 0, weights * x_single[0], weights * x_single[1] - ) - - angles = biases + weights_times_data - return angles - - -param_and_x_to_st = lambda param, x_single: angles_to_st( - param_and_x_to_angles(param, x_single) -) - -# We'll measure the first qubit only (if its 1 we label _donut_, if its 0 we label _not donut_) - - -def param_and_x_to_probability(param, x_single): - st = param_and_x_to_st(param, x_single) - all_probs = jnp.square(jnp.abs(st)) - first_qubit_probs = jnp.sum(all_probs, axis=range(1, n_qubits)) - return first_qubit_probs[1] - - -# For binary classification, the likelihood for our full data set $(x_{1:N}, y_{1:N})$ is -# -# $$ -# \begin{equation} -# p(y_{1:N} \mid b, w, x_{1:N}) = \prod_{i=1}^N p(y_i \mid b, w, x_i) = \prod_{i=1}^N (1 - q_{(b,w)}(x_i))^{I[y_i = 0]}q_{(b,w)}(x_i)^{I[y_i = 1]}, -# \end{equation} -# $$ -# -# where $q_{(b, w)}(x)$ is the probability the quantum circuit classifies input $x$ as donut given variational parameter vectors $(b, w)$. This gives log-likelihood -# -# $$ -# \begin{equation} -# \log p(y_{1:N} \mid b, w, x_{1:N}) = \sum_{i=1}^N I[y_i = 0] \log(1 - q_{(b,w)}(x_i)) + I[y_i = 1] \log q_{(b,w)}(x_i), -# \end{equation} -# $$ -# -# which we would like to maximise. -# -# Unfortunately, the log-likelihood **cannot** be approximated unbiasedly using shots, that is we can approximate $q_{(b,w)}(x_i)$ unbiasedly but not $\log(q_{(b,w)}(x_i))$. -# Note that in qujax simulations we can use the statetensor to calculate this exactly, but it is still good to keep in mind loss functions that can also be used with shots from a quantum device. -# -# Instead we can minimise an expected distance between shots and data -# -# $$ -# \begin{equation} -# C(b, w, x, y) = E_{p(y' \mid q_{(b, w)}(x))}[\ell(y', y)] = (1 - q_{(b, w)}(x)) \ell(0, y) + q_{(b, w)}(x)\ell(1, y), -# \end{equation} -# $$ -# -# where $y'$ is a shot, $y$ is a data label and $\ell$ is some distance between bitstrings - here we simply set $\ell(0, 0) = \ell(1, 1) = 0$ and $\ell(0, 1) = \ell(1, 0) = 1$ (which coincides with the Hamming distance for this binary example). -# -# The full batch cost function is -# -# $$ -# \begin{equation} -# C(b, w) = \frac1N \sum_{i=1}^N C(b,\, w,\, x_i,\, y_i). -# \end{equation} -# $$ - -# Note that to calculate the cost function we need to evaluate the statetensor for every input point $x_i$. If the dataset becomes too large, we can easily minibatch. - - -def param_to_cost(param): - donut_probs = vmap(param_and_x_to_probability, in_axes=(None, 0))(param, x) - costs = jnp.where(y, 1 - donut_probs, donut_probs) - return costs.mean() - - -# # Ready to descend some gradients? -# We'll just use vanilla gradient descent here - -param_to_cost_and_grad = jit(value_and_grad(param_to_cost)) - -n_iter = 1000 -stepsize = 1e-1 -param = random.uniform(random.PRNGKey(1), shape=(n_params,), minval=0, maxval=2) -costs = jnp.zeros(n_iter) -for i in range(n_iter): - cost, grad = param_to_cost_and_grad(param) - costs = costs.at[i].set(cost) - param = param - stepsize * grad - print(i, "Cost: ", cost, end="\r") - -plt.plot(costs) -plt.xlabel("Iteration") -plt.ylabel("Cost") - -# # Visualise trained classifier - -linsp = jnp.linspace(-1, 1, 100) -Z = vmap( - lambda a: vmap(lambda b: param_and_x_to_probability(param, jnp.array([a, b])))( - linsp - ) -)(linsp) - -plt.contourf(linsp, linsp, Z, cmap="Purples", alpha=0.8) -circle_linsp = jnp.linspace(0, 2 * jnp.pi, 100) -plt.plot(inner_rad * jnp.cos(circle_linsp), inner_rad * jnp.sin(circle_linsp), c="red") -plt.plot(outer_rad * jnp.cos(circle_linsp), outer_rad * jnp.sin(circle_linsp), c="red") - -# Looks good, it has clearly grasped the donut shape. Sincerest apologies if you are now hungry! 🍩 diff --git a/examples/python/pytket-qujax_heisenberg_vqe.py b/examples/python/pytket-qujax_heisenberg_vqe.py deleted file mode 100644 index 4be11fbb..00000000 --- a/examples/python/pytket-qujax_heisenberg_vqe.py +++ /dev/null @@ -1,192 +0,0 @@ -# # VQE example with `pytket-qujax` - -# See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html). - -from jax import numpy as jnp, random, value_and_grad, jit -from pytket import Circuit -from pytket.circuit.display import render_circuit_jupyter -import matplotlib.pyplot as plt - - -# ## Let's start with a TKET circuit - -import qujax -from pytket.extensions.qujax.qujax_convert import tk_to_qujax - -# We place barriers to stop tket automatically rearranging gates and we also store the number of circuit parameters as we'll need this later. - - -def get_circuit(n_qubits, depth): - n_params = 2 * n_qubits * (depth + 1) - - param = jnp.zeros((n_params,)) - - circuit = Circuit(n_qubits) - - k = 0 - for i in range(n_qubits): - circuit.H(i) - for i in range(n_qubits): - circuit.Rx(param[k], i) - k += 1 - for i in range(n_qubits): - circuit.Ry(param[k], i) - k += 1 - - for _ in range(depth): - for i in range(0, n_qubits - 1): - circuit.CZ(i, i + 1) - circuit.add_barrier(range(0, n_qubits)) - for i in range(n_qubits): - circuit.Rx(param[k], i) - k += 1 - for i in range(n_qubits): - circuit.Ry(param[k], i) - k += 1 - return circuit, n_params - - -n_qubits = 4 -depth = 2 -circuit, n_params = get_circuit(n_qubits, depth) -render_circuit_jupyter(circuit) - -# ## Now let's invoke qujax -# The `pytket.extensions.qujax.tk_to_qujax` function will generate a parameters -> statetensor function for us. - -param_to_st = tk_to_qujax(circuit) - -# Let's try it out on some random parameters values. Be aware that's JAX's random number generator requires a `jax.random.PRNGkey` every time it's called - more info on that [here](https://jax.readthedocs.io/en/latest/jax.random.html). -# Be aware that we still have convention where parameters are specified as multiples of $\pi$ - that is in [0,2]. - -params = random.uniform(random.PRNGKey(0), shape=(n_params,), minval=0.0, maxval=2.0) -statetensor = param_to_st(params) -print(statetensor) -print(statetensor.shape) - -# Note that this function also has an optional second argument where an initiating `statetensor_in` can be provided. If it is not provided it will default to the all 0s state (as we use here). - -# We can obtain statevector by simply calling `.flatten()` - -statevector = statetensor.flatten() -statevector.shape - -# And sampling probabilities by squaring the absolute value of the statevector - -sample_probs = jnp.square(jnp.abs(statevector)) -plt.bar(jnp.arange(statevector.size), sample_probs) - -# ## Cost function - -# Now we have our `param_to_st` function we are free to define a cost function that acts on bitstrings (e.g. maxcut) or integers by directly wrapping a function around `param_to_st`. However, cost functions defined via quantum Hamiltonians are a bit more involved. -# Fortunately, we can encode an Hamiltonian in JAX via the `qujax.get_statetensor_to_expectation_func` function which generates a statetensor -> expected value function for us. -# It takes three arguments as input -# - `gate_seq_seq`: A list of string (or array) lists encoding the gates in each term of the Hamiltonian. I.e. `[['X','X'], ['Y','Y'], ['Z','Z']]` corresponds to $H = aX_iX_j + bY_kY_l + cZ_mZ_n$ with qubit indices $i,j,k,l,m,n$ specified in the second argument and coefficients $a,b,c$ specified in the third argument -# - `qubit_inds_seq`: A list of integer lists encoding which qubit indices to apply the aforementioned gates. I.e. `[[0, 1],[0,1],[0,1]]`. Must have the same structure as `gate_seq_seq` above. -# - `coefficients`: A list of floats encoding any coefficients in the Hamiltonian. I.e. `[2.3, 0.8, 1.2]` corresponds to $a=2.3,b=0.8,c=1.2$ above. Must have the same length as the two above arguments. - -# More specifically let's consider the problem of finding the ground state of the quantum Heisenberg Hamiltonian - -# $$ -# \begin{equation} -# H = \sum_{i=1}^{n_\text{qubits}-1} X_i X_{i+1} + Y_i Y_{i+1} + Z_i Z_{i+1}. -# \end{equation} -# $$ -# -# As described, we define the Hamiltonian via its gate strings, qubit indices and coefficients. - -hamiltonian_gates = [["X", "X"], ["Y", "Y"], ["Z", "Z"]] * (n_qubits - 1) -hamiltonian_qubit_inds = [ - [int(i), int(i) + 1] for i in jnp.repeat(jnp.arange(n_qubits), 3) -] -coefficients = [1.0] * len(hamiltonian_qubit_inds) - -print("Gates:\t", hamiltonian_gates) -print("Qubits:\t", hamiltonian_qubit_inds) -print("Coefficients:\t", coefficients) - -# Now let's get the Hamiltonian as a pure JAX function - -st_to_expectation = qujax.get_statetensor_to_expectation_func( - hamiltonian_gates, hamiltonian_qubit_inds, coefficients -) - -# Let's check it works on the statetensor we've already generated. - -expected_val = st_to_expectation(statetensor) -expected_val - -# Now let's wrap the `param_to_st` and `st_to_expectation` together to give us an all in one `param_to_expectation` cost function. - -param_to_expectation = lambda param: st_to_expectation(param_to_st(param)) - -param_to_expectation(params) - -# Sanity check that a different, randomly generated set of parameters gives us a new expected value. - -new_params = random.uniform( - random.PRNGKey(1), shape=(n_params,), minval=0.0, maxval=2.0 -) -param_to_expectation(new_params) - -# ## Exact gradients within a VQE algorithm -# The `param_to_expectation` function we created is a pure JAX function and outputs a scalar. This means we can pass it to `jax.grad` (or even better `jax.value_and_grad`). - -cost_and_grad = value_and_grad(param_to_expectation) - -# The `cost_and_grad` function returns a tuple with the exact cost value and exact gradient evaluated at the parameters. - -cost_and_grad(params) - -# ## Now we have all the tools we need to design our VQE! -# We'll just use vanilla gradient descent with a constant stepsize - - -def vqe(init_param, n_steps, stepsize): - params = jnp.zeros((n_steps, n_params)) - params = params.at[0].set(init_param) - - cost_vals = jnp.zeros(n_steps) - cost_vals = cost_vals.at[0].set(param_to_expectation(init_param)) - - for step in range(1, n_steps): - cost_val, cost_grad = cost_and_grad(params[step - 1]) - cost_vals = cost_vals.at[step].set(cost_val) - - new_param = params[step - 1] - stepsize * cost_grad - params = params.at[step].set(new_param) - - print("Iteration:", step, "\tCost:", cost_val, end="\r") - print("\n") - return params, cost_vals - - -# Ok enough talking, let's run (and whilst we're at it we'll time it too) - -# %time -vqe_params, vqe_cost_vals = vqe(params, n_steps=250, stepsize=0.01) - -# Let's plot the results... - -plt.plot(vqe_cost_vals) -plt.xlabel("Iteration") -plt.ylabel("Cost") - -# Pretty good! - -# ## `jax.jit` speedup -# One last thing... We can significantly speed up the VQE above via the `jax.jit`. In our current implementation, the expensive `cost_and_grad` function is compiled to [XLA](https://www.tensorflow.org/xla) and then executed at each call. By invoking `jax.jit` we ensure that the function is compiled only once (on the first call) and then simply executed at each future call - this is much faster! - -cost_and_grad = jit(cost_and_grad) - -# We'll demonstrate this using the second set of initial parameters we randomly generated (to be sure of no caching). - -# %time -new_vqe_params, new_vqe_cost_vals = vqe(new_params, n_steps=250, stepsize=0.01) - -# That's some speedup! -# But let's also plot the training to be sure it converged correctly - -plt.plot(new_vqe_cost_vals) -plt.xlabel("Iteration") -plt.ylabel("Cost") diff --git a/examples/python/pytket-qujax_qaoa.py b/examples/python/pytket-qujax_qaoa.py deleted file mode 100644 index a817d8ae..00000000 --- a/examples/python/pytket-qujax_qaoa.py +++ /dev/null @@ -1,149 +0,0 @@ -# # Symbolic circuits with `pytket-qujax` -# In this notebook we will show how to manipulate symbolic circuits with the `pytket-qujax` extension. In particular, we will consider a QAOA and an Ising Hamiltonian. - -# See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html). - -from pytket import Circuit -from pytket.circuit.display import render_circuit_jupyter -from jax import numpy as jnp, random, value_and_grad, jit -from sympy import Symbol -import matplotlib.pyplot as plt - -import qujax -from pytket.extensions.qujax import tk_to_qujax - -# # QAOA -# The Quantum Approximate Optimization Algorithm (QAOA), first introduced by [Farhi et al.](https://arxiv.org/pdf/1411.4028.pdf), is a quantum variational algorithm used to solve optimization problems. It consists of a unitary $U(\beta, \gamma)$ formed by alternate repetitions of $U(\beta)=e^{-i\beta H_B}$ and $U(\gamma)=e^{-i\gamma H_P}$, where $H_B$ is the mixing Hamiltonian and $H_P$ the problem Hamiltonian. The goal is to find the optimal parameters that minimize $H_P$. -# Given a depth $d$, the expression of the final unitary is $U(\beta, \gamma) = U(\beta_d)U(\gamma_d)\cdots U(\beta_1)U(\gamma_1)$. Notice that for each repetition the parameters are different. -# -# ## Problem Hamiltonian -# QAOA uses a problem dependent ansatz. Therefore, we first need to know the problem that we want to solve. In this case we will consider an Ising Hamiltonian with only $Z$ interactions. Given a set of pairs (or qubit indices) $E$, the problem Hamiltonian will be: -# -# $$ -# \begin{equation} -# H_P = \sum_{(i, j) \in E}\alpha_{ij}Z_iZ_j, -# \end{equation} -# $$ -# -# where $\alpha_{ij}$ are the coefficients. -# Let's build our problem Hamiltonian with random coefficients and a set of pairs for a given number of qubits: - -n_qubits = 4 -hamiltonian_qubit_inds = [(0, 1), (1, 2), (0, 2), (1, 3)] -hamiltonian_gates = [["Z", "Z"]] * (len(hamiltonian_qubit_inds)) - -# Notice that in order to use the random package from jax we first need to define a seeded key - -seed = 13 -key = random.PRNGKey(seed) -coefficients = random.uniform(key, shape=(len(hamiltonian_qubit_inds),)) - -print("Gates:\t", hamiltonian_gates) -print("Qubits:\t", hamiltonian_qubit_inds) -print("Coefficients:\t", coefficients) - -# ## Variational Circuit -# Before constructing the circuit, we still need to select the mixing Hamiltonian. In our case, we will be using $X$ gates in each qubit, so $H_B = \sum_{i=1}^{n}X_i$, where $n$ is the number of qubits. Notice that the unitary $U(\beta)$, given this mixing Hamiltonian, is an $X$ rotation in each qubit with angle $\beta$. -# As for the unitary corresponding to the problem Hamiltonian, $U(\gamma)$, it has the following form: -# -# $$ -# \begin{equation} -# U(\gamma)=\prod_{(i, j) \in E}e^{-i\gamma\alpha_{ij}Z_i Z_j} -# \end{equation} -# $$ -# -# The operation $e^{-i\gamma\alpha_{ij}Z_iZ_j}$ can be performed using two CNOT gates with qubit $i$ as control and qubit $j$ as target and a $Z$ rotation in qubit $j$ in between them, with angle $\gamma\alpha_{ij}$. -# Finally, the initial state used, in general, with the QAOA is an equal superposition of all the basis states. This can be achieved adding a first layer of Hadamard gates in each qubit at the beginning of the circuit. - -# With all the building blocks, let's construct the symbolic circuit using tket. Notice that in order to define the parameters, we use the ```Symbol``` object from the `sympy` package. More info can be found in this [documentation](https://tket.quantinuum.com/user-manual/manual_circuit.html#symbolic-circuits). In order to later convert the circuit to qujax, we need to return the list of symbolic parameters as well. - - -def qaoa_circuit(n_qubits, depth): - circuit = Circuit(n_qubits) - p_keys = [] - - # Initial State - for i in range(n_qubits): - circuit.H(i) - - for d in range(depth): - # Hamiltonian unitary - gamma_d = Symbol(f"γ_{d}") - for index in range(len(hamiltonian_qubit_inds)): - pair = hamiltonian_qubit_inds[index] - coef = coefficients[index] - - circuit.CX(pair[0], pair[1]) - circuit.Rz(gamma_d * coef, pair[1]) - circuit.CX(pair[0], pair[1]) - circuit.add_barrier(range(0, n_qubits)) - p_keys.append(gamma_d) - - # Mixing unitary - beta_d = Symbol(f"β_{d}") - for i in range(n_qubits): - circuit.Rx(beta_d, i) - p_keys.append(beta_d) - - return circuit, p_keys - - -depth = 3 -circuit, keys = qaoa_circuit(n_qubits, depth) - -keys - -# Let's check the circuit: - -render_circuit_jupyter(circuit) - -# # Now for `qujax` -# The `pytket.extensions.qujax.tk_to_qujax` function will generate a parameters -> statetensor function for us. However, in order to convert a symbolic circuit we first need to define the `symbol_map`. This object maps each symbol key to their corresponding index. In our case, since the object `keys` contains the symbols in the correct order, we can simply construct the dictionary as follows: - -symbol_map = {keys[i]: i for i in range(len(keys))} - -symbol_map - -# Then, we invoke the `tk_to_qujax` with both the circuit and the symbolic map. - -param_to_st = tk_to_qujax(circuit, symbol_map=symbol_map) - -# And we also construct the expectation map using the problem Hamiltonian via qujax: - -st_to_expectation = qujax.get_statetensor_to_expectation_func( - hamiltonian_gates, hamiltonian_qubit_inds, coefficients -) - -param_to_expectation = lambda param: st_to_expectation(param_to_st(param)) - -# # Training process -# We construct a function that, given a parameter vector, returns the value of the cost function and the gradient. -# We also `jit` to avoid recompilation, this means that the expensive `cost_and_grad` function is compiled once into a very fast XLA (C++) function which is then executed at each iteration. Alternatively, we could get the same speedup by replacing our `for` loop with `jax.lax.scan`. You can read more about JIT compilation in the [JAX documentation](https://jax.readthedocs.io/en/latest/jax-101/02-jitting.html). - -cost_and_grad = jit(value_and_grad(param_to_expectation)) - -# For the training process we'll use vanilla gradient descent with a constant stepsize: - -seed = 123 -key = random.PRNGKey(seed) -init_param = random.uniform(key, shape=(len(symbol_map),)) - -n_steps = 150 -stepsize = 0.01 - -param = init_param - -cost_vals = jnp.zeros(n_steps) -cost_vals = cost_vals.at[0].set(param_to_expectation(init_param)) - -for step in range(1, n_steps): - cost_val, cost_grad = cost_and_grad(param) - cost_vals = cost_vals.at[step].set(cost_val) - param = param - stepsize * cost_grad - print("Iteration:", step, "\tCost:", cost_val, end="\r") - -# Let's visualise the gradient descent - -plt.plot(cost_vals) -plt.xlabel("Iteration") -plt.ylabel("Cost") diff --git a/examples/python/qasm b/examples/python/qasm deleted file mode 120000 index a8dcdc14..00000000 --- a/examples/python/qasm +++ /dev/null @@ -1 +0,0 @@ -../qasm/ \ No newline at end of file diff --git a/examples/python/qiskit_integration.py b/examples/python/qiskit_integration.py deleted file mode 100644 index 0963bad1..00000000 --- a/examples/python/qiskit_integration.py +++ /dev/null @@ -1,81 +0,0 @@ -# # Integrating `pytket` into Qiskit software - -# In this tutorial, we will focus on: -# - Using `pytket` for compilation or providing devices/simulators within Qiskit workflows; -# - Adapting Qiskit code to use `pytket` directly. - -# See the [pytket-qiskit docs](https://tket.quantinuum.com/extensions/pytket-qiskit/) for more information. - -# This example assumes some familiarity with the Qiskit algorithms library. We have chosen a small variational quantum eigensolver (VQE) for our example, but the same principles apply to a wide range of quantum algorithms. -# -# To run this example, you will need `pytket-qiskit`, as well as the separate `qiskit-optimization` package. You will also need IBMQ credentials stored on your local machine. -# -# Qiskit has risen to prominence as the most popular platform for the development of quantum software, providing an open source, full-stack solution with a large feature list and extensive examples from the developers and community. For many researchers who have already invested in building a large codebase built on top of Qiskit, the idea of switching entirely to a new platform can look like a time-sink and may require reversion to take advantage of the new tools that get regularly added to Qiskit. -# -# The interoperability provided by `pytket-qiskit` allows Qiskit users to start taking advantage of some of the unique features of `pytket` without having to completely rewrite their software. - -# Let's take as an example an ansatz for computing the ground-state energy of a hydrogen molecule. - -from qiskit.quantum_info import SparsePauliOp - -H2_op = SparsePauliOp.from_list( - [ - ("II", -1.052373245772859), - ("IZ", 0.39793742484318045), - ("ZI", -0.39793742484318045), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423156), - ] -) - -# First let's use qiskit's NumPyEigensolver to compute the exact answer: -from qiskit.algorithms.eigensolvers import NumPyEigensolver - -es = NumPyEigensolver(k=1) -exact_result = es.compute_eigenvalues(H2_op).eigenvalues[0].real -print("Exact result:", exact_result) - -# The following function will attempt to find an approximation to this using VQE, given a qiskit BackendEstimator on which to run circuits: - -from qiskit.algorithms.minimum_eigensolvers.vqe import VQE -from qiskit.algorithms.optimizers import SPSA -from qiskit.circuit.library import EfficientSU2 - - -def vqe_solve(op, maxiter, qestimator): - optimizer = SPSA(maxiter=maxiter) - ansatz = EfficientSU2(op.num_qubits, entanglement="linear") - vqe = VQE(estimator=qestimator, ansatz=ansatz, optimizer=optimizer) - return vqe.compute_minimum_eigenvalue(op).eigenvalue - - -# We will run this on a pytket `IBMQEmulatorBackend`. This is a noisy simulator whose characteristics match those of the real device, in this case "ibmq_belem" (a 5-qubit machine). The characteristics are retrieved from the device when the backend is constructed, so we must first load our IBMQ account. Circuits will be compiled to match the connectivity of the device and simulated using a basic noise model [constructed from the device parameters](https://qiskit.org/documentation/apidoc/aer_noise.html). - -from pytket.extensions.qiskit import IBMQEmulatorBackend - -b_emu = IBMQEmulatorBackend("ibmq_belem", instance="ibm-q/open/main") - -# Most qiskit algorithms require a qiskit `primitive` as input; this in turn is constructed from a `qiskit.providers.Backend`. The `TketBackend` class wraps a pytket backend as a `qiskit.providers.Backend`. - -from pytket.extensions.qiskit.tket_backend import TketBackend -from qiskit.primitives import BackendEstimator - -qis_backend = TketBackend(b_emu) -qestimator = BackendEstimator(qis_backend, options={"shots": 8192}) - -# Note that we could have used any other pytket shots backend instead of `b_emu` here. The `pytket` extension modules provide an interface to a wide variety of devices and simulators from different quantum software platforms. -# -# We can now run the VQE algorithm. In this example we use only 50 iterations, but greater accuracy may be achieved by increasing this number: - -# #print("VQE result:", vqe_solve(H2_op, 50, qestimator)) - -# Another way to improve the accuracy of results is to apply optimisations to the circuit in an attempt to reduce the overall noise. When we construct our qiskit backend, we can pass in a pytket compilation pass as an additional parameter. There is a wide range of options here; we recommend the device-specific default compilation pass, provided by each tket backend. This pass will ensure that all the hardware constraints of the device are met. We can enable tket's most aggressive optimisation level by setting the parameter `optimisation_level=2`. - -qis_backend2 = TketBackend(b_emu, b_emu.default_compilation_pass(optimisation_level=2)) -qestimator2 = BackendEstimator(qis_backend2, options={"shots": 8192}) - -# Let's run the optimisation again: - -# #print("VQE result (with optimisation):", vqe_solve(H2_op, 50, qestimator2)) - -# These are small two-qubit circuits, so the improvement may be small, but with larger, more complex circuits, the reduction in noise from compilation will make a greater difference and allow VQE experiments to converge with fewer iterations. diff --git a/examples/python/spam_example.py b/examples/python/spam_example.py deleted file mode 100644 index 899420fe..00000000 --- a/examples/python/spam_example.py +++ /dev/null @@ -1,164 +0,0 @@ -# # Calibration and correction of state preparation and measurement (SPAM) - -# Quantum Computers available in the NISQ-era are limited by significant sources of device noise which cause errors in computation. One such noise source is errors in the preparation and measurement of quantum states, more commonly know as SPAM. -# -# If device SPAM error can be characterised, then device results can be modified to mitigate the error. Characterisation proceeds by determining overlap between different prepared basis states when measured, and mitigation modifies the distribution over output states of the corrected circuit. No modification of the quantum circuit being corrected is required. The ``` pytket``` ```SpamCorrecter``` class supports characterisation and mitigation of device SPAM error. -# -# In this tutorial we will show how the ```SpamCorrecter``` class can be used to modify real results and improve device performance when running experiments. -# -# This tutorial will require installation of ```pytket```, ```pytket_qiskit``` and ```qiskit```, all available on pip. -# -# First, import the ```SpamCorrecter``` class. - -from pytket.utils.spam import SpamCorrecter - -# The SpamCorrecter class has methods for generating State Preparation and Measurement (SPAM) calibration experiments for pytket backends and correcting counts generated from those same backends. -# -# Let's first mitigate error from a noisy simulation, using a noise model straight from the 5-qubit IBMQ manila device. This will require a preloaded IBMQ account. - -from qiskit import IBMQ - -IBMQ.load_account() - -from pytket.extensions.qiskit import process_characterisation - -ibmq_manila_backend = IBMQ.providers()[0].get_backend("ibmq_manila") -pytket_manila_characterisation = process_characterisation(ibmq_manila_backend) -pytket_manila_architecture = pytket_manila_characterisation["Architecture"] - -import networkx as nx -import matplotlib.pyplot as plt - -manila_graph = nx.Graph(pytket_manila_architecture.coupling) -nx.draw(manila_graph, labels={node: node for node in manila_graph.nodes()}) - -# SPAM correction requires subsets of qubits which are assumed to only have SPAM errors correlated with each other, and no other qubits. -# -# Correlated errors are usually dependent on the connectivity layout of devices, as shown above. -# -# As manila is a small 5-qubit device with few connections, let's assume that all qubits have correlated SPAM errors. The number of calibration circuits produced is exponential in the maximum number of correlated circuits, so finding good subsets of correlated qubits is important for characterising larger devices with smaller experimental overhead. -# -# We can produce an ```IBMQEmulatorBackend``` to run this. This uses a noise model from ```ibmq_manila``` produced using qiskit-aer. We can then execute all calibration circuits through the backend. - -from pytket.extensions.qiskit import IBMQEmulatorBackend, AerBackend - -n_shots = 8192 -pytket_noisy_sim_backend = IBMQEmulatorBackend("ibmq_manila") -manila_node_subsets = pytket_noisy_sim_backend.backend_info.architecture.nodes -manila_spam = SpamCorrecter([manila_node_subsets], pytket_noisy_sim_backend) - -# The SpamCorrecter uses these subsets of qubits to produce calibration circuits. - -calibration_circuits = manila_spam.calibration_circuits() -print("Number of calibration circuits: ", len(calibration_circuits)) - -sim_handles = pytket_noisy_sim_backend.process_circuits(calibration_circuits, n_shots) - -# Count results from the simulator are then used to calculate the matrices used for SPAM correction for ```ibmq_manila```. - -sim_count_results = pytket_noisy_sim_backend.get_results(sim_handles) -manila_spam.calculate_matrices(sim_count_results) - -from pytket import Circuit - -ghz_circuit = ( - Circuit(len(pytket_noisy_sim_backend.backend_info.architecture.nodes)) - .H(0) - .CX(0, 1) - .CX(1, 2) - .measure_all() -) -ghz_circuit = pytket_noisy_sim_backend.get_compiled_circuit(ghz_circuit) -ghz_noisy_handle = pytket_noisy_sim_backend.process_circuit(ghz_circuit, n_shots) -ghz_noisy_result = pytket_noisy_sim_backend.get_result(ghz_noisy_handle) - -# We also run a noiseless simulation so we can compare performance. - -pytket_noiseless_sim_backend = AerBackend() -ghz_noiseless_handle = pytket_noiseless_sim_backend.process_circuit( - ghz_circuit, n_shots -) -ghz_noiseless_result = pytket_noiseless_sim_backend.get_result(ghz_noiseless_handle) - -# Noisy simulator counts are corrected using the ```SpamCorrecter``` objects ```correct_counts``` method. -# -# To correctly amend counts, the ```correct_counts``` method requires a ``ParallelMeasures`` type object, a list of ``Dict[Qubit, Bit]`` where each dictionary denotes a set of Qubit measured in parallel and the Bit their measured values are assigned to. -# -# The ``SpamCorrecter`` class has a helper method ``get_parallel_measure`` for retrieving this object for a Circuit. - -ghz_parallel_measure = manila_spam.get_parallel_measure(ghz_circuit) - -ghz_spam_corrected_result = manila_spam.correct_counts( - ghz_noisy_result, ghz_parallel_measure -) - -# Import and define the Jensen-Shannon divergence, which we will use for comparing performance. The Jensen-Shannon divergence is a symmetric and finite measure of similarity between two probability distributions. A smaller divergence implies more similarity between two probability distributions. - -from scipy.stats import entropy -import numpy as np -import itertools - - -def binseq(k): - return ["".join(x) for x in itertools.product("01", repeat=k)] - - -def probs_from_counts(result): - counts = result.get_counts() - counts_dict = dict() - for x in counts: - counts_dict["".join(str(e) for e in x)] = counts[x] - converted = [] - binary_strings = binseq(len(list(counts.keys())[0])) - for b in binary_strings: - converted.append(counts_dict.get(b, 0)) - return converted / np.sum(converted) - - -def JSD(P, Q): - _P = P / np.linalg.norm(P, ord=1) - _Q = Q / np.linalg.norm(Q, ord=1) - _M = 0.5 * (_P + _Q) - return 0.5 * (entropy(_P, _M) + entropy(_Q, _M)) - - -# Convert our counts results to a probability distribution over the basis states for comparison. - -ghz_noiseless_probabilities = probs_from_counts(ghz_noiseless_result) -ghz_noisy_probabilities = probs_from_counts(ghz_noisy_result) -ghz_spam_corrected_probabilities = probs_from_counts(ghz_spam_corrected_result) - -print( - "Jensen-Shannon Divergence between noiseless simulation probability distribution and noisy simulation probability distribution: ", - JSD(ghz_noiseless_probabilities, ghz_noisy_probabilities), -) -print( - "Jensen-Shannon Divergence between noiseless simulation probability distribution and spam corrected noisy simulation probability distribution: ", - JSD(ghz_noiseless_probabilities, ghz_spam_corrected_probabilities), -) - -# In our noisy simulated case, spam corrected results produced a distribution closer to the expected distribution. -# -# There are two methods available for correcting counts: the default ```bayesian```, and ```invert```. Further information on each method is available at our [documentation](https://cqcl.github.io/tket/pytket/api/utils.html#module-pytket.utils.spam). -# -# Let's look at how the ```invert``` method performs. - -ghz_invert_corrected_result = manila_spam.correct_counts( - ghz_noisy_result, ghz_parallel_measure, method="invert" -) -ghz_invert_probabilities = probs_from_counts(ghz_invert_corrected_result) - -print( - "Jensen-Shannon Divergence between noiseless simulation probability distribution and Bayesian-corrected noisy simulation probability distribution: ", - JSD(ghz_noiseless_probabilities, ghz_spam_corrected_probabilities), -) -print( - "Jensen-Shannon Divergence between noiseless simulation probability distribution and invert-corrected noisy simulation probability distribution: ", - JSD(ghz_noiseless_probabilities, ghz_invert_probabilities), -) - -# To see how SPAM correction performs on results from a real IBMQ quantum device, try replacing `IBMQEmulatorBackend` with `IBMQBackend`. - -from pytket.extensions.qiskit import IBMQBackend - -ibm_backend = IBMQBackend("ibmq_manila") diff --git a/examples/python/symbolics_example.py b/examples/python/symbolics_example.py deleted file mode 100644 index 81756417..00000000 --- a/examples/python/symbolics_example.py +++ /dev/null @@ -1,77 +0,0 @@ -# # Symbolic compilation - -# Motivation: in compilation, particularly of hybrid classical-quantum variational algorithms in which the structure of a circuit remains constant but the parameters of some gates change, it can be useful to compile using symbolic parameters and optimise the circuit without knowledge of what these parameters will be instantiated to afterwards. -# -# In this tutorial, we will show how to compile a circuit containing mathematical symbols, and then instantiate the symbols afterwards. To do this, you need to have `pytket` installed. Run: -# -# `pip install pytket` -# -# To begin, we will import the `Circuit` and `Transform` classes from `pytket`, and the `fresh_symbol` method from `pytket.circuit`. - -from pytket.circuit import Circuit, fresh_symbol -from pytket.transform import Transform - -# Now, we can construct a circuit containing symbols. You can ask for symbols by calling the `fresh_symbol` method with a string as an argument. This string represents the preferred symbol name; if this has already been used elsewhere, an appropriate suffix of the form `_x`, with `x` a natural number, will be added to generate a new symbol, as shown below: - -a = fresh_symbol("a") -a1 = fresh_symbol("a") -print(a) -print(a1) - -# We are going to make a circuit using just three 'phase gadgets': `Rz` gates surrounded by ladders of `CX` gates. - -b = fresh_symbol("b") -circ = Circuit(4) -circ.CX(0, 1) -circ.CX(1, 2) -circ.CX(2, 3) -circ.Rz(a, 3) -circ.CX(2, 3) -circ.CX(1, 2) -circ.CX(0, 1) -circ.CX(3, 2) -circ.CX(2, 1) -circ.CX(1, 0) -circ.Rz(b, 0) -circ.CX(1, 0) -circ.CX(2, 1) -circ.CX(3, 2) -circ.CX(0, 1) -circ.CX(1, 2) -circ.CX(2, 3) -circ.Rz(0.5, 3) -circ.CX(2, 3) -circ.CX(1, 2) -circ.CX(0, 1) - -# Now we can use the `render_circuit_jupyter` method to display the circuit. - -from pytket.circuit.display import render_circuit_jupyter - -render_circuit_jupyter(circ) - -# Now let's use a transform to shrink the circuit. For more detail on transforms, see the `transform_example` notebook. - -Transform.OptimisePhaseGadgets().apply(circ) -render_circuit_jupyter(circ) - -# Note that the type of gate has changed to `U1`, but the phase gadgets have been successfully combined. The `U1` gate is an IBM-specific gate that is equivalent to an `Rz`. -# -# We can now instantiate the symbols with some desired values. We make a dictionary, with each key a symbol name, and each value a double. Note that this value is in units of 'half-turns', in which a value of $1$ corresponds to a rotation of $\pi$. -# -# Before instantiating our parameters we make a copy of the circuit, so that we can repeat the exercise without the need for recompilation. - -symbol_circ = circ.copy() - -symbol_dict = {a: 0.5, b: 0.75} -circ.symbol_substitution(symbol_dict) - -render_circuit_jupyter(circ) - -# Because this symbol substitution was called on the copy, we still have our original symbolic circuit. - -render_circuit_jupyter(symbol_circ) - -# Note: the expression tree for this symbolic expression is very small, consisting of only a couple of different operations, but tket is capable of handling large and complex expressions containing many different types of operation, such as trigonometric functions. -# -# It is usually possible to instantiate a symbolic circuit with specific values that allow further optimisation: for example, if we had chosen $a=1.5$ and $b=0$, this circuit would be reduce to the identity. If there are likely to be many parameters set to trivial values (such as $0$ or $1$), it can be beneficial to perform further optimisation after instantiation. diff --git a/examples/python/ucc_vqe.py b/examples/python/ucc_vqe.py deleted file mode 100644 index ba58caeb..00000000 --- a/examples/python/ucc_vqe.py +++ /dev/null @@ -1,515 +0,0 @@ -# # VQE with UCC ansatz - -# In this tutorial, we will focus on: -# - building parameterised ansätze for variational algorithms; -# - compilation tools for UCC-style ansätze. - -# This example assumes the reader is familiar with the Variational Quantum Eigensolver and its application to electronic structure problems through the Unitary Coupled Cluster approach. -# -# To run this example, you will need `pytket` and `pytket-qiskit`, as well as `openfermion`, `scipy`, and `sympy`. -# -# We will start with a basic implementation and then gradually modify it to make it faster, more general, and less noisy. The final solution is given in full at the bottom of the notebook. -# -# Suppose we have some electronic configuration problem, expressed via a physical Hamiltonian. (The Hamiltonian and excitations in this example were obtained using `qiskit-aqua` version 0.5.2 and `pyscf` for H2, bond length 0.75A, sto3g basis, Jordan-Wigner encoding, with no qubit reduction or orbital freezing.). We express it succinctly using the openfermion library: - -import openfermion as of - -hamiltonian = ( - -0.8153001706270075 * of.QubitOperator("") - + 0.16988452027940318 * of.QubitOperator("Z0") - + -0.21886306781219608 * of.QubitOperator("Z1") - + 0.16988452027940323 * of.QubitOperator("Z2") - + -0.2188630678121961 * of.QubitOperator("Z3") - + 0.12005143072546047 * of.QubitOperator("Z0 Z1") - + 0.16821198673715723 * of.QubitOperator("Z0 Z2") - + 0.16549431486978672 * of.QubitOperator("Z0 Z3") - + 0.16549431486978672 * of.QubitOperator("Z1 Z2") - + 0.1739537877649417 * of.QubitOperator("Z1 Z3") - + 0.12005143072546047 * of.QubitOperator("Z2 Z3") - + 0.04544288414432624 * of.QubitOperator("X0 X1 X2 X3") - + 0.04544288414432624 * of.QubitOperator("X0 X1 Y2 Y3") - + 0.04544288414432624 * of.QubitOperator("Y0 Y1 X2 X3") - + 0.04544288414432624 * of.QubitOperator("Y0 Y1 Y2 Y3") -) -nuclear_repulsion_energy = 0.70556961456 - -# We would like to define our ansatz for arbitrary parameter values. For simplicity, let's start with a Hardware Efficient Ansatz. - -from pytket import Circuit - -# Hardware efficient ansatz: - - -def hea(params): - ansatz = Circuit(4) - for i in range(4): - ansatz.Ry(params[i], i) - for i in range(3): - ansatz.CX(i, i + 1) - for i in range(4): - ansatz.Ry(params[4 + i], i) - return ansatz - - -# We can use this to build the objective function for our optimisation. - -from pytket.extensions.qiskit import AerBackend -from pytket.utils.expectations import expectation_from_counts - -backend = AerBackend() - -# Naive objective function: - - -def objective(params): - energy = 0 - for term, coeff in hamiltonian.terms.items(): - if not term: - energy += coeff - continue - circ = hea(params) - circ.add_c_register("c", len(term)) - for i, (q, pauli) in enumerate(term): - if pauli == "X": - circ.H(q) - elif pauli == "Y": - circ.V(q) - circ.Measure(q, i) - compiled_circ = backend.get_compiled_circuit(circ) - counts = backend.run_circuit(compiled_circ, n_shots=4000).get_counts() - energy += coeff * expectation_from_counts(counts) - return energy + nuclear_repulsion_energy - - -# This objective function is then run through a classical optimiser to find the set of parameter values that minimise the energy of the system. For the sake of example, we will just run this with a single parameter value. - -arg_values = [ - -7.31158201e-02, - -1.64514836e-04, - 1.12585591e-03, - -2.58367544e-03, - 1.00006068e00, - -1.19551357e-03, - 9.99963988e-01, - 2.53283285e-03, -] - -energy = objective(arg_values) -print(energy) - -# The HEA is designed to cram as many orthogonal degrees of freedom into a small circuit as possible to be able to explore a large region of the Hilbert space whilst the circuits themselves can be run with minimal noise. These ansätze give virtually-optimal circuits by design, but suffer from an excessive number of variational parameters making convergence slow, barren plateaus where the classical optimiser fails to make progress, and spanning a space where most states lack a physical interpretation. These drawbacks can necessitate adding penalties and may mean that the ansatz cannot actually express the true ground state. -# -# The UCC ansatz, on the other hand, is derived from the electronic configuration. It sacrifices efficiency of the circuit for the guarantee of physical states and the variational parameters all having some meaningful effect, which helps the classical optimisation to converge. -# -# This starts by defining the terms of our single and double excitations. These would usually be generated using the orbital configurations, so we will just use a hard-coded example here for the purposes of demonstration. - -from pytket.circuit import Qubit -from pytket.pauli import Pauli, QubitPauliString - -q = [Qubit(i) for i in range(4)] -xyii = QubitPauliString([q[0], q[1]], [Pauli.X, Pauli.Y]) -yxii = QubitPauliString([q[0], q[1]], [Pauli.Y, Pauli.X]) -iixy = QubitPauliString([q[2], q[3]], [Pauli.X, Pauli.Y]) -iiyx = QubitPauliString([q[2], q[3]], [Pauli.Y, Pauli.X]) -xxxy = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.X, Pauli.Y]) -xxyx = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.Y, Pauli.X]) -xyxx = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.X, Pauli.X]) -yxxx = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.X, Pauli.X]) -yyyx = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.Y, Pauli.X]) -yyxy = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.X, Pauli.Y]) -yxyy = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.Y, Pauli.Y]) -xyyy = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.Y, Pauli.Y]) - -singles_a = {xyii: 1.0, yxii: -1.0} -singles_b = {iixy: 1.0, iiyx: -1.0} -doubles = { - xxxy: 0.25, - xxyx: -0.25, - xyxx: 0.25, - yxxx: -0.25, - yyyx: -0.25, - yyxy: 0.25, - yxyy: -0.25, - xyyy: 0.25, -} - -# Building the ansatz circuit itself is often done naively by defining the map from each term down to basic gates and then applying it to each term. - - -def add_operator_term(circuit: Circuit, term: QubitPauliString, angle: float): - qubits = [] - for q, p in term.map.items(): - if p != Pauli.I: - qubits.append(q) - if p == Pauli.X: - circuit.H(q) - elif p == Pauli.Y: - circuit.V(q) - for i in range(len(qubits) - 1): - circuit.CX(i, i + 1) - circuit.Rz(angle, len(qubits) - 1) - for i in reversed(range(len(qubits) - 1)): - circuit.CX(i, i + 1) - for q, p in term.map.items(): - if p == Pauli.X: - circuit.H(q) - elif p == Pauli.Y: - circuit.Vdg(q) - - -# Unitary Coupled Cluster Singles & Doubles ansatz: - - -def ucc(params): - ansatz = Circuit(4) - # Set initial reference state - ansatz.X(1).X(3) - # Evolve by excitations - for term, coeff in singles_a.items(): - add_operator_term(ansatz, term, coeff * params[0]) - for term, coeff in singles_b.items(): - add_operator_term(ansatz, term, coeff * params[1]) - for term, coeff in doubles.items(): - add_operator_term(ansatz, term, coeff * params[2]) - return ansatz - - -# This is already quite verbose, but `pytket` has a neat shorthand construction for these operator terms using the `PauliExpBox` construction. We can then decompose these into basic gates using the `DecomposeBoxes` compiler pass. - -from pytket.circuit import PauliExpBox -from pytket.passes import DecomposeBoxes - - -def add_excitation(circ, term_dict, param): - for term, coeff in term_dict.items(): - qubits, paulis = zip(*term.map.items()) - pbox = PauliExpBox(paulis, coeff * param) - circ.add_gate(pbox, qubits) - - -# UCC ansatz with syntactic shortcuts: - - -def ucc(params): - ansatz = Circuit(4) - ansatz.X(1).X(3) - add_excitation(ansatz, singles_a, params[0]) - add_excitation(ansatz, singles_b, params[1]) - add_excitation(ansatz, doubles, params[2]) - DecomposeBoxes().apply(ansatz) - return ansatz - - -# The objective function can also be simplified using a utility method for constructing the measurement circuits and processing for expectation value calculations. For that, we convert the Hamiltonian to a pytket QubitPauliOperator: - -from pytket.utils.operators import QubitPauliOperator - - -pauli_sym = {"I": Pauli.I, "X": Pauli.X, "Y": Pauli.Y, "Z": Pauli.Z} - - -def qps_from_openfermion(paulis): - """Convert OpenFermion tensor of Paulis to pytket QubitPauliString.""" - qlist = [] - plist = [] - for q, p in paulis: - qlist.append(Qubit(q)) - plist.append(pauli_sym[p]) - return QubitPauliString(qlist, plist) - - -def qpo_from_openfermion(openf_op): - """Convert OpenFermion QubitOperator to pytket QubitPauliOperator.""" - tk_op = dict() - for term, coeff in openf_op.terms.items(): - string = qps_from_openfermion(term) - tk_op[string] = coeff - return QubitPauliOperator(tk_op) - - -hamiltonian_op = qpo_from_openfermion(hamiltonian) - -# Simplified objective function using utilities: -from pytket.utils.expectations import get_operator_expectation_value - - -def objective(params): - circ = ucc(params) - return ( - get_operator_expectation_value(circ, hamiltonian_op, backend, n_shots=4000) - + nuclear_repulsion_energy - ) - - -arg_values = [-3.79002933e-05, 2.42964799e-05, 4.63447157e-01] - -energy = objective(arg_values) -print(energy) - -# This is now the simplest form that this operation can take, but it isn't necessarily the most effective. When we decompose the ansatz circuit into basic gates, it is still very expensive. We can employ some of the circuit simplification passes available in `pytket` to reduce its size and improve fidelity in practice. -# -# A good example is to decompose each `PauliExpBox` into basic gates and then apply `FullPeepholeOptimise`, which defines a compilation strategy utilising all of the simplifications in `pytket` that act locally on small regions of a circuit. We can examine the effectiveness by looking at the number of two-qubit gates before and after simplification, which tends to be a good indicator of fidelity for near-term systems where these gates are often slow and inaccurate. - -from pytket import OpType -from pytket.passes import FullPeepholeOptimise - -test_circuit = ucc(arg_values) - -print("CX count before", test_circuit.n_gates_of_type(OpType.CX)) -print("CX depth before", test_circuit.depth_by_type(OpType.CX)) - -FullPeepholeOptimise().apply(test_circuit) - -print("CX count after FPO", test_circuit.n_gates_of_type(OpType.CX)) -print("CX depth after FPO", test_circuit.depth_by_type(OpType.CX)) - -# These simplification techniques are very general and are almost always beneficial to apply to a circuit if you want to eliminate local redundancies. But UCC ansätze have extra structure that we can exploit further. They are defined entirely out of exponentiated tensors of Pauli matrices, giving the regular structure described by the `PauliExpBox`es. Under many circumstances, it is more efficient to not synthesise these constructions individually, but simultaneously in groups. The `PauliSimp` pass finds the description of a given circuit as a sequence of `PauliExpBox`es and resynthesises them (by default, in groups of commuting terms). This can cause great change in the overall structure and shape of the circuit, enabling the identification and elimination of non-local redundancy. - -from pytket.passes import PauliSimp - -test_circuit = ucc(arg_values) - -print("CX count before", test_circuit.n_gates_of_type(OpType.CX)) -print("CX depth before", test_circuit.depth_by_type(OpType.CX)) - -PauliSimp().apply(test_circuit) - -print("CX count after PS", test_circuit.n_gates_of_type(OpType.CX)) -print("CX depth after PS", test_circuit.depth_by_type(OpType.CX)) - -FullPeepholeOptimise().apply(test_circuit) - -print("CX count after PS+FPO", test_circuit.n_gates_of_type(OpType.CX)) -print("CX depth after PS+FPO", test_circuit.depth_by_type(OpType.CX)) - -# To include this into our routines, we can just add the simplification passes to the objective function. The `get_operator_expectation_value` utility handles compiling to meet the requirements of the backend, so we don't have to worry about that here. - -# Objective function with circuit simplification: - - -def objective(params): - circ = ucc(params) - PauliSimp().apply(circ) - FullPeepholeOptimise().apply(circ) - return ( - get_operator_expectation_value(circ, hamiltonian_op, backend, n_shots=4000) - + nuclear_repulsion_energy - ) - - -# These circuit simplification techniques have tried to preserve the exact unitary of the circuit, but there are ways to change the unitary whilst preserving the correctness of the algorithm as a whole. -# -# For example, the excitation terms are generated by trotterisation of the excitation operator, and the order of the terms does not change the unitary in the limit of many trotter steps, so in this sense we are free to sequence the terms how we like and it is sensible to do this in a way that enables efficient synthesis of the circuit. Prioritising collecting terms into commuting sets is a very beneficial heuristic for this and can be performed using the `gen_term_sequence_circuit` method to group the terms together into collections of `PauliExpBox`es and the `GuidedPauliSimp` pass to utilise these sets for synthesis. - -from pytket.passes import GuidedPauliSimp -from pytket.utils import gen_term_sequence_circuit - - -def ucc(params): - singles_params = {qps: params[0] * coeff for qps, coeff in singles.items()} - doubles_params = {qps: params[1] * coeff for qps, coeff in doubles.items()} - excitation_op = QubitPauliOperator({**singles_params, **doubles_params}) - reference_circ = Circuit(4).X(1).X(3) - ansatz = gen_term_sequence_circuit(excitation_op, reference_circ) - GuidedPauliSimp().apply(ansatz) - FullPeepholeOptimise().apply(ansatz) - return ansatz - - -# Adding these simplification routines doesn't come for free. Compiling and simplifying the circuit to achieve the best results possible can be a difficult task, which can take some time for the classical computer to perform. -# -# During a VQE run, we will call this objective function many times and run many measurement circuits within each, but the circuits that are run on the quantum computer are almost identical, having the same gate structure but with different gate parameters and measurements. We have already exploited this within the body of the objective function by simplifying the ansatz circuit before we call `get_operator_expectation_value`, so it is only done once per objective calculation rather than once per measurement circuit. -# -# We can go even further by simplifying it once outside of the objective function, and then instantiating the simplified ansatz with the parameter values needed. For this, we will construct the UCC ansatz circuit using symbolic (parametric) gates. - -from sympy import symbols - -# Symbolic UCC ansatz generation: - -syms = symbols("p0 p1 p2") -singles_a_syms = {qps: syms[0] * coeff for qps, coeff in singles_a.items()} -singles_b_syms = {qps: syms[1] * coeff for qps, coeff in singles_b.items()} -doubles_syms = {qps: syms[2] * coeff for qps, coeff in doubles.items()} -excitation_op = QubitPauliOperator({**singles_a_syms, **singles_b_syms, **doubles_syms}) -ucc_ref = Circuit(4).X(1).X(3) -ucc = gen_term_sequence_circuit(excitation_op, ucc_ref) -GuidedPauliSimp().apply(ucc) -FullPeepholeOptimise().apply(ucc) - -# Objective function using the symbolic ansatz: - - -def objective(params): - circ = ucc.copy() - sym_map = dict(zip(syms, params)) - circ.symbol_substitution(sym_map) - return ( - get_operator_expectation_value(circ, hamiltonian_op, backend, n_shots=4000) - + nuclear_repulsion_energy - ) - - -# We have now got some very good use of `pytket` for simplifying each individual circuit used in our experiment and for minimising the amount of time spent compiling, but there is still more we can do in terms of reducing the amount of work the quantum computer has to do. Currently, each (non-trivial) term in our measurement hamiltonian is measured by a different circuit within each expectation value calculation. Measurement reduction techniques exist for identifying when these observables commute and hence can be simultaneously measured, reducing the number of circuits required for the full expectation value calculation. -# -# This is built in to the `get_operator_expectation_value` method and can be applied by specifying a way to partition the measuremrnt terms. `PauliPartitionStrat.CommutingSets` can greatly reduce the number of measurement circuits by combining any number of terms that mutually commute. However, this involves potentially adding an arbitrary Clifford circuit to change the basis of the measurements which can be costly on NISQ devices, so `PauliPartitionStrat.NonConflictingSets` trades off some of the reduction in circuit number to guarantee that only single-qubit gates are introduced. - -from pytket.partition import PauliPartitionStrat - -# Objective function using measurement reduction: - - -def objective(params): - circ = ucc.copy() - sym_map = dict(zip(syms, params)) - circ.symbol_substitution(sym_map) - return ( - get_operator_expectation_value( - circ, - operator, - backend, - n_shots=4000, - partition_strat=PauliPartitionStrat.CommutingSets, - ) - + nuclear_repulsion_energy - ) - - -# At this point, we have completely transformed how our VQE objective function works, improving its resilience to noise, cutting the number of circuits run, and maintaining fast runtimes. In doing this, we have explored a number of the features `pytket` offers that are beneficial to VQE and the UCC method: -# - high-level syntactic constructs for evolution operators; -# - utility methods for easy expectation value calculations; -# - both generic and domain-specific circuit simplification methods; -# - symbolic circuit compilation; -# - measurement reduction for expectation value calculations. - -# For the sake of completeness, the following gives the full code for the final solution, including passing the objective function to a classical optimiser to find the ground state: - -import openfermion as of -from scipy.optimize import minimize -from sympy import symbols - -from pytket.extensions.qiskit import AerBackend -from pytket.circuit import Circuit, Qubit -from pytket.partition import PauliPartitionStrat -from pytket.passes import GuidedPauliSimp, FullPeepholeOptimise -from pytket.pauli import Pauli, QubitPauliString -from pytket.utils import get_operator_expectation_value, gen_term_sequence_circuit -from pytket.utils.operators import QubitPauliOperator - -# Obtain electronic Hamiltonian: - -hamiltonian = ( - -0.8153001706270075 * of.QubitOperator("") - + 0.16988452027940318 * of.QubitOperator("Z0") - + -0.21886306781219608 * of.QubitOperator("Z1") - + 0.16988452027940323 * of.QubitOperator("Z2") - + -0.2188630678121961 * of.QubitOperator("Z3") - + 0.12005143072546047 * of.QubitOperator("Z0 Z1") - + 0.16821198673715723 * of.QubitOperator("Z0 Z2") - + 0.16549431486978672 * of.QubitOperator("Z0 Z3") - + 0.16549431486978672 * of.QubitOperator("Z1 Z2") - + 0.1739537877649417 * of.QubitOperator("Z1 Z3") - + 0.12005143072546047 * of.QubitOperator("Z2 Z3") - + 0.04544288414432624 * of.QubitOperator("X0 X1 X2 X3") - + 0.04544288414432624 * of.QubitOperator("X0 X1 Y2 Y3") - + 0.04544288414432624 * of.QubitOperator("Y0 Y1 X2 X3") - + 0.04544288414432624 * of.QubitOperator("Y0 Y1 Y2 Y3") -) -nuclear_repulsion_energy = 0.70556961456 - -pauli_sym = {"I": Pauli.I, "X": Pauli.X, "Y": Pauli.Y, "Z": Pauli.Z} - - -def qps_from_openfermion(paulis): - """Convert OpenFermion tensor of Paulis to pytket QubitPauliString.""" - qlist = [] - plist = [] - for q, p in paulis: - qlist.append(Qubit(q)) - plist.append(pauli_sym[p]) - return QubitPauliString(qlist, plist) - - -def qpo_from_openfermion(openf_op): - """Convert OpenFermion QubitOperator to pytket QubitPauliOperator.""" - tk_op = dict() - for term, coeff in openf_op.terms.items(): - string = qps_from_openfermion(term) - tk_op[string] = coeff - return QubitPauliOperator(tk_op) - - -hamiltonian_op = qpo_from_openfermion(hamiltonian) - -# Obtain terms for single and double excitations: - -q = [Qubit(i) for i in range(4)] -xyii = QubitPauliString([q[0], q[1]], [Pauli.X, Pauli.Y]) -yxii = QubitPauliString([q[0], q[1]], [Pauli.Y, Pauli.X]) -iixy = QubitPauliString([q[2], q[3]], [Pauli.X, Pauli.Y]) -iiyx = QubitPauliString([q[2], q[3]], [Pauli.Y, Pauli.X]) -xxxy = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.X, Pauli.Y]) -xxyx = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.Y, Pauli.X]) -xyxx = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.X, Pauli.X]) -yxxx = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.X, Pauli.X]) -yyyx = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.Y, Pauli.X]) -yyxy = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.X, Pauli.Y]) -yxyy = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.Y, Pauli.Y]) -xyyy = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.Y, Pauli.Y]) - -# Symbolic UCC ansatz generation: - -syms = symbols("p0 p1 p2") -singles_syms = {xyii: syms[0], yxii: -syms[0], iixy: syms[1], iiyx: -syms[1]} -doubles_syms = { - xxxy: 0.25 * syms[2], - xxyx: -0.25 * syms[2], - xyxx: 0.25 * syms[2], - yxxx: -0.25 * syms[2], - yyyx: -0.25 * syms[2], - yyxy: 0.25 * syms[2], - yxyy: -0.25 * syms[2], - xyyy: 0.25 * syms[2], -} -excitation_op = QubitPauliOperator({**singles_syms, **doubles_syms}) -ucc_ref = Circuit(4).X(0).X(2) -ucc = gen_term_sequence_circuit(excitation_op, ucc_ref) - -# Circuit simplification: - -GuidedPauliSimp().apply(ucc) -FullPeepholeOptimise().apply(ucc) - -# Connect to a simulator/device: - -backend = AerBackend() - -# Objective function: - - -def objective(params): - circ = ucc.copy() - sym_map = dict(zip(syms, params)) - circ.symbol_substitution(sym_map) - return ( - get_operator_expectation_value( - circ, - hamiltonian_op, - backend, - n_shots=4000, - partition_strat=PauliPartitionStrat.CommutingSets, - ) - + nuclear_repulsion_energy - ).real - - -# Optimise against the objective function: - -initial_params = [1e-4, 1e-4, 4e-1] -# #result = minimize(objective, initial_params, method="Nelder-Mead") -# #print("Final parameter values", result.x) -# #print("Final energy value", result.fun) - -# Exercises: -# - Replace the `get_operator_expectation_value` call with its implementation and use this to pull the analysis for measurement reduction outside of the objective function, so our circuits can be fully determined and compiled once. This means that the `symbol_substitution` method will need to be applied to each measurement circuit instead of just the state preparation circuit. -# - Use the `SpamCorrecter` class to add some mitigation of the measurement errors. Start by running the characterisation circuits first, before your main VQE loop, then apply the mitigation to each of the circuits run within the objective function. -# - Change the `backend` by passing in a `Qiskit` `NoiseModel` to simulate a noisy device. Compare the accuracy of the objective function both with and without the circuit simplification. Try running a classical optimiser over the objective function and compare the convergence rates with different noise models. If you have access to a QPU, try changing the `backend` to connect to that and compare the results to the simulator. diff --git a/manual_constraints.txt b/manual_constraints.txt deleted file mode 100644 index 337753f6..00000000 --- a/manual_constraints.txt +++ /dev/null @@ -1,2 +0,0 @@ -sphinx_autodoc_annotation >= 1.0 -sphinx_book_theme ~= 1.1.2 diff --git a/manual_requirements.txt b/manual_requirements.txt deleted file mode 100644 index b3ade4cf..00000000 --- a/manual_requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ - pytket[ZX] == 1.27.0 - pytket-qiskit == 0.53.0 - pytket-cirq == 0.36.0 \ No newline at end of file From 395903eef281e7fd452bce500aa9910eeffaae96 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:31:23 +0100 Subject: [PATCH 07/76] update manual workflow --- .github/workflows/check-manual.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/check-manual.yml b/.github/workflows/check-manual.yml index 231cdc75..1a2db3ec 100644 --- a/.github/workflows/check-manual.yml +++ b/.github/workflows/check-manual.yml @@ -43,14 +43,9 @@ jobs: python-version: 3.11 - name: install python requirements for manual run: | - python -m pip install --upgrade pip - python -m pip install wheel - python -m pip install -c manual_constraints.txt sphinx sphinx-book-theme jupyter-sphinx quimb - python -m pip install -r manual_requirements.txt - python -m pip install kahypar - python -m pip install sphinx-copybutton - python -m pip install ipyparallel - python -m pip install qiskit-algorithms + python -m pip install poetry + poetry install + poetry shell - name: install graphviz run: | sudo apt-get update @@ -58,5 +53,4 @@ jobs: - name: build manual run: | cd manual/ - sed "s/REQUIREMENTS/$(sed -e 's/[\&/]/\\&/g' -e 's/$/\\n/' ../manual_requirements.txt | tr -d '\n')/" index-rst-template > index.rst sphinx-build -b html . build -W From 89005ec54379e2d093dab7804bea427d581c5c3b Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:37:24 +0100 Subject: [PATCH 08/76] move manual and examples into docs directory --- .gitignore | 2 ++ {examples => docs/examples}/CONTRIBUTING.md | 0 .../examples}/Forest_portability_example.ipynb | 0 {examples => docs/examples}/Getting_started.ipynb | 0 {examples => docs/examples}/_config.yml | 0 .../examples}/_static/Quantinuum_logo_black.png | Bin .../examples}/_static/Quantinuum_logo_white.png | Bin {examples => docs/examples}/_static/custom.css | 0 {examples => docs/examples}/_toc.yml | 0 .../examples}/ansatz_sequence_example.ipynb | 0 {examples => docs/examples}/backends_example.ipynb | 0 {examples => docs/examples}/c.qasm | 0 {examples => docs/examples}/c.quip | 0 {examples => docs/examples}/c.tex | 0 {examples => docs/examples}/check-examples | 0 {examples => docs/examples}/ci-tested-notebooks.txt | 0 .../examples}/circuit_analysis_example.ipynb | 0 .../examples}/circuit_generation_example.ipynb | 0 .../examples}/comparing_simulators.ipynb | 0 .../examples}/compilation_example.ipynb | 0 .../examples}/conditional_gate_example.ipynb | 0 {examples => docs/examples}/constraints.txt | 0 .../examples}/contextual_optimization.ipynb | 0 {examples => docs/examples}/creating_backends.ipynb | 0 .../examples}/creating_backends_exercise.py | 0 .../examples}/entanglement_swapping.ipynb | 0 .../examples}/example_requirements.txt | 0 .../examples}/expectation_value_example.ipynb | 0 {examples => docs/examples}/images/phase_est.png | Bin {examples => docs/examples}/images/qft.png | Bin .../examples}/maintained-notebooks.txt | 0 {examples => docs/examples}/mapping_example.ipynb | 0 .../examples}/measurement_reduction_example.ipynb | 0 {examples => docs/examples}/oxfordQIS.py | 0 {examples => docs/examples}/oxfordQIS_exercises.pdf | Bin {examples => docs/examples}/phase_estimation.ipynb | 0 .../examples}/pytket-qujax-classification.ipynb | 0 .../examples}/pytket-qujax_heisenberg_vqe.ipynb | 0 {examples => docs/examples}/pytket-qujax_qaoa.ipynb | 0 {examples => docs/examples}/qasm/W-state.qasm | 0 {examples => docs/examples}/qasm/adder.qasm | 0 {examples => docs/examples}/qasm/bigadder.qasm | 0 .../examples}/qasm/entangled_registers.qasm | 0 {examples => docs/examples}/qasm/inverseqft1.qasm | 0 {examples => docs/examples}/qasm/inverseqft2.qasm | 0 {examples => docs/examples}/qasm/ipea_3_pi_8.qasm | 0 {examples => docs/examples}/qasm/pea_3_pi_8.qasm | 0 .../examples}/qasm/plaquette_check.qasm | 0 {examples => docs/examples}/qasm/problem.qasm | 0 {examples => docs/examples}/qasm/qec.qasm | 0 {examples => docs/examples}/qasm/qft.qasm | 0 {examples => docs/examples}/qasm/qpt.qasm | 0 {examples => docs/examples}/qasm/rb.qasm | 0 .../examples}/qasm/routing_example_circuit.qasm | 0 {examples => docs/examples}/qasm/simple.qasm | 0 {examples => docs/examples}/qasm/teleport.qasm | 0 {examples => docs/examples}/qasm/teleportv2.qasm | 0 .../examples}/qiskit_integration.ipynb | 0 {examples => docs/examples}/spam_example.ipynb | 0 {examples => docs/examples}/spambench.py | 0 {examples => docs/examples}/start-rigetti-vms | 0 {examples => docs/examples}/stop-vms | 0 {examples => docs/examples}/symbolics_example.ipynb | 0 {examples => docs/examples}/ucc_vqe.ipynb | 0 {manual => docs/manual}/README.md | 0 .../manual}/_static/Quantinuum_logo_black.png | Bin .../manual}/_static/Quantinuum_logo_white.png | Bin {manual => docs/manual}/_static/custom.css | 0 {manual => docs/manual}/conf.py | 0 {manual => docs/manual}/index.rst | 0 {manual => docs/manual}/manual_assertion.rst | 0 {manual => docs/manual}/manual_backend.rst | 0 {manual => docs/manual}/manual_circuit.rst | 0 {manual => docs/manual}/manual_compiler.rst | 0 {manual => docs/manual}/manual_intro.rst | 0 {manual => docs/manual}/manual_noise.rst | 0 {manual => docs/manual}/manual_zx.rst | 0 examples/load-ibmq-account | 11 ----------- 78 files changed, 2 insertions(+), 11 deletions(-) rename {examples => docs/examples}/CONTRIBUTING.md (100%) rename {examples => docs/examples}/Forest_portability_example.ipynb (100%) rename {examples => docs/examples}/Getting_started.ipynb (100%) rename {examples => docs/examples}/_config.yml (100%) rename {examples => docs/examples}/_static/Quantinuum_logo_black.png (100%) rename {examples => docs/examples}/_static/Quantinuum_logo_white.png (100%) rename {examples => docs/examples}/_static/custom.css (100%) rename {examples => docs/examples}/_toc.yml (100%) rename {examples => docs/examples}/ansatz_sequence_example.ipynb (100%) rename {examples => docs/examples}/backends_example.ipynb (100%) rename {examples => docs/examples}/c.qasm (100%) rename {examples => docs/examples}/c.quip (100%) rename {examples => docs/examples}/c.tex (100%) rename {examples => docs/examples}/check-examples (100%) rename {examples => docs/examples}/ci-tested-notebooks.txt (100%) rename {examples => docs/examples}/circuit_analysis_example.ipynb (100%) rename {examples => docs/examples}/circuit_generation_example.ipynb (100%) rename {examples => docs/examples}/comparing_simulators.ipynb (100%) rename {examples => docs/examples}/compilation_example.ipynb (100%) rename {examples => docs/examples}/conditional_gate_example.ipynb (100%) rename {examples => docs/examples}/constraints.txt (100%) rename {examples => docs/examples}/contextual_optimization.ipynb (100%) rename {examples => docs/examples}/creating_backends.ipynb (100%) rename {examples => docs/examples}/creating_backends_exercise.py (100%) rename {examples => docs/examples}/entanglement_swapping.ipynb (100%) rename {examples => docs/examples}/example_requirements.txt (100%) rename {examples => docs/examples}/expectation_value_example.ipynb (100%) rename {examples => docs/examples}/images/phase_est.png (100%) rename {examples => docs/examples}/images/qft.png (100%) rename {examples => docs/examples}/maintained-notebooks.txt (100%) rename {examples => docs/examples}/mapping_example.ipynb (100%) rename {examples => docs/examples}/measurement_reduction_example.ipynb (100%) rename {examples => docs/examples}/oxfordQIS.py (100%) rename {examples => docs/examples}/oxfordQIS_exercises.pdf (100%) rename {examples => docs/examples}/phase_estimation.ipynb (100%) rename {examples => docs/examples}/pytket-qujax-classification.ipynb (100%) rename {examples => docs/examples}/pytket-qujax_heisenberg_vqe.ipynb (100%) rename {examples => docs/examples}/pytket-qujax_qaoa.ipynb (100%) rename {examples => docs/examples}/qasm/W-state.qasm (100%) rename {examples => docs/examples}/qasm/adder.qasm (100%) rename {examples => docs/examples}/qasm/bigadder.qasm (100%) rename {examples => docs/examples}/qasm/entangled_registers.qasm (100%) rename {examples => docs/examples}/qasm/inverseqft1.qasm (100%) rename {examples => docs/examples}/qasm/inverseqft2.qasm (100%) rename {examples => docs/examples}/qasm/ipea_3_pi_8.qasm (100%) rename {examples => docs/examples}/qasm/pea_3_pi_8.qasm (100%) rename {examples => docs/examples}/qasm/plaquette_check.qasm (100%) rename {examples => docs/examples}/qasm/problem.qasm (100%) rename {examples => docs/examples}/qasm/qec.qasm (100%) rename {examples => docs/examples}/qasm/qft.qasm (100%) rename {examples => docs/examples}/qasm/qpt.qasm (100%) rename {examples => docs/examples}/qasm/rb.qasm (100%) rename {examples => docs/examples}/qasm/routing_example_circuit.qasm (100%) rename {examples => docs/examples}/qasm/simple.qasm (100%) rename {examples => docs/examples}/qasm/teleport.qasm (100%) rename {examples => docs/examples}/qasm/teleportv2.qasm (100%) rename {examples => docs/examples}/qiskit_integration.ipynb (100%) rename {examples => docs/examples}/spam_example.ipynb (100%) rename {examples => docs/examples}/spambench.py (100%) rename {examples => docs/examples}/start-rigetti-vms (100%) rename {examples => docs/examples}/stop-vms (100%) rename {examples => docs/examples}/symbolics_example.ipynb (100%) rename {examples => docs/examples}/ucc_vqe.ipynb (100%) rename {manual => docs/manual}/README.md (100%) rename {manual => docs/manual}/_static/Quantinuum_logo_black.png (100%) rename {manual => docs/manual}/_static/Quantinuum_logo_white.png (100%) rename {manual => docs/manual}/_static/custom.css (100%) rename {manual => docs/manual}/conf.py (100%) rename {manual => docs/manual}/index.rst (100%) rename {manual => docs/manual}/manual_assertion.rst (100%) rename {manual => docs/manual}/manual_backend.rst (100%) rename {manual => docs/manual}/manual_circuit.rst (100%) rename {manual => docs/manual}/manual_compiler.rst (100%) rename {manual => docs/manual}/manual_intro.rst (100%) rename {manual => docs/manual}/manual_noise.rst (100%) rename {manual => docs/manual}/manual_zx.rst (100%) delete mode 100755 examples/load-ibmq-account diff --git a/.gitignore b/.gitignore index 7b054f48..fc102113 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ dist manual/build manual/jupyter_execute examples/_build/ +docs/manual/jupyter_execute +docs/examples/_build/ *.ipynb_checkpoints \ No newline at end of file diff --git a/examples/CONTRIBUTING.md b/docs/examples/CONTRIBUTING.md similarity index 100% rename from examples/CONTRIBUTING.md rename to docs/examples/CONTRIBUTING.md diff --git a/examples/Forest_portability_example.ipynb b/docs/examples/Forest_portability_example.ipynb similarity index 100% rename from examples/Forest_portability_example.ipynb rename to docs/examples/Forest_portability_example.ipynb diff --git a/examples/Getting_started.ipynb b/docs/examples/Getting_started.ipynb similarity index 100% rename from examples/Getting_started.ipynb rename to docs/examples/Getting_started.ipynb diff --git a/examples/_config.yml b/docs/examples/_config.yml similarity index 100% rename from examples/_config.yml rename to docs/examples/_config.yml diff --git a/examples/_static/Quantinuum_logo_black.png b/docs/examples/_static/Quantinuum_logo_black.png similarity index 100% rename from examples/_static/Quantinuum_logo_black.png rename to docs/examples/_static/Quantinuum_logo_black.png diff --git a/examples/_static/Quantinuum_logo_white.png b/docs/examples/_static/Quantinuum_logo_white.png similarity index 100% rename from examples/_static/Quantinuum_logo_white.png rename to docs/examples/_static/Quantinuum_logo_white.png diff --git a/examples/_static/custom.css b/docs/examples/_static/custom.css similarity index 100% rename from examples/_static/custom.css rename to docs/examples/_static/custom.css diff --git a/examples/_toc.yml b/docs/examples/_toc.yml similarity index 100% rename from examples/_toc.yml rename to docs/examples/_toc.yml diff --git a/examples/ansatz_sequence_example.ipynb b/docs/examples/ansatz_sequence_example.ipynb similarity index 100% rename from examples/ansatz_sequence_example.ipynb rename to docs/examples/ansatz_sequence_example.ipynb diff --git a/examples/backends_example.ipynb b/docs/examples/backends_example.ipynb similarity index 100% rename from examples/backends_example.ipynb rename to docs/examples/backends_example.ipynb diff --git a/examples/c.qasm b/docs/examples/c.qasm similarity index 100% rename from examples/c.qasm rename to docs/examples/c.qasm diff --git a/examples/c.quip b/docs/examples/c.quip similarity index 100% rename from examples/c.quip rename to docs/examples/c.quip diff --git a/examples/c.tex b/docs/examples/c.tex similarity index 100% rename from examples/c.tex rename to docs/examples/c.tex diff --git a/examples/check-examples b/docs/examples/check-examples similarity index 100% rename from examples/check-examples rename to docs/examples/check-examples diff --git a/examples/ci-tested-notebooks.txt b/docs/examples/ci-tested-notebooks.txt similarity index 100% rename from examples/ci-tested-notebooks.txt rename to docs/examples/ci-tested-notebooks.txt diff --git a/examples/circuit_analysis_example.ipynb b/docs/examples/circuit_analysis_example.ipynb similarity index 100% rename from examples/circuit_analysis_example.ipynb rename to docs/examples/circuit_analysis_example.ipynb diff --git a/examples/circuit_generation_example.ipynb b/docs/examples/circuit_generation_example.ipynb similarity index 100% rename from examples/circuit_generation_example.ipynb rename to docs/examples/circuit_generation_example.ipynb diff --git a/examples/comparing_simulators.ipynb b/docs/examples/comparing_simulators.ipynb similarity index 100% rename from examples/comparing_simulators.ipynb rename to docs/examples/comparing_simulators.ipynb diff --git a/examples/compilation_example.ipynb b/docs/examples/compilation_example.ipynb similarity index 100% rename from examples/compilation_example.ipynb rename to docs/examples/compilation_example.ipynb diff --git a/examples/conditional_gate_example.ipynb b/docs/examples/conditional_gate_example.ipynb similarity index 100% rename from examples/conditional_gate_example.ipynb rename to docs/examples/conditional_gate_example.ipynb diff --git a/examples/constraints.txt b/docs/examples/constraints.txt similarity index 100% rename from examples/constraints.txt rename to docs/examples/constraints.txt diff --git a/examples/contextual_optimization.ipynb b/docs/examples/contextual_optimization.ipynb similarity index 100% rename from examples/contextual_optimization.ipynb rename to docs/examples/contextual_optimization.ipynb diff --git a/examples/creating_backends.ipynb b/docs/examples/creating_backends.ipynb similarity index 100% rename from examples/creating_backends.ipynb rename to docs/examples/creating_backends.ipynb diff --git a/examples/creating_backends_exercise.py b/docs/examples/creating_backends_exercise.py similarity index 100% rename from examples/creating_backends_exercise.py rename to docs/examples/creating_backends_exercise.py diff --git a/examples/entanglement_swapping.ipynb b/docs/examples/entanglement_swapping.ipynb similarity index 100% rename from examples/entanglement_swapping.ipynb rename to docs/examples/entanglement_swapping.ipynb diff --git a/examples/example_requirements.txt b/docs/examples/example_requirements.txt similarity index 100% rename from examples/example_requirements.txt rename to docs/examples/example_requirements.txt diff --git a/examples/expectation_value_example.ipynb b/docs/examples/expectation_value_example.ipynb similarity index 100% rename from examples/expectation_value_example.ipynb rename to docs/examples/expectation_value_example.ipynb diff --git a/examples/images/phase_est.png b/docs/examples/images/phase_est.png similarity index 100% rename from examples/images/phase_est.png rename to docs/examples/images/phase_est.png diff --git a/examples/images/qft.png b/docs/examples/images/qft.png similarity index 100% rename from examples/images/qft.png rename to docs/examples/images/qft.png diff --git a/examples/maintained-notebooks.txt b/docs/examples/maintained-notebooks.txt similarity index 100% rename from examples/maintained-notebooks.txt rename to docs/examples/maintained-notebooks.txt diff --git a/examples/mapping_example.ipynb b/docs/examples/mapping_example.ipynb similarity index 100% rename from examples/mapping_example.ipynb rename to docs/examples/mapping_example.ipynb diff --git a/examples/measurement_reduction_example.ipynb b/docs/examples/measurement_reduction_example.ipynb similarity index 100% rename from examples/measurement_reduction_example.ipynb rename to docs/examples/measurement_reduction_example.ipynb diff --git a/examples/oxfordQIS.py b/docs/examples/oxfordQIS.py similarity index 100% rename from examples/oxfordQIS.py rename to docs/examples/oxfordQIS.py diff --git a/examples/oxfordQIS_exercises.pdf b/docs/examples/oxfordQIS_exercises.pdf similarity index 100% rename from examples/oxfordQIS_exercises.pdf rename to docs/examples/oxfordQIS_exercises.pdf diff --git a/examples/phase_estimation.ipynb b/docs/examples/phase_estimation.ipynb similarity index 100% rename from examples/phase_estimation.ipynb rename to docs/examples/phase_estimation.ipynb diff --git a/examples/pytket-qujax-classification.ipynb b/docs/examples/pytket-qujax-classification.ipynb similarity index 100% rename from examples/pytket-qujax-classification.ipynb rename to docs/examples/pytket-qujax-classification.ipynb diff --git a/examples/pytket-qujax_heisenberg_vqe.ipynb b/docs/examples/pytket-qujax_heisenberg_vqe.ipynb similarity index 100% rename from examples/pytket-qujax_heisenberg_vqe.ipynb rename to docs/examples/pytket-qujax_heisenberg_vqe.ipynb diff --git a/examples/pytket-qujax_qaoa.ipynb b/docs/examples/pytket-qujax_qaoa.ipynb similarity index 100% rename from examples/pytket-qujax_qaoa.ipynb rename to docs/examples/pytket-qujax_qaoa.ipynb diff --git a/examples/qasm/W-state.qasm b/docs/examples/qasm/W-state.qasm similarity index 100% rename from examples/qasm/W-state.qasm rename to docs/examples/qasm/W-state.qasm diff --git a/examples/qasm/adder.qasm b/docs/examples/qasm/adder.qasm similarity index 100% rename from examples/qasm/adder.qasm rename to docs/examples/qasm/adder.qasm diff --git a/examples/qasm/bigadder.qasm b/docs/examples/qasm/bigadder.qasm similarity index 100% rename from examples/qasm/bigadder.qasm rename to docs/examples/qasm/bigadder.qasm diff --git a/examples/qasm/entangled_registers.qasm b/docs/examples/qasm/entangled_registers.qasm similarity index 100% rename from examples/qasm/entangled_registers.qasm rename to docs/examples/qasm/entangled_registers.qasm diff --git a/examples/qasm/inverseqft1.qasm b/docs/examples/qasm/inverseqft1.qasm similarity index 100% rename from examples/qasm/inverseqft1.qasm rename to docs/examples/qasm/inverseqft1.qasm diff --git a/examples/qasm/inverseqft2.qasm b/docs/examples/qasm/inverseqft2.qasm similarity index 100% rename from examples/qasm/inverseqft2.qasm rename to docs/examples/qasm/inverseqft2.qasm diff --git a/examples/qasm/ipea_3_pi_8.qasm b/docs/examples/qasm/ipea_3_pi_8.qasm similarity index 100% rename from examples/qasm/ipea_3_pi_8.qasm rename to docs/examples/qasm/ipea_3_pi_8.qasm diff --git a/examples/qasm/pea_3_pi_8.qasm b/docs/examples/qasm/pea_3_pi_8.qasm similarity index 100% rename from examples/qasm/pea_3_pi_8.qasm rename to docs/examples/qasm/pea_3_pi_8.qasm diff --git a/examples/qasm/plaquette_check.qasm b/docs/examples/qasm/plaquette_check.qasm similarity index 100% rename from examples/qasm/plaquette_check.qasm rename to docs/examples/qasm/plaquette_check.qasm diff --git a/examples/qasm/problem.qasm b/docs/examples/qasm/problem.qasm similarity index 100% rename from examples/qasm/problem.qasm rename to docs/examples/qasm/problem.qasm diff --git a/examples/qasm/qec.qasm b/docs/examples/qasm/qec.qasm similarity index 100% rename from examples/qasm/qec.qasm rename to docs/examples/qasm/qec.qasm diff --git a/examples/qasm/qft.qasm b/docs/examples/qasm/qft.qasm similarity index 100% rename from examples/qasm/qft.qasm rename to docs/examples/qasm/qft.qasm diff --git a/examples/qasm/qpt.qasm b/docs/examples/qasm/qpt.qasm similarity index 100% rename from examples/qasm/qpt.qasm rename to docs/examples/qasm/qpt.qasm diff --git a/examples/qasm/rb.qasm b/docs/examples/qasm/rb.qasm similarity index 100% rename from examples/qasm/rb.qasm rename to docs/examples/qasm/rb.qasm diff --git a/examples/qasm/routing_example_circuit.qasm b/docs/examples/qasm/routing_example_circuit.qasm similarity index 100% rename from examples/qasm/routing_example_circuit.qasm rename to docs/examples/qasm/routing_example_circuit.qasm diff --git a/examples/qasm/simple.qasm b/docs/examples/qasm/simple.qasm similarity index 100% rename from examples/qasm/simple.qasm rename to docs/examples/qasm/simple.qasm diff --git a/examples/qasm/teleport.qasm b/docs/examples/qasm/teleport.qasm similarity index 100% rename from examples/qasm/teleport.qasm rename to docs/examples/qasm/teleport.qasm diff --git a/examples/qasm/teleportv2.qasm b/docs/examples/qasm/teleportv2.qasm similarity index 100% rename from examples/qasm/teleportv2.qasm rename to docs/examples/qasm/teleportv2.qasm diff --git a/examples/qiskit_integration.ipynb b/docs/examples/qiskit_integration.ipynb similarity index 100% rename from examples/qiskit_integration.ipynb rename to docs/examples/qiskit_integration.ipynb diff --git a/examples/spam_example.ipynb b/docs/examples/spam_example.ipynb similarity index 100% rename from examples/spam_example.ipynb rename to docs/examples/spam_example.ipynb diff --git a/examples/spambench.py b/docs/examples/spambench.py similarity index 100% rename from examples/spambench.py rename to docs/examples/spambench.py diff --git a/examples/start-rigetti-vms b/docs/examples/start-rigetti-vms similarity index 100% rename from examples/start-rigetti-vms rename to docs/examples/start-rigetti-vms diff --git a/examples/stop-vms b/docs/examples/stop-vms similarity index 100% rename from examples/stop-vms rename to docs/examples/stop-vms diff --git a/examples/symbolics_example.ipynb b/docs/examples/symbolics_example.ipynb similarity index 100% rename from examples/symbolics_example.ipynb rename to docs/examples/symbolics_example.ipynb diff --git a/examples/ucc_vqe.ipynb b/docs/examples/ucc_vqe.ipynb similarity index 100% rename from examples/ucc_vqe.ipynb rename to docs/examples/ucc_vqe.ipynb diff --git a/manual/README.md b/docs/manual/README.md similarity index 100% rename from manual/README.md rename to docs/manual/README.md diff --git a/manual/_static/Quantinuum_logo_black.png b/docs/manual/_static/Quantinuum_logo_black.png similarity index 100% rename from manual/_static/Quantinuum_logo_black.png rename to docs/manual/_static/Quantinuum_logo_black.png diff --git a/manual/_static/Quantinuum_logo_white.png b/docs/manual/_static/Quantinuum_logo_white.png similarity index 100% rename from manual/_static/Quantinuum_logo_white.png rename to docs/manual/_static/Quantinuum_logo_white.png diff --git a/manual/_static/custom.css b/docs/manual/_static/custom.css similarity index 100% rename from manual/_static/custom.css rename to docs/manual/_static/custom.css diff --git a/manual/conf.py b/docs/manual/conf.py similarity index 100% rename from manual/conf.py rename to docs/manual/conf.py diff --git a/manual/index.rst b/docs/manual/index.rst similarity index 100% rename from manual/index.rst rename to docs/manual/index.rst diff --git a/manual/manual_assertion.rst b/docs/manual/manual_assertion.rst similarity index 100% rename from manual/manual_assertion.rst rename to docs/manual/manual_assertion.rst diff --git a/manual/manual_backend.rst b/docs/manual/manual_backend.rst similarity index 100% rename from manual/manual_backend.rst rename to docs/manual/manual_backend.rst diff --git a/manual/manual_circuit.rst b/docs/manual/manual_circuit.rst similarity index 100% rename from manual/manual_circuit.rst rename to docs/manual/manual_circuit.rst diff --git a/manual/manual_compiler.rst b/docs/manual/manual_compiler.rst similarity index 100% rename from manual/manual_compiler.rst rename to docs/manual/manual_compiler.rst diff --git a/manual/manual_intro.rst b/docs/manual/manual_intro.rst similarity index 100% rename from manual/manual_intro.rst rename to docs/manual/manual_intro.rst diff --git a/manual/manual_noise.rst b/docs/manual/manual_noise.rst similarity index 100% rename from manual/manual_noise.rst rename to docs/manual/manual_noise.rst diff --git a/manual/manual_zx.rst b/docs/manual/manual_zx.rst similarity index 100% rename from manual/manual_zx.rst rename to docs/manual/manual_zx.rst diff --git a/examples/load-ibmq-account b/examples/load-ibmq-account deleted file mode 100755 index 1418b159..00000000 --- a/examples/load-ibmq-account +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -from qiskit import IBMQ - -if not IBMQ.stored_account(): - token = os.getenv("PYTKET_QA_QISKIT_TOKEN") - if token: - IBMQ.save_account(token) - -IBMQ.load_account() From 376c32d18e7777462c3bcb8a48dc6a7d99ef8297 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:39:38 +0100 Subject: [PATCH 09/76] update build script --- build-manual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-manual b/build-manual index 6f3f3560..d1a369f4 100755 --- a/build-manual +++ b/build-manual @@ -1,6 +1,6 @@ #!/bin/sh -cd manual/ +cd docs/manual rm -rf build/ sphinx-build -b html . build -W From 254379b3fc67064680ff0104eecd54fb4da7cc1e Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:42:17 +0100 Subject: [PATCH 10/76] build-manual -> build-docs --- .github/workflows/check-manual.yml | 3 +-- build-manual => build-docs | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename build-manual => build-docs (100%) diff --git a/.github/workflows/check-manual.yml b/.github/workflows/check-manual.yml index 1a2db3ec..771788c8 100644 --- a/.github/workflows/check-manual.yml +++ b/.github/workflows/check-manual.yml @@ -52,5 +52,4 @@ jobs: sudo apt-get install graphviz - name: build manual run: | - cd manual/ - sphinx-build -b html . build -W + ./build-docs diff --git a/build-manual b/build-docs similarity index 100% rename from build-manual rename to build-docs From 5b1dc3252488e919817368d71cf92b2b47f035dc Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:45:45 +0100 Subject: [PATCH 11/76] update .gitignore --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index fc102113..7036c4b2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,8 @@ dist .vscode .venv .mypy_cache -manual/build -manual/jupyter_execute +docs/manual/build +docs/manual/jupyter_execute examples/_build/ docs/manual/jupyter_execute docs/examples/_build/ From 890dfc1874329176398c75af72fca9bff66ff4a5 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:46:25 +0100 Subject: [PATCH 12/76] delete jupyterbook files --- docs/examples/_config.yml | 29 --------------- docs/examples/_toc.yml | 51 -------------------------- docs/examples/example_requirements.txt | 6 --- 3 files changed, 86 deletions(-) delete mode 100644 docs/examples/_config.yml delete mode 100644 docs/examples/_toc.yml delete mode 100644 docs/examples/example_requirements.txt diff --git a/docs/examples/_config.yml b/docs/examples/_config.yml deleted file mode 100644 index c719baec..00000000 --- a/docs/examples/_config.yml +++ /dev/null @@ -1,29 +0,0 @@ -title: pytket examples - -sphinx: - config: - html_show_copyright: false - html_theme_options: - logo: - link: https://tket.quantinuum.com/ - image_light: _static/Quantinuum_logo_black.png - image_dark: _static/Quantinuum_logo_white.png - navigation_with_keys: True - - -execute: - # Exclude some examples from execution (these are still deployed as html pages) - exclude_patterns: [".venv/*", "Forest_portability_example.ipynb", "backends_example.ipynb", "qiskit_integration.ipynb", "comparing_simulators.ipynb", "expectation_value_example.ipynb", "pytket-qujax_heisenberg_vqe.ipynb", "spam_example.ipynb", "entanglement_swapping.ipynb", "pytket-qujax-classification.ipynb"] - timeout: 90 # The maximum time (in seconds) each notebook cell is allowed to run. - -# Information about where the book exists on the web -repository: - url: https://github.com/CQCL/pytket # Notebook files are located in pytket/examples - path_to_book: docs # Optional path to your book, relative to the repository root - branch: main # Which branch of the repository should be used when creating links (optional) - -# Add GitHub buttons to your book -# See https://jupyterbook.org/customize/config.html#add-a-link-to-your-repository -html: - use_issues_button: true - use_repository_button: true diff --git a/docs/examples/_toc.yml b/docs/examples/_toc.yml deleted file mode 100644 index 1c576363..00000000 --- a/docs/examples/_toc.yml +++ /dev/null @@ -1,51 +0,0 @@ -# Table of contents -# Learn more at https://jupyterbook.org/customize/toc.html - -format: jb-book -root: Getting_started -parts: - - caption: pytket documentation - chapters: - - url: https://tket.quantinuum.com/api-docs/ - title: pytket API docs - - url: https://tket.quantinuum.com/api-docs/extensions - title: pytket extensions - - url: https://tket.quantinuum.com/user-manual - title: Manual - - url: https://tket.quantinuum.com/examples - title: Example notebooks - - url: https://tket.quantinuum.com/ - title: TKET website - - caption: Building Quantum Circuits - chapters: - - file: circuit_generation_example - - file: circuit_analysis_example - - file: conditional_gate_example - - caption: Backends - chapters: - - file: backends_example - - file: comparing_simulators - - file: Forest_portability_example - - file: creating_backends - - file: qiskit_integration - - caption: Circuit Compilation - chapters: - - file: compilation_example - - file: symbolics_example - - file: mapping_example - - file: ansatz_sequence_example - - file: measurement_reduction_example - - file: contextual_optimization - - caption: Algorithms and Protocols - chapters: - - file: phase_estimation - - file: ucc_vqe - - file: pytket-qujax_heisenberg_vqe - - file: pytket-qujax-classification - - file: pytket-qujax_qaoa - - file: expectation_value_example - - file: entanglement_swapping - - file: spam_example - - caption: Contributing - chapters: - - file: CONTRIBUTING diff --git a/docs/examples/example_requirements.txt b/docs/examples/example_requirements.txt deleted file mode 100644 index 21815305..00000000 --- a/docs/examples/example_requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -pytket == 1.27.0 -pytket-cirq == 0.36.0 -pytket-qiskit == 0.53.0 -pytket-qujax == 0.19.0 -openfermion -pytest From 0113fd8701e89d122a7eb7ac9aa32e222d32c0af Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:29:40 +0100 Subject: [PATCH 13/76] add openfermion nbsphinx and ipyparallel --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 61500fa9..2e762e98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,9 @@ kahypar = "^1.3.5" sphinx-copybutton = "^0.5.2" jupyter-sphinx = "^0.5.3" quimb = "^1.8.1" +nbsphinx = "^0.9.4" +openfermion = "^1.6.1" +ipyparallel = "^8.8.0" [build-system] From f217f478967fa9b45c4c0c1dd850d35ae60cd316 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:35:48 +0100 Subject: [PATCH 14/76] add static files --- docs/manual/_static/Quantinuum_logo_black.png | Bin 18245 -> 0 bytes docs/manual/_static/Quantinuum_logo_white.png | Bin 16704 -> 0 bytes docs/manual/_static/custom.css | 38 ------------------ 3 files changed, 38 deletions(-) delete mode 100644 docs/manual/_static/Quantinuum_logo_black.png delete mode 100644 docs/manual/_static/Quantinuum_logo_white.png delete mode 100644 docs/manual/_static/custom.css diff --git a/docs/manual/_static/Quantinuum_logo_black.png b/docs/manual/_static/Quantinuum_logo_black.png deleted file mode 100644 index 5569581b8e420f14eee561cbf1bf78ca0c96882f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18245 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 diff --git a/docs/manual/_static/custom.css b/docs/manual/_static/custom.css deleted file mode 100644 index c2a2148c..00000000 --- a/docs/manual/_static/custom.css +++ /dev/null @@ -1,38 +0,0 @@ -.wy-side-nav-search, -.wy-nav-top { - background: #5A46BE; -} - -.wy-grid-for-nav, -.wy-body-for-nav, -.wy-nav-side, -.wy-side-scroll, -.wy-menu, -.wy-menu-vertical { - background-color: #FFFFFF; -} - -.wy-menu-vertical a:hover { - background-color: #d9d9d9; -} - -.btn-link:visited, -.btn-link, -a:visited, -.a.reference.external, -.a.reference.internal, -.wy-menu-vertical a, -.wy-menu-vertical li, -.wy-menu-vertical ul, -.span.pre, -.sig-param, -.std.std-ref, - - -html[data-theme=light] { - --pst-color-inline-code: rgb(199, 37, 78) !important; -} - -.sig-name { - font-size: 1.25rem; -} \ No newline at end of file From b3b95f62eafc192848fec0ab680558c1fbfa97e7 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:37:11 +0100 Subject: [PATCH 15/76] move static folder --- docs/_static/Quantinuum_logo_black.png | Bin 0 -> 18245 bytes docs/_static/Quantinuum_logo_white.png | Bin 0 -> 16704 bytes docs/_static/custom.css | 38 +++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 docs/_static/Quantinuum_logo_black.png create mode 100644 docs/_static/Quantinuum_logo_white.png create mode 100644 docs/_static/custom.css diff --git a/docs/_static/Quantinuum_logo_black.png b/docs/_static/Quantinuum_logo_black.png new file mode 100644 index 0000000000000000000000000000000000000000..5569581b8e420f14eee561cbf1bf78ca0c96882f GIT binary patch literal 18245 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 literal 0 HcmV?d00001 diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 00000000..c2a2148c --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,38 @@ +.wy-side-nav-search, +.wy-nav-top { + background: #5A46BE; +} + +.wy-grid-for-nav, +.wy-body-for-nav, +.wy-nav-side, +.wy-side-scroll, +.wy-menu, +.wy-menu-vertical { + background-color: #FFFFFF; +} + +.wy-menu-vertical a:hover { + background-color: #d9d9d9; +} + +.btn-link:visited, +.btn-link, +a:visited, +.a.reference.external, +.a.reference.internal, +.wy-menu-vertical a, +.wy-menu-vertical li, +.wy-menu-vertical ul, +.span.pre, +.sig-param, +.std.std-ref, + + +html[data-theme=light] { + --pst-color-inline-code: rgb(199, 37, 78) !important; +} + +.sig-name { + font-size: 1.25rem; +} \ No newline at end of file From baa75a754e8b3af18de8535421cd963c20b2b3b3 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:38:21 +0100 Subject: [PATCH 16/76] delete duplicate static files --- .../_static/Quantinuum_logo_black.png | Bin 18245 -> 0 bytes .../_static/Quantinuum_logo_white.png | Bin 16704 -> 0 bytes docs/examples/_static/custom.css | 37 ------------------ 3 files changed, 37 deletions(-) delete mode 100644 docs/examples/_static/Quantinuum_logo_black.png delete mode 100644 docs/examples/_static/Quantinuum_logo_white.png delete mode 100644 docs/examples/_static/custom.css diff --git a/docs/examples/_static/Quantinuum_logo_black.png b/docs/examples/_static/Quantinuum_logo_black.png deleted file mode 100644 index 5569581b8e420f14eee561cbf1bf78ca0c96882f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18245 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 diff --git a/docs/examples/_static/custom.css b/docs/examples/_static/custom.css deleted file mode 100644 index 618fe1b1..00000000 --- a/docs/examples/_static/custom.css +++ /dev/null @@ -1,37 +0,0 @@ -.wy-side-nav-search, -.wy-nav-top { - background: #5A46BE; -} - -.wy-grid-for-nav, -.wy-body-for-nav, -.wy-nav-side, -.wy-side-scroll, -.wy-menu, -.wy-menu-vertical { - background-color: #FFFFFF; -} - -.wy-menu-vertical a:hover { - background-color: #d9d9d9; -} - -.btn-link:visited, -.btn-link, -a:visited, -.a.reference.external, -.a.reference.internal, -.wy-menu-vertical a, -.wy-menu-vertical li, -.wy-menu-vertical ul, -.span.pre, -.sig-param, -.std.std-ref, - -html[data-theme=light] { - --pst-color-inline-code: rgb(199, 37, 78) !important; -} - -.sig-name { - font-size: 1.25rem; -} \ No newline at end of file From 098e19d46418d7067d5991ea8194c6cfb64b733a Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:40:00 +0100 Subject: [PATCH 17/76] change manual title --- docs/manual/manual_index.rst | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/manual/manual_index.rst diff --git a/docs/manual/manual_index.rst b/docs/manual/manual_index.rst new file mode 100644 index 00000000..32eef43c --- /dev/null +++ b/docs/manual/manual_index.rst @@ -0,0 +1,31 @@ +Manual +====== + +.. toctree:: + :caption: Manual Sections: + :maxdepth: 2 + + manual_intro.rst + manual_circuit.rst + manual_backend.rst + manual_compiler.rst + manual_noise.rst + manual_assertion.rst + manual_zx.rst + +.. toctree:: + :caption: pytket documentation: + :maxdepth: 1 + + pytket API docs + pytket extensions + Manual + Example notebooks + TKET website + +Indices and Tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` From f87c73bf4375d99256bcaa53fd96eae4d6fe3d74 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:08:09 +0100 Subject: [PATCH 18/76] use Myst-NB instead of NBSphinx --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2e762e98..9c4c7359 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,9 +19,9 @@ kahypar = "^1.3.5" sphinx-copybutton = "^0.5.2" jupyter-sphinx = "^0.5.3" quimb = "^1.8.1" -nbsphinx = "^0.9.4" openfermion = "^1.6.1" ipyparallel = "^8.8.0" +myst-nb = "^1.1.0" [build-system] From 645621d31fe8b4c036bed32a25c3581df4294d62 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:08:47 +0100 Subject: [PATCH 19/76] clear out old files and update configuration --- docs/.nojekyll | 0 docs/404.html | 17 ----------------- docs/{manual => }/conf.py | 21 ++++++++++++++++++++- docs/index.html | 17 ----------------- docs/index.rst | 6 ++++++ docs/manual/index.rst | 31 ------------------------------- docs/manual/manual_index.rst | 2 +- 7 files changed, 27 insertions(+), 67 deletions(-) delete mode 100644 docs/.nojekyll delete mode 100644 docs/404.html rename docs/{manual => }/conf.py (72%) delete mode 100644 docs/index.html create mode 100644 docs/index.rst delete mode 100644 docs/manual/index.rst diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/404.html b/docs/404.html deleted file mode 100644 index 4eb44b42..00000000 --- a/docs/404.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - TKET docs have moved to tket.quantinuum.com - - -

- If you are not redirected automatically, follow - this link to the user manual on tket.quantinuum.com. -

- - diff --git a/docs/manual/conf.py b/docs/conf.py similarity index 72% rename from docs/manual/conf.py rename to docs/conf.py index 7c65a4a6..00467af9 100644 --- a/docs/manual/conf.py +++ b/docs/conf.py @@ -14,11 +14,12 @@ "jupyter_sphinx", "sphinx_copybutton", "sphinx.ext.autosectionlabel", + "myst_nb", ] html_theme = "pydata_sphinx_theme" -html_title = "pytket user manual" +html_title = "Manual" html_theme_options = { "navigation_with_keys": True, @@ -54,3 +55,21 @@ ), "sympy": ("https://docs.sympy.org/latest/", None), } + + +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + ".venv/*", + "examples/Forest_portability_example.ipynb", + "examples/backends_example.ipynb", + "examples/qiskit_integration.ipynb", + "examples/comparing_simulators.ipynb", + "examples/expectation_value_example.ipynb", + "examples/pytket-qujax_heisenberg_vqe.ipynb", + "examples/spam_example.ipynb", + "examples/entanglement_swapping.ipynb", + "examples/pytket-qujax-classification.ipynb", + "jupyter_execute/", +] diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 4eb44b42..00000000 --- a/docs/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - TKET docs have moved to tket.quantinuum.com - - -

- If you are not redirected automatically, follow - this link to the user manual on tket.quantinuum.com. -

- - diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..358acbe9 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,6 @@ +.. toctree:: + :caption: pytket docs + :maxdepth: 2 + + manual/manual_index.rst + examples/Getting_started.ipynb \ No newline at end of file diff --git a/docs/manual/index.rst b/docs/manual/index.rst deleted file mode 100644 index ff515ba0..00000000 --- a/docs/manual/index.rst +++ /dev/null @@ -1,31 +0,0 @@ -Pytket User Manual -================== - -.. toctree:: - :caption: Manual Sections: - :maxdepth: 2 - - manual_intro.rst - manual_circuit.rst - manual_backend.rst - manual_compiler.rst - manual_noise.rst - manual_assertion.rst - manual_zx.rst - -.. toctree:: - :caption: pytket documentation: - :maxdepth: 1 - - pytket API docs - pytket extensions - Manual - Example notebooks - TKET website - -Indices and Tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/docs/manual/manual_index.rst b/docs/manual/manual_index.rst index 32eef43c..ca3202b4 100644 --- a/docs/manual/manual_index.rst +++ b/docs/manual/manual_index.rst @@ -20,7 +20,7 @@ Manual pytket API docs pytket extensions Manual - Example notebooks + Example notebooks TKET website Indices and Tables From a688a934b71ff0ab2e9d8ec1bc0d17d734272f0e Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:11:44 +0100 Subject: [PATCH 20/76] regenerate lock file --- poetry.lock | 567 +++++++++++++++++++++++++++++++++++++++++++++++-- pyproject.toml | 1 + 2 files changed, 556 insertions(+), 12 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7c6cb116..8637d957 100644 --- a/poetry.lock +++ b/poetry.lock @@ -383,6 +383,20 @@ google-api-core = {version = ">=1.14.0", extras = ["grpc"]} proto-plus = ">=1.20.0" protobuf = ">=3.15.0" +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -734,6 +748,20 @@ files = [ {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, ] +[[package]] +name = "deprecation" +version = "2.1.0" +description = "A library to handle automated deprecations" +optional = false +python-versions = "*" +files = [ + {file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"}, + {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"}, +] + +[package.dependencies] +packaging = "*" + [[package]] name = "dill" version = "0.3.8" @@ -774,6 +802,17 @@ files = [ [package.extras] dev-env = ["black (==22.3.0)", "isort (==5.7.*)", "mypy (==0.931.*)", "pylint (==2.10.*)", "pytest (==6.2.*)", "twine (==3.3.*)", "wheel"] +[[package]] +name = "entrypoints" +version = "0.4" +description = "Discover and load entry points from installed packages." +optional = false +python-versions = ">=3.6" +files = [ + {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, + {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, +] + [[package]] name = "exceptiongroup" version = "1.2.1" @@ -961,6 +1000,77 @@ dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"] +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + [[package]] name = "grpcio" version = "1.64.1" @@ -1035,6 +1145,39 @@ googleapis-common-protos = ">=1.5.5" grpcio = ">=1.64.1" protobuf = ">=5.26.1,<6.0dev" +[[package]] +name = "h5py" +version = "3.11.0" +description = "Read and write HDF5 files from Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "h5py-3.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1625fd24ad6cfc9c1ccd44a66dac2396e7ee74940776792772819fc69f3a3731"}, + {file = "h5py-3.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c072655ad1d5fe9ef462445d3e77a8166cbfa5e599045f8aa3c19b75315f10e5"}, + {file = "h5py-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77b19a40788e3e362b54af4dcf9e6fde59ca016db2c61360aa30b47c7b7cef00"}, + {file = "h5py-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:ef4e2f338fc763f50a8113890f455e1a70acd42a4d083370ceb80c463d803972"}, + {file = "h5py-3.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbd732a08187a9e2a6ecf9e8af713f1d68256ee0f7c8b652a32795670fb481ba"}, + {file = "h5py-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75bd7b3d93fbeee40860fd70cdc88df4464e06b70a5ad9ce1446f5f32eb84007"}, + {file = "h5py-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c416f8eb0daae39dabe71415cb531f95dce2d81e1f61a74537a50c63b28ab3"}, + {file = "h5py-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:083e0329ae534a264940d6513f47f5ada617da536d8dccbafc3026aefc33c90e"}, + {file = "h5py-3.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a76cae64080210389a571c7d13c94a1a6cf8cb75153044fd1f822a962c97aeab"}, + {file = "h5py-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3736fe21da2b7d8a13fe8fe415f1272d2a1ccdeff4849c1421d2fb30fd533bc"}, + {file = "h5py-3.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6ae84a14103e8dc19266ef4c3e5d7c00b68f21d07f2966f0ca7bdb6c2761fb"}, + {file = "h5py-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:21dbdc5343f53b2e25404673c4f00a3335aef25521bd5fa8c707ec3833934892"}, + {file = "h5py-3.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:754c0c2e373d13d6309f408325343b642eb0f40f1a6ad21779cfa9502209e150"}, + {file = "h5py-3.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:731839240c59ba219d4cb3bc5880d438248533366f102402cfa0621b71796b62"}, + {file = "h5py-3.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ec9df3dd2018904c4cc06331951e274f3f3fd091e6d6cc350aaa90fa9b42a76"}, + {file = "h5py-3.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:55106b04e2c83dfb73dc8732e9abad69d83a436b5b82b773481d95d17b9685e1"}, + {file = "h5py-3.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f4e025e852754ca833401777c25888acb96889ee2c27e7e629a19aee288833f0"}, + {file = "h5py-3.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c4b760082626120031d7902cd983d8c1f424cdba2809f1067511ef283629d4b"}, + {file = "h5py-3.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67462d0669f8f5459529de179f7771bd697389fcb3faab54d63bf788599a48ea"}, + {file = "h5py-3.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:d9c944d364688f827dc889cf83f1fca311caf4fa50b19f009d1f2b525edd33a3"}, + {file = "h5py-3.11.0.tar.gz", hash = "sha256:7b7e8f78072a2edec87c9836f25f34203fd492a4475709a18b417a33cfb21fa9"}, +] + +[package.dependencies] +numpy = ">=1.17.3" + [[package]] name = "ibm-cloud-sdk-core" version = "3.20.0" @@ -1086,6 +1229,25 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] +[[package]] +name = "importlib-metadata" +version = "7.1.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -1130,6 +1292,38 @@ pyqt5 = ["pyqt5"] pyside6 = ["pyside6"] test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] +[[package]] +name = "ipyparallel" +version = "8.8.0" +description = "Interactive Parallel Computing with IPython" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipyparallel-8.8.0-py3-none-any.whl", hash = "sha256:d2db60fd8baea09e02e7a13a761e1798868c0a717e3e2fa8fb52ffc037cccd35"}, + {file = "ipyparallel-8.8.0.tar.gz", hash = "sha256:2404d59f86a3aaa3bd27bf6b57df777bff5c1363c1c6e60403759d16ed42dc7b"}, +] + +[package.dependencies] +decorator = "*" +entrypoints = "*" +ipykernel = ">=4.4" +ipython = ">=4" +jupyter-client = ">=5" +psutil = "*" +python-dateutil = ">=2.1" +pyzmq = ">=18" +tornado = ">=5.1" +tqdm = "*" +traitlets = ">=4.3" + +[package.extras] +benchmark = ["asv"] +labextension = ["jupyter-server", "jupyterlab (>=3)"] +nbext = ["jupyter-server", "notebook"] +retroextension = ["jupyter-server", "retrolab"] +serverextension = ["jupyter-server"] +test = ["ipython[test]", "pytest", "pytest-asyncio", "pytest-cov", "testpath"] + [[package]] name = "ipython" version = "8.25.0" @@ -1203,14 +1397,14 @@ files = [ [package.dependencies] ml-dtypes = ">=0.2.0" numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, - {version = ">=1.23.2", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, {version = ">=1.22", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] opt-einsum = "*" scipy = [ - {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, {version = ">=1.9", markers = "python_version < \"3.12\""}, + {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, ] [package.extras] @@ -1258,8 +1452,8 @@ files = [ ml-dtypes = ">=0.2.0" numpy = ">=1.22" scipy = [ - {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, {version = ">=1.9", markers = "python_version < \"3.12\""}, + {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, ] [package.extras] @@ -1336,6 +1530,33 @@ files = [ [package.dependencies] referencing = ">=0.31.0" +[[package]] +name = "jupyter-cache" +version = "1.0.0" +description = "A defined interface for working with a cache of jupyter notebooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "jupyter_cache-1.0.0-py3-none-any.whl", hash = "sha256:594b1c4e29b488b36547e12477645f489dbdc62cc939b2408df5679f79245078"}, + {file = "jupyter_cache-1.0.0.tar.gz", hash = "sha256:d0fa7d7533cd5798198d8889318269a8c1382ed3b22f622c09a9356521f48687"}, +] + +[package.dependencies] +attrs = "*" +click = "*" +importlib-metadata = "*" +nbclient = ">=0.2" +nbformat = "*" +pyyaml = "*" +sqlalchemy = ">=1.3.12,<3" +tabulate = "*" + +[package.extras] +cli = ["click-log"] +code-style = ["pre-commit (>=2.12)"] +rtd = ["ipykernel", "jupytext", "myst-nb", "nbdime", "sphinx-book-theme", "sphinx-copybutton"] +testing = ["coverage", "ipykernel", "jupytext", "matplotlib", "nbdime", "nbformat (>=5.1)", "numpy", "pandas", "pytest (>=6,<8)", "pytest-cov", "pytest-regressions", "sympy"] + [[package]] name = "jupyter-client" version = "8.6.2" @@ -1605,6 +1826,30 @@ files = [ {file = "llvmlite-0.42.0.tar.gz", hash = "sha256:f92b09243c0cc3f457da8b983f67bd8e1295d0f5b3746c7a1861d7a99403854a"}, ] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +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" @@ -1740,6 +1985,36 @@ files = [ [package.dependencies] traitlets = "*" +[[package]] +name = "mdit-py-plugins" +version = "0.4.1" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, + {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +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 = "mistune" version = "3.0.2" @@ -1779,9 +2054,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, - {version = ">=1.23.3", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, {version = ">=1.21.2", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, + {version = ">=1.23.3", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] [package.extras] @@ -1804,6 +2079,60 @@ docs = ["sphinx"] gmpy = ["gmpy2 (>=2.1.0a4)"] tests = ["pytest (>=4.6)"] +[[package]] +name = "myst-nb" +version = "1.1.0" +description = "A Jupyter Notebook Sphinx reader built on top of the MyST markdown parser." +optional = false +python-versions = ">=3.9" +files = [ + {file = "myst_nb-1.1.0-py3-none-any.whl", hash = "sha256:0ac29b2a346f9a1257edbfb5d6c47d528728a37e6b9438903c2821f69fda9235"}, + {file = "myst_nb-1.1.0.tar.gz", hash = "sha256:9278840e844f5d780b5acc5400cbf63d97caaccf8eb442a55ebd9a03e2522d5e"}, +] + +[package.dependencies] +importlib_metadata = "*" +ipykernel = "*" +ipython = "*" +jupyter-cache = ">=0.5" +myst-parser = ">=1.0.0" +nbclient = "*" +nbformat = ">=5.0" +pyyaml = "*" +sphinx = ">=5" +typing-extensions = "*" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["alabaster", "altair", "bokeh", "coconut (>=1.4.3,<3.1.0)", "ipykernel (>=5.5,<7.0)", "ipywidgets", "jupytext (>=1.11.2,<1.16.0)", "matplotlib", "numpy", "pandas", "plotly", "sphinx-book-theme (>=0.3)", "sphinx-copybutton", "sphinx-design (>=0.4.0,<0.5.0)", "sphinxcontrib-bibtex", "sympy"] +testing = ["beautifulsoup4", "coverage (>=6.4,<8.0)", "ipykernel (>=5.5,<7.0)", "ipython (!=8.1.0,<8.17)", "ipywidgets (>=8)", "jupytext (>=1.11.2,<1.16.0)", "matplotlib (==3.7.*)", "nbdime", "numpy", "pandas (==1.5.*)", "pyarrow", "pytest (>=7.1,<8.0)", "pytest-cov (>=3,<5)", "pytest-param-files (>=0.3.3,<0.4.0)", "pytest-regressions", "sympy (>=1.10.1)"] + +[[package]] +name = "myst-parser" +version = "3.0.1" +description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," +optional = false +python-versions = ">=3.8" +files = [ + {file = "myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1"}, + {file = "myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87"}, +] + +[package.dependencies] +docutils = ">=0.18,<0.22" +jinja2 = "*" +markdown-it-py = ">=3.0,<4.0" +mdit-py-plugins = ">=0.4,<1.0" +pyyaml = "*" +sphinx = ">=6,<8" + +[package.extras] +code-style = ["pre-commit (>=3.0,<4.0)"] +linkify = ["linkify-it-py (>=2.0,<3.0)"] +rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] +testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"] +testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"] + [[package]] name = "nbclient" version = "0.10.0" @@ -1992,6 +2321,31 @@ files = [ {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] +[[package]] +name = "openfermion" +version = "1.6.1" +description = "The electronic structure package for quantum computers." +optional = false +python-versions = "*" +files = [ + {file = "openfermion-1.6.1-py3-none-any.whl", hash = "sha256:9cc65558ac4915ab25a3366f71b80acf22b16116c9f6132c08990fd2f59b557e"}, + {file = "openfermion-1.6.1.tar.gz", hash = "sha256:d7f1df4cf98feaa577a070cb432e3d05863c73a4f3bc1f7d112914cf2a3c9bcb"}, +] + +[package.dependencies] +cirq-core = ">=1.0,<2.0" +deprecation = "*" +h5py = ">=2.8" +networkx = "*" +numpy = ">=1.11.0" +pubchempy = "*" +requests = ">=2.18" +scipy = ">=1.1.0" +sympy = "*" + +[package.extras] +resources = ["ase", "jax", "jaxlib", "pyscf"] + [[package]] name = "opt-einsum" version = "3.3.0" @@ -2061,9 +2415,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, - {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -2264,13 +2618,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "prompt-toolkit" -version = "3.0.46" +version = "3.0.47" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.46-py3-none-any.whl", hash = "sha256:45abe60a8300f3c618b23c16c4bb98c6fc80af8ce8b17c7ae92db48db3ee63c1"}, - {file = "prompt_toolkit-3.0.46.tar.gz", hash = "sha256:869c50d682152336e23c4db7f74667639b5047494202ffe7670817053fd57795"}, + {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, + {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, ] [package.dependencies] @@ -2352,6 +2706,19 @@ files = [ {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] +[[package]] +name = "pubchempy" +version = "1.0.4" +description = "A simple Python wrapper around the PubChem PUG REST API." +optional = false +python-versions = "*" +files = [ + {file = "PubChemPy-1.0.4.tar.gz", hash = "sha256:24e9dc2fc90ab153b2764bf805e510b1410700884faf0510a9e7cf0d61d8ed0e"}, +] + +[package.extras] +pandas = ["pandas"] + [[package]] name = "pure-eval" version = "0.2.2" @@ -2764,6 +3131,66 @@ files = [ {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, ] +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + [[package]] name = "pyzmq" version = "26.0.3" @@ -3574,6 +4001,93 @@ lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] +[[package]] +name = "sqlalchemy" +version = "2.0.30" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-win32.whl", hash = "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"}, + {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"}, + {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + [[package]] name = "sspilib" version = "0.1.0" @@ -3704,6 +4218,20 @@ files = [ [package.dependencies] mpmath = ">=1.1.0,<1.4.0" +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + [[package]] name = "tinycss2" version = "1.3.0" @@ -3979,7 +4507,22 @@ files = [ {file = "widgetsnbextension-4.0.11.tar.gz", hash = "sha256:8b22a8f1910bfd188e596fe7fc05dcbd87e810c8a4ba010bdb3da86637398474"}, ] +[[package]] +name = "zipp" +version = "3.19.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, +] + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "6d3befebd955b1f27fc8c9580d04a6eb2265fe7d62e46c36f0618ab54d5e4e59" +content-hash = "9af018e64f3a9df6f063d8299f7f0235152ef7b26756c7e19d3a0b95d076408d" diff --git a/pyproject.toml b/pyproject.toml index 9c4c7359..694c4190 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,6 @@ [tool.poetry] name = "pytket-docs" +package-mode = false version = "0.1.0" description = "" authors = ["CalMacCQ <93673602+CalMacCQ@users.noreply.github.com>"] From bee3d855a647e46f100b23aa4e0957e3d33b9a38 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:12:32 +0100 Subject: [PATCH 21/76] update .giignore and build-docs script --- .gitignore | 6 +++++- build-docs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 7036c4b2..1936c09b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,8 @@ docs/manual/jupyter_execute examples/_build/ docs/manual/jupyter_execute docs/examples/_build/ -*.ipynb_checkpoints \ No newline at end of file +*.ipynb_checkpoints +docs/build/jupyter_execute/ +docs/build/examples/_build/ +docs/build/ +docs/jupyter_execute \ No newline at end of file diff --git a/build-docs b/build-docs index d1a369f4..8b8c32a0 100755 --- a/build-docs +++ b/build-docs @@ -1,7 +1,7 @@ #!/bin/sh -cd docs/manual +cd docs/ rm -rf build/ -sphinx-build -b html . build -W +sphinx-build -b html . build From db94684f7a38f3ebed72a7f119a8ded21af8066a Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 17 Jun 2024 14:22:54 +0100 Subject: [PATCH 22/76] rename build script --- build-docs => build-docs.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename build-docs => build-docs.sh (100%) diff --git a/build-docs b/build-docs.sh similarity index 100% rename from build-docs rename to build-docs.sh From c3efce3fcf4a0709acf5552806c19b3eecd7f962 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 17 Jun 2024 15:51:46 +0100 Subject: [PATCH 23/76] fix duplicate jupyter execution bug --- docs/conf.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 00467af9..b4aeac4b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -57,6 +57,8 @@ } +nb_execution_mode = "cache" + exclude_patterns = [ "_build", "Thumbs.db", @@ -71,5 +73,7 @@ "examples/spam_example.ipynb", "examples/entanglement_swapping.ipynb", "examples/pytket-qujax-classification.ipynb", - "jupyter_execute/", + "jupyter_execute/*", + "manual/README.md", + ".jupyter_cache", ] From 90e297bad858446837954eb39569f08682fddd5c Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 17 Jun 2024 15:53:56 +0100 Subject: [PATCH 24/76] fix some headings --- docs/examples/Getting_started.ipynb | 237 ------------------ docs/examples/Getting_started.rst | 36 +++ .../examples/circuit_generation_example.ipynb | 2 +- docs/manual/manual_compiler.rst | 4 +- docs/manual/manual_index.rst | 9 - 5 files changed, 39 insertions(+), 249 deletions(-) delete mode 100644 docs/examples/Getting_started.ipynb create mode 100644 docs/examples/Getting_started.rst diff --git a/docs/examples/Getting_started.ipynb b/docs/examples/Getting_started.ipynb deleted file mode 100644 index fb1ef8d5..00000000 --- a/docs/examples/Getting_started.ipynb +++ /dev/null @@ -1,237 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "f5f6b50c-9dbc-4a85-afe2-1b1ff717d091", - "metadata": {}, - "source": [ - "# pytket examples" - ] - }, - { - "cell_type": "markdown", - "id": "e6d90a8a-4303-4d6d-9e51-ca8fcec0a2a5", - "metadata": {}, - "source": [ - "## Building a circuit with the `Circuit` class\n", - "\n", - "\n", - "You can create a circuit by creating an instance of the `Circuit` class and adding gates manually." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "32a81360-0002-4c82-9a9e-a0f90851f34b", - "metadata": {}, - "outputs": [], - "source": [ - "from pytket import Circuit\n", - "\n", - "ghz_circ = Circuit(3)\n", - "ghz_circ.H(0)\n", - "ghz_circ.CX(0, 1)\n", - "ghz_circ.CX(1, 2)\n", - "ghz_circ.add_barrier(ghz_circ.qubits)\n", - "ghz_circ.measure_all()" - ] - }, - { - "cell_type": "markdown", - "id": "147c590e-89e5-4e4e-9fb9-d10ac5689fec", - "metadata": {}, - "source": [ - "Now let's draw a nice picture of the circuit with the circuit renderer" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "92df4f4d-487c-4c56-a072-9a1d2e3c07fc", - "metadata": {}, - "outputs": [], - "source": [ - "from pytket.circuit.display import render_circuit_jupyter\n", - "\n", - "render_circuit_jupyter(ghz_circ)" - ] - }, - { - "cell_type": "markdown", - "id": "bdec557e", - "metadata": {}, - "source": [ - "See also the [Circuit construction](https://tket.quantinuum.com/user-manual/manual_circuit.html) section of the user manual." - ] - }, - { - "cell_type": "markdown", - "id": "820b0215-4ca8-450c-bc1a-62bed65e2740", - "metadata": {}, - "source": [ - "## Build a `Circuit` from a QASM file" - ] - }, - { - "cell_type": "markdown", - "id": "7c9089dc-cf4d-4efd-b90e-5485218f90a5", - "metadata": {}, - "source": [ - "Alternatively we can import a circuit from a QASM file using [pytket.qasm](https://tket.quantinuum.com/api-docs/qasm.html). There are also functions for generating a circuit from a QASM string or exporting to a qasm file.\n", - "\n", - "\n", - "Note that its also possible to import a circuit from quipper using [pytket.quipper](https://tket.quantinuum.com/api-docs/quipper.html) module." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1bf32877-f5e0-4b09-a12d-7aa8fa7d5d20", - "metadata": {}, - "outputs": [], - "source": [ - "from pytket.qasm import circuit_from_qasm\n", - "\n", - "w_state_circ = circuit_from_qasm(\"qasm/W-state.qasm\")\n", - "render_circuit_jupyter(w_state_circ)" - ] - }, - { - "cell_type": "markdown", - "id": "ef0c31c5-9203-475b-abe1-5a0b53dc5e87", - "metadata": {}, - "source": [ - "## Import a circuit from qiskit (or other SDK)\n", - "\n", - "Its possible to generate a circuit directly from a qiskit `QuantumCircuit` using the [qiskit_to_tk](https://tket.quantinuum.com/extensions/pytket-qiskit/api.html#pytket.extensions.qiskit.tk_to_qiskit) function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "23e753f2-3f6e-4a3d-a2e7-ece869561249", - "metadata": {}, - "outputs": [], - "source": [ - "from qiskit import QuantumCircuit\n", - "\n", - "qiskit_circ = QuantumCircuit(3)\n", - "qiskit_circ.h(range(3))\n", - "qiskit_circ.ccx(2, 1 ,0)\n", - "qiskit_circ.cx(0, 1)\n", - "print(qiskit_circ)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5357e5d4-e0d8-4a59-9fe8-426067f411e0", - "metadata": {}, - "outputs": [], - "source": [ - "from pytket.extensions.qiskit import qiskit_to_tk\n", - "\n", - "tket_circ = qiskit_to_tk(qiskit_circ)\n", - "\n", - "render_circuit_jupyter(tket_circ)" - ] - }, - { - "cell_type": "markdown", - "id": "7688630f-89e2-48c7-9d1e-53a9dedf088a", - "metadata": {}, - "source": [ - "Note that pytket and qiskit use opposite qubit ordering conventions. So circuits which look identical may correspond to different unitary operations." - ] - }, - { - "cell_type": "markdown", - "id": "1eee94cb-d7a2-469d-9c48-747a6e9271a1", - "metadata": {}, - "source": [ - "Circuit conversion functions are also available for [pytket-cirq](https://tket.quantinuum.com/extensions/pytket-cirq/), [pytket-pennylane](https://tket.quantinuum.com/extensions/pytket-pennylane/), [pytket-braket](https://tket.quantinuum.com/extensions/pytket-braket/) and more." - ] - }, - { - "cell_type": "markdown", - "id": "ae85113b-5f8c-43b0-93c8-dc89002deece", - "metadata": {}, - "source": [ - "## Using Backends" - ] - }, - { - "cell_type": "markdown", - "id": "f2684ec5-30d9-4003-ad6a-624ec20bd268", - "metadata": {}, - "source": [ - "In pytket a `Backend` represents an interface to a quantum device or simulator.\n", - "\n", - "We will show a simple example of running the `ghz_circ` defined above on the `AerBackend` simulator." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "42f4e1f6-2281-4780-bb11-7132c4be5313", - "metadata": {}, - "outputs": [], - "source": [ - "render_circuit_jupyter(ghz_circ)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "868189e3-197c-427b-843e-6a991d1cfec9", - "metadata": {}, - "outputs": [], - "source": [ - "from pytket.extensions.qiskit import AerBackend\n", - "\n", - "backend = AerBackend()\n", - "result = backend.run_circuit(ghz_circ)\n", - "print(result.get_counts())" - ] - }, - { - "cell_type": "markdown", - "id": "eadff4bc-a1d3-42d0-bc0e-6bf515f40715", - "metadata": {}, - "source": [ - "The `AerBackend` simulator is highly idealised having a broad gateset, and no restrictive connectivity or device noise.\n", - "\n", - "The Hadamard and CX gate are supported operations of the simulator so we can run the GHZ circuit without changing any of the operations. For more realistic cases a compiler will have to solve for the limited gateset of the target backend as well as other backend requirements." - ] - }, - { - "cell_type": "markdown", - "id": "3321855a-683b-4848-81bd-1d238626f45b", - "metadata": {}, - "source": [ - "See the [Running on Backends](https://tket.quantinuum.com/user-manual/manual_backend.html) section of the user manual and the [backends example notebook](https://tket.quantinuum.com/examples/backends_example.html) for more." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/examples/Getting_started.rst b/docs/examples/Getting_started.rst new file mode 100644 index 00000000..618dfcbd --- /dev/null +++ b/docs/examples/Getting_started.rst @@ -0,0 +1,36 @@ +Getting Started +=============== + +.. toctree:: + :caption: Example Notebooks: + :maxdepth: 2 + + ansatz_sequence_example + circuit_analysis_example + circuit_generation_example + compilation_example + conditional_gate_example + contextual_optimization + creating_backends + measurement_reduction_example + mapping_example + symbolics_example + phase_estimation + pytket-qujax_qaoa + ucc_vqe + CONTRIBUTING.md + + + + + +.. jupyter-execute:: + + from pytket import Circuit + + ghz_circ = Circuit(3) + ghz_circ.H(0) + ghz_circ.CX(0, 1) + ghz_circ.CX(1, 2) + ghz_circ.add_barrier(ghz_circ.qubits) + ghz_circ.measure_all() \ No newline at end of file diff --git a/docs/examples/circuit_generation_example.ipynb b/docs/examples/circuit_generation_example.ipynb index a3e3bbd0..4381865e 100644 --- a/docs/examples/circuit_generation_example.ipynb +++ b/docs/examples/circuit_generation_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Circuit generation"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This notebook will provide a brief introduction to some of the more advanced methods of circuit generation available in `pytket`, including:
\n", "* how to address wires and registers;
\n", "* reading in circuits from QASM and Quipper ASCII files;
\n", "* various types of 'boxes';
\n", "* composition of circuits (both 'horizontally' and 'vertically');
\n", "* use of symbolic gate parameters;
\n", "* representation of classically controlled gates."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Wires, unit IDs and registers"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Let's get started by constructing a circuit with 3 qubits and 2 classical bits:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import Circuit\n", "from pytket.circuit.display import render_circuit_jupyter as draw"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = Circuit(1, 2)\n", "print(circ.qubits)\n", "print(circ.bits)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The qubits have automatically been assigned to a register with name `q` and indices 0, 1 and 2, while the bits have been assigned to a register with name `c` and indices 0 and 1.
\n", "
\n", "We can give these units arbitrary names and indices of arbitrary dimension:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import Qubit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["new_q1 = Qubit(\"alpha\", 0)\n", "new_q2 = Qubit(\"beta\", 2, 1)\n", "circ.add_qubit(new_q1)\n", "circ.add_qubit(new_q2)\n", "print(circ.qubits)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can also add a new register of qubits in one go:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["delta_reg = circ.add_q_register(\"delta\", 2)\n", "print(circ.qubits)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Similar commands are available for classical bits.
\n", "
\n", "We can add gates to the circuit as follows:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ.CX(delta_reg[0], delta_reg[1])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This command appends a CX gate with control `q[0]` and target `q[1]`. Note that the integer arguments are automatically converted to the default unit IDs. For simple circuits it is often easiest to stick to the default register and refer to the qubits by integers. To add gates to our own named units, we simply pass the `Qubit` (or classical `Bit`) as an argument. (We can't mix the two conventions in one command, however.)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ.H(new_q1)\n", "circ.CX(Qubit(\"q\", 0), new_q2)\n", "circ.Rz(0.5, new_q2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Let's have a look at our circuit using the interactive circuit renderer:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit.display import render_circuit_jupyter as draw"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["draw(circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Exporting to and importing from standard formats"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can export a `Circuit` to a file in QASM format. Conversely, if we have such a file we can import it into `pytket`. There are some limitations on the circuits that can be converted: for example, multi-dimensional indices (as in `beta` and `gamma` above) are not allowed.
\n", "
\n", "Here is a simple example:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.qasm import circuit_from_qasm, circuit_to_qasm"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = Circuit(3, 1)\n", "circ.H(0)\n", "circ.CX(0, 1)\n", "circ.CX(1, 2)\n", "circ.Rz(0.25, 2)\n", "circ.Measure(2, 0)\n", "draw(circ)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["qasmfile = \"c.qasm\"\n", "circuit_to_qasm(circ, qasmfile)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["with open(qasmfile, encoding=\"utf-8\") as f:\n", " print(f.read())"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c1 = circuit_from_qasm(qasmfile)\n", "circ == c1"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can also import files in the Quipper ASCII format:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.quipper import circuit_from_quipper"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["quipfile = \"c.quip\"\n", "with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n", " f.write(\n", " \"\"\"Inputs: 0:Qbit, 1:Qbit\n", "QGate[\"W\"](0,1)\n", "QGate[\"omega\"](1)\n", "QGate[\"swap\"](0,1)\n", "QGate[\"W\"]*(1,0)\n", "Outputs: 0:Qbit, 1:Qbit\n", "\"\"\"\n", " )"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = circuit_from_quipper(quipfile)\n", "draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that the Quipper gates that are not supported directly in `pytket` (`W` and `omega`) are translated into equivalent sequences of `pytket` gates. See the [pytket.quipper](https://tket.quantinuum.com/api-docs/quipper.html) docs for more.
\n", "
\n", "Quipper subroutines are also supported, corresponding to `CircBox` operations in `pytket`:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n", " f.write(\n", " \"\"\"Inputs: 0:Qbit, 1:Qbit, 2:Qbit\n", "QGate[\"H\"](0)\n", "Subroutine(x2)[\"sub\", shape \"([Q,Q],())\"] (2,1) -> (2,1)\n", "QGate[\"H\"](1)\n", "Outputs: 0:Qbit, 1:Qbit, 2:Qbit \\n\n", "Subroutine: \"sub\"\n", "Shape: \"([Q,Q],())\"\n", "Controllable: no\n", "Inputs: 0:Qbit, 1:Qbit\n", "QGate[\"Y\"](0)\n", "QGate[\"not\"](1) with controls=[+0]\n", "QGate[\"Z\"](1)\n", "Outputs: 0:Qbit, 1:Qbit\n", "\"\"\"\n", " )"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = circuit_from_quipper(quipfile)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Boxes"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `CircBox` is an example of a `pytket` 'box', which is a reusable encapsulation of a circuit inside another. We can recover the circuit 'inside' the box using the `get_circuit()` method:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cmds = c.get_commands()\n", "boxed_circuit = cmds[1].op.get_circuit()\n", "draw(boxed_circuit)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `CircBox` is the most general type of box, implementing an arbitrary circuit. But `pytket` supports several other useful box types:
\n", "* [Unitary1qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary1qBox) (implementing an arbitrary $2 \\times 2$ unitary matrix);
\n", "* [Unitary2qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary2qBox) (implementing an arbitrary $4 \\times 4$ unitary matrix);
\n", "* [ExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.ExpBox) (implementing $e^{itA}$ for an arbitrary $4 \\times 4$ hermitian matrix $A$ and parameter $t$);
\n", "* [PauliExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.PauliExpBox) (implementing $e^{-\\frac{1}{2} i \\pi t (\\sigma_0 \\otimes \\sigma_1 \\otimes \\cdots)}$ for arbitrary Pauli operators $\\sigma_i \\in \\{\\mathrm{I}, \\mathrm{X}, \\mathrm{Y}, \\mathrm{Z}\\}$ and parameter $t$)."]}, {"cell_type": "markdown", "metadata": {}, "source": ["An example will illustrate how these various box types are added to a circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from math import sqrt"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["import numpy as np\n", "from pytket.circuit import CircBox, ExpBox, PauliExpBox, Unitary1qBox, Unitary2qBox\n", "from pytket.pauli import Pauli"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["boxycirc = Circuit(3)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Add a `CircBox`:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["subcirc = Circuit(2, name=\"MY BOX\")\n", "subcirc.X(0).Y(1).CZ(0, 1)\n", "cbox = CircBox(subcirc)\n", "boxycirc.add_gate(cbox, args=[Qubit(0), Qubit(1)])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Add a `Unitary1qBox`:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["m1 = np.asarray([[1 / 2, sqrt(3) / 2], [sqrt(3) / 2, -1 / 2]])\n", "m1box = Unitary1qBox(m1)\n", "boxycirc.add_unitary1qbox(m1box, 2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Add a `Unitary2qBox`:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["m2 = np.asarray([[0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0]])\n", "m2box = Unitary2qBox(m2)\n", "boxycirc.add_unitary2qbox(m2box, 1, 2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Add an `ExpBox`:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["A = np.asarray(\n", " [[1, 2, 3, 4 + 1j], [2, 0, 1j, -1], [3, -1j, 2, 1j], [4 - 1j, -1, -1j, 1]]\n", ")\n", "ebox = ExpBox(A, 0.5)\n", "boxycirc.add_expbox(ebox, 0, 1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Add a `PauliExpBox`:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pbox = PauliExpBox([Pauli.X, Pauli.Z, Pauli.X], 0.75)\n", "boxycirc.add_gate(pbox, [0, 1, 2])"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["draw(boxycirc)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Try clicking on boxes in the diagram above to get information about the underlying subroutine."]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `get_circuit()` method is available for all box types, and returns a `Circuit` object. For example if we look inside the `ExpBox`:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["draw(ebox.get_circuit())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Circuit composition"]}, {"cell_type": "markdown", "metadata": {}, "source": ["For more discussion of circuit composition see the corresponding section of the [manual](https://tket.quantinuum.com/user-manual/manual_circuit.html#composing-circuits)."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Circuits can be composed either serially, whereby wires are joined together, or in parallel, using the `append()` command.
\n", "
\n", "For a simple illustration of serial composition, let's create two circuits with compatible set of wires, and append the second to the first:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(2)\n", "c.CX(0, 1)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c1 = Circuit(2)\n", "c1.CZ(1, 0)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.append(c1)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In the above example, there was a one-to-one match between the unit IDs in the two circuits, and they were matched up accordingly. The same applied with named unit IDs:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["x, y = Qubit(\"x\"), Qubit(\"y\")"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit()\n", "c.add_qubit(x)\n", "c.add_qubit(y)\n", "c.CX(x, y)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c1 = Circuit()\n", "c1.add_qubit(x)\n", "c1.add_qubit(y)\n", "c1.CZ(y, x)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.append(c1)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["If either circuit contains wires not matching any wires in the other, those are added to the other circuit before composition:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["z = Qubit(\"z\")\n", "c1.add_qubit(z)\n", "c1.CY(y, z)\n", "c.append(c1)\n", "print(c.qubits)\n", "draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["If the sets of unit IDs for the two circuits are disjoint, then the composition is entirely parallel.
\n", "
\n", "What if we want to serially compose two circuits having different sets of `Qubit`? In that case, we can use the `rename_units()` method on one or other of them to bring them into line. This method takes a dictionary mapping current unit IDs to new one:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c2 = Circuit()\n", "c2.add_q_register(\"w\", 3)\n", "w = [Qubit(\"w\", i) for i in range(3)]\n", "c2.H(w[0]).CX(w[0], w[1]).CRz(0.25, w[1], w[2])"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.rename_units({x: w[0], y: w[1], z: w[2]})"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.append(c2)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Symbolic parameters"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Many of the gates supported by `pytket` are parametrized by one or more phase parameters, which represent rotations in multiples of $\\pi$. For example, $\\mathrm{Rz}(\\frac{1}{2})$ represents a quarter turn, i.e. a rotation of $\\pi/2$, about the Z axis. If we know the values of these parameters we can add the gates directly to our circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(1)\n", "c.Rz(0.5, 0)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["However, we may wish to construct and manipulate circuits containing such parametrized gates without specifying the values. This allows us to do calculations in a general setting, only later substituting values for the parameters.
\n", "
\n", "Thus `pytket` allows us to specify any of the parameters as symbols. All manipulations (such as combination and cancellation of gates) are performed on the symbolic representation:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from sympy import Symbol"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["a = Symbol(\"a\")\n", "c.Rz(a, 0)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["When we apply any transformation to this circuit, the symbolic parameter is preserved in the result:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.transform import Transform"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["Transform.RemoveRedundancies().apply(c)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["To substitute values for symbols, we use the `symbol_substitution()` method, supplying a dictionary from symbols to values:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.symbol_substitution({a: 0.75})"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can also substitute symbols for other symbols:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["b = Symbol(\"b\")\n", "c = Circuit(1)\n", "c.Rz(a + b, 0)\n", "c.symbol_substitution({b: 2 * a})\n", "draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Custom gates"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can define custom parametrized gates in `pytket` by first setting up a circuit containing symbolic parameters and then converting this to a parametrized operation type:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import CustomGateDef"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["a = Symbol(\"a\")\n", "b = Symbol(\"b\")\n", "setup = Circuit(3)\n", "setup.CX(0, 1)\n", "setup.Rz(a + 0.5, 2)\n", "setup.CRz(b, 0, 2)\n", "my_gate = CustomGateDef.define(\"g\", setup, [a, b])\n", "c = Circuit(4)\n", "c.add_custom_gate(my_gate, [0.2, 1.3], [0, 3, 1])\n", "draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Custom gates can also receive symbolic parameters:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["x = Symbol(\"x\")\n", "c.add_custom_gate(my_gate, [x, 1.0], [0, 1, 2])\n", "draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Decomposing boxes and custom gates"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Having defined a circuit containing custom gates, we may now want to decompose it into elementary gates. The `DecomposeBoxes()` transform allows us to do this:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["Transform.DecomposeBoxes().apply(c)\n", "draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The same transform works on circuits composed of arbitrary boxes. Let's try it on a copy of the circuit we built up earlier out of various box types."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = boxycirc.copy()\n", "Transform.DecomposeBoxes().apply(c)\n", "draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that the unitaries have been decomposed into elementary gates."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Classical controls"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Most of the examples above involve only pure quantum gates. However, `pytket` can also represent gates whose operation is conditional on one or more classical inputs.
\n", "
\n", "For example, suppose we want to run the complex circuit `c` we've just constructed, then measure qubits 0 and 1, and finally apply an $\\mathrm{Rz}(\\frac{1}{2})$ rotation to qubit 2 if and only if the measurements were 0 and 1 respectively.
\n", "
\n", "First, we'll add two classical wires to the circuit to store the measurement results:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import Bit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.add_c_register(\"m\", 2)\n", "m = [Bit(\"m\", i) for i in range(2)]"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Classically conditioned operations depend on all their inputs being 1. Since we want to condition on `m[0]` being 0, we must first apply an X gate to its qubit, and then measure:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["q = [Qubit(\"q\", i) for i in range(3)]\n", "c.X(q[0])\n", "c.Measure(q[0], m[0])\n", "c.Measure(q[1], m[1])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Finally we add the classically conditioned Rz operation, using the `add_gate()` method:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import OpType"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.add_gate(OpType.Rz, [0.5], [q[2]], condition_bits=[m[0], m[1]], condition_value=3)\n", "draw(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that many of the transforms and compilation passes will not accept circuits that contain classical controls."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Circuit generation"]},{"cell_type":"markdown","metadata":{},"source":["This notebook will provide a brief introduction to some of the more advanced methods of circuit generation available in `pytket`, including:
\n","* how to address wires and registers;
\n","* reading in circuits from QASM and Quipper ASCII files;
\n","* various types of 'boxes';
\n","* composition of circuits (both 'horizontally' and 'vertically');
\n","* use of symbolic gate parameters;
\n","* representation of classically controlled gates."]},{"cell_type":"markdown","metadata":{},"source":["## Wires, unit IDs and registers"]},{"cell_type":"markdown","metadata":{},"source":["Let's get started by constructing a circuit with 3 qubits and 2 classical bits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit\n","from pytket.circuit.display import render_circuit_jupyter as draw"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(1, 2)\n","print(circ.qubits)\n","print(circ.bits)"]},{"cell_type":"markdown","metadata":{},"source":["The qubits have automatically been assigned to a register with name `q` and indices 0, 1 and 2, while the bits have been assigned to a register with name `c` and indices 0 and 1.
\n","
\n","We can give these units arbitrary names and indices of arbitrary dimension:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Qubit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["new_q1 = Qubit(\"alpha\", 0)\n","new_q2 = Qubit(\"beta\", 2, 1)\n","circ.add_qubit(new_q1)\n","circ.add_qubit(new_q2)\n","print(circ.qubits)"]},{"cell_type":"markdown","metadata":{},"source":["We can also add a new register of qubits in one go:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["delta_reg = circ.add_q_register(\"delta\", 2)\n","print(circ.qubits)"]},{"cell_type":"markdown","metadata":{},"source":["Similar commands are available for classical bits.
\n","
\n","We can add gates to the circuit as follows:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.CX(delta_reg[0], delta_reg[1])"]},{"cell_type":"markdown","metadata":{},"source":["This command appends a CX gate with control `q[0]` and target `q[1]`. Note that the integer arguments are automatically converted to the default unit IDs. For simple circuits it is often easiest to stick to the default register and refer to the qubits by integers. To add gates to our own named units, we simply pass the `Qubit` (or classical `Bit`) as an argument. (We can't mix the two conventions in one command, however.)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.H(new_q1)\n","circ.CX(Qubit(\"q\", 0), new_q2)\n","circ.Rz(0.5, new_q2)"]},{"cell_type":"markdown","metadata":{},"source":["Let's have a look at our circuit using the interactive circuit renderer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit.display import render_circuit_jupyter as draw"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(circ)"]},{"cell_type":"markdown","metadata":{},"source":["## Exporting to and importing from standard formats"]},{"cell_type":"markdown","metadata":{},"source":["We can export a `Circuit` to a file in QASM format. Conversely, if we have such a file we can import it into `pytket`. There are some limitations on the circuits that can be converted: for example, multi-dimensional indices (as in `beta` and `gamma` above) are not allowed.
\n","
\n","Here is a simple example:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.qasm import circuit_from_qasm, circuit_to_qasm"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(3, 1)\n","circ.H(0)\n","circ.CX(0, 1)\n","circ.CX(1, 2)\n","circ.Rz(0.25, 2)\n","circ.Measure(2, 0)\n","draw(circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qasmfile = \"c.qasm\"\n","circuit_to_qasm(circ, qasmfile)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["with open(qasmfile, encoding=\"utf-8\") as f:\n"," print(f.read())"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = circuit_from_qasm(qasmfile)\n","circ == c1"]},{"cell_type":"markdown","metadata":{},"source":["We can also import files in the Quipper ASCII format:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.quipper import circuit_from_quipper"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["quipfile = \"c.quip\"\n","with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n"," f.write(\n"," \"\"\"Inputs: 0:Qbit, 1:Qbit\n","QGate[\"W\"](0,1)\n","QGate[\"omega\"](1)\n","QGate[\"swap\"](0,1)\n","QGate[\"W\"]*(1,0)\n","Outputs: 0:Qbit, 1:Qbit\n","\"\"\"\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = circuit_from_quipper(quipfile)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the Quipper gates that are not supported directly in `pytket` (`W` and `omega`) are translated into equivalent sequences of `pytket` gates. See the [pytket.quipper](https://tket.quantinuum.com/api-docs/quipper.html) docs for more.
\n","
\n","Quipper subroutines are also supported, corresponding to `CircBox` operations in `pytket`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n"," f.write(\n"," \"\"\"Inputs: 0:Qbit, 1:Qbit, 2:Qbit\n","QGate[\"H\"](0)\n","Subroutine(x2)[\"sub\", shape \"([Q,Q],())\"] (2,1) -> (2,1)\n","QGate[\"H\"](1)\n","Outputs: 0:Qbit, 1:Qbit, 2:Qbit \\n\n","Subroutine: \"sub\"\n","Shape: \"([Q,Q],())\"\n","Controllable: no\n","Inputs: 0:Qbit, 1:Qbit\n","QGate[\"Y\"](0)\n","QGate[\"not\"](1) with controls=[+0]\n","QGate[\"Z\"](1)\n","Outputs: 0:Qbit, 1:Qbit\n","\"\"\"\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = circuit_from_quipper(quipfile)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Boxes in `pytket`"]},{"cell_type":"markdown","metadata":{},"source":["The `CircBox` is an example of a `pytket` 'box', which is a reusable encapsulation of a circuit inside another. We can recover the circuit 'inside' the box using the `get_circuit()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cmds = c.get_commands()\n","boxed_circuit = cmds[1].op.get_circuit()\n","draw(boxed_circuit)"]},{"cell_type":"markdown","metadata":{},"source":["The `CircBox` is the most general type of box, implementing an arbitrary circuit. But `pytket` supports several other useful box types:
\n","* [Unitary1qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary1qBox) (implementing an arbitrary $2 \\times 2$ unitary matrix);
\n","* [Unitary2qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary2qBox) (implementing an arbitrary $4 \\times 4$ unitary matrix);
\n","* [ExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.ExpBox) (implementing $e^{itA}$ for an arbitrary $4 \\times 4$ hermitian matrix $A$ and parameter $t$);
\n","* [PauliExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.PauliExpBox) (implementing $e^{-\\frac{1}{2} i \\pi t (\\sigma_0 \\otimes \\sigma_1 \\otimes \\cdots)}$ for arbitrary Pauli operators $\\sigma_i \\in \\{\\mathrm{I}, \\mathrm{X}, \\mathrm{Y}, \\mathrm{Z}\\}$ and parameter $t$)."]},{"cell_type":"markdown","metadata":{},"source":["An example will illustrate how these various box types are added to a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from math import sqrt"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import numpy as np\n","from pytket.circuit import CircBox, ExpBox, PauliExpBox, Unitary1qBox, Unitary2qBox\n","from pytket.pauli import Pauli"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["boxycirc = Circuit(3)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `CircBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["subcirc = Circuit(2, name=\"MY BOX\")\n","subcirc.X(0).Y(1).CZ(0, 1)\n","cbox = CircBox(subcirc)\n","boxycirc.add_gate(cbox, args=[Qubit(0), Qubit(1)])"]},{"cell_type":"markdown","metadata":{},"source":["Add a `Unitary1qBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["m1 = np.asarray([[1 / 2, sqrt(3) / 2], [sqrt(3) / 2, -1 / 2]])\n","m1box = Unitary1qBox(m1)\n","boxycirc.add_unitary1qbox(m1box, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `Unitary2qBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["m2 = np.asarray([[0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0]])\n","m2box = Unitary2qBox(m2)\n","boxycirc.add_unitary2qbox(m2box, 1, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Add an `ExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["A = np.asarray(\n"," [[1, 2, 3, 4 + 1j], [2, 0, 1j, -1], [3, -1j, 2, 1j], [4 - 1j, -1, -1j, 1]]\n",")\n","ebox = ExpBox(A, 0.5)\n","boxycirc.add_expbox(ebox, 0, 1)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `PauliExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pbox = PauliExpBox([Pauli.X, Pauli.Z, Pauli.X], 0.75)\n","boxycirc.add_gate(pbox, [0, 1, 2])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(boxycirc)"]},{"cell_type":"markdown","metadata":{},"source":["Try clicking on boxes in the diagram above to get information about the underlying subroutine."]},{"cell_type":"markdown","metadata":{},"source":["The `get_circuit()` method is available for all box types, and returns a `Circuit` object. For example if we look inside the `ExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(ebox.get_circuit())"]},{"cell_type":"markdown","metadata":{},"source":["## Circuit composition"]},{"cell_type":"markdown","metadata":{},"source":["For more discussion of circuit composition see the corresponding section of the [manual](https://tket.quantinuum.com/user-manual/manual_circuit.html#composing-circuits)."]},{"cell_type":"markdown","metadata":{},"source":["Circuits can be composed either serially, whereby wires are joined together, or in parallel, using the `append()` command.
\n","
\n","For a simple illustration of serial composition, let's create two circuits with compatible set of wires, and append the second to the first:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(2)\n","c.CX(0, 1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = Circuit(2)\n","c1.CZ(1, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["In the above example, there was a one-to-one match between the unit IDs in the two circuits, and they were matched up accordingly. The same applied with named unit IDs:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["x, y = Qubit(\"x\"), Qubit(\"y\")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","c.add_qubit(x)\n","c.add_qubit(y)\n","c.CX(x, y)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = Circuit()\n","c1.add_qubit(x)\n","c1.add_qubit(y)\n","c1.CZ(y, x)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["If either circuit contains wires not matching any wires in the other, those are added to the other circuit before composition:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["z = Qubit(\"z\")\n","c1.add_qubit(z)\n","c1.CY(y, z)\n","c.append(c1)\n","print(c.qubits)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["If the sets of unit IDs for the two circuits are disjoint, then the composition is entirely parallel.
\n","
\n","What if we want to serially compose two circuits having different sets of `Qubit`? In that case, we can use the `rename_units()` method on one or other of them to bring them into line. This method takes a dictionary mapping current unit IDs to new one:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c2 = Circuit()\n","c2.add_q_register(\"w\", 3)\n","w = [Qubit(\"w\", i) for i in range(3)]\n","c2.H(w[0]).CX(w[0], w[1]).CRz(0.25, w[1], w[2])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.rename_units({x: w[0], y: w[1], z: w[2]})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c2)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Symbolic parameters"]},{"cell_type":"markdown","metadata":{},"source":["Many of the gates supported by `pytket` are parametrized by one or more phase parameters, which represent rotations in multiples of $\\pi$. For example, $\\mathrm{Rz}(\\frac{1}{2})$ represents a quarter turn, i.e. a rotation of $\\pi/2$, about the Z axis. If we know the values of these parameters we can add the gates directly to our circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(1)\n","c.Rz(0.5, 0)"]},{"cell_type":"markdown","metadata":{},"source":["However, we may wish to construct and manipulate circuits containing such parametrized gates without specifying the values. This allows us to do calculations in a general setting, only later substituting values for the parameters.
\n","
\n","Thus `pytket` allows us to specify any of the parameters as symbols. All manipulations (such as combination and cancellation of gates) are performed on the symbolic representation:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from sympy import Symbol"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = Symbol(\"a\")\n","c.Rz(a, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["When we apply any transformation to this circuit, the symbolic parameter is preserved in the result:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.transform import Transform"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.RemoveRedundancies().apply(c)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["To substitute values for symbols, we use the `symbol_substitution()` method, supplying a dictionary from symbols to values:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.symbol_substitution({a: 0.75})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can also substitute symbols for other symbols:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b = Symbol(\"b\")\n","c = Circuit(1)\n","c.Rz(a + b, 0)\n","c.symbol_substitution({b: 2 * a})\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Custom gates"]},{"cell_type":"markdown","metadata":{},"source":["We can define custom parametrized gates in `pytket` by first setting up a circuit containing symbolic parameters and then converting this to a parametrized operation type:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import CustomGateDef"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = Symbol(\"a\")\n","b = Symbol(\"b\")\n","setup = Circuit(3)\n","setup.CX(0, 1)\n","setup.Rz(a + 0.5, 2)\n","setup.CRz(b, 0, 2)\n","my_gate = CustomGateDef.define(\"g\", setup, [a, b])\n","c = Circuit(4)\n","c.add_custom_gate(my_gate, [0.2, 1.3], [0, 3, 1])\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Custom gates can also receive symbolic parameters:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["x = Symbol(\"x\")\n","c.add_custom_gate(my_gate, [x, 1.0], [0, 1, 2])\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Decomposing boxes and custom gates"]},{"cell_type":"markdown","metadata":{},"source":["Having defined a circuit containing custom gates, we may now want to decompose it into elementary gates. The `DecomposeBoxes()` transform allows us to do this:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.DecomposeBoxes().apply(c)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["The same transform works on circuits composed of arbitrary boxes. Let's try it on a copy of the circuit we built up earlier out of various box types."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = boxycirc.copy()\n","Transform.DecomposeBoxes().apply(c)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the unitaries have been decomposed into elementary gates."]},{"cell_type":"markdown","metadata":{},"source":["## Classical controls"]},{"cell_type":"markdown","metadata":{},"source":["Most of the examples above involve only pure quantum gates. However, `pytket` can also represent gates whose operation is conditional on one or more classical inputs.
\n","
\n","For example, suppose we want to run the complex circuit `c` we've just constructed, then measure qubits 0 and 1, and finally apply an $\\mathrm{Rz}(\\frac{1}{2})$ rotation to qubit 2 if and only if the measurements were 0 and 1 respectively.
\n","
\n","First, we'll add two classical wires to the circuit to store the measurement results:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Bit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.add_c_register(\"m\", 2)\n","m = [Bit(\"m\", i) for i in range(2)]"]},{"cell_type":"markdown","metadata":{},"source":["Classically conditioned operations depend on all their inputs being 1. Since we want to condition on `m[0]` being 0, we must first apply an X gate to its qubit, and then measure:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["q = [Qubit(\"q\", i) for i in range(3)]\n","c.X(q[0])\n","c.Measure(q[0], m[0])\n","c.Measure(q[1], m[1])"]},{"cell_type":"markdown","metadata":{},"source":["Finally we add the classically conditioned Rz operation, using the `add_gate()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import OpType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.add_gate(OpType.Rz, [0.5], [q[2]], condition_bits=[m[0], m[1]], condition_value=3)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that many of the transforms and compilation passes will not accept circuits that contain classical controls."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/manual/manual_compiler.rst b/docs/manual/manual_compiler.rst index 0b7a6029..6b84136a 100644 --- a/docs/manual/manual_compiler.rst +++ b/docs/manual/manual_compiler.rst @@ -14,8 +14,8 @@ The primary goals of compilation are two-fold: solving the constraints of the :p Each compiler pass inherits from the :py:class:`~pytket.passes.BasePass` class, capturing a method of transforming a :py:class:`~pytket.circuit.Circuit`. The main functionality is built into the :py:meth:`BasePass.apply()` method, which applies the transformation to a :py:class:`~pytket.circuit.Circuit` in-place. The :py:meth:`~pytket.backends.Backend.get_compiled_circuit()` method is a wrapper around the :py:meth:`~pytket.passes.BasePass.apply()` from the :py:class:`~pytket.backends.Backend` 's recommended pass sequence. This chapter will explore these compiler passes, the different kinds of constraints they are used to solve and optimisations they apply, to help you identify which ones are appropriate for a given task. -Predicates ----------- +Compilation Predicates +---------------------- .. Predicates capture properties a circuit could satisfy .. Primarily used to describe requirements of the backends diff --git a/docs/manual/manual_index.rst b/docs/manual/manual_index.rst index ca3202b4..cf0041fc 100644 --- a/docs/manual/manual_index.rst +++ b/docs/manual/manual_index.rst @@ -13,15 +13,6 @@ Manual manual_assertion.rst manual_zx.rst -.. toctree:: - :caption: pytket documentation: - :maxdepth: 1 - - pytket API docs - pytket extensions - Manual - Example notebooks - TKET website Indices and Tables ================== From 65c105f2fcc583570348316f5e0e59bdeff211d1 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 17 Jun 2024 15:54:42 +0100 Subject: [PATCH 25/76] ingore cached files --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1936c09b..5fb31912 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ docs/examples/_build/ docs/build/jupyter_execute/ docs/build/examples/_build/ docs/build/ -docs/jupyter_execute \ No newline at end of file +docs/jupyter_execute +docs/.jupyter_cache \ No newline at end of file From 3c88171a406104eab1fab0fddcc045c2eebf55c3 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 17 Jun 2024 18:59:55 +0100 Subject: [PATCH 26/76] collapse secondary sidebars --- docs/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index b4aeac4b..c0df419f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,8 +34,10 @@ "icon": "fa-brands fa-github", } ], + "secondary_sidebar_items": {"manual/**": [], "examples/**": []}, } + html_static_path = ["_static"] html_css_files = ["custom.css"] From 954778599c9dfc5639ada5d587cbcc7b8efbbc8a Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 4 Jul 2024 15:45:32 +0100 Subject: [PATCH 27/76] use quantinuum theming --- .gitmodules | 4 + docs/_static/custom.css | 19 + docs/_static/nav-config.js | 46 + docs/conf.py | 31 +- docs/quantinuum-sphinx/.nojekyll | 0 .../_static/assets/github.svg | 3 + .../_static/assets/slack.svg | 1 + .../_static/assets/stack.svg | 1 + .../quantinuum-sphinx/_static/assets/test.svg | 0 .../quantinuum-sphinx/_static/index.global.js | 142 + docs/quantinuum-sphinx/_static/styles.css | 16 + docs/quantinuum-sphinx/_static/tailwind.css | 3054 +++++++++++++++++ docs/quantinuum-sphinx/_static/tokens.css | 63 + docs/quantinuum-sphinx/_templates/page.html | 313 ++ 14 files changed, 3668 insertions(+), 25 deletions(-) create mode 100644 .gitmodules create mode 100644 docs/_static/nav-config.js create mode 100644 docs/quantinuum-sphinx/.nojekyll create mode 100644 docs/quantinuum-sphinx/_static/assets/github.svg create mode 100644 docs/quantinuum-sphinx/_static/assets/slack.svg create mode 100644 docs/quantinuum-sphinx/_static/assets/stack.svg create mode 100644 docs/quantinuum-sphinx/_static/assets/test.svg create mode 100644 docs/quantinuum-sphinx/_static/index.global.js create mode 100644 docs/quantinuum-sphinx/_static/styles.css create mode 100644 docs/quantinuum-sphinx/_static/tailwind.css create mode 100644 docs/quantinuum-sphinx/_static/tokens.css create mode 100644 docs/quantinuum-sphinx/_templates/page.html diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e2d9df30 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "quantinuum-sphinx"] + path = docs/quantinuum-sphinx + url = https://github.com/CQCL/quantinuum-sphinx.git + branch = dist diff --git a/docs/_static/custom.css b/docs/_static/custom.css index c2a2148c..4beccdbe 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -35,4 +35,23 @@ html[data-theme=light] { .sig-name { font-size: 1.25rem; +} + +.navbar .navbar-brand img { + height: 100%; + max-width: 100%; + width: auto; +} + +.navbar .navbar-brand { + align-items: center; + display: flex; + flex-shrink: 0; + gap: .5rem; + height: var(--pst-header-height); + margin: 0; + max-height: var(--pst-header-height); + padding: .5rem 0; + position: relative; + width: auto; } \ No newline at end of file diff --git a/docs/_static/nav-config.js b/docs/_static/nav-config.js new file mode 100644 index 00000000..00e998ed --- /dev/null +++ b/docs/_static/nav-config.js @@ -0,0 +1,46 @@ +const navConfig = { + + "navTextLinks": [ + { + "title": "API Docs", + "href": "../api-docs", + "pathMatch": "somewhere", + }, + { + "title": "Examples", + "href": "../examples", + "pathMatch": "somewhere", + }, + { + "title": "Blog", + "href": "../blog/", + "pathMatch": "somewhere", + }, + { + "title": "User Manual", + "href": "../user-manual", + "pathMatch": "somewhere", + }, + ], + "navProductName": "TKET", + "navIconLinks": [ + { + "title": "TKET Github", + "href": "https://github.com/CQCL/tket", + "pathMatch": "somewhere", + "iconImageURL": "_static/assets/github.svg", + }, + { + "title": "TKET Slack Channel", + "href": "https://tketusers.slack.com/", + "pathMatch": "somewhere", + "iconImageURL": "_static/assets/slack.svg", + }, + { + "title": "TKET Stack Exchange", + "href": "https://quantumcomputing.stackexchange.com/questions/tagged/pytket", + "pathMatch": "somewhere", + "iconImageURL": "_static/assets/stack.svg", + }, + ], +} \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index c0df419f..536abaf4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,6 +6,9 @@ copyright = "2020-2024, Quantinuum" author = "Quantinuum" + +html_title = "User guide" + extensions = [ "sphinx.ext.autodoc", "sphinx.ext.autosummary", @@ -17,32 +20,10 @@ "myst_nb", ] -html_theme = "pydata_sphinx_theme" - -html_title = "Manual" - -html_theme_options = { - "navigation_with_keys": True, - "logo": { - "image_light": "_static/Quantinuum_logo_black.png", - "image_dark": "_static/Quantinuum_logo_white.png", - }, - "icon_links": [ - { - "name": "GitHub", - "url": "https://github.com/CQCL/tket", - "icon": "fa-brands fa-github", - } - ], - "secondary_sidebar_items": {"manual/**": [], "examples/**": []}, -} - - -html_static_path = ["_static"] - -html_css_files = ["custom.css"] +html_theme = "furo" +templates_path = ["./quantinuum-sphinx/_templates/"] +html_static_path = ["./quantinuum-sphinx/_static/", "_static/"] -# -- Extension configuration ------------------------------------------------- pytketdoc_base = "https://tket.quantinuum.com/api-docs/" diff --git a/docs/quantinuum-sphinx/.nojekyll b/docs/quantinuum-sphinx/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/docs/quantinuum-sphinx/_static/assets/github.svg b/docs/quantinuum-sphinx/_static/assets/github.svg new file mode 100644 index 00000000..a8d11740 --- /dev/null +++ b/docs/quantinuum-sphinx/_static/assets/github.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/quantinuum-sphinx/_static/assets/slack.svg b/docs/quantinuum-sphinx/_static/assets/slack.svg new file mode 100644 index 00000000..4f101e0f --- /dev/null +++ b/docs/quantinuum-sphinx/_static/assets/slack.svg @@ -0,0 +1 @@ + diff --git a/docs/quantinuum-sphinx/_static/assets/stack.svg b/docs/quantinuum-sphinx/_static/assets/stack.svg new file mode 100644 index 00000000..74d5333c --- /dev/null +++ b/docs/quantinuum-sphinx/_static/assets/stack.svg @@ -0,0 +1 @@ + diff --git a/docs/quantinuum-sphinx/_static/assets/test.svg b/docs/quantinuum-sphinx/_static/assets/test.svg new file mode 100644 index 00000000..e69de29b diff --git a/docs/quantinuum-sphinx/_static/index.global.js b/docs/quantinuum-sphinx/_static/index.global.js new file mode 100644 index 00000000..97f0a295 --- /dev/null +++ b/docs/quantinuum-sphinx/_static/index.global.js @@ -0,0 +1,142 @@ +"use strict";(()=>{var Fy=Object.create;var $s=Object.defineProperty,By=Object.defineProperties,Ny=Object.getOwnPropertyDescriptor,zy=Object.getOwnPropertyDescriptors,Uy=Object.getOwnPropertyNames,El=Object.getOwnPropertySymbols,qy=Object.getPrototypeOf,Ks=Object.prototype.hasOwnProperty,Sc=Object.prototype.propertyIsEnumerable;var Ic=(e,t,a)=>t in e?$s(e,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[t]=a,w=(e,t)=>{for(var a in t||(t={}))Ks.call(t,a)&&Ic(e,a,t[a]);if(El)for(var a of El(t))Sc.call(t,a)&&Ic(e,a,t[a]);return e},b=(e,t)=>By(e,zy(t));var A=(e,t)=>{var a={};for(var r in e)Ks.call(e,r)&&t.indexOf(r)<0&&(a[r]=e[r]);if(e!=null&&El)for(var r of El(e))t.indexOf(r)<0&&Sc.call(e,r)&&(a[r]=e[r]);return a};var ka=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Hy=(e,t,a,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Uy(t))!Ks.call(e,o)&&o!==a&&$s(e,o,{get:()=>t[o],enumerable:!(r=Ny(t,o))||r.enumerable});return e};var N=(e,t,a)=>(a=e!=null?Fy(qy(e)):{},Hy(t||!e||!e.__esModule?$s(a,"default",{value:e,enumerable:!0}):a,e));var Pe=(e,t,a)=>new Promise((r,o)=>{var n=s=>{try{u(a.next(s))}catch(i){o(i)}},l=s=>{try{u(a.throw(s))}catch(i){o(i)}},u=s=>s.done?r(s.value):Promise.resolve(s.value).then(n,l);u((a=a.apply(e,t)).next())});var Fc=ka(Y=>{"use strict";var mn=Symbol.for("react.element"),Vy=Symbol.for("react.portal"),jy=Symbol.for("react.fragment"),Wy=Symbol.for("react.strict_mode"),Gy=Symbol.for("react.profiler"),Zy=Symbol.for("react.provider"),$y=Symbol.for("react.context"),Ky=Symbol.for("react.forward_ref"),Xy=Symbol.for("react.suspense"),Qy=Symbol.for("react.memo"),Yy=Symbol.for("react.lazy"),kc=Symbol.iterator;function Jy(e){return e===null||typeof e!="object"?null:(e=kc&&e[kc]||e["@@iterator"],typeof e=="function"?e:null)}var Tc={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},_c=Object.assign,Rc={};function po(e,t,a){this.props=e,this.context=t,this.refs=Rc,this.updater=a||Tc}po.prototype.isReactComponent={};po.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};po.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Dc(){}Dc.prototype=po.prototype;function Qs(e,t,a){this.props=e,this.context=t,this.refs=Rc,this.updater=a||Tc}var Ys=Qs.prototype=new Dc;Ys.constructor=Qs;_c(Ys,po.prototype);Ys.isPureReactComponent=!0;var Pc=Array.isArray,bc=Object.prototype.hasOwnProperty,Js={current:null},Ec={key:!0,ref:!0,__self:!0,__source:!0};function Ac(e,t,a){var r,o={},n=null,l=null;if(t!=null)for(r in t.ref!==void 0&&(l=t.ref),t.key!==void 0&&(n=""+t.key),t)bc.call(t,r)&&!Ec.hasOwnProperty(r)&&(o[r]=t[r]);var u=arguments.length-2;if(u===1)o.children=a;else if(1{"use strict";Bc.exports=Fc()});var Zc=ka(ie=>{"use strict";function oi(e,t){var a=e.length;e.push(t);e:for(;0>>1,o=e[r];if(0>>1;rBl(u,a))sBl(i,u)?(e[r]=i,e[s]=a,r=s):(e[r]=u,e[l]=a,r=l);else if(sBl(i,a))e[r]=i,e[s]=a,r=s;else break e}}return t}function Bl(e,t){var a=e.sortIndex-t.sortIndex;return a!==0?a:e.id-t.id}typeof performance=="object"&&typeof performance.now=="function"?(Nc=performance,ie.unstable_now=function(){return Nc.now()}):(ti=Date,zc=ti.now(),ie.unstable_now=function(){return ti.now()-zc});var Nc,ti,zc,ia=[],Ga=[],oL=1,bt=null,je=3,Ul=!1,kr=!1,gn=!1,Hc=typeof setTimeout=="function"?setTimeout:null,Vc=typeof clearTimeout=="function"?clearTimeout:null,Uc=typeof setImmediate!="undefined"?setImmediate:null;typeof navigator!="undefined"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function ni(e){for(var t=Gt(Ga);t!==null;){if(t.callback===null)zl(Ga);else if(t.startTime<=e)zl(Ga),t.sortIndex=t.expirationTime,oi(ia,t);else break;t=Gt(Ga)}}function li(e){if(gn=!1,ni(e),!kr)if(Gt(ia)!==null)kr=!0,si(ui);else{var t=Gt(Ga);t!==null&&ii(li,t.startTime-e)}}function ui(e,t){kr=!1,gn&&(gn=!1,Vc(vn),vn=-1),Ul=!0;var a=je;try{for(ni(t),bt=Gt(ia);bt!==null&&(!(bt.expirationTime>t)||e&&!Gc());){var r=bt.callback;if(typeof r=="function"){bt.callback=null,je=bt.priorityLevel;var o=r(bt.expirationTime<=t);t=ie.unstable_now(),typeof o=="function"?bt.callback=o:bt===Gt(ia)&&zl(ia),ni(t)}else zl(ia);bt=Gt(ia)}if(bt!==null)var n=!0;else{var l=Gt(Ga);l!==null&&ii(li,l.startTime-t),n=!1}return n}finally{bt=null,je=a,Ul=!1}}var ql=!1,Nl=null,vn=-1,jc=5,Wc=-1;function Gc(){return!(ie.unstable_now()-Wce||125r?(e.sortIndex=a,oi(Ga,e),Gt(ia)===null&&e===Gt(Ga)&&(gn?(Vc(vn),vn=-1):gn=!0,ii(li,a-r))):(e.sortIndex=o,oi(ia,e),kr||Ul||(kr=!0,si(ui))),e};ie.unstable_shouldYield=Gc;ie.unstable_wrapCallback=function(e){var t=je;return function(){var a=je;je=t;try{return e.apply(this,arguments)}finally{je=a}}}});var Kc=ka((VS,$c)=>{"use strict";$c.exports=Zc()});var Jh=ka(Pt=>{"use strict";var nL=G(),St=Kc();function _(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,a=1;at}return!1}function rt(e,t,a,r,o,n,l){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=o,this.mustUseProperty=a,this.propertyName=e,this.type=t,this.sanitizeURL=n,this.removeEmptyString=l}var He={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){He[e]=new rt(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];He[t]=new rt(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){He[e]=new rt(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){He[e]=new rt(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){He[e]=new rt(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){He[e]=new rt(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){He[e]=new rt(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){He[e]=new rt(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){He[e]=new rt(e,5,!1,e.toLowerCase(),null,!1,!1)});var Sd=/[\-:]([a-z])/g;function kd(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Sd,kd);He[t]=new rt(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Sd,kd);He[t]=new rt(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Sd,kd);He[t]=new rt(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){He[e]=new rt(e,1,!1,e.toLowerCase(),null,!1,!1)});He.xlinkHref=new rt("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){He[e]=new rt(e,1,!1,e.toLowerCase(),null,!0,!0)});function Pd(e,t,a,r){var o=He.hasOwnProperty(t)?He[t]:null;(o!==null?o.type!==0:r||!(2u||o[l]!==n[u]){var s=` +`+o[l].replace(" at new "," at ");return e.displayName&&s.includes("")&&(s=s.replace("",e.displayName)),s}while(1<=l&&0<=u);break}}}finally{fi=!1,Error.prepareStackTrace=a}return(e=e?e.displayName||e.name:"")?Pn(e):""}function dL(e){switch(e.tag){case 5:return Pn(e.type);case 16:return Pn("Lazy");case 13:return Pn("Suspense");case 19:return Pn("SuspenseList");case 0:case 2:case 15:return e=ci(e.type,!1),e;case 11:return e=ci(e.type.render,!1),e;case 1:return e=ci(e.type,!0),e;default:return""}}function Fi(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case vo:return"Fragment";case go:return"Portal";case Ei:return"Profiler";case Md:return"StrictMode";case Ai:return"Suspense";case Oi:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case om:return(e.displayName||"Context")+".Consumer";case rm:return(e._context.displayName||"Context")+".Provider";case Td:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case _d:return t=e.displayName||null,t!==null?t:Fi(e.type)||"Memo";case $a:t=e._payload,e=e._init;try{return Fi(e(t))}catch(a){}}return null}function fL(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Fi(t);case 8:return t===Md?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function sr(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function lm(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function cL(e){var t=lm(e)?"checked":"value",a=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof a!="undefined"&&typeof a.get=="function"&&typeof a.set=="function"){var o=a.get,n=a.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(l){r=""+l,n.call(this,l)}}),Object.defineProperty(e,t,{enumerable:a.enumerable}),{getValue:function(){return r},setValue:function(l){r=""+l},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Vl(e){e._valueTracker||(e._valueTracker=cL(e))}function um(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var a=t.getValue(),r="";return e&&(r=lm(e)?e.checked?"true":"false":e.value),e=r,e!==a?(t.setValue(e),!0):!1}function vu(e){if(e=e||(typeof document!="undefined"?document:void 0),typeof e=="undefined")return null;try{return e.activeElement||e.body}catch(t){return e.body}}function Bi(e,t){var a=t.checked;return Ie({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:a!=null?a:e._wrapperState.initialChecked})}function Jc(e,t){var a=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;a=sr(t.value!=null?t.value:a),e._wrapperState={initialChecked:r,initialValue:a,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function sm(e,t){t=t.checked,t!=null&&Pd(e,"checked",t,!1)}function Ni(e,t){sm(e,t);var a=sr(t.value),r=t.type;if(a!=null)r==="number"?(a===0&&e.value===""||e.value!=a)&&(e.value=""+a):e.value!==""+a&&(e.value=""+a);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?zi(e,t.type,a):t.hasOwnProperty("defaultValue")&&zi(e,t.type,sr(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function ep(e,t,a){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,a||t===e.value||(e.value=t),e.defaultValue=t}a=e.name,a!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,a!==""&&(e.name=a)}function zi(e,t,a){(t!=="number"||vu(e.ownerDocument)!==e)&&(a==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+a&&(e.defaultValue=""+a))}var Mn=Array.isArray;function To(e,t,a,r){if(e=e.options,t){t={};for(var o=0;o"+t.valueOf().toString()+"",t=jl.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Un(e,t){if(t){var a=e.firstChild;if(a&&a===e.lastChild&&a.nodeType===3){a.nodeValue=t;return}}e.textContent=t}var Rn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},pL=["Webkit","ms","Moz","O"];Object.keys(Rn).forEach(function(e){pL.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Rn[t]=Rn[e]})});function cm(e,t,a){return t==null||typeof t=="boolean"||t===""?"":a||typeof t!="number"||t===0||Rn.hasOwnProperty(e)&&Rn[e]?(""+t).trim():t+"px"}function pm(e,t){e=e.style;for(var a in t)if(t.hasOwnProperty(a)){var r=a.indexOf("--")===0,o=cm(a,t[a],r);a==="float"&&(a="cssFloat"),r?e.setProperty(a,o):e[a]=o}}var mL=Ie({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Hi(e,t){if(t){if(mL[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(_(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(_(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(_(61))}if(t.style!=null&&typeof t.style!="object")throw Error(_(62))}}function Vi(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ji=null;function Rd(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Wi=null,_o=null,Ro=null;function rp(e){if(e=ol(e)){if(typeof Wi!="function")throw Error(_(280));var t=e.stateNode;t&&(t=Wu(t),Wi(e.stateNode,e.type,t))}}function mm(e){_o?Ro?Ro.push(e):Ro=[e]:_o=e}function hm(){if(_o){var e=_o,t=Ro;if(Ro=_o=null,rp(e),t)for(e=0;e>>=0,e===0?32:31-(kL(e)/PL|0)|0}var Wl=64,Gl=4194304;function Tn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function wu(e,t){var a=e.pendingLanes;if(a===0)return 0;var r=0,o=e.suspendedLanes,n=e.pingedLanes,l=a&268435455;if(l!==0){var u=l&~o;u!==0?r=Tn(u):(n&=l,n!==0&&(r=Tn(n)))}else l=a&~o,l!==0?r=Tn(l):n!==0&&(r=Tn(n));if(r===0)return 0;if(t!==0&&t!==r&&!(t&o)&&(o=r&-r,n=t&-t,o>=n||o===16&&(n&4194240)!==0))return t;if(r&4&&(r|=a&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0a;a++)t.push(e);return t}function al(e,t,a){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Qt(t),e[t]=a}function RL(e,t){var a=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=bn),cp=" ",pp=!1;function Om(e,t){switch(e){case"keyup":return ow.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Fm(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var xo=!1;function lw(e,t){switch(e){case"compositionend":return Fm(t);case"keypress":return t.which!==32?null:(pp=!0,cp);case"textInput":return e=t.data,e===cp&&pp?null:e;default:return null}}function uw(e,t){if(xo)return e==="compositionend"||!Nd&&Om(e,t)?(e=Em(),su=Od=Ya=null,xo=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:a,offset:t-e};e=r}e:{for(;a;){if(a.nextSibling){a=a.nextSibling;break e}a=a.parentNode}a=void 0}a=gp(a)}}function Um(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Um(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function qm(){for(var e=window,t=vu();t instanceof e.HTMLIFrameElement;){try{var a=typeof t.contentWindow.location.href=="string"}catch(r){a=!1}if(a)e=t.contentWindow;else break;t=vu(e.document)}return t}function zd(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function gw(e){var t=qm(),a=e.focusedElem,r=e.selectionRange;if(t!==a&&a&&a.ownerDocument&&Um(a.ownerDocument.documentElement,a)){if(r!==null&&zd(a)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in a)a.selectionStart=t,a.selectionEnd=Math.min(e,a.value.length);else if(e=(t=a.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var o=a.textContent.length,n=Math.min(r.start,o);r=r.end===void 0?n:Math.min(r.end,o),!e.extend&&n>r&&(o=r,r=n,n=o),o=vp(a,n);var l=vp(a,r);o&&l&&(e.rangeCount!==1||e.anchorNode!==o.node||e.anchorOffset!==o.offset||e.focusNode!==l.node||e.focusOffset!==l.offset)&&(t=t.createRange(),t.setStart(o.node,o.offset),e.removeAllRanges(),n>r?(e.addRange(t),e.extend(l.node,l.offset)):(t.setEnd(l.node,l.offset),e.addRange(t)))}}for(t=[],e=a;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof a.focus=="function"&&a.focus(),a=0;a=document.documentMode,yo=null,Qi=null,An=null,Yi=!1;function xp(e,t,a){var r=a.window===a?a.document:a.nodeType===9?a:a.ownerDocument;Yi||yo==null||yo!==vu(r)||(r=yo,"selectionStart"in r&&zd(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),An&&Gn(An,r)||(An=r,r=Su(Qi,"onSelect"),0Co||(e.current=od[Co],od[Co]=null,Co--)}function de(e,t){Co++,od[Co]=e.current,e.current=t}var ir={},$e=fr(ir),dt=fr(!1),Er=ir;function Oo(e,t){var a=e.type.contextTypes;if(!a)return ir;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var o={},n;for(n in a)o[n]=t[n];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function ft(e){return e=e.childContextTypes,e!=null}function Pu(){he(dt),he($e)}function Mp(e,t,a){if($e.current!==ir)throw Error(_(168));de($e,t),de(dt,a)}function Xm(e,t,a){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return a;r=r.getChildContext();for(var o in r)if(!(o in t))throw Error(_(108,fL(e)||"Unknown",o));return Ie({},a,r)}function Mu(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||ir,Er=$e.current,de($e,e),de(dt,dt.current),!0}function Tp(e,t,a){var r=e.stateNode;if(!r)throw Error(_(169));a?(e=Xm(e,t,Er),r.__reactInternalMemoizedMergedChildContext=e,he(dt),he($e),de($e,e)):he(dt),de(dt,a)}var Ma=null,Gu=!1,Ii=!1;function Qm(e){Ma===null?Ma=[e]:Ma.push(e)}function Pw(e){Gu=!0,Qm(e)}function cr(){if(!Ii&&Ma!==null){Ii=!0;var e=0,t=ne;try{var a=Ma;for(ne=1;e>=l,o-=l,Ta=1<<32-Qt(t)+o|a<S?(M=k,k=null):M=k.sibling;var P=p(m,k,h[S],y);if(P===null){k===null&&(k=M);break}e&&k&&P.alternate===null&&t(m,k),c=n(P,c,S),I===null?C=P:I.sibling=P,I=P,k=M}if(S===h.length)return a(m,k),Le&&Pr(m,S),C;if(k===null){for(;SS?(M=k,k=null):M=k.sibling;var U=p(m,k,P.value,y);if(U===null){k===null&&(k=M);break}e&&k&&U.alternate===null&&t(m,k),c=n(U,c,S),I===null?C=U:I.sibling=U,I=U,k=M}if(P.done)return a(m,k),Le&&Pr(m,S),C;if(k===null){for(;!P.done;S++,P=h.next())P=f(m,P.value,y),P!==null&&(c=n(P,c,S),I===null?C=P:I.sibling=P,I=P);return Le&&Pr(m,S),C}for(k=r(m,k);!P.done;S++,P=h.next())P=v(k,m,S,P.value,y),P!==null&&(e&&P.alternate!==null&&k.delete(P.key===null?S:P.key),c=n(P,c,S),I===null?C=P:I.sibling=P,I=P);return e&&k.forEach(function(j){return t(m,j)}),Le&&Pr(m,S),C}function L(m,c,h,y){if(typeof h=="object"&&h!==null&&h.type===vo&&h.key===null&&(h=h.props.children),typeof h=="object"&&h!==null){switch(h.$$typeof){case Hl:e:{for(var C=h.key,I=c;I!==null;){if(I.key===C){if(C=h.type,C===vo){if(I.tag===7){a(m,I.sibling),c=o(I,h.props.children),c.return=m,m=c;break e}}else if(I.elementType===C||typeof C=="object"&&C!==null&&C.$$typeof===$a&&Dp(C)===I.type){a(m,I.sibling),c=o(I,h.props),c.ref=Cn(m,I,h),c.return=m,m=c;break e}a(m,I);break}else t(m,I);I=I.sibling}h.type===vo?(c=br(h.props.children,m.mode,y,h.key),c.return=m,m=c):(y=gu(h.type,h.key,h.props,null,m.mode,y),y.ref=Cn(m,c,h),y.return=m,m=y)}return l(m);case go:e:{for(I=h.key;c!==null;){if(c.key===I)if(c.tag===4&&c.stateNode.containerInfo===h.containerInfo&&c.stateNode.implementation===h.implementation){a(m,c.sibling),c=o(c,h.children||[]),c.return=m,m=c;break e}else{a(m,c);break}else t(m,c);c=c.sibling}c=Di(h,m.mode,y),c.return=m,m=c}return l(m);case $a:return I=h._init,L(m,c,I(h._payload),y)}if(Mn(h))return x(m,c,h,y);if(xn(h))return g(m,c,h,y);ru(m,h)}return typeof h=="string"&&h!==""||typeof h=="number"?(h=""+h,c!==null&&c.tag===6?(a(m,c.sibling),c=o(c,h),c.return=m,m=c):(a(m,c),c=Ri(h,m.mode,y),c.return=m,m=c),l(m)):a(m,c)}return L}var Bo=th(!0),ah=th(!1),Ru=fr(null),Du=null,ko=null,Vd=null;function jd(){Vd=ko=Du=null}function Wd(e){var t=Ru.current;he(Ru),e._currentValue=t}function ud(e,t,a){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===a)break;e=e.return}}function bo(e,t){Du=e,Vd=ko=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(it=!0),e.firstContext=null)}function Bt(e){var t=e._currentValue;if(Vd!==e)if(e={context:e,memoizedValue:t,next:null},ko===null){if(Du===null)throw Error(_(308));ko=e,Du.dependencies={lanes:0,firstContext:e}}else ko=ko.next=e;return t}var _r=null;function Gd(e){_r===null?_r=[e]:_r.push(e)}function rh(e,t,a,r){var o=t.interleaved;return o===null?(a.next=a,Gd(t)):(a.next=o.next,o.next=a),t.interleaved=a,Ea(e,r)}function Ea(e,t){e.lanes|=t;var a=e.alternate;for(a!==null&&(a.lanes|=t),a=e,e=e.return;e!==null;)e.childLanes|=t,a=e.alternate,a!==null&&(a.childLanes|=t),a=e,e=e.return;return a.tag===3?a.stateNode:null}var Ka=!1;function Zd(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function oh(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ra(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function or(e,t,a){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,re&2){var o=r.pending;return o===null?t.next=t:(t.next=o.next,o.next=t),r.pending=t,Ea(e,a)}return o=r.interleaved,o===null?(t.next=t,Gd(r)):(t.next=o.next,o.next=t),r.interleaved=t,Ea(e,a)}function du(e,t,a){if(t=t.updateQueue,t!==null&&(t=t.shared,(a&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,a|=r,t.lanes=a,bd(e,a)}}function bp(e,t){var a=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,a===r)){var o=null,n=null;if(a=a.firstBaseUpdate,a!==null){do{var l={eventTime:a.eventTime,lane:a.lane,tag:a.tag,payload:a.payload,callback:a.callback,next:null};n===null?o=n=l:n=n.next=l,a=a.next}while(a!==null);n===null?o=n=t:n=n.next=t}else o=n=t;a={baseState:r.baseState,firstBaseUpdate:o,lastBaseUpdate:n,shared:r.shared,effects:r.effects},e.updateQueue=a;return}e=a.lastBaseUpdate,e===null?a.firstBaseUpdate=t:e.next=t,a.lastBaseUpdate=t}function bu(e,t,a,r){var o=e.updateQueue;Ka=!1;var n=o.firstBaseUpdate,l=o.lastBaseUpdate,u=o.shared.pending;if(u!==null){o.shared.pending=null;var s=u,i=s.next;s.next=null,l===null?n=i:l.next=i,l=s;var d=e.alternate;d!==null&&(d=d.updateQueue,u=d.lastBaseUpdate,u!==l&&(u===null?d.firstBaseUpdate=i:u.next=i,d.lastBaseUpdate=s))}if(n!==null){var f=o.baseState;l=0,d=i=s=null,u=n;do{var p=u.lane,v=u.eventTime;if((r&p)===p){d!==null&&(d=d.next={eventTime:v,lane:0,tag:u.tag,payload:u.payload,callback:u.callback,next:null});e:{var x=e,g=u;switch(p=t,v=a,g.tag){case 1:if(x=g.payload,typeof x=="function"){f=x.call(v,f,p);break e}f=x;break e;case 3:x.flags=x.flags&-65537|128;case 0:if(x=g.payload,p=typeof x=="function"?x.call(v,f,p):x,p==null)break e;f=Ie({},f,p);break e;case 2:Ka=!0}}u.callback!==null&&u.lane!==0&&(e.flags|=64,p=o.effects,p===null?o.effects=[u]:p.push(u))}else v={eventTime:v,lane:p,tag:u.tag,payload:u.payload,callback:u.callback,next:null},d===null?(i=d=v,s=f):d=d.next=v,l|=p;if(u=u.next,u===null){if(u=o.shared.pending,u===null)break;p=u,u=p.next,p.next=null,o.lastBaseUpdate=p,o.shared.pending=null}}while(!0);if(d===null&&(s=f),o.baseState=s,o.firstBaseUpdate=i,o.lastBaseUpdate=d,t=o.shared.interleaved,t!==null){o=t;do l|=o.lane,o=o.next;while(o!==t)}else n===null&&(o.shared.lanes=0);Fr|=l,e.lanes=l,e.memoizedState=f}}function Ep(e,t,a){if(e=t.effects,t.effects=null,e!==null)for(t=0;ta?a:4,e(!0);var r=ki.transition;ki.transition={};try{e(!1),t()}finally{ne=a,ki.transition=r}}function wh(){return Nt().memoizedState}function Rw(e,t,a){var r=lr(e);if(a={lane:r,action:a,hasEagerState:!1,eagerState:null,next:null},Ch(e))Ih(t,a);else if(a=rh(e,t,a,r),a!==null){var o=at();Yt(a,e,r,o),Sh(a,t,r)}}function Dw(e,t,a){var r=lr(e),o={lane:r,action:a,hasEagerState:!1,eagerState:null,next:null};if(Ch(e))Ih(t,o);else{var n=e.alternate;if(e.lanes===0&&(n===null||n.lanes===0)&&(n=t.lastRenderedReducer,n!==null))try{var l=t.lastRenderedState,u=n(l,a);if(o.hasEagerState=!0,o.eagerState=u,Jt(u,l)){var s=t.interleaved;s===null?(o.next=o,Gd(t)):(o.next=s.next,s.next=o),t.interleaved=o;return}}catch(i){}finally{}a=rh(e,t,o,r),a!==null&&(o=at(),Yt(a,e,r,o),Sh(a,t,r))}}function Ch(e){var t=e.alternate;return e===Ce||t!==null&&t===Ce}function Ih(e,t){On=Au=!0;var a=e.pending;a===null?t.next=t:(t.next=a.next,a.next=t),e.pending=t}function Sh(e,t,a){if(a&4194240){var r=t.lanes;r&=e.pendingLanes,a|=r,t.lanes=a,bd(e,a)}}var Ou={readContext:Bt,useCallback:We,useContext:We,useEffect:We,useImperativeHandle:We,useInsertionEffect:We,useLayoutEffect:We,useMemo:We,useReducer:We,useRef:We,useState:We,useDebugValue:We,useDeferredValue:We,useTransition:We,useMutableSource:We,useSyncExternalStore:We,useId:We,unstable_isNewReconciler:!1},bw={readContext:Bt,useCallback:function(e,t){return fa().memoizedState=[e,t===void 0?null:t],e},useContext:Bt,useEffect:Op,useImperativeHandle:function(e,t,a){return a=a!=null?a.concat([e]):null,cu(4194308,4,gh.bind(null,t,e),a)},useLayoutEffect:function(e,t){return cu(4194308,4,e,t)},useInsertionEffect:function(e,t){return cu(4,2,e,t)},useMemo:function(e,t){var a=fa();return t=t===void 0?null:t,e=e(),a.memoizedState=[e,t],e},useReducer:function(e,t,a){var r=fa();return t=a!==void 0?a(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Rw.bind(null,Ce,e),[r.memoizedState,e]},useRef:function(e){var t=fa();return e={current:e},t.memoizedState=e},useState:Ap,useDebugValue:tf,useDeferredValue:function(e){return fa().memoizedState=e},useTransition:function(){var e=Ap(!1),t=e[0];return e=_w.bind(null,e[1]),fa().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,a){var r=Ce,o=fa();if(Le){if(a===void 0)throw Error(_(407));a=a()}else{if(a=t(),Be===null)throw Error(_(349));Or&30||sh(r,t,a)}o.memoizedState=a;var n={value:a,getSnapshot:t};return o.queue=n,Op(dh.bind(null,r,n,e),[e]),r.flags|=2048,el(9,ih.bind(null,r,n,a,t),void 0,null),a},useId:function(){var e=fa(),t=Be.identifierPrefix;if(Le){var a=_a,r=Ta;a=(r&~(1<<32-Qt(r)-1)).toString(32)+a,t=":"+t+"R"+a,a=Yn++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=l.createElement(a,{is:r.is}):(e=l.createElement(a),a==="select"&&(l=e,r.multiple?l.multiple=!0:r.size&&(l.size=r.size))):e=l.createElementNS(e,a),e[ca]=t,e[Kn]=r,Ah(e,t,!1,!1),t.stateNode=e;e:{switch(l=Vi(a,r),a){case"dialog":me("cancel",e),me("close",e),o=r;break;case"iframe":case"object":case"embed":me("load",e),o=r;break;case"video":case"audio":for(o=0;o<_n.length;o++)me(_n[o],e);o=r;break;case"source":me("error",e),o=r;break;case"img":case"image":case"link":me("error",e),me("load",e),o=r;break;case"details":me("toggle",e),o=r;break;case"input":Jc(e,r),o=Bi(e,r),me("invalid",e);break;case"option":o=r;break;case"select":e._wrapperState={wasMultiple:!!r.multiple},o=Ie({},r,{value:void 0}),me("invalid",e);break;case"textarea":tp(e,r),o=Ui(e,r),me("invalid",e);break;default:o=r}Hi(a,o),u=o;for(n in u)if(u.hasOwnProperty(n)){var s=u[n];n==="style"?pm(e,s):n==="dangerouslySetInnerHTML"?(s=s?s.__html:void 0,s!=null&&fm(e,s)):n==="children"?typeof s=="string"?(a!=="textarea"||s!=="")&&Un(e,s):typeof s=="number"&&Un(e,""+s):n!=="suppressContentEditableWarning"&&n!=="suppressHydrationWarning"&&n!=="autoFocus"&&(zn.hasOwnProperty(n)?s!=null&&n==="onScroll"&&me("scroll",e):s!=null&&Pd(e,n,s,l))}switch(a){case"input":Vl(e),ep(e,r,!1);break;case"textarea":Vl(e),ap(e);break;case"option":r.value!=null&&e.setAttribute("value",""+sr(r.value));break;case"select":e.multiple=!!r.multiple,n=r.value,n!=null?To(e,!!r.multiple,n,!1):r.defaultValue!=null&&To(e,!!r.multiple,r.defaultValue,!0);break;default:typeof o.onClick=="function"&&(e.onclick=ku)}switch(a){case"button":case"input":case"select":case"textarea":r=!!r.autoFocus;break e;case"img":r=!0;break e;default:r=!1}}r&&(t.flags|=4)}t.ref!==null&&(t.flags|=512,t.flags|=2097152)}return Ge(t),null;case 6:if(e&&t.stateNode!=null)Fh(e,t,e.memoizedProps,r);else{if(typeof r!="string"&&t.stateNode===null)throw Error(_(166));if(a=Rr(Qn.current),Rr(ma.current),au(t)){if(r=t.stateNode,a=t.memoizedProps,r[ca]=t,(n=r.nodeValue!==a)&&(e=It,e!==null))switch(e.tag){case 3:tu(r.nodeValue,a,(e.mode&1)!==0);break;case 5:e.memoizedProps.suppressHydrationWarning!==!0&&tu(r.nodeValue,a,(e.mode&1)!==0)}n&&(t.flags|=4)}else r=(a.nodeType===9?a:a.ownerDocument).createTextNode(r),r[ca]=t,t.stateNode=r}return Ge(t),null;case 13:if(he(we),r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(Le&&Ct!==null&&t.mode&1&&!(t.flags&128))eh(),Fo(),t.flags|=98560,n=!1;else if(n=au(t),r!==null&&r.dehydrated!==null){if(e===null){if(!n)throw Error(_(318));if(n=t.memoizedState,n=n!==null?n.dehydrated:null,!n)throw Error(_(317));n[ca]=t}else Fo(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Ge(t),n=!1}else Xt!==null&&(Id(Xt),Xt=null),n=!0;if(!n)return t.flags&65536?t:null}return t.flags&128?(t.lanes=a,t):(r=r!==null,r!==(e!==null&&e.memoizedState!==null)&&r&&(t.child.flags|=8192,t.mode&1&&(e===null||we.current&1?Oe===0&&(Oe=3):sf())),t.updateQueue!==null&&(t.flags|=4),Ge(t),null);case 4:return No(),hd(e,t),e===null&&Zn(t.stateNode.containerInfo),Ge(t),null;case 10:return Wd(t.type._context),Ge(t),null;case 17:return ft(t.type)&&Pu(),Ge(t),null;case 19:if(he(we),n=t.memoizedState,n===null)return Ge(t),null;if(r=(t.flags&128)!==0,l=n.rendering,l===null)if(r)In(n,!1);else{if(Oe!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(l=Eu(e),l!==null){for(t.flags|=128,In(n,!1),r=l.updateQueue,r!==null&&(t.updateQueue=r,t.flags|=4),t.subtreeFlags=0,r=a,a=t.child;a!==null;)n=a,e=r,n.flags&=14680066,l=n.alternate,l===null?(n.childLanes=0,n.lanes=e,n.child=null,n.subtreeFlags=0,n.memoizedProps=null,n.memoizedState=null,n.updateQueue=null,n.dependencies=null,n.stateNode=null):(n.childLanes=l.childLanes,n.lanes=l.lanes,n.child=l.child,n.subtreeFlags=0,n.deletions=null,n.memoizedProps=l.memoizedProps,n.memoizedState=l.memoizedState,n.updateQueue=l.updateQueue,n.type=l.type,e=l.dependencies,n.dependencies=e===null?null:{lanes:e.lanes,firstContext:e.firstContext}),a=a.sibling;return de(we,we.current&1|2),t.child}e=e.sibling}n.tail!==null&&Te()>Uo&&(t.flags|=128,r=!0,In(n,!1),t.lanes=4194304)}else{if(!r)if(e=Eu(l),e!==null){if(t.flags|=128,r=!0,a=e.updateQueue,a!==null&&(t.updateQueue=a,t.flags|=4),In(n,!0),n.tail===null&&n.tailMode==="hidden"&&!l.alternate&&!Le)return Ge(t),null}else 2*Te()-n.renderingStartTime>Uo&&a!==1073741824&&(t.flags|=128,r=!0,In(n,!1),t.lanes=4194304);n.isBackwards?(l.sibling=t.child,t.child=l):(a=n.last,a!==null?a.sibling=l:t.child=l,n.last=l)}return n.tail!==null?(t=n.tail,n.rendering=t,n.tail=t.sibling,n.renderingStartTime=Te(),t.sibling=null,a=we.current,de(we,r?a&1|2:a&1),t):(Ge(t),null);case 22:case 23:return uf(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?wt&1073741824&&(Ge(t),t.subtreeFlags&6&&(t.flags|=8192)):Ge(t),null;case 24:return null;case 25:return null}throw Error(_(156,t.tag))}function Uw(e,t){switch(qd(t),t.tag){case 1:return ft(t.type)&&Pu(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return No(),he(dt),he($e),Xd(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Kd(t),null;case 13:if(he(we),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(_(340));Fo()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return he(we),null;case 4:return No(),null;case 10:return Wd(t.type._context),null;case 22:case 23:return uf(),null;case 24:return null;default:return null}}var nu=!1,Ze=!1,qw=typeof WeakSet=="function"?WeakSet:Set,B=null;function Po(e,t){var a=e.ref;if(a!==null)if(typeof a=="function")try{a(null)}catch(r){Me(e,t,r)}else a.current=null}function gd(e,t,a){try{a()}catch(r){Me(e,t,r)}}var Gp=!1;function Hw(e,t){if(Ji=Cu,e=qm(),zd(e)){if("selectionStart"in e)var a={start:e.selectionStart,end:e.selectionEnd};else e:{a=(a=e.ownerDocument)&&a.defaultView||window;var r=a.getSelection&&a.getSelection();if(r&&r.rangeCount!==0){a=r.anchorNode;var o=r.anchorOffset,n=r.focusNode;r=r.focusOffset;try{a.nodeType,n.nodeType}catch(y){a=null;break e}var l=0,u=-1,s=-1,i=0,d=0,f=e,p=null;t:for(;;){for(var v;f!==a||o!==0&&f.nodeType!==3||(u=l+o),f!==n||r!==0&&f.nodeType!==3||(s=l+r),f.nodeType===3&&(l+=f.nodeValue.length),(v=f.firstChild)!==null;)p=f,f=v;for(;;){if(f===e)break t;if(p===a&&++i===o&&(u=l),p===n&&++d===r&&(s=l),(v=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=v}a=u===-1||s===-1?null:{start:u,end:s}}else a=null}a=a||{start:0,end:0}}else a=null;for(ed={focusedElem:e,selectionRange:a},Cu=!1,B=t;B!==null;)if(t=B,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,B=e;else for(;B!==null;){t=B;try{var x=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(x!==null){var g=x.memoizedProps,L=x.memoizedState,m=t.stateNode,c=m.getSnapshotBeforeUpdate(t.elementType===t.type?g:$t(t.type,g),L);m.__reactInternalSnapshotBeforeUpdate=c}break;case 3:var h=t.stateNode.containerInfo;h.nodeType===1?h.textContent="":h.nodeType===9&&h.documentElement&&h.removeChild(h.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(_(163))}}catch(y){Me(t,t.return,y)}if(e=t.sibling,e!==null){e.return=t.return,B=e;break}B=t.return}return x=Gp,Gp=!1,x}function Fn(e,t,a){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var o=r=r.next;do{if((o.tag&e)===e){var n=o.destroy;o.destroy=void 0,n!==void 0&&gd(t,a,n)}o=o.next}while(o!==r)}}function Ku(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var a=t=t.next;do{if((a.tag&e)===e){var r=a.create;a.destroy=r()}a=a.next}while(a!==t)}}function vd(e){var t=e.ref;if(t!==null){var a=e.stateNode;switch(e.tag){case 5:e=a;break;default:e=a}typeof t=="function"?t(e):t.current=e}}function Bh(e){var t=e.alternate;t!==null&&(e.alternate=null,Bh(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[ca],delete t[Kn],delete t[rd],delete t[Sw],delete t[kw])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Nh(e){return e.tag===5||e.tag===3||e.tag===4}function Zp(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Nh(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function xd(e,t,a){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?a.nodeType===8?a.parentNode.insertBefore(e,t):a.insertBefore(e,t):(a.nodeType===8?(t=a.parentNode,t.insertBefore(e,a)):(t=a,t.appendChild(e)),a=a._reactRootContainer,a!=null||t.onclick!==null||(t.onclick=ku));else if(r!==4&&(e=e.child,e!==null))for(xd(e,t,a),e=e.sibling;e!==null;)xd(e,t,a),e=e.sibling}function yd(e,t,a){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?a.insertBefore(e,t):a.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(yd(e,t,a),e=e.sibling;e!==null;)yd(e,t,a),e=e.sibling}var Ue=null,Kt=!1;function Za(e,t,a){for(a=a.child;a!==null;)zh(e,t,a),a=a.sibling}function zh(e,t,a){if(pa&&typeof pa.onCommitFiberUnmount=="function")try{pa.onCommitFiberUnmount(qu,a)}catch(u){}switch(a.tag){case 5:Ze||Po(a,t);case 6:var r=Ue,o=Kt;Ue=null,Za(e,t,a),Ue=r,Kt=o,Ue!==null&&(Kt?(e=Ue,a=a.stateNode,e.nodeType===8?e.parentNode.removeChild(a):e.removeChild(a)):Ue.removeChild(a.stateNode));break;case 18:Ue!==null&&(Kt?(e=Ue,a=a.stateNode,e.nodeType===8?Ci(e.parentNode,a):e.nodeType===1&&Ci(e,a),jn(e)):Ci(Ue,a.stateNode));break;case 4:r=Ue,o=Kt,Ue=a.stateNode.containerInfo,Kt=!0,Za(e,t,a),Ue=r,Kt=o;break;case 0:case 11:case 14:case 15:if(!Ze&&(r=a.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){o=r=r.next;do{var n=o,l=n.destroy;n=n.tag,l!==void 0&&(n&2||n&4)&&gd(a,t,l),o=o.next}while(o!==r)}Za(e,t,a);break;case 1:if(!Ze&&(Po(a,t),r=a.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=a.memoizedProps,r.state=a.memoizedState,r.componentWillUnmount()}catch(u){Me(a,t,u)}Za(e,t,a);break;case 21:Za(e,t,a);break;case 22:a.mode&1?(Ze=(r=Ze)||a.memoizedState!==null,Za(e,t,a),Ze=r):Za(e,t,a);break;default:Za(e,t,a)}}function $p(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var a=e.stateNode;a===null&&(a=e.stateNode=new qw),t.forEach(function(r){var o=Qw.bind(null,e,r);a.has(r)||(a.add(r),r.then(o,o))})}}function Zt(e,t){var a=t.deletions;if(a!==null)for(var r=0;ro&&(o=l),r&=~n}if(r=o,r=Te()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*jw(r/1960))-r,10e?16:e,Ja===null)var r=!1;else{if(e=Ja,Ja=null,Nu=0,re&6)throw Error(_(331));var o=re;for(re|=4,B=e.current;B!==null;){var n=B,l=n.child;if(B.flags&16){var u=n.deletions;if(u!==null){for(var s=0;sTe()-nf?Dr(e,0):of|=a),ct(e,t)}function Zh(e,t){t===0&&(e.mode&1?(t=Gl,Gl<<=1,!(Gl&130023424)&&(Gl=4194304)):t=1);var a=at();e=Ea(e,t),e!==null&&(al(e,t,a),ct(e,a))}function Xw(e){var t=e.memoizedState,a=0;t!==null&&(a=t.retryLane),Zh(e,a)}function Qw(e,t){var a=0;switch(e.tag){case 13:var r=e.stateNode,o=e.memoizedState;o!==null&&(a=o.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(_(314))}r!==null&&r.delete(t),Zh(e,a)}var $h;$h=function(e,t,a){if(e!==null)if(e.memoizedProps!==t.pendingProps||dt.current)it=!0;else{if(!(e.lanes&a)&&!(t.flags&128))return it=!1,Nw(e,t,a);it=!!(e.flags&131072)}else it=!1,Le&&t.flags&1048576&&Ym(t,_u,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;pu(e,t),e=t.pendingProps;var o=Oo(t,$e.current);bo(t,a),o=Yd(null,t,r,e,o,a);var n=Jd();return t.flags|=1,typeof o=="object"&&o!==null&&typeof o.render=="function"&&o.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,ft(r)?(n=!0,Mu(t)):n=!1,t.memoizedState=o.state!==null&&o.state!==void 0?o.state:null,Zd(t),o.updater=$u,t.stateNode=o,o._reactInternals=t,id(t,r,e,a),t=cd(null,t,r,!0,n,a)):(t.tag=0,Le&&n&&Ud(t),tt(null,t,o,a),t=t.child),t;case 16:r=t.elementType;e:{switch(pu(e,t),e=t.pendingProps,o=r._init,r=o(r._payload),t.type=r,o=t.tag=Jw(r),e=$t(r,e),o){case 0:t=fd(null,t,r,e,a);break e;case 1:t=Vp(null,t,r,e,a);break e;case 11:t=qp(null,t,r,e,a);break e;case 14:t=Hp(null,t,r,$t(r.type,e),a);break e}throw Error(_(306,r,""))}return t;case 0:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:$t(r,o),fd(e,t,r,o,a);case 1:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:$t(r,o),Vp(e,t,r,o,a);case 3:e:{if(Dh(t),e===null)throw Error(_(387));r=t.pendingProps,n=t.memoizedState,o=n.element,oh(e,t),bu(t,r,null,a);var l=t.memoizedState;if(r=l.element,n.isDehydrated)if(n={element:r,isDehydrated:!1,cache:l.cache,pendingSuspenseBoundaries:l.pendingSuspenseBoundaries,transitions:l.transitions},t.updateQueue.baseState=n,t.memoizedState=n,t.flags&256){o=zo(Error(_(423)),t),t=jp(e,t,r,a,o);break e}else if(r!==o){o=zo(Error(_(424)),t),t=jp(e,t,r,a,o);break e}else for(Ct=rr(t.stateNode.containerInfo.firstChild),It=t,Le=!0,Xt=null,a=ah(t,null,r,a),t.child=a;a;)a.flags=a.flags&-3|4096,a=a.sibling;else{if(Fo(),r===o){t=Aa(e,t,a);break e}tt(e,t,r,a)}t=t.child}return t;case 5:return nh(t),e===null&&ld(t),r=t.type,o=t.pendingProps,n=e!==null?e.memoizedProps:null,l=o.children,td(r,o)?l=null:n!==null&&td(r,n)&&(t.flags|=32),Rh(e,t),tt(e,t,l,a),t.child;case 6:return e===null&&ld(t),null;case 13:return bh(e,t,a);case 4:return $d(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=Bo(t,null,r,a):tt(e,t,r,a),t.child;case 11:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:$t(r,o),qp(e,t,r,o,a);case 7:return tt(e,t,t.pendingProps,a),t.child;case 8:return tt(e,t,t.pendingProps.children,a),t.child;case 12:return tt(e,t,t.pendingProps.children,a),t.child;case 10:e:{if(r=t.type._context,o=t.pendingProps,n=t.memoizedProps,l=o.value,de(Ru,r._currentValue),r._currentValue=l,n!==null)if(Jt(n.value,l)){if(n.children===o.children&&!dt.current){t=Aa(e,t,a);break e}}else for(n=t.child,n!==null&&(n.return=t);n!==null;){var u=n.dependencies;if(u!==null){l=n.child;for(var s=u.firstContext;s!==null;){if(s.context===r){if(n.tag===1){s=Ra(-1,a&-a),s.tag=2;var i=n.updateQueue;if(i!==null){i=i.shared;var d=i.pending;d===null?s.next=s:(s.next=d.next,d.next=s),i.pending=s}}n.lanes|=a,s=n.alternate,s!==null&&(s.lanes|=a),ud(n.return,a,t),u.lanes|=a;break}s=s.next}}else if(n.tag===10)l=n.type===t.type?null:n.child;else if(n.tag===18){if(l=n.return,l===null)throw Error(_(341));l.lanes|=a,u=l.alternate,u!==null&&(u.lanes|=a),ud(l,a,t),l=n.sibling}else l=n.child;if(l!==null)l.return=n;else for(l=n;l!==null;){if(l===t){l=null;break}if(n=l.sibling,n!==null){n.return=l.return,l=n;break}l=l.return}n=l}tt(e,t,o.children,a),t=t.child}return t;case 9:return o=t.type,r=t.pendingProps.children,bo(t,a),o=Bt(o),r=r(o),t.flags|=1,tt(e,t,r,a),t.child;case 14:return r=t.type,o=$t(r,t.pendingProps),o=$t(r.type,o),Hp(e,t,r,o,a);case 15:return Th(e,t,t.type,t.pendingProps,a);case 17:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:$t(r,o),pu(e,t),t.tag=1,ft(r)?(e=!0,Mu(t)):e=!1,bo(t,a),kh(t,r,o),id(t,r,o,a),cd(null,t,r,!0,e,a);case 19:return Eh(e,t,a);case 22:return _h(e,t,a)}throw Error(_(156,t.tag))};function Kh(e,t){return Cm(e,t)}function Yw(e,t,a,r){this.tag=e,this.key=a,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ot(e,t,a,r){return new Yw(e,t,a,r)}function df(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Jw(e){if(typeof e=="function")return df(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Td)return 11;if(e===_d)return 14}return 2}function ur(e,t){var a=e.alternate;return a===null?(a=Ot(e.tag,t,e.key,e.mode),a.elementType=e.elementType,a.type=e.type,a.stateNode=e.stateNode,a.alternate=e,e.alternate=a):(a.pendingProps=t,a.type=e.type,a.flags=0,a.subtreeFlags=0,a.deletions=null),a.flags=e.flags&14680064,a.childLanes=e.childLanes,a.lanes=e.lanes,a.child=e.child,a.memoizedProps=e.memoizedProps,a.memoizedState=e.memoizedState,a.updateQueue=e.updateQueue,t=e.dependencies,a.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},a.sibling=e.sibling,a.index=e.index,a.ref=e.ref,a}function gu(e,t,a,r,o,n){var l=2;if(r=e,typeof e=="function")df(e)&&(l=1);else if(typeof e=="string")l=5;else e:switch(e){case vo:return br(a.children,o,n,t);case Md:l=8,o|=8;break;case Ei:return e=Ot(12,a,t,o|2),e.elementType=Ei,e.lanes=n,e;case Ai:return e=Ot(13,a,t,o),e.elementType=Ai,e.lanes=n,e;case Oi:return e=Ot(19,a,t,o),e.elementType=Oi,e.lanes=n,e;case nm:return Qu(a,o,n,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case rm:l=10;break e;case om:l=9;break e;case Td:l=11;break e;case _d:l=14;break e;case $a:l=16,r=null;break e}throw Error(_(130,e==null?e:typeof e,""))}return t=Ot(l,a,t,o),t.elementType=e,t.type=r,t.lanes=n,t}function br(e,t,a,r){return e=Ot(7,e,r,t),e.lanes=a,e}function Qu(e,t,a,r){return e=Ot(22,e,r,t),e.elementType=nm,e.lanes=a,e.stateNode={isHidden:!1},e}function Ri(e,t,a){return e=Ot(6,e,null,t),e.lanes=a,e}function Di(e,t,a){return t=Ot(4,e.children!==null?e.children:[],e.key,t),t.lanes=a,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function eC(e,t,a,r,o){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=mi(0),this.expirationTimes=mi(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=mi(0),this.identifierPrefix=r,this.onRecoverableError=o,this.mutableSourceEagerHydrationData=null}function ff(e,t,a,r,o,n,l,u,s){return e=new eC(e,t,a,u,s),t===1?(t=1,n===!0&&(t|=8)):t=0,n=Ot(3,null,null,t),e.current=n,n.stateNode=e,n.memoizedState={element:r,isDehydrated:a,cache:null,transitions:null,pendingSuspenseBoundaries:null},Zd(n),e}function tC(e,t,a){var r=3{"use strict";function eg(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__=="undefined"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(eg)}catch(e){console.error(e)}}eg(),tg.exports=Jh()});var rg=ka(hf=>{"use strict";var ag=Vo();hf.createRoot=ag.createRoot,hf.hydrateRoot=ag.hydrateRoot;var GS});var ng=ka(as=>{"use strict";var lC=G(),uC=Symbol.for("react.element"),sC=Symbol.for("react.fragment"),iC=Object.prototype.hasOwnProperty,dC=lC.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,fC={key:!0,ref:!0,__self:!0,__source:!0};function og(e,t,a){var r,o={},n=null,l=null;a!==void 0&&(n=""+a),t.key!==void 0&&(n=""+t.key),t.ref!==void 0&&(l=t.ref);for(r in t)iC.call(t,r)&&!fC.hasOwnProperty(r)&&(o[r]=t[r]);if(e&&e.defaultProps)for(r in t=e.defaultProps,t)o[r]===void 0&&(o[r]=t[r]);return{$$typeof:uC,type:e,key:n,ref:l,props:o,_owner:dC.current}}as.Fragment=sC;as.jsx=og;as.jsxs=og});var Ke=ka((KS,lg)=>{"use strict";lg.exports=ng()});var Ay=N(rg());var pt=function(){return pt=Object.assign||function(e){for(var t,a=1,r=arguments.length;ae.forEach(a=>function(r,o){typeof r=="function"?r(o):r!=null&&(r.current=o)}(a,t))}function se(...e){return(0,sg.useCallback)(Fa(...e),e)}var ha=(0,ke.forwardRef)((e,t)=>{let l=e,{children:a}=l,r=A(l,["children"]),o=ke.Children.toArray(a),n=o.find(pC);if(n){let u=n.props.children,s=o.map(i=>i===n?ke.Children.count(u)>1?ke.Children.only(null):(0,ke.isValidElement)(u)?u.props.children:null:i);return(0,ke.createElement)(gf,E({},r,{ref:t}),(0,ke.isValidElement)(u)?(0,ke.cloneElement)(u,void 0,s):null)}return(0,ke.createElement)(gf,E({},r,{ref:t}),a)});ha.displayName="Slot";var gf=(0,ke.forwardRef)((e,t)=>{let o=e,{children:a}=o,r=A(o,["children"]);return(0,ke.isValidElement)(a)?(0,ke.cloneElement)(a,b(w({},mC(r,a.props)),{ref:t?Fa(t,a.ref):a.ref})):ke.Children.count(a)>1?ke.Children.only(null):null});gf.displayName="SlotClone";var cC=({children:e})=>(0,ke.createElement)(ke.Fragment,null,e);function pC(e){return(0,ke.isValidElement)(e)&&e.type===cC}function mC(e,t){let a=w({},t);for(let r in t){let o=e[r],n=t[r];/^on[A-Z]/.test(r)?o&&n?a[r]=(...l)=>{n(...l),o(...l)}:o&&(a[r]=o):r==="style"?a[r]=w(w({},o),n):r==="className"&&(a[r]=[o,n].filter(Boolean).join(" "))}return w(w({},e),a)}function ig(e){var t,a,r="";if(typeof e=="string"||typeof e=="number")r+=e;else if(typeof e=="object")if(Array.isArray(e))for(t=0;ttypeof e=="boolean"?"".concat(e):e===0?"0":e,fg=rs,os=(e,t)=>a=>{var r;if((t==null?void 0:t.variants)==null)return fg(e,a==null?void 0:a.class,a==null?void 0:a.className);let{variants:o,defaultVariants:n}=t,l=Object.keys(o).map(i=>{let d=a==null?void 0:a[i],f=n==null?void 0:n[i];if(d===null)return null;let p=dg(d)||dg(f);return o[i][p]}),u=a&&Object.entries(a).reduce((i,d)=>{let[f,p]=d;return p===void 0||(i[f]=p),i},{}),s=t==null||(r=t.compoundVariants)===null||r===void 0?void 0:r.reduce((i,d)=>{let x=d,{class:f,className:p}=x,v=A(x,["class","className"]);return Object.entries(v).every(g=>{let[L,m]=g;return Array.isArray(m)?m.includes(w(w({},n),u)[L]):w(w({},n),u)[L]===m})?[...i,f,p]:i},[]);return fg(e,l,s,a==null?void 0:a.class,a==null?void 0:a.className)};var xf="-";function hC(e){let t=function(o){let{theme:n,prefix:l}=o,u={nextPart:new Map,validators:[]};return function(i,d){return d?i.map(([f,p])=>[f,p.map(v=>typeof v=="string"?d+v:typeof v=="object"?Object.fromEntries(Object.entries(v).map(([x,g])=>[d+x,g])):v)]):i}(Object.entries(o.classGroups),l).forEach(([i,d])=>{vf(d,u,i,n)}),u}(e),{conflictingClassGroups:a,conflictingClassGroupModifiers:r}=e;return{getClassGroupId:function(o){let n=o.split(xf);return n[0]===""&&n.length!==1&&n.shift(),mg(n,t)||function(l){if(cg.test(l)){let u=cg.exec(l)[1],s=u==null?void 0:u.substring(0,u.indexOf(":"));if(s)return"arbitrary.."+s}}(o)},getConflictingClassGroupIds:function(o,n){let l=a[o]||[];return n&&r[o]?[...l,...r[o]]:l}}}function mg(e,t){var l;if(e.length===0)return t.classGroupId;let a=e[0],r=t.nextPart.get(a),o=r?mg(e.slice(1),r):void 0;if(o)return o;if(t.validators.length===0)return;let n=e.join(xf);return(l=t.validators.find(({validator:u})=>u(n)))==null?void 0:l.classGroupId}var cg=/^\[(.+)\]$/;function vf(e,t,a,r){e.forEach(o=>{if(typeof o!="string"){if(typeof o=="function")return o.isThemeGetter?void vf(o(r),t,a,r):void t.validators.push({validator:o,classGroupId:a});Object.entries(o).forEach(([n,l])=>{vf(l,pg(t,n),a,r)})}else(o===""?t:pg(t,o)).classGroupId=a})}function pg(e,t){let a=e;return t.split(xf).forEach(r=>{a.nextPart.has(r)||a.nextPart.set(r,{nextPart:new Map,validators:[]}),a=a.nextPart.get(r)}),a}function gC(e){if(e<1)return{get:()=>{},set:()=>{}};let t=0,a=new Map,r=new Map;function o(n,l){a.set(n,l),t++,t>e&&(t=0,r=a,a=new Map)}return{get(n){let l=a.get(n);return l!==void 0?l:(l=r.get(n))!==void 0?(o(n,l),l):void 0},set(n,l){a.has(n)?a.set(n,l):o(n,l)}}}var hg="!";function vC(e){let t=e.separator,a=t.length===1,r=t[0],o=t.length;return function(n){let l=[],u,s=0,i=0;for(let p=0;pi?u-i:void 0}}}var xC=/\s+/;function yC(){let e,t,a=0,r="";for(;ad(i),e());return a=function(i){return w({cache:gC(i.cacheSize),splitModifiers:vC(i)},hC(i))}(s),r=a.cache.get,o=a.cache.set,n=l,l(u)};function l(u){let s=r(u);if(s)return s;let i=function(d,f){let{splitModifiers:p,getClassGroupId:v,getConflictingClassGroupIds:x}=f,g=new Set;return d.trim().split(xC).map(L=>{let{modifiers:m,hasImportantModifier:c,baseClassName:h,maybePostfixModifierPosition:y}=p(L),C=v(y?h.substring(0,y):h),I=!!y;if(!C){if(!y)return{isTailwindClass:!1,originalClassName:L};if(C=v(h),!C)return{isTailwindClass:!1,originalClassName:L};I=!1}let k=function(S){if(S.length<=1)return S;let M=[],P=[];return S.forEach(U=>{U[0]==="["?(M.push(...P.sort(),U),P=[]):P.push(U)}),M.push(...P.sort()),M}(m).join(":");return{isTailwindClass:!0,modifierId:c?k+hg:k,classGroupId:C,originalClassName:L,hasPostfixModifier:I}}).reverse().filter(L=>{if(!L.isTailwindClass)return!0;let{modifierId:m,classGroupId:c,hasPostfixModifier:h}=L,y=m+c;return!g.has(y)&&(g.add(y),x(c,h).forEach(C=>g.add(m+C)),!0)}).reverse().map(L=>L.originalClassName).join(" ")}(u,a);return o(u,i),i}return function(){return n(yC.apply(null,arguments))}}function ge(e){let t=a=>a[e]||[];return t.isThemeGetter=!0,t}var vg=/^\[(?:([a-z-]+):)?(.+)\]$/i,wC=/^\d+\/\d+$/,CC=new Set(["px","full","screen"]),IC=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,SC=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,kC=/^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,PC=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/;function ea(e){return Ur(e)||CC.has(e)||wC.test(e)}function pr(e){return jo(e,"length",AC)}function Ur(e){return!!e&&!Number.isNaN(Number(e))}function ns(e){return jo(e,"number",Ur)}function ll(e){return!!e&&Number.isInteger(Number(e))}function MC(e){return e.endsWith("%")&&Ur(e.slice(0,-1))}function X(e){return vg.test(e)}function mr(e){return IC.test(e)}var TC=new Set(["length","size","percentage"]);function _C(e){return jo(e,TC,xg)}function RC(e){return jo(e,"position",xg)}var DC=new Set(["image","url"]);function bC(e){return jo(e,DC,FC)}function EC(e){return jo(e,"",OC)}function ul(){return!0}function jo(e,t,a){let r=vg.exec(e);return!!r&&(r[1]?typeof t=="string"?r[1]===t:t.has(r[1]):a(r[2]))}function AC(e){return SC.test(e)}function xg(){return!1}function OC(e){return kC.test(e)}function FC(e){return PC.test(e)}function BC(){let e=ge("colors"),t=ge("spacing"),a=ge("blur"),r=ge("brightness"),o=ge("borderColor"),n=ge("borderRadius"),l=ge("borderSpacing"),u=ge("borderWidth"),s=ge("contrast"),i=ge("grayscale"),d=ge("hueRotate"),f=ge("invert"),p=ge("gap"),v=ge("gradientColorStops"),x=ge("gradientColorStopPositions"),g=ge("inset"),L=ge("margin"),m=ge("opacity"),c=ge("padding"),h=ge("saturate"),y=ge("scale"),C=ge("sepia"),I=ge("skew"),k=ge("space"),S=ge("translate"),M=()=>["auto",X,t],P=()=>[X,t],U=()=>["",ea,pr],j=()=>["auto",Ur,X],ae=()=>["","0",X],Z=()=>[Ur,ns],ue=()=>[Ur,X];return{cacheSize:500,separator:":",theme:{colors:[ul],spacing:[ea,pr],blur:["none","",mr,X],brightness:Z(),borderColor:[e],borderRadius:["none","","full",mr,X],borderSpacing:P(),borderWidth:U(),contrast:Z(),grayscale:ae(),hueRotate:ue(),invert:ae(),gap:P(),gradientColorStops:[e],gradientColorStopPositions:[MC,pr],inset:M(),margin:M(),opacity:Z(),padding:P(),saturate:Z(),scale:Z(),sepia:ae(),skew:ue(),space:P(),translate:P()},classGroups:{aspect:[{aspect:["auto","square","video",X]}],container:["container"],columns:[{columns:[mr]}],"break-after":[{"break-after":["auto","avoid","all","avoid-page","page","left","right","column"]}],"break-before":[{"break-before":["auto","avoid","all","avoid-page","page","left","right","column"]}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],float:[{float:["right","left","none"]}],clear:[{clear:["left","right","both","none"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:["bottom","center","left","left-bottom","left-top","right","right-bottom","right-top","top",X]}],overflow:[{overflow:["auto","hidden","clip","visible","scroll"]}],"overflow-x":[{"overflow-x":["auto","hidden","clip","visible","scroll"]}],"overflow-y":[{"overflow-y":["auto","hidden","clip","visible","scroll"]}],overscroll:[{overscroll:["auto","contain","none"]}],"overscroll-x":[{"overscroll-x":["auto","contain","none"]}],"overscroll-y":[{"overscroll-y":["auto","contain","none"]}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:[g]}],"inset-x":[{"inset-x":[g]}],"inset-y":[{"inset-y":[g]}],start:[{start:[g]}],end:[{end:[g]}],top:[{top:[g]}],right:[{right:[g]}],bottom:[{bottom:[g]}],left:[{left:[g]}],visibility:["visible","invisible","collapse"],z:[{z:["auto",ll,X]}],basis:[{basis:M()}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["wrap","wrap-reverse","nowrap"]}],flex:[{flex:["1","auto","initial","none",X]}],grow:[{grow:ae()}],shrink:[{shrink:ae()}],order:[{order:["first","last","none",ll,X]}],"grid-cols":[{"grid-cols":[ul]}],"col-start-end":[{col:["auto",{span:["full",ll,X]},X]}],"col-start":[{"col-start":j()}],"col-end":[{"col-end":j()}],"grid-rows":[{"grid-rows":[ul]}],"row-start-end":[{row:["auto",{span:[ll,X]},X]}],"row-start":[{"row-start":j()}],"row-end":[{"row-end":j()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":["auto","min","max","fr",X]}],"auto-rows":[{"auto-rows":["auto","min","max","fr",X]}],gap:[{gap:[p]}],"gap-x":[{"gap-x":[p]}],"gap-y":[{"gap-y":[p]}],"justify-content":[{justify:["normal","start","end","center","between","around","evenly","stretch"]}],"justify-items":[{"justify-items":["start","end","center","stretch"]}],"justify-self":[{"justify-self":["auto","start","end","center","stretch"]}],"align-content":[{content:["normal","start","end","center","between","around","evenly","stretch","baseline"]}],"align-items":[{items:["start","end","center","baseline","stretch"]}],"align-self":[{self:["auto","start","end","center","stretch","baseline"]}],"place-content":[{"place-content":["start","end","center","between","around","evenly","stretch","baseline"]}],"place-items":[{"place-items":["start","end","center","baseline","stretch"]}],"place-self":[{"place-self":["auto","start","end","center","stretch"]}],p:[{p:[c]}],px:[{px:[c]}],py:[{py:[c]}],ps:[{ps:[c]}],pe:[{pe:[c]}],pt:[{pt:[c]}],pr:[{pr:[c]}],pb:[{pb:[c]}],pl:[{pl:[c]}],m:[{m:[L]}],mx:[{mx:[L]}],my:[{my:[L]}],ms:[{ms:[L]}],me:[{me:[L]}],mt:[{mt:[L]}],mr:[{mr:[L]}],mb:[{mb:[L]}],ml:[{ml:[L]}],"space-x":[{"space-x":[k]}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":[k]}],"space-y-reverse":["space-y-reverse"],w:[{w:["auto","min","max","fit",X,t]}],"min-w":[{"min-w":["min","max","fit",X,ea]}],"max-w":[{"max-w":["0","none","full","min","max","fit","prose",{screen:[mr]},mr,X]}],h:[{h:[X,t,"auto","min","max","fit"]}],"min-h":[{"min-h":["min","max","fit",ea,X]}],"max-h":[{"max-h":[X,t,"min","max","fit"]}],"font-size":[{text:["base",mr,pr]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:["thin","extralight","light","normal","medium","semibold","bold","extrabold","black",ns]}],"font-family":[{font:[ul]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractons"],tracking:[{tracking:["tighter","tight","normal","wide","wider","widest",X]}],"line-clamp":[{"line-clamp":["none",Ur,ns]}],leading:[{leading:["none","tight","snug","normal","relaxed","loose",ea,X]}],"list-image":[{"list-image":["none",X]}],"list-style-type":[{list:["none","disc","decimal",X]}],"list-style-position":[{list:["inside","outside"]}],"placeholder-color":[{placeholder:[e]}],"placeholder-opacity":[{"placeholder-opacity":[m]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"text-color":[{text:[e]}],"text-opacity":[{"text-opacity":[m]}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:["solid","dashed","dotted","double","none","wavy"]}],"text-decoration-thickness":[{decoration:["auto","from-font",ea,pr]}],"underline-offset":[{"underline-offset":["auto",ea,X]}],"text-decoration-color":[{decoration:[e]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],indent:[{indent:P()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",X]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",X]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-opacity":[{"bg-opacity":[m]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:["bottom","center","left","left-bottom","left-top","right","right-bottom","right-top","top",RC]}],"bg-repeat":[{bg:["no-repeat",{repeat:["","x","y","round","space"]}]}],"bg-size":[{bg:["auto","cover","contain",_C]}],"bg-image":[{bg:["none",{"gradient-to":["t","tr","r","br","b","bl","l","tl"]},bC]}],"bg-color":[{bg:[e]}],"gradient-from-pos":[{from:[x]}],"gradient-via-pos":[{via:[x]}],"gradient-to-pos":[{to:[x]}],"gradient-from":[{from:[v]}],"gradient-via":[{via:[v]}],"gradient-to":[{to:[v]}],rounded:[{rounded:[n]}],"rounded-s":[{"rounded-s":[n]}],"rounded-e":[{"rounded-e":[n]}],"rounded-t":[{"rounded-t":[n]}],"rounded-r":[{"rounded-r":[n]}],"rounded-b":[{"rounded-b":[n]}],"rounded-l":[{"rounded-l":[n]}],"rounded-ss":[{"rounded-ss":[n]}],"rounded-se":[{"rounded-se":[n]}],"rounded-ee":[{"rounded-ee":[n]}],"rounded-es":[{"rounded-es":[n]}],"rounded-tl":[{"rounded-tl":[n]}],"rounded-tr":[{"rounded-tr":[n]}],"rounded-br":[{"rounded-br":[n]}],"rounded-bl":[{"rounded-bl":[n]}],"border-w":[{border:[u]}],"border-w-x":[{"border-x":[u]}],"border-w-y":[{"border-y":[u]}],"border-w-s":[{"border-s":[u]}],"border-w-e":[{"border-e":[u]}],"border-w-t":[{"border-t":[u]}],"border-w-r":[{"border-r":[u]}],"border-w-b":[{"border-b":[u]}],"border-w-l":[{"border-l":[u]}],"border-opacity":[{"border-opacity":[m]}],"border-style":[{border:["solid","dashed","dotted","double","none","hidden"]}],"divide-x":[{"divide-x":[u]}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":[u]}],"divide-y-reverse":["divide-y-reverse"],"divide-opacity":[{"divide-opacity":[m]}],"divide-style":[{divide:["solid","dashed","dotted","double","none"]}],"border-color":[{border:[o]}],"border-color-x":[{"border-x":[o]}],"border-color-y":[{"border-y":[o]}],"border-color-t":[{"border-t":[o]}],"border-color-r":[{"border-r":[o]}],"border-color-b":[{"border-b":[o]}],"border-color-l":[{"border-l":[o]}],"divide-color":[{divide:[o]}],"outline-style":[{outline:["","solid","dashed","dotted","double","none"]}],"outline-offset":[{"outline-offset":[ea,X]}],"outline-w":[{outline:[ea,pr]}],"outline-color":[{outline:[e]}],"ring-w":[{ring:U()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:[e]}],"ring-opacity":[{"ring-opacity":[m]}],"ring-offset-w":[{"ring-offset":[ea,pr]}],"ring-offset-color":[{"ring-offset":[e]}],shadow:[{shadow:["","inner","none",mr,EC]}],"shadow-color":[{shadow:[ul]}],opacity:[{opacity:[m]}],"mix-blend":[{"mix-blend":["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity","plus-lighter"]}],"bg-blend":[{"bg-blend":["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity","plus-lighter"]}],filter:[{filter:["","none"]}],blur:[{blur:[a]}],brightness:[{brightness:[r]}],contrast:[{contrast:[s]}],"drop-shadow":[{"drop-shadow":["","none",mr,X]}],grayscale:[{grayscale:[i]}],"hue-rotate":[{"hue-rotate":[d]}],invert:[{invert:[f]}],saturate:[{saturate:[h]}],sepia:[{sepia:[C]}],"backdrop-filter":[{"backdrop-filter":["","none"]}],"backdrop-blur":[{"backdrop-blur":[a]}],"backdrop-brightness":[{"backdrop-brightness":[r]}],"backdrop-contrast":[{"backdrop-contrast":[s]}],"backdrop-grayscale":[{"backdrop-grayscale":[i]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[d]}],"backdrop-invert":[{"backdrop-invert":[f]}],"backdrop-opacity":[{"backdrop-opacity":[m]}],"backdrop-saturate":[{"backdrop-saturate":[h]}],"backdrop-sepia":[{"backdrop-sepia":[C]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":[l]}],"border-spacing-x":[{"border-spacing-x":[l]}],"border-spacing-y":[{"border-spacing-y":[l]}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["none","all","","colors","opacity","shadow","transform",X]}],duration:[{duration:ue()}],ease:[{ease:["linear","in","out","in-out",X]}],delay:[{delay:ue()}],animate:[{animate:["none","spin","ping","pulse","bounce",X]}],transform:[{transform:["","gpu","none"]}],scale:[{scale:[y]}],"scale-x":[{"scale-x":[y]}],"scale-y":[{"scale-y":[y]}],rotate:[{rotate:[ll,X]}],"translate-x":[{"translate-x":[S]}],"translate-y":[{"translate-y":[S]}],"skew-x":[{"skew-x":[I]}],"skew-y":[{"skew-y":[I]}],"transform-origin":[{origin:["center","top","top-right","right","bottom-right","bottom","bottom-left","left","top-left",X]}],accent:[{accent:["auto",e]}],appearance:["appearance-none"],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",X]}],"caret-color":[{caret:[e]}],"pointer-events":[{"pointer-events":["none","auto"]}],resize:[{resize:["none","y","x",""]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":P()}],"scroll-mx":[{"scroll-mx":P()}],"scroll-my":[{"scroll-my":P()}],"scroll-ms":[{"scroll-ms":P()}],"scroll-me":[{"scroll-me":P()}],"scroll-mt":[{"scroll-mt":P()}],"scroll-mr":[{"scroll-mr":P()}],"scroll-mb":[{"scroll-mb":P()}],"scroll-ml":[{"scroll-ml":P()}],"scroll-p":[{"scroll-p":P()}],"scroll-px":[{"scroll-px":P()}],"scroll-py":[{"scroll-py":P()}],"scroll-ps":[{"scroll-ps":P()}],"scroll-pe":[{"scroll-pe":P()}],"scroll-pt":[{"scroll-pt":P()}],"scroll-pr":[{"scroll-pr":P()}],"scroll-pb":[{"scroll-pb":P()}],"scroll-pl":[{"scroll-pl":P()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",X]}],fill:[{fill:[e,"none"]}],"stroke-w":[{stroke:[ea,pr,ns]}],stroke:[{stroke:[e,"none"]}],sr:["sr-only","not-sr-only"]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]}}}var yg=LC(BC);function De(...e){return yg(rs(e))}var Cg=os("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"bg-primary text-primary-foreground shadow hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",outline:"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-9 px-4 py-2",sm:"h-8 rounded-md px-3 text-xs",lg:"h-10 rounded-md px-8",icon:"h-9 w-9"}},defaultVariants:{variant:"default",size:"default"}}),ls=wg.forwardRef((e,t)=>{var{className:a,variant:r,size:o,asChild:n=!1}=e,l=Se(e,["className","variant","size","asChild"]);return(0,Lg.jsx)(n?ha:"button",Object.assign({className:De(Cg({variant:r,size:o,className:a})),ref:t},l))});ls.displayName="Button";var ta=N(G(),1);function zt(e,t=[]){let a=[],r=()=>{let o=a.map(n=>(0,ta.createContext)(n));return function(n){let l=(n==null?void 0:n[e])||o;return(0,ta.useMemo)(()=>({[`__scope${e}`]:b(w({},n),{[e]:l})}),[n,l])}};return r.scopeName=e,[function(o,n){let l=(0,ta.createContext)(n),u=a.length;function s(i){let g=i,{scope:d,children:f}=g,p=A(g,["scope","children"]),v=(d==null?void 0:d[e][u])||l,x=(0,ta.useMemo)(()=>p,Object.values(p));return(0,ta.createElement)(v.Provider,{value:x},f)}return a=[...a,n],s.displayName=o+"Provider",[s,function(i,d){let f=(d==null?void 0:d[e][u])||l,p=(0,ta.useContext)(f);if(p)return p;if(n!==void 0)return n;throw new Error(`\`${i}\` must be used within \`${o}\``)}]},NC(r,...t)]}function NC(...e){let t=e[0];if(e.length===1)return t;let a=()=>{let r=e.map(o=>({useScope:o(),scopeName:o.scopeName}));return function(o){let n=r.reduce((l,{useScope:u,scopeName:s})=>w(w({},l),u(o)[`__scope${s}`]),{});return(0,ta.useMemo)(()=>({[`__scope${t.scopeName}`]:n}),[n])}};return a.scopeName=t.scopeName,a}var aa=N(G(),1);function qr(e){let t=e+"CollectionProvider",[a,r]=zt(t),[o,n]=a(t,{collectionRef:{current:null},itemMap:new Map}),l=e+"CollectionSlot",u=e+"CollectionItemSlot",s="data-radix-collection-item";return[{Provider:i=>{let{scope:d,children:f}=i,p=aa.default.useRef(null),v=aa.default.useRef(new Map).current;return aa.default.createElement(o,{scope:d,itemMap:v,collectionRef:p},f)},Slot:aa.default.forwardRef((i,d)=>{let{scope:f,children:p}=i,v=n(l,f),x=se(d,v.collectionRef);return aa.default.createElement(ha,{ref:x},p)}),ItemSlot:aa.default.forwardRef((i,d)=>{let m=i,{scope:f,children:p}=m,v=A(m,["scope","children"]),x=aa.default.useRef(null),g=se(d,x),L=n(u,f);return aa.default.useEffect(()=>(L.itemMap.set(x,w({ref:x},v)),()=>{L.itemMap.delete(x)})),aa.default.createElement(ha,{[s]:"",ref:g},p)})},function(i){let d=n(e+"CollectionConsumer",i);return aa.default.useCallback(()=>{let f=d.collectionRef.current;if(!f)return[];let p=Array.from(f.querySelectorAll(`[${s}]`));return Array.from(d.itemMap.values()).sort((v,x)=>p.indexOf(v.ref.current)-p.indexOf(x.ref.current))},[d.collectionRef,d.itemMap])},r]}function q(e,t,{checkForDefaultPrevented:a=!0}={}){return function(r){if(e==null||e(r),a===!1||!r.defaultPrevented)return t==null?void 0:t(r)}}var hr=N(G(),1);var Wo=N(G(),1);function fe(e){let t=(0,Wo.useRef)(e);return(0,Wo.useEffect)(()=>{t.current=e}),(0,Wo.useMemo)(()=>(...a)=>{var r;return(r=t.current)===null||r===void 0?void 0:r.call(t,...a)},[])}function Go({prop:e,defaultProp:t,onChange:a=()=>{}}){let[r,o]=function({defaultProp:s,onChange:i}){let d=(0,hr.useState)(s),[f]=d,p=(0,hr.useRef)(f),v=fe(i);return(0,hr.useEffect)(()=>{p.current!==f&&(v(f),p.current=f)},[f,p,v]),d}({defaultProp:t,onChange:a}),n=e!==void 0,l=n?e:r,u=fe(a);return[l,(0,hr.useCallback)(s=>{if(n){let i=typeof s=="function"?s(e):s;i!==e&&u(i)}else o(s)},[n,e,o,u])]}var Zo=N(G(),1),Ig=N(Vo(),1);var oe=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","span","svg","ul"].reduce((e,t)=>{let a=(0,Zo.forwardRef)((r,o)=>{let s=r,{asChild:n}=s,l=A(s,["asChild"]),u=n?ha:t;return(0,Zo.useEffect)(()=>{window[Symbol.for("radix-ui")]=!0},[]),(0,Zo.createElement)(u,E({},l,{ref:o}))});return a.displayName=`Primitive.${t}`,b(w({},e),{[t]:a})},{});function Hr(e,t){e&&(0,Ig.flushSync)(()=>e.dispatchEvent(t))}var Sg=N(G(),1),mt=globalThis!=null&&globalThis.document?Sg.useLayoutEffect:()=>{};var Xe=N(G(),1),kg=N(Vo(),1);var ga=e=>{let{present:t,children:a}=e,r=function(l){let[u,s]=(0,Xe.useState)(),i=(0,Xe.useRef)({}),d=(0,Xe.useRef)(l),f=(0,Xe.useRef)("none"),p=l?"mounted":"unmounted",[v,x]=function(g,L){return(0,Xe.useReducer)((m,c)=>{let h=L[m][c];return h!=null?h:m},g)}(p,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return(0,Xe.useEffect)(()=>{let g=us(i.current);f.current=v==="mounted"?g:"none"},[v]),mt(()=>{let g=i.current,L=d.current;if(L!==l){let m=f.current,c=us(g);l?x("MOUNT"):c==="none"||(g==null?void 0:g.display)==="none"?x("UNMOUNT"):x(L&&m!==c?"ANIMATION_OUT":"UNMOUNT"),d.current=l}},[l,x]),mt(()=>{if(u){let g=m=>{let c=us(i.current).includes(m.animationName);m.target===u&&c&&(0,kg.flushSync)(()=>x("ANIMATION_END"))},L=m=>{m.target===u&&(f.current=us(i.current))};return u.addEventListener("animationstart",L),u.addEventListener("animationcancel",g),u.addEventListener("animationend",g),()=>{u.removeEventListener("animationstart",L),u.removeEventListener("animationcancel",g),u.removeEventListener("animationend",g)}}x("ANIMATION_END")},[u,x]),{isPresent:["mounted","unmountSuspended"].includes(v),ref:(0,Xe.useCallback)(g=>{g&&(i.current=getComputedStyle(g)),s(g)},[])}}(t),o=typeof a=="function"?a({present:r.isPresent}):Xe.Children.only(a),n=se(r.ref,o.ref);return typeof a=="function"||r.isPresent?(0,Xe.cloneElement)(o,{ref:n}):null};function us(e){return(e==null?void 0:e.animationName)||"none"}ga.displayName="Presence";var ss=N(G(),1);var zC=ss.useId||(()=>{}),UC=0;function gr(e){let[t,a]=ss.useState(zC());return mt(()=>{e||a(r=>r!=null?r:String(UC++))},[e]),e||(t?`radix-${t}`:"")}var is=N(G(),1),qC=(0,is.createContext)(void 0);function $o(e){let t=(0,is.useContext)(qC);return e||t||"ltr"}var J=N(G(),1);function ra(e,t){if(e==null)return{};var a,r,o={},n=Object.keys(e);for(r=0;r=0||(o[a]=e[a]);return o}var HC=["color"],z1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,HC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M4.93179 5.43179C4.75605 5.60753 4.75605 5.89245 4.93179 6.06819C5.10753 6.24392 5.39245 6.24392 5.56819 6.06819L7.49999 4.13638L9.43179 6.06819C9.60753 6.24392 9.89245 6.24392 10.0682 6.06819C10.2439 5.89245 10.2439 5.60753 10.0682 5.43179L7.81819 3.18179C7.73379 3.0974 7.61933 3.04999 7.49999 3.04999C7.38064 3.04999 7.26618 3.0974 7.18179 3.18179L4.93179 5.43179ZM10.0682 9.56819C10.2439 9.39245 10.2439 9.10753 10.0682 8.93179C9.89245 8.75606 9.60753 8.75606 9.43179 8.93179L7.49999 10.8636L5.56819 8.93179C5.39245 8.75606 5.10753 8.75606 4.93179 8.93179C4.75605 9.10753 4.75605 9.39245 4.93179 9.56819L7.18179 11.8182C7.35753 11.9939 7.64245 11.9939 7.81819 11.8182L10.0682 9.56819Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),VC=["color"],Pg=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,VC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),jC=["color"],Mg=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,jC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),WC=["color"],U1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,WC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M8.84182 3.13514C9.04327 3.32401 9.05348 3.64042 8.86462 3.84188L5.43521 7.49991L8.86462 11.1579C9.05348 11.3594 9.04327 11.6758 8.84182 11.8647C8.64036 12.0535 8.32394 12.0433 8.13508 11.8419L4.38508 7.84188C4.20477 7.64955 4.20477 7.35027 4.38508 7.15794L8.13508 3.15794C8.32394 2.95648 8.64036 2.94628 8.84182 3.13514Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),GC=["color"],Tg=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,GC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M6.1584 3.13508C6.35985 2.94621 6.67627 2.95642 6.86514 3.15788L10.6151 7.15788C10.7954 7.3502 10.7954 7.64949 10.6151 7.84182L6.86514 11.8418C6.67627 12.0433 6.35985 12.0535 6.1584 11.8646C5.95694 11.6757 5.94673 11.3593 6.1356 11.1579L9.565 7.49985L6.1356 3.84182C5.94673 3.64036 5.95694 3.32394 6.1584 3.13508Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),ZC=["color"],q1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,ZC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M3.13523 8.84197C3.3241 9.04343 3.64052 9.05363 3.84197 8.86477L7.5 5.43536L11.158 8.86477C11.3595 9.05363 11.6759 9.04343 11.8648 8.84197C12.0536 8.64051 12.0434 8.32409 11.842 8.13523L7.84197 4.38523C7.64964 4.20492 7.35036 4.20492 7.15803 4.38523L3.15803 8.13523C2.95657 8.32409 2.94637 8.64051 3.13523 8.84197Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),$C=["color"],H1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,$C);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),KC=["color"],_g=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,KC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M9.875 7.5C9.875 8.81168 8.81168 9.875 7.5 9.875C6.18832 9.875 5.125 8.81168 5.125 7.5C5.125 6.18832 6.18832 5.125 7.5 5.125C8.81168 5.125 9.875 6.18832 9.875 7.5Z",fill:r}))}),XC=["color"],V1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,XC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M3.625 7.5C3.625 8.12132 3.12132 8.625 2.5 8.625C1.87868 8.625 1.375 8.12132 1.375 7.5C1.375 6.87868 1.87868 6.375 2.5 6.375C3.12132 6.375 3.625 6.87868 3.625 7.5ZM8.625 7.5C8.625 8.12132 8.12132 8.625 7.5 8.625C6.87868 8.625 6.375 8.12132 6.375 7.5C6.375 6.87868 6.87868 6.375 7.5 6.375C8.12132 6.375 8.625 6.87868 8.625 7.5ZM12.5 8.625C13.1213 8.625 13.625 8.12132 13.625 7.5C13.625 6.87868 13.1213 6.375 12.5 6.375C11.8787 6.375 11.375 6.87868 11.375 7.5C11.375 8.12132 11.8787 8.625 12.5 8.625Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),QC=["color"],j1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,QC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M5.5 4.625C6.12132 4.625 6.625 4.12132 6.625 3.5C6.625 2.87868 6.12132 2.375 5.5 2.375C4.87868 2.375 4.375 2.87868 4.375 3.5C4.375 4.12132 4.87868 4.625 5.5 4.625ZM9.5 4.625C10.1213 4.625 10.625 4.12132 10.625 3.5C10.625 2.87868 10.1213 2.375 9.5 2.375C8.87868 2.375 8.375 2.87868 8.375 3.5C8.375 4.12132 8.87868 4.625 9.5 4.625ZM10.625 7.5C10.625 8.12132 10.1213 8.625 9.5 8.625C8.87868 8.625 8.375 8.12132 8.375 7.5C8.375 6.87868 8.87868 6.375 9.5 6.375C10.1213 6.375 10.625 6.87868 10.625 7.5ZM5.5 8.625C6.12132 8.625 6.625 8.12132 6.625 7.5C6.625 6.87868 6.12132 6.375 5.5 6.375C4.87868 6.375 4.375 6.87868 4.375 7.5C4.375 8.12132 4.87868 8.625 5.5 8.625ZM10.625 11.5C10.625 12.1213 10.1213 12.625 9.5 12.625C8.87868 12.625 8.375 12.1213 8.375 11.5C8.375 10.8787 8.87868 10.375 9.5 10.375C10.1213 10.375 10.625 10.8787 10.625 11.5ZM5.5 12.625C6.12132 12.625 6.625 12.1213 6.625 11.5C6.625 10.8787 6.12132 10.375 5.5 10.375C4.87868 10.375 4.375 10.8787 4.375 11.5C4.375 12.1213 4.87868 12.625 5.5 12.625Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),YC=["color"],W1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,YC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M10 6.5C10 8.433 8.433 10 6.5 10C4.567 10 3 8.433 3 6.5C3 4.567 4.567 3 6.5 3C8.433 3 10 4.567 10 6.5ZM9.30884 10.0159C8.53901 10.6318 7.56251 11 6.5 11C4.01472 11 2 8.98528 2 6.5C2 4.01472 4.01472 2 6.5 2C8.98528 2 11 4.01472 11 6.5C11 7.56251 10.6318 8.53901 10.0159 9.30884L12.8536 12.1464C13.0488 12.3417 13.0488 12.6583 12.8536 12.8536C12.6583 13.0488 12.3417 13.0488 12.1464 12.8536L9.30884 10.0159Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))});var ce=N(G(),1);var Rg=N(G(),1);function Dg(e,t=globalThis==null?void 0:globalThis.document){let a=fe(e);(0,Rg.useEffect)(()=>{let r=o=>{o.key==="Escape"&&a(o)};return t.addEventListener("keydown",r),()=>t.removeEventListener("keydown",r)},[a,t])}var yf="dismissableLayer.update",JC="dismissableLayer.pointerDownOutside",eI="dismissableLayer.focusOutside",bg,Og=(0,ce.createContext)({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),ds=(0,ce.forwardRef)((e,t)=>{var a;let S=e,{disableOutsidePointerEvents:r=!1,onEscapeKeyDown:o,onPointerDownOutside:n,onFocusOutside:l,onInteractOutside:u,onDismiss:s}=S,i=A(S,["disableOutsidePointerEvents","onEscapeKeyDown","onPointerDownOutside","onFocusOutside","onInteractOutside","onDismiss"]),d=(0,ce.useContext)(Og),[f,p]=(0,ce.useState)(null),v=(a=f==null?void 0:f.ownerDocument)!==null&&a!==void 0?a:globalThis==null?void 0:globalThis.document,[,x]=(0,ce.useState)({}),g=se(t,M=>p(M)),L=Array.from(d.layers),[m]=[...d.layersWithOutsidePointerEventsDisabled].slice(-1),c=L.indexOf(m),h=f?L.indexOf(f):-1,y=d.layersWithOutsidePointerEventsDisabled.size>0,C=h>=c,I=function(M,P=globalThis==null?void 0:globalThis.document){let U=fe(M),j=(0,ce.useRef)(!1),ae=(0,ce.useRef)(()=>{});return(0,ce.useEffect)(()=>{let Z=Q=>{if(Q.target&&!j.current){let Je=function(){Ag(JC,U,le,{discrete:!0})};var xe=Je;let le={originalEvent:Q};Q.pointerType==="touch"?(P.removeEventListener("click",ae.current),ae.current=Je,P.addEventListener("click",ae.current,{once:!0})):Je()}else P.removeEventListener("click",ae.current);j.current=!1},ue=window.setTimeout(()=>{P.addEventListener("pointerdown",Z)},0);return()=>{window.clearTimeout(ue),P.removeEventListener("pointerdown",Z),P.removeEventListener("click",ae.current)}},[P,U]),{onPointerDownCapture:()=>j.current=!0}}(M=>{let P=M.target,U=[...d.branches].some(j=>j.contains(P));C&&!U&&(n==null||n(M),u==null||u(M),M.defaultPrevented||s==null||s())},v),k=function(M,P=globalThis==null?void 0:globalThis.document){let U=fe(M),j=(0,ce.useRef)(!1);return(0,ce.useEffect)(()=>{let ae=Z=>{Z.target&&!j.current&&Ag(eI,U,{originalEvent:Z},{discrete:!1})};return P.addEventListener("focusin",ae),()=>P.removeEventListener("focusin",ae)},[P,U]),{onFocusCapture:()=>j.current=!0,onBlurCapture:()=>j.current=!1}}(M=>{let P=M.target;[...d.branches].some(U=>U.contains(P))||(l==null||l(M),u==null||u(M),M.defaultPrevented||s==null||s())},v);return Dg(M=>{h===d.layers.size-1&&(o==null||o(M),!M.defaultPrevented&&s&&(M.preventDefault(),s()))},v),(0,ce.useEffect)(()=>{if(f)return r&&(d.layersWithOutsidePointerEventsDisabled.size===0&&(bg=v.body.style.pointerEvents,v.body.style.pointerEvents="none"),d.layersWithOutsidePointerEventsDisabled.add(f)),d.layers.add(f),Eg(),()=>{r&&d.layersWithOutsidePointerEventsDisabled.size===1&&(v.body.style.pointerEvents=bg)}},[f,v,r,d]),(0,ce.useEffect)(()=>()=>{f&&(d.layers.delete(f),d.layersWithOutsidePointerEventsDisabled.delete(f),Eg())},[f,d]),(0,ce.useEffect)(()=>{let M=()=>x({});return document.addEventListener(yf,M),()=>document.removeEventListener(yf,M)},[]),(0,ce.createElement)(oe.div,E({},i,{ref:g,style:w({pointerEvents:y?C?"auto":"none":void 0},e.style),onFocusCapture:q(e.onFocusCapture,k.onFocusCapture),onBlurCapture:q(e.onBlurCapture,k.onBlurCapture),onPointerDownCapture:q(e.onPointerDownCapture,I.onPointerDownCapture)}))}),tk=(0,ce.forwardRef)((e,t)=>{let a=(0,ce.useContext)(Og),r=(0,ce.useRef)(null),o=se(t,r);return(0,ce.useEffect)(()=>{let n=r.current;if(n)return a.branches.add(n),()=>{a.branches.delete(n)}},[a.branches]),(0,ce.createElement)(oe.div,E({},e,{ref:o}))});function Eg(){let e=new CustomEvent(yf);document.dispatchEvent(e)}function Ag(e,t,a,{discrete:r}){let o=a.originalEvent.target,n=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:a});t&&o.addEventListener(e,t,{once:!0}),r?Hr(o,n):o.dispatchEvent(n)}var ht=N(G(),1);var Lf="focusScope.autoFocusOnMount",wf="focusScope.autoFocusOnUnmount",Fg={bubbles:!1,cancelable:!0},qg=(0,ht.forwardRef)((e,t)=>{let g=e,{loop:a=!1,trapped:r=!1,onMountAutoFocus:o,onUnmountAutoFocus:n}=g,l=A(g,["loop","trapped","onMountAutoFocus","onUnmountAutoFocus"]),[u,s]=(0,ht.useState)(null),i=fe(o),d=fe(n),f=(0,ht.useRef)(null),p=se(t,L=>s(L)),v=(0,ht.useRef)({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;(0,ht.useEffect)(()=>{if(r){let h=function(k){if(v.paused||!u)return;let S=k.target;u.contains(S)?f.current=S:vr(f.current,{select:!0})},y=function(k){if(v.paused||!u)return;let S=k.relatedTarget;S!==null&&(u.contains(S)||vr(f.current,{select:!0}))},C=function(k){if(document.activeElement===document.body)for(let S of k)S.removedNodes.length>0&&vr(u)};var L=h,m=y,c=C;document.addEventListener("focusin",h),document.addEventListener("focusout",y);let I=new MutationObserver(C);return u&&I.observe(u,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",h),document.removeEventListener("focusout",y),I.disconnect()}}},[r,u,v.paused]),(0,ht.useEffect)(()=>{if(u){zg.add(v);let m=document.activeElement;if(!u.contains(m)){let c=new CustomEvent(Lf,Fg);u.addEventListener(Lf,i),u.dispatchEvent(c),c.defaultPrevented||(function(h,{select:y=!1}={}){let C=document.activeElement;for(let I of h)if(vr(I,{select:y}),document.activeElement!==C)return}((L=Bg(u),L.filter(h=>h.tagName!=="A")),{select:!0}),document.activeElement===m&&vr(u))}return()=>{u.removeEventListener(Lf,i),setTimeout(()=>{let c=new CustomEvent(wf,Fg);u.addEventListener(wf,d),u.dispatchEvent(c),c.defaultPrevented||vr(m!=null?m:document.body,{select:!0}),u.removeEventListener(wf,d),zg.remove(v)},0)}}var L},[u,i,d,v]);let x=(0,ht.useCallback)(L=>{if(!a&&!r||v.paused)return;let m=L.key==="Tab"&&!L.altKey&&!L.ctrlKey&&!L.metaKey,c=document.activeElement;if(m&&c){let h=L.currentTarget,[y,C]=function(I){let k=Bg(I),S=Ng(k,I),M=Ng(k.reverse(),I);return[S,M]}(h);y&&C?L.shiftKey||c!==C?L.shiftKey&&c===y&&(L.preventDefault(),a&&vr(C,{select:!0})):(L.preventDefault(),a&&vr(y,{select:!0})):c===h&&L.preventDefault()}},[a,r,v.paused]);return(0,ht.createElement)(oe.div,E({tabIndex:-1},l,{ref:p,onKeyDown:x}))});function Bg(e){let t=[],a=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:r=>{let o=r.tagName==="INPUT"&&r.type==="hidden";return r.disabled||r.hidden||o?NodeFilter.FILTER_SKIP:r.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;a.nextNode();)t.push(a.currentNode);return t}function Ng(e,t){for(let a of e)if(!tI(a,{upTo:t}))return a}function tI(e,{upTo:t}){if(getComputedStyle(e).visibility==="hidden")return!0;for(;e;){if(t!==void 0&&e===t)return!1;if(getComputedStyle(e).display==="none")return!0;e=e.parentElement}return!1}function vr(e,{select:t=!1}={}){if(e&&e.focus){let a=document.activeElement;e.focus({preventScroll:!0}),e!==a&&function(r){return r instanceof HTMLInputElement&&"select"in r}(e)&&t&&e.select()}}var zg=function(){let e=[];return{add(t){let a=e[0];t!==a&&(a==null||a.pause()),e=Ug(e,t),e.unshift(t)},remove(t){var a;e=Ug(e,t),(a=e[0])===null||a===void 0||a.resume()}}}();function Ug(e,t){let a=[...e],r=a.indexOf(t);return r!==-1&&a.splice(r,1),a}var Vg=N(G(),1),Cf=0;function jg(){(0,Vg.useEffect)(()=>{var e,t;let a=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",(e=a[0])!==null&&e!==void 0?e:Hg()),document.body.insertAdjacentElement("beforeend",(t=a[1])!==null&&t!==void 0?t:Hg()),Cf++,()=>{Cf===1&&document.querySelectorAll("[data-radix-focus-guard]").forEach(r=>r.remove()),Cf--}},[])}function Hg(){let e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.cssText="outline: none; opacity: 0; position: fixed; pointer-events: none",e}var hs=N(G(),1);var Qe=N(G(),1);var Ko="right-scroll-bar-position",Xo="width-before-scroll-bar",Wg="with-scroll-bars-hidden",Gg="--removed-body-scroll-bar-size";function Zg(e,t){return typeof e=="function"?e(t):e&&(e.current=t),e}var $g=N(G(),1);function Kg(e,t){var a=(0,$g.useState)(function(){return{value:e,callback:t,facade:{get current(){return a.value},set current(r){var o=a.value;o!==r&&(a.value=r,a.callback(r,o))}}}})[0];return a.callback=t,a.facade}function Xg(e,t){return Kg(t||null,function(a){return e.forEach(function(r){return Zg(r,a)})})}var wk=N(G(),1);function aI(e){return e}function Qg(e){e===void 0&&(e={});var t=function(a,r){r===void 0&&(r=aI);var o=[],n=!1;return{read:function(){if(n)throw new Error("Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.");return o.length?o[o.length-1]:a},useMedium:function(l){var u=r(l,n);return o.push(u),function(){o=o.filter(function(s){return s!==u})}},assignSyncMedium:function(l){for(n=!0;o.length;){var u=o;o=[],u.forEach(l)}o={push:function(s){return l(s)},filter:function(){return o}}},assignMedium:function(l){n=!0;var u=[];if(o.length){var s=o;o=[],s.forEach(l),u=o}var i=function(){var f=u;u=[],f.forEach(l)},d=function(){return Promise.resolve().then(i)};d(),o={push:function(f){u.push(f),d()},filter:function(f){return u=u.filter(f),o}}}}}(null);return t.options=pt({async:!0,ssr:!1},e),t}var Yg=N(G(),1),Jg=function(e){var t=e.sideCar,a=Se(e,["sideCar"]);if(!t)throw new Error("Sidecar: please provide `sideCar` property to import the right car");var r=t.read();if(!r)throw new Error("Sidecar medium not found");return Yg.createElement(r,pt({},a))};function ev(e,t){return e.useMedium(t),Jg}Jg.isSideCarExport=!0;var fs=Qg();var If=function(){},sl=Qe.forwardRef(function(e,t){var a=Qe.useRef(null),r=Qe.useState({onScrollCapture:If,onWheelCapture:If,onTouchMoveCapture:If}),o=r[0],n=r[1],l=e.forwardProps,u=e.children,s=e.className,i=e.removeScrollBar,d=e.enabled,f=e.shards,p=e.sideCar,v=e.noIsolation,x=e.inert,g=e.allowPinchZoom,L=e.as,m=L===void 0?"div":L,c=Se(e,["forwardProps","children","className","removeScrollBar","enabled","shards","sideCar","noIsolation","inert","allowPinchZoom","as"]),h=p,y=Xg([a,t]),C=pt(pt({},c),o);return Qe.createElement(Qe.Fragment,null,d&&Qe.createElement(h,{sideCar:fs,removeScrollBar:i,shards:f,noIsolation:v,inert:x,setCallbacks:n,allowPinchZoom:!!g,lockRef:a}),l?Qe.cloneElement(Qe.Children.only(u),pt(pt({},C),{ref:y})):Qe.createElement(m,pt({},C,{className:s,ref:y}),u))});sl.defaultProps={enabled:!0,removeScrollBar:!0,inert:!1},sl.classNames={fullWidth:Xo,zeroRight:Ko};var Kk=N(G(),1);var ve=N(G(),1);var ps=N(G(),1);var rv=N(G(),1);var tv=function(){if(typeof __webpack_nonce__!="undefined")return __webpack_nonce__};var av=function(){var e=0,t=null;return{add:function(a){var r,o;e==0&&(t=function(){if(!document)return null;var n=document.createElement("style");n.type="text/css";var l=tv();return l&&n.setAttribute("nonce",l),n}())&&(o=a,(r=t).styleSheet?r.styleSheet.cssText=o:r.appendChild(document.createTextNode(o)),function(n){(document.head||document.getElementsByTagName("head")[0]).appendChild(n)}(t)),e++},remove:function(){!--e&&t&&(t.parentNode&&t.parentNode.removeChild(t),t=null)}}};var ov=function(){var e=av();return function(t,a){rv.useEffect(function(){return e.add(t),function(){e.remove()}},[t&&a])}};var cs=function(){var e=ov();return function(t){var a=t.styles,r=t.dynamic;return e(a,r),null}};var rI={left:0,top:0,right:0,gap:0},Sf=function(e){return parseInt(e||"",10)||0},nv=function(e){if(e===void 0&&(e="margin"),typeof window=="undefined")return rI;var t=function(o){var n=window.getComputedStyle(document.body),l=n[o==="padding"?"paddingLeft":"marginLeft"],u=n[o==="padding"?"paddingTop":"marginTop"],s=n[o==="padding"?"paddingRight":"marginRight"];return[Sf(l),Sf(u),Sf(s)]}(e),a=document.documentElement.clientWidth,r=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,r-a+t[2]-t[0])}};var oI=cs(),nI=function(e,t,a,r){var o=e.left,n=e.top,l=e.right,u=e.gap;return a===void 0&&(a="margin"),` + .`.concat(Wg,` { + overflow: hidden `).concat(r,`; + padding-right: `).concat(u,"px ").concat(r,`; + } + body { + overflow: hidden `).concat(r,`; + overscroll-behavior: contain; + `).concat([t&&"position: relative ".concat(r,";"),a==="margin"&&` + padding-left: `.concat(o,`px; + padding-top: `).concat(n,`px; + padding-right: `).concat(l,`px; + margin-left:0; + margin-top:0; + margin-right: `).concat(u,"px ").concat(r,`; + `),a==="padding"&&"padding-right: ".concat(u,"px ").concat(r,";")].filter(Boolean).join(""),` + } + + .`).concat(Ko,` { + right: `).concat(u,"px ").concat(r,`; + } + + .`).concat(Xo,` { + margin-right: `).concat(u,"px ").concat(r,`; + } + + .`).concat(Ko," .").concat(Ko,` { + right: 0 `).concat(r,`; + } + + .`).concat(Xo," .").concat(Xo,` { + margin-right: 0 `).concat(r,`; + } + + body { + `).concat(Gg,": ").concat(u,`px; + } +`)},lv=function(e){var t=e.noRelative,a=e.noImportant,r=e.gapMode,o=r===void 0?"margin":r,n=ps.useMemo(function(){return nv(o)},[o]);return ps.createElement(oI,{styles:nI(n,!t,o,a?"":"!important")})};var kf=!1;if(typeof window!="undefined")try{il=Object.defineProperty({},"passive",{get:function(){return kf=!0,!0}}),window.addEventListener("test",il,il),window.removeEventListener("test",il,il)}catch(e){kf=!1}var il,Vr=!!kf&&{passive:!1};var uv=function(e,t){var a=window.getComputedStyle(e);return a[t]!=="hidden"&&!(a.overflowY===a.overflowX&&!function(r){return r.tagName==="TEXTAREA"}(e)&&a[t]==="visible")},Pf=function(e,t){var a=t;do{if(typeof ShadowRoot!="undefined"&&a instanceof ShadowRoot&&(a=a.host),sv(e,a)){var r=iv(e,a);if(r[1]>r[2])return!0}a=a.parentNode}while(a&&a!==document.body);return!1},sv=function(e,t){return e==="v"?function(a){return uv(a,"overflowY")}(t):function(a){return uv(a,"overflowX")}(t)},iv=function(e,t){return e==="v"?[(a=t).scrollTop,a.scrollHeight,a.clientHeight]:function(r){return[r.scrollLeft,r.scrollWidth,r.clientWidth]}(t);var a},dv=function(e,t,a,r,o){var n=function(L,m){return L==="h"&&m==="rtl"?-1:1}(e,window.getComputedStyle(t).direction),l=n*r,u=a.target,s=t.contains(u),i=!1,d=l>0,f=0,p=0;do{var v=iv(e,u),x=v[0],g=v[1]-v[2]-n*x;(x||g)&&sv(e,u)&&(f+=g,p+=x),u=u.parentNode}while(!s&&u!==document.body||s&&(t.contains(u)||t===u));return(d&&(o&&f===0||!o&&l>f)||!d&&(o&&p===0||!o&&-l>p))&&(i=!0),i};var ms=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},fv=function(e){return[e.deltaX,e.deltaY]},cv=function(e){return e&&"current"in e?e.current:e},lI=function(e){return` + .block-interactivity-`.concat(e,` {pointer-events: none;} + .allow-interactivity-`).concat(e,` {pointer-events: all;} +`)},uI=0,Qo=[];function pv(e){var t=ve.useRef([]),a=ve.useRef([0,0]),r=ve.useRef(),o=ve.useState(uI++)[0],n=ve.useState(function(){return cs()})[0],l=ve.useRef(e);ve.useEffect(function(){l.current=e},[e]),ve.useEffect(function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var g=ug([e.lockRef.current],(e.shards||[]).map(cv),!0).filter(Boolean);return g.forEach(function(L){return L.classList.add("allow-interactivity-".concat(o))}),function(){document.body.classList.remove("block-interactivity-".concat(o)),g.forEach(function(L){return L.classList.remove("allow-interactivity-".concat(o))})}}},[e.inert,e.lockRef.current,e.shards]);var u=ve.useCallback(function(g,L){if("touches"in g&&g.touches.length===2)return!l.current.allowPinchZoom;var m,c=ms(g),h=a.current,y="deltaX"in g?g.deltaX:h[0]-c[0],C="deltaY"in g?g.deltaY:h[1]-c[1],I=g.target,k=Math.abs(y)>Math.abs(C)?"h":"v";if("touches"in g&&k==="h"&&I.type==="range")return!1;var S=Pf(k,I);if(!S)return!0;if(S?m=k:(m=k==="v"?"h":"v",S=Pf(k,I)),!S)return!1;if(!r.current&&"changedTouches"in g&&(y||C)&&(r.current=m),!m)return!0;var M=r.current||m;return dv(M,L,g,M==="h"?y:C,!0)},[]),s=ve.useCallback(function(g){var L=g;if(Qo.length&&Qo[Qo.length-1]===n){var m="deltaY"in L?fv(L):ms(L),c=t.current.filter(function(y){return y.name===L.type&&y.target===L.target&&(C=y.delta,I=m,C[0]===I[0]&&C[1]===I[1]);var C,I})[0];if(c&&c.should)L.cancelable&&L.preventDefault();else if(!c){var h=(l.current.shards||[]).map(cv).filter(Boolean).filter(function(y){return y.contains(L.target)});(h.length>0?u(L,h[0]):!l.current.noIsolation)&&L.cancelable&&L.preventDefault()}}},[]),i=ve.useCallback(function(g,L,m,c){var h={name:g,delta:L,target:m,should:c};t.current.push(h),setTimeout(function(){t.current=t.current.filter(function(y){return y!==h})},1)},[]),d=ve.useCallback(function(g){a.current=ms(g),r.current=void 0},[]),f=ve.useCallback(function(g){i(g.type,fv(g),g.target,u(g,e.lockRef.current))},[]),p=ve.useCallback(function(g){i(g.type,ms(g),g.target,u(g,e.lockRef.current))},[]);ve.useEffect(function(){return Qo.push(n),e.setCallbacks({onScrollCapture:f,onWheelCapture:f,onTouchMoveCapture:p}),document.addEventListener("wheel",s,Vr),document.addEventListener("touchmove",s,Vr),document.addEventListener("touchstart",d,Vr),function(){Qo=Qo.filter(function(g){return g!==n}),document.removeEventListener("wheel",s,Vr),document.removeEventListener("touchmove",s,Vr),document.removeEventListener("touchstart",d,Vr)}},[]);var v=e.removeScrollBar,x=e.inert;return ve.createElement(ve.Fragment,null,x?ve.createElement(n,{styles:lI(o)}):null,v?ve.createElement(lv,{gapMode:"margin"}):null)}var mv=ev(fs,pv);var Mf=hs.forwardRef(function(e,t){return hs.createElement(sl,pt({},e,{ref:t,sideCar:mv}))});Mf.classNames=sl.classNames;var Yo=new WeakMap,gs=new WeakMap,vs={},hv=0,gv=function(e){return e&&(e.host||gv(e.parentNode))},sI=function(e,t,a,r){var o=function(f,p){return p.map(function(v){if(f.contains(v))return v;var x=gv(v);return x&&f.contains(x)?x:(console.error("aria-hidden",v,"in not contained inside",f,". Doing nothing"),null)}).filter(function(v){return!!v})}(t,Array.isArray(e)?e:[e]);vs[a]||(vs[a]=new WeakMap);var n=vs[a],l=[],u=new Set,s=new Set(o),i=function(f){f&&!u.has(f)&&(u.add(f),i(f.parentNode))};o.forEach(i);var d=function(f){f&&!s.has(f)&&Array.prototype.forEach.call(f.children,function(p){if(u.has(p))d(p);else{var v=p.getAttribute(r),x=v!==null&&v!=="false",g=(Yo.get(p)||0)+1,L=(n.get(p)||0)+1;Yo.set(p,g),n.set(p,L),l.push(p),g===1&&x&&gs.set(p,!0),L===1&&p.setAttribute(a,"true"),x||p.setAttribute(r,"true")}})};return d(t),u.clear(),hv++,function(){l.forEach(function(f){var p=Yo.get(f)-1,v=n.get(f)-1;Yo.set(f,p),n.set(f,v),p||(gs.has(f)||f.removeAttribute(r),gs.delete(f)),v||f.removeAttribute(a)}),--hv||(Yo=new WeakMap,Yo=new WeakMap,gs=new WeakMap,vs={})}},vv=function(e,t,a){a===void 0&&(a="data-aria-hidden");var r=Array.from(Array.isArray(e)?e:[e]),o=t||function(n){return typeof document=="undefined"?null:(Array.isArray(n)?n[0]:n).ownerDocument.body}(e);return o?(r.push.apply(r,Array.from(o.querySelectorAll("[aria-live]"))),sI(r,o,a,"aria-hidden")):function(){return null}};var xs=N(G(),1);function xv(e){let t=(0,xs.useRef)({value:e,previous:e});return(0,xs.useMemo)(()=>(t.current.value!==e&&(t.current.previous=t.current.value,t.current.value=e),t.current.previous),[e])}var yv=N(G(),1);function Lv(e){let[t,a]=(0,yv.useState)(void 0);return mt(()=>{if(e){a({width:e.offsetWidth,height:e.offsetHeight});let r=new ResizeObserver(o=>{if(!Array.isArray(o)||!o.length)return;let n=o[0],l,u;if("borderBoxSize"in n){let s=n.borderBoxSize,i=Array.isArray(s)?s[0]:s;l=i.inlineSize,u=i.blockSize}else l=e.offsetWidth,u=e.offsetHeight;a({width:l,height:u})});return r.observe(e,{box:"border-box"}),()=>r.unobserve(e)}a(void 0)},[e]),t}var R=N(G(),1);var Ne=N(G(),1);var wv=["top","right","bottom","left"],va=Math.min,ot=Math.max,fl=Math.round,cl=Math.floor,Ba=e=>({x:e,y:e}),iI={left:"right",right:"left",bottom:"top",top:"bottom"},dI={start:"end",end:"start"};function ys(e,t,a){return ot(e,va(t,a))}function xa(e,t){return typeof e=="function"?e(t):e}function ya(e){return e.split("-")[0]}function jr(e){return e.split("-")[1]}function Ls(e){return e==="x"?"y":"x"}function ws(e){return e==="y"?"height":"width"}function Wr(e){return["top","bottom"].includes(ya(e))?"y":"x"}function Cs(e){return Ls(Wr(e))}function Cv(e,t,a){a===void 0&&(a=!1);let r=jr(e),o=Cs(e),n=ws(o),l=o==="x"?r===(a?"end":"start")?"right":"left":r==="start"?"bottom":"top";return t.reference[n]>t.floating[n]&&(l=dl(l)),[l,dl(l)]}function Iv(e){let t=dl(e);return[Tf(e),t,Tf(t)]}function Tf(e){return e.replace(/start|end/g,t=>dI[t])}function Sv(e,t,a,r){let o=jr(e),n=function(l,u,s){let i=["left","right"],d=["right","left"],f=["top","bottom"],p=["bottom","top"];switch(l){case"top":case"bottom":return s?u?d:i:u?i:d;case"left":case"right":return u?f:p;default:return[]}}(ya(e),a==="start",r);return o&&(n=n.map(l=>l+"-"+o),t&&(n=n.concat(n.map(Tf)))),n}function dl(e){return e.replace(/left|right|bottom|top/g,t=>iI[t])}function fI(e){return w({top:0,right:0,bottom:0,left:0},e)}function _f(e){return typeof e!="number"?fI(e):{top:e,right:e,bottom:e,left:e}}function Jo(e){return b(w({},e),{top:e.y,left:e.x,right:e.x+e.width,bottom:e.y+e.height})}function kv(e,t,a){let{reference:r,floating:o}=e,n=Wr(t),l=Cs(t),u=ws(l),s=ya(t),i=n==="y",d=r.x+r.width/2-o.width/2,f=r.y+r.height/2-o.height/2,p=r[u]/2-o[u]/2,v;switch(s){case"top":v={x:d,y:r.y-o.height};break;case"bottom":v={x:d,y:r.y+r.height};break;case"right":v={x:r.x+r.width,y:f};break;case"left":v={x:r.x-o.width,y:f};break;default:v={x:r.x,y:r.y}}switch(jr(t)){case"start":v[l]-=p*(a&&i?-1:1);break;case"end":v[l]+=p*(a&&i?-1:1)}return v}var Tv=(e,t,a)=>Pe(void 0,null,function*(){let{placement:r="bottom",strategy:o="absolute",middleware:n=[],platform:l}=a,u=n.filter(Boolean),s=yield l.isRTL==null?void 0:l.isRTL(t),i=yield l.getElementRects({reference:e,floating:t,strategy:o}),{x:d,y:f}=kv(i,r,s),p=r,v={},x=0;for(let g=0;g({name:"arrow",options:e,fn(a){return Pe(this,null,function*(){let{x:r,y:o,placement:n,rects:l,platform:u,elements:s,middlewareData:i}=a,{element:d,padding:f=0}=xa(e,a)||{};if(d==null)return{};let p=_f(f),v={x:r,y:o},x=Cs(n),g=ws(x),L=yield u.getDimensions(d),m=x==="y",c=m?"top":"left",h=m?"bottom":"right",y=m?"clientHeight":"clientWidth",C=l.reference[g]+l.reference[x]-v[x]-l.floating[g],I=v[x]-l.reference[x],k=yield u.getOffsetParent==null?void 0:u.getOffsetParent(d),S=k?k[y]:0;S&&(yield u.isElement==null?void 0:u.isElement(k))||(S=s.floating[y]||l.floating[g]);let M=C/2-I/2,P=S/2-L[g]/2-1,U=va(p[c],P),j=va(p[h],P),ae=U,Z=S-L[g]-j,ue=S/2-L[g]/2+M,Q=ys(ae,ue,Z),xe=!i.arrow&&jr(n)!=null&&ue!=Q&&l.reference[g]/2-(ueZ<=0)){var P,U;let Z=(((P=l.flip)==null?void 0:P.index)||0)+1,ue=I[Z];if(ue)return{data:{index:Z,overflows:M},reset:{placement:ue}};let Q=(U=M.filter(xe=>xe.overflows[0]<=0).sort((xe,le)=>xe.overflows[1]-le.overflows[1])[0])==null?void 0:U.placement;if(!Q)switch(x){case"bestFit":{var j;let xe=(j=M.map(le=>[le.placement,le.overflows.filter(Je=>Je>0).reduce((Je,W)=>Je+W,0)]).sort((le,Je)=>le[1]-Je[1])[0])==null?void 0:j[0];xe&&(Q=xe);break}case"initialPlacement":Q=s}if(n!==Q)return{reset:{placement:Q}}}return{}})}}};function Pv(e,t){return{top:e.top-t.height,right:e.right-t.width,bottom:e.bottom-t.height,left:e.left-t.width}}function Mv(e){return wv.some(t=>e[t]>=0)}var Rv=function(e){return e===void 0&&(e={}),{name:"hide",options:e,fn(a){return Pe(this,null,function*(){let{rects:r}=a,l=xa(e,a),{strategy:o="referenceHidden"}=l,n=A(l,["strategy"]);switch(o){case"referenceHidden":{let u=Pv(yield pl(a,b(w({},n),{elementContext:"reference"})),r.reference);return{data:{referenceHiddenOffsets:u,referenceHidden:Mv(u)}}}case"escaped":{let u=Pv(yield pl(a,b(w({},n),{altBoundary:!0})),r.floating);return{data:{escapedOffsets:u,escaped:Mv(u)}}}default:return{}}})}}},Dv=function(e){return e===void 0&&(e=0),{name:"offset",options:e,fn(a){return Pe(this,null,function*(){var r,o;let{x:n,y:l,placement:u,middlewareData:s}=a,i=yield function(d,f){return Pe(this,null,function*(){let{placement:p,platform:v,elements:x}=d,g=yield v.isRTL==null?void 0:v.isRTL(x.floating),L=ya(p),m=jr(p),c=Wr(p)==="y",h=["left","top"].includes(L)?-1:1,y=g&&c?-1:1,C=xa(f,d),{mainAxis:I,crossAxis:k,alignmentAxis:S}=typeof C=="number"?{mainAxis:C,crossAxis:0,alignmentAxis:null}:w({mainAxis:0,crossAxis:0,alignmentAxis:null},C);return m&&typeof S=="number"&&(k=m==="end"?-1*S:S),c?{x:k*y,y:I*h}:{x:I*h,y:k*y}})}(a,e);return u===((r=s.offset)==null?void 0:r.placement)&&(o=s.arrow)!=null&&o.alignmentOffset?{}:{x:n+i.x,y:l+i.y,data:b(w({},i),{placement:u})}})}}},bv=function(e){return e===void 0&&(e={}),{name:"shift",options:e,fn(a){return Pe(this,null,function*(){let{x:r,y:o,placement:n}=a,m=xa(e,a),{mainAxis:l=!0,crossAxis:u=!1,limiter:s={fn:c=>{let{x:h,y}=c;return{x:h,y}}}}=m,i=A(m,["mainAxis","crossAxis","limiter"]),d={x:r,y:o},f=yield pl(a,i),p=Wr(ya(n)),v=Ls(p),x=d[v],g=d[p];if(l){let c=v==="y"?"bottom":"right",h=x+f[v==="y"?"top":"left"],y=x-f[c];x=ys(h,x,y)}if(u){let c=p==="y"?"bottom":"right",h=g+f[p==="y"?"top":"left"],y=g-f[c];g=ys(h,g,y)}let L=s.fn(b(w({},a),{[v]:x,[p]:g}));return b(w({},L),{data:{x:L.x-r,y:L.y-o}})})}}},Ev=function(e){return e===void 0&&(e={}),{options:e,fn(t){let{x:a,y:r,placement:o,rects:n,middlewareData:l}=t,{offset:u=0,mainAxis:s=!0,crossAxis:i=!0}=xa(e,t),d={x:a,y:r},f=Wr(o),p=Ls(f),v=d[p],x=d[f],g=xa(u,t),L=typeof g=="number"?{mainAxis:g,crossAxis:0}:w({mainAxis:0,crossAxis:0},g);if(s){let h=p==="y"?"height":"width",y=n.reference[p]-n.floating[h]+L.mainAxis,C=n.reference[p]+n.reference[h]-L.mainAxis;vC&&(v=C)}if(i){var m,c;let h=p==="y"?"width":"height",y=["top","left"].includes(ya(o)),C=n.reference[f]-n.floating[h]+(y&&((m=l.offset)==null?void 0:m[f])||0)+(y?0:L.crossAxis),I=n.reference[f]+n.reference[h]+(y?0:((c=l.offset)==null?void 0:c[f])||0)-(y?L.crossAxis:0);xI&&(x=I)}return{[p]:v,[f]:x}}}},Av=function(e){return e===void 0&&(e={}),{name:"size",options:e,fn(a){return Pe(this,null,function*(){let{placement:r,rects:o,platform:n,elements:l}=a,k=xa(e,a),{apply:u=()=>{}}=k,s=A(k,["apply"]),i=yield pl(a,s),d=ya(r),f=jr(r),p=Wr(r)==="y",{width:v,height:x}=o.floating,g,L;d==="top"||d==="bottom"?(g=d,L=f===((yield n.isRTL==null?void 0:n.isRTL(l.floating))?"start":"end")?"left":"right"):(L=d,g=f==="end"?"top":"bottom");let m=x-i[g],c=v-i[L],h=!a.middlewareData.shift,y=m,C=c;if(p){let S=v-i.left-i.right;C=f||h?va(c,S):S}else{let S=x-i.top-i.bottom;y=f||h?va(m,S):S}if(h&&!f){let S=ot(i.left,0),M=ot(i.right,0),P=ot(i.top,0),U=ot(i.bottom,0);p?C=v-2*(S!==0||M!==0?S+M:ot(i.left,i.right)):y=x-2*(P!==0||U!==0?P+U:ot(i.top,i.bottom))}yield u(b(w({},a),{availableWidth:C,availableHeight:y}));let I=yield n.getDimensions(l.floating);return v!==I.width||x!==I.height?{reset:{rects:!0}}:{}})}}};function Na(e){return Fv(e)?(e.nodeName||"").toLowerCase():"#document"}function gt(e){var t;return(e==null||(t=e.ownerDocument)==null?void 0:t.defaultView)||window}function La(e){var t;return(t=(Fv(e)?e.ownerDocument:e.document)||window.document)==null?void 0:t.documentElement}function Fv(e){return e instanceof Node||e instanceof gt(e).Node}function wa(e){return e instanceof Element||e instanceof gt(e).Element}function oa(e){return e instanceof HTMLElement||e instanceof gt(e).HTMLElement}function Ov(e){return typeof ShadowRoot!="undefined"&&(e instanceof ShadowRoot||e instanceof gt(e).ShadowRoot)}function tn(e){let{overflow:t,overflowX:a,overflowY:r,display:o}=Mt(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+a)&&!["inline","contents"].includes(o)}function Bv(e){return["table","td","th"].includes(Na(e))}function Is(e){let t=Ss(),a=Mt(e);return a.transform!=="none"||a.perspective!=="none"||!!a.containerType&&a.containerType!=="normal"||!t&&!!a.backdropFilter&&a.backdropFilter!=="none"||!t&&!!a.filter&&a.filter!=="none"||["transform","perspective","filter"].some(r=>(a.willChange||"").includes(r))||["paint","layout","strict","content"].some(r=>(a.contain||"").includes(r))}function Nv(e){let t=Gr(e);for(;oa(t)&&!ml(t);){if(Is(t))return t;t=Gr(t)}return null}function Ss(){return!(typeof CSS=="undefined"||!CSS.supports)&&CSS.supports("-webkit-backdrop-filter","none")}function ml(e){return["html","body","#document"].includes(Na(e))}function Mt(e){return gt(e).getComputedStyle(e)}function hl(e){return wa(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Gr(e){if(Na(e)==="html")return e;let t=e.assignedSlot||e.parentNode||Ov(e)&&e.host||La(e);return Ov(t)?t.host:t}function zv(e){let t=Gr(e);return ml(t)?e.ownerDocument?e.ownerDocument.body:e.body:oa(t)&&tn(t)?t:zv(t)}function en(e,t,a){var r;t===void 0&&(t=[]),a===void 0&&(a=!0);let o=zv(e),n=o===((r=e.ownerDocument)==null?void 0:r.body),l=gt(o);return n?t.concat(l,l.visualViewport||[],tn(o)?o:[],l.frameElement&&a?en(l.frameElement):[]):t.concat(o,en(o,[],a))}function Vv(e){let t=Mt(e),a=parseFloat(t.width)||0,r=parseFloat(t.height)||0,o=oa(e),n=o?e.offsetWidth:a,l=o?e.offsetHeight:r,u=fl(a)!==n||fl(r)!==l;return u&&(a=n,r=l),{width:a,height:r,$:u}}function Df(e){return wa(e)?e:e.contextElement}function an(e){let t=Df(e);if(!oa(t))return Ba(1);let a=t.getBoundingClientRect(),{width:r,height:o,$:n}=Vv(t),l=(n?fl(a.width):a.width)/r,u=(n?fl(a.height):a.height)/o;return l&&Number.isFinite(l)||(l=1),u&&Number.isFinite(u)||(u=1),{x:l,y:u}}var cI=Ba(0);function jv(e){let t=gt(e);return Ss()&&t.visualViewport?{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}:cI}function Zr(e,t,a,r){t===void 0&&(t=!1),a===void 0&&(a=!1);let o=e.getBoundingClientRect(),n=Df(e),l=Ba(1);t&&(r?wa(r)&&(l=an(r)):l=an(e));let u=function(p,v,x){return v===void 0&&(v=!1),!(!x||v&&x!==gt(p))&&v}(n,a,r)?jv(n):Ba(0),s=(o.left+u.x)/l.x,i=(o.top+u.y)/l.y,d=o.width/l.x,f=o.height/l.y;if(n){let p=gt(n),v=r&&wa(r)?gt(r):r,x=p.frameElement;for(;x&&r&&v!==p;){let g=an(x),L=x.getBoundingClientRect(),m=Mt(x),c=L.left+(x.clientLeft+parseFloat(m.paddingLeft))*g.x,h=L.top+(x.clientTop+parseFloat(m.paddingTop))*g.y;s*=g.x,i*=g.y,d*=g.x,f*=g.y,s+=c,i+=h,x=gt(x).frameElement}}return Jo({width:d,height:f,x:s,y:i})}function Wv(e){return Zr(La(e)).left+hl(e).scrollLeft}function Uv(e,t,a){let r;if(t==="viewport")r=function(o,n){let l=gt(o),u=La(o),s=l.visualViewport,i=u.clientWidth,d=u.clientHeight,f=0,p=0;if(s){i=s.width,d=s.height;let v=Ss();(!v||v&&n==="fixed")&&(f=s.offsetLeft,p=s.offsetTop)}return{width:i,height:d,x:f,y:p}}(e,a);else if(t==="document")r=function(o){let n=La(o),l=hl(o),u=o.ownerDocument.body,s=ot(n.scrollWidth,n.clientWidth,u.scrollWidth,u.clientWidth),i=ot(n.scrollHeight,n.clientHeight,u.scrollHeight,u.clientHeight),d=-l.scrollLeft+Wv(o),f=-l.scrollTop;return Mt(u).direction==="rtl"&&(d+=ot(n.clientWidth,u.clientWidth)-s),{width:s,height:i,x:d,y:f}}(La(e));else if(wa(t))r=function(o,n){let l=Zr(o,!0,n==="fixed"),u=l.top+o.clientTop,s=l.left+o.clientLeft,i=oa(o)?an(o):Ba(1);return{width:o.clientWidth*i.x,height:o.clientHeight*i.y,x:s*i.x,y:u*i.y}}(t,a);else{let o=jv(e);r=b(w({},t),{x:t.x-o.x,y:t.y-o.y})}return Jo(r)}function Gv(e,t){let a=Gr(e);return!(a===t||!wa(a)||ml(a))&&(Mt(a).position==="fixed"||Gv(a,t))}function pI(e,t,a){let r=oa(t),o=La(t),n=a==="fixed",l=Zr(e,!0,n,t),u={scrollLeft:0,scrollTop:0},s=Ba(0);if(r||!r&&!n)if((Na(t)!=="body"||tn(o))&&(u=hl(t)),r){let i=Zr(t,!0,n,t);s.x=i.x+t.clientLeft,s.y=i.y+t.clientTop}else o&&(s.x=Wv(o));return{x:l.left+u.scrollLeft-s.x,y:l.top+u.scrollTop-s.y,width:l.width,height:l.height}}function qv(e,t){return oa(e)&&Mt(e).position!=="fixed"?t?t(e):e.offsetParent:null}function Hv(e,t){let a=gt(e);if(!oa(e))return a;let r=qv(e,t);for(;r&&Bv(r)&&Mt(r).position==="static";)r=qv(r,t);return r&&(Na(r)==="html"||Na(r)==="body"&&Mt(r).position==="static"&&!Is(r))?a:r||Nv(e)||a}var mI={convertOffsetParentRelativeRectToViewportRelativeRect:function(e){let{rect:t,offsetParent:a,strategy:r}=e,o=oa(a),n=La(a);if(a===n)return t;let l={scrollLeft:0,scrollTop:0},u=Ba(1),s=Ba(0);if((o||!o&&r!=="fixed")&&((Na(a)!=="body"||tn(n))&&(l=hl(a)),oa(a))){let i=Zr(a);u=an(a),s.x=i.x+a.clientLeft,s.y=i.y+a.clientTop}return{width:t.width*u.x,height:t.height*u.y,x:t.x*u.x-l.scrollLeft*u.x+s.x,y:t.y*u.y-l.scrollTop*u.y+s.y}},getDocumentElement:La,getClippingRect:function(e){let{element:t,boundary:a,rootBoundary:r,strategy:o}=e,n=[...a==="clippingAncestors"?function(s,i){let d=i.get(s);if(d)return d;let f=en(s,[],!1).filter(g=>wa(g)&&Na(g)!=="body"),p=null,v=Mt(s).position==="fixed",x=v?Gr(s):s;for(;wa(x)&&!ml(x);){let g=Mt(x),L=Is(x);L||g.position!=="fixed"||(p=null),(v?!L&&!p:!L&&g.position==="static"&&p&&["absolute","fixed"].includes(p.position)||tn(x)&&!L&&Gv(s,x))?f=f.filter(m=>m!==x):p=g,x=Gr(x)}return i.set(s,f),f}(t,this._c):[].concat(a),r],l=n[0],u=n.reduce((s,i)=>{let d=Uv(t,i,o);return s.top=ot(d.top,s.top),s.right=va(d.right,s.right),s.bottom=va(d.bottom,s.bottom),s.left=ot(d.left,s.left),s},Uv(t,l,o));return{width:u.right-u.left,height:u.bottom-u.top,x:u.left,y:u.top}},getOffsetParent:Hv,getElementRects:function(e){return Pe(this,null,function*(){let{reference:t,floating:a,strategy:r}=e,o=this.getOffsetParent||Hv,n=this.getDimensions;return{reference:pI(t,yield o(a),r),floating:w({x:0,y:0},yield n(a))}})},getClientRects:function(e){return Array.from(e.getClientRects())},getDimensions:function(e){return Vv(e)},getScale:an,isElement:wa,isRTL:function(e){return Mt(e).direction==="rtl"}};function Zv(e,t,a,r){r===void 0&&(r={});let{ancestorScroll:o=!0,ancestorResize:n=!0,elementResize:l=typeof ResizeObserver=="function",layoutShift:u=typeof IntersectionObserver=="function",animationFrame:s=!1}=r,i=Df(e),d=o||n?[...i?en(i):[],...en(t)]:[];d.forEach(L=>{o&&L.addEventListener("scroll",a,{passive:!0}),n&&L.addEventListener("resize",a)});let f=i&&u?function(L,m){let c,h=null,y=La(L);function C(){clearTimeout(c),h&&h.disconnect(),h=null}return function I(k,S){k===void 0&&(k=!1),S===void 0&&(S=1),C();let{left:M,top:P,width:U,height:j}=L.getBoundingClientRect();if(k||m(),!U||!j)return;let ae={rootMargin:-cl(P)+"px "+-cl(y.clientWidth-(M+U))+"px "+-cl(y.clientHeight-(P+j))+"px "+-cl(M)+"px",threshold:ot(0,va(1,S))||1},Z=!0;function ue(Q){let xe=Q[0].intersectionRatio;if(xe!==S){if(!Z)return I();xe?I(!1,xe):c=setTimeout(()=>{I(!1,1e-7)},100)}Z=!1}try{h=new IntersectionObserver(ue,b(w({},ae),{root:y.ownerDocument}))}catch(Q){h=new IntersectionObserver(ue,ae)}h.observe(L)}(!0),C}(i,a):null,p,v=-1,x=null;l&&(x=new ResizeObserver(L=>{let[m]=L;m&&m.target===i&&x&&(x.unobserve(t),cancelAnimationFrame(v),v=requestAnimationFrame(()=>{x&&x.observe(t)})),a()}),i&&!s&&x.observe(i),x.observe(t));let g=s?Zr(e):null;return s&&function L(){let m=Zr(e);!g||m.x===g.x&&m.y===g.y&&m.width===g.width&&m.height===g.height||a(),g=m,p=requestAnimationFrame(L)}(),a(),()=>{d.forEach(L=>{o&&L.removeEventListener("scroll",a),n&&L.removeEventListener("resize",a)}),f&&f(),x&&x.disconnect(),x=null,s&&cancelAnimationFrame(p)}}var $v=(e,t,a)=>{let r=new Map,o=w({platform:mI},a),n=b(w({},o.platform),{_c:r});return Tv(e,t,b(w({},o),{platform:n}))};var be=N(G(),1),Ms=N(G(),1),Qv=N(Vo(),1);var Yv=e=>({name:"arrow",options:e,fn(t){let{element:a,padding:r}=typeof e=="function"?e(t):e;return a&&(o=a,{}.hasOwnProperty.call(o,"current"))?a.current!=null?Rf({element:a.current,padding:r}).fn(t):{}:a?Rf({element:a,padding:r}).fn(t):{};var o}}),ks=typeof document!="undefined"?Ms.useLayoutEffect:Ms.useEffect;function Ps(e,t){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(typeof e=="function"&&e.toString()===t.toString())return!0;let a,r,o;if(e&&t&&typeof e=="object"){if(Array.isArray(e)){if(a=e.length,a!=t.length)return!1;for(r=a;r--!=0;)if(!Ps(e[r],t[r]))return!1;return!0}if(o=Object.keys(e),a=o.length,a!==Object.keys(t).length)return!1;for(r=a;r--!=0;)if(!{}.hasOwnProperty.call(t,o[r]))return!1;for(r=a;r--!=0;){let n=o[r];if((n!=="_owner"||!e.$$typeof)&&!Ps(e[n],t[n]))return!1}return!0}return e!=e&&t!=t}function Jv(e){return typeof window=="undefined"?1:(e.ownerDocument.defaultView||window).devicePixelRatio||1}function Kv(e,t){let a=Jv(e);return Math.round(t*a)/a}function Xv(e){let t=be.useRef(e);return ks(()=>{t.current=e}),t}function ex(e){e===void 0&&(e={});let{placement:t="bottom",strategy:a="absolute",middleware:r=[],platform:o,elements:{reference:n,floating:l}={},transform:u=!0,whileElementsMounted:s,open:i}=e,[d,f]=be.useState({x:0,y:0,strategy:a,placement:t,middlewareData:{},isPositioned:!1}),[p,v]=be.useState(r);Ps(p,r)||v(r);let[x,g]=be.useState(null),[L,m]=be.useState(null),c=be.useCallback(Q=>{Q!=I.current&&(I.current=Q,g(Q))},[g]),h=be.useCallback(Q=>{Q!==k.current&&(k.current=Q,m(Q))},[m]),y=n||x,C=l||L,I=be.useRef(null),k=be.useRef(null),S=be.useRef(d),M=Xv(s),P=Xv(o),U=be.useCallback(()=>{if(!I.current||!k.current)return;let Q={placement:t,strategy:a,middleware:p};P.current&&(Q.platform=P.current),$v(I.current,k.current,Q).then(xe=>{let le=b(w({},xe),{isPositioned:!0});j.current&&!Ps(S.current,le)&&(S.current=le,Qv.flushSync(()=>{f(le)}))})},[p,t,a,P]);ks(()=>{i===!1&&S.current.isPositioned&&(S.current.isPositioned=!1,f(Q=>b(w({},Q),{isPositioned:!1})))},[i]);let j=be.useRef(!1);ks(()=>(j.current=!0,()=>{j.current=!1}),[]),ks(()=>{if(y&&(I.current=y),C&&(k.current=C),y&&C){if(M.current)return M.current(y,C,U);U()}},[y,C,U,M]);let ae=be.useMemo(()=>({reference:I,floating:k,setReference:c,setFloating:h}),[c,h]),Z=be.useMemo(()=>({reference:y,floating:C}),[y,C]),ue=be.useMemo(()=>{let Q={position:a,left:0,top:0};if(!Z.floating)return Q;let xe=Kv(Z.floating,d.x),le=Kv(Z.floating,d.y);return u?w(b(w({},Q),{transform:"translate("+xe+"px, "+le+"px)"}),Jv(Z.floating)>=1.5&&{willChange:"transform"}):{position:a,left:xe,top:le}},[a,u,Z.floating,d.x,d.y]);return be.useMemo(()=>b(w({},d),{update:U,refs:ae,elements:Z,floatingStyles:ue}),[d,U,ae,Z,ue])}var tx="Popper",[ax,bf]=zt(tx),[hI,rx]=ax(tx),gI=e=>{let{__scopePopper:t,children:a}=e,[r,o]=(0,Ne.useState)(null);return(0,Ne.createElement)(hI,{scope:t,anchor:r,onAnchorChange:o},a)},vI=(0,Ne.forwardRef)((e,t)=>{let s=e,{__scopePopper:a,virtualRef:r}=s,o=A(s,["__scopePopper","virtualRef"]),n=rx("PopperAnchor",a),l=(0,Ne.useRef)(null),u=se(t,l);return(0,Ne.useEffect)(()=>{n.onAnchorChange((r==null?void 0:r.current)||l.current)}),r?null:(0,Ne.createElement)(oe.div,E({},o,{ref:u}))}),ox="PopperContent",[xI,EP]=ax(ox),yI=(0,Ne.forwardRef)((e,t)=>{var a,r,o,n,l,u,s,i;let ja=e,{__scopePopper:d,side:f="bottom",sideOffset:p=0,align:v="center",alignOffset:x=0,arrowPadding:g=0,avoidCollisions:L=!0,collisionBoundary:m=[],collisionPadding:c=0,sticky:h="partial",hideWhenDetached:y=!1,updatePositionStrategy:C="optimized",onPlaced:I}=ja,k=A(ja,["__scopePopper","side","sideOffset","align","alignOffset","arrowPadding","avoidCollisions","collisionBoundary","collisionPadding","sticky","hideWhenDetached","updatePositionStrategy","onPlaced"]),S=rx(ox,d),[M,P]=(0,Ne.useState)(null),U=se(t,Lt=>P(Lt)),[j,ae]=(0,Ne.useState)(null),Z=Lv(j),ue=(a=Z==null?void 0:Z.width)!==null&&a!==void 0?a:0,Q=(r=Z==null?void 0:Z.height)!==null&&r!==void 0?r:0,xe=f+(v!=="center"?"-"+v:""),le=typeof c=="number"?c:w({top:0,right:0,bottom:0,left:0},c),Je=Array.isArray(m)?m:[m],W=Je.length>0,ye={padding:le,boundary:Je.filter(LI),altBoundary:W},{refs:yt,floatingStyles:Dt,placement:sa,isPositioned:jt,middlewareData:Ve}=ex({strategy:"fixed",placement:xe,whileElementsMounted:(...Lt)=>Zv(...Lt,{animationFrame:C==="always"}),elements:{reference:S.anchor},middleware:[Dv({mainAxis:p+Q,alignmentAxis:x}),L&&bv(w({mainAxis:!0,crossAxis:!1,limiter:h==="partial"?Ev():void 0},ye)),L&&_v(w({},ye)),Av(b(w({},ye),{apply:({elements:Lt,rects:Rl,availableWidth:Dl,availableHeight:Wa})=>{let{width:Zs,height:Oy}=Rl.reference,bl=Lt.floating.style;bl.setProperty("--radix-popper-available-width",`${Dl}px`),bl.setProperty("--radix-popper-available-height",`${Wa}px`),bl.setProperty("--radix-popper-anchor-width",`${Zs}px`),bl.setProperty("--radix-popper-anchor-height",`${Oy}px`)}})),j&&Yv({element:j,padding:g}),wI({arrowWidth:ue,arrowHeight:Q}),y&&Rv(w({strategy:"referenceHidden"},ye))]}),[lt,Sa]=nx(sa),_e=fe(I);mt(()=>{jt&&(_e==null||_e())},[jt,_e]);let ut=(o=Ve.arrow)===null||o===void 0?void 0:o.x,Wt=(n=Ve.arrow)===null||n===void 0?void 0:n.y,Cr=((l=Ve.arrow)===null||l===void 0?void 0:l.centerOffset)!==0,[Ir,Sr]=(0,Ne.useState)();return mt(()=>{M&&Sr(window.getComputedStyle(M).zIndex)},[M]),(0,Ne.createElement)("div",{ref:yt.setFloating,"data-radix-popper-content-wrapper":"",style:b(w({},Dt),{transform:jt?Dt.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:Ir,"--radix-popper-transform-origin":[(u=Ve.transformOrigin)===null||u===void 0?void 0:u.x,(s=Ve.transformOrigin)===null||s===void 0?void 0:s.y].join(" ")}),dir:e.dir},(0,Ne.createElement)(xI,{scope:d,placedSide:lt,onArrowChange:ae,arrowX:ut,arrowY:Wt,shouldHideArrow:Cr},(0,Ne.createElement)(oe.div,E({"data-side":lt,"data-align":Sa},k,{ref:U,style:b(w({},k.style),{animation:jt?void 0:"none",opacity:(i=Ve.hide)!==null&&i!==void 0&&i.referenceHidden?0:void 0})}))))});function LI(e){return e!==null}var wI=e=>({name:"transformOrigin",options:e,fn(t){var a,r,o,n,l;let{placement:u,rects:s,middlewareData:i}=t,d=((a=i.arrow)===null||a===void 0?void 0:a.centerOffset)!==0,f=d?0:e.arrowWidth,p=d?0:e.arrowHeight,[v,x]=nx(u),g={start:"0%",center:"50%",end:"100%"}[x],L=((r=(o=i.arrow)===null||o===void 0?void 0:o.x)!==null&&r!==void 0?r:0)+f/2,m=((n=(l=i.arrow)===null||l===void 0?void 0:l.y)!==null&&n!==void 0?n:0)+p/2,c="",h="";return v==="bottom"?(c=d?g:`${L}px`,h=-p+"px"):v==="top"?(c=d?g:`${L}px`,h=`${s.floating.height+p}px`):v==="right"?(c=-p+"px",h=d?g:`${m}px`):v==="left"&&(c=`${s.floating.width+p}px`,h=d?g:`${m}px`),{data:{x:c,y:h}}}});function nx(e){let[t,a="center"]=e.split("-");return[t,a]}var lx=gI,ux=vI,sx=yI;var pe=N(G(),1);var Ef="rovingFocusGroup.onEntryFocus",CI={bubbles:!1,cancelable:!0},Of="RovingFocusGroup",[Af,ix,II]=qr(Of),[SI,Ff]=zt(Of,[II]),[kI,PI]=SI(Of),MI=(0,pe.forwardRef)((e,t)=>(0,pe.createElement)(Af.Provider,{scope:e.__scopeRovingFocusGroup},(0,pe.createElement)(Af.Slot,{scope:e.__scopeRovingFocusGroup},(0,pe.createElement)(TI,E({},e,{ref:t}))))),TI=(0,pe.forwardRef)((e,t)=>{let k=e,{__scopeRovingFocusGroup:a,orientation:r,loop:o=!1,dir:n,currentTabStopId:l,defaultCurrentTabStopId:u,onCurrentTabStopIdChange:s,onEntryFocus:i}=k,d=A(k,["__scopeRovingFocusGroup","orientation","loop","dir","currentTabStopId","defaultCurrentTabStopId","onCurrentTabStopIdChange","onEntryFocus"]),f=(0,pe.useRef)(null),p=se(t,f),v=$o(n),[x=null,g]=Go({prop:l,defaultProp:u,onChange:s}),[L,m]=(0,pe.useState)(!1),c=fe(i),h=ix(a),y=(0,pe.useRef)(!1),[C,I]=(0,pe.useState)(0);return(0,pe.useEffect)(()=>{let S=f.current;if(S)return S.addEventListener(Ef,c),()=>S.removeEventListener(Ef,c)},[c]),(0,pe.createElement)(kI,{scope:a,orientation:r,dir:v,loop:o,currentTabStopId:x,onItemFocus:(0,pe.useCallback)(S=>g(S),[g]),onItemShiftTab:(0,pe.useCallback)(()=>m(!0),[]),onFocusableItemAdd:(0,pe.useCallback)(()=>I(S=>S+1),[]),onFocusableItemRemove:(0,pe.useCallback)(()=>I(S=>S-1),[])},(0,pe.createElement)(oe.div,E({tabIndex:L||C===0?-1:0,"data-orientation":r},d,{ref:p,style:w({outline:"none"},e.style),onMouseDown:q(e.onMouseDown,()=>{y.current=!0}),onFocus:q(e.onFocus,S=>{let M=!y.current;if(S.target===S.currentTarget&&M&&!L){let P=new CustomEvent(Ef,CI);if(S.currentTarget.dispatchEvent(P),!P.defaultPrevented){let U=h().filter(j=>j.focusable);dx([U.find(j=>j.active),U.find(j=>j.id===x),...U].filter(Boolean).map(j=>j.ref.current))}}y.current=!1}),onBlur:q(e.onBlur,()=>m(!1))})))}),_I=(0,pe.forwardRef)((e,t)=>{let x=e,{__scopeRovingFocusGroup:a,focusable:r=!0,active:o=!1,tabStopId:n}=x,l=A(x,["__scopeRovingFocusGroup","focusable","active","tabStopId"]),u=gr(),s=n||u,i=PI("RovingFocusGroupItem",a),d=i.currentTabStopId===s,f=ix(a),{onFocusableItemAdd:p,onFocusableItemRemove:v}=i;return(0,pe.useEffect)(()=>{if(r)return p(),()=>v()},[r,p,v]),(0,pe.createElement)(Af.ItemSlot,{scope:a,id:s,focusable:r,active:o},(0,pe.createElement)(oe.span,E({tabIndex:d?0:-1,"data-orientation":i.orientation},l,{ref:t,onMouseDown:q(e.onMouseDown,g=>{r?i.onItemFocus(s):g.preventDefault()}),onFocus:q(e.onFocus,()=>i.onItemFocus(s)),onKeyDown:q(e.onKeyDown,g=>{if(g.key==="Tab"&&g.shiftKey)return void i.onItemShiftTab();if(g.target!==g.currentTarget)return;let L=function(h,y,C){let I=function(k,S){return S!=="rtl"?k:k==="ArrowLeft"?"ArrowRight":k==="ArrowRight"?"ArrowLeft":k}(h.key,C);return y==="vertical"&&["ArrowLeft","ArrowRight"].includes(I)||y==="horizontal"&&["ArrowUp","ArrowDown"].includes(I)?void 0:RI[I]}(g,i.orientation,i.dir);if(L!==void 0){g.preventDefault();let h=f().filter(y=>y.focusable).map(y=>y.ref.current);if(L==="last")h.reverse();else if(L==="prev"||L==="next"){L==="prev"&&h.reverse();let y=h.indexOf(g.currentTarget);h=i.loop?(c=y+1,(m=h).map((C,I)=>m[(c+I)%m.length])):h.slice(y+1)}setTimeout(()=>dx(h))}var m,c})})))}),RI={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function dx(e){let t=document.activeElement;for(let a of e)if(a===t||(a.focus(),document.activeElement!==t))return}var fx=MI,cx=_I;var Bf=["Enter"," "],mx=["ArrowUp","PageDown","End"],DI=["ArrowDown","PageUp","Home",...mx],bI={ltr:[...Bf,"ArrowRight"],rtl:[...Bf,"ArrowLeft"]},EI={ltr:["ArrowLeft"],rtl:["ArrowRight"]},Rs="Menu",[gl,AI,OI]=qr(Rs),[$r,zf]=zt(Rs,[OI,bf,Ff]),Uf=bf(),hx=Ff(),[FI,rn]=$r(Rs),[BI,xl]=$r(Rs),NI=e=>{let{__scopeMenu:t,open:a=!1,children:r,dir:o,onOpenChange:n,modal:l=!0}=e,u=Uf(t),[s,i]=(0,R.useState)(null),d=(0,R.useRef)(!1),f=fe(n),p=$o(o);return(0,R.useEffect)(()=>{let v=()=>{d.current=!0,document.addEventListener("pointerdown",x,{capture:!0,once:!0}),document.addEventListener("pointermove",x,{capture:!0,once:!0})},x=()=>d.current=!1;return document.addEventListener("keydown",v,{capture:!0}),()=>{document.removeEventListener("keydown",v,{capture:!0}),document.removeEventListener("pointerdown",x,{capture:!0}),document.removeEventListener("pointermove",x,{capture:!0})}},[]),(0,R.createElement)(lx,u,(0,R.createElement)(FI,{scope:t,open:a,onOpenChange:f,content:s,onContentChange:i},(0,R.createElement)(BI,{scope:t,onClose:(0,R.useCallback)(()=>f(!1),[f]),isUsingKeyboardRef:d,dir:p,modal:l},r)))},gx=(0,R.forwardRef)((e,t)=>{let n=e,{__scopeMenu:a}=n,r=A(n,["__scopeMenu"]),o=Uf(a);return(0,R.createElement)(ux,E({},o,r,{ref:t}))}),zI="MenuPortal",[f2,vx]=$r(zI,{forceMount:void 0});var na="MenuContent",[UI,qf]=$r(na),qI=(0,R.forwardRef)((e,t)=>{let a=vx(na,e.__scopeMenu),u=e,{forceMount:r=a.forceMount}=u,o=A(u,["forceMount"]),n=rn(na,e.__scopeMenu),l=xl(na,e.__scopeMenu);return(0,R.createElement)(gl.Provider,{scope:e.__scopeMenu},(0,R.createElement)(ga,{present:r||n.open},(0,R.createElement)(gl.Slot,{scope:e.__scopeMenu},l.modal?(0,R.createElement)(HI,E({},o,{ref:t})):(0,R.createElement)(VI,E({},o,{ref:t})))))}),HI=(0,R.forwardRef)((e,t)=>{let a=rn(na,e.__scopeMenu),r=(0,R.useRef)(null),o=se(t,r);return(0,R.useEffect)(()=>{let n=r.current;if(n)return vv(n)},[]),(0,R.createElement)(Hf,E({},e,{ref:o,trapFocus:a.open,disableOutsidePointerEvents:a.open,disableOutsideScroll:!0,onFocusOutside:q(e.onFocusOutside,n=>n.preventDefault(),{checkForDefaultPrevented:!1}),onDismiss:()=>a.onOpenChange(!1)}))}),VI=(0,R.forwardRef)((e,t)=>{let a=rn(na,e.__scopeMenu);return(0,R.createElement)(Hf,E({},e,{ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>a.onOpenChange(!1)}))}),Hf=(0,R.forwardRef)((e,t)=>{let Je=e,{__scopeMenu:a,loop:r=!1,trapFocus:o,onOpenAutoFocus:n,onCloseAutoFocus:l,disableOutsidePointerEvents:u,onEntryFocus:s,onEscapeKeyDown:i,onPointerDownOutside:d,onFocusOutside:f,onInteractOutside:p,onDismiss:v,disableOutsideScroll:x}=Je,g=A(Je,["__scopeMenu","loop","trapFocus","onOpenAutoFocus","onCloseAutoFocus","disableOutsidePointerEvents","onEntryFocus","onEscapeKeyDown","onPointerDownOutside","onFocusOutside","onInteractOutside","onDismiss","disableOutsideScroll"]),L=rn(na,a),m=xl(na,a),c=Uf(a),h=hx(a),y=AI(a),[C,I]=(0,R.useState)(null),k=(0,R.useRef)(null),S=se(t,k,L.onContentChange),M=(0,R.useRef)(0),P=(0,R.useRef)(""),U=(0,R.useRef)(0),j=(0,R.useRef)(null),ae=(0,R.useRef)("right"),Z=(0,R.useRef)(0),ue=x?Mf:R.Fragment,Q=x?{as:ha,allowPinchZoom:!0}:void 0,xe=W=>{var ye,yt;let Dt=P.current+W,sa=y().filter(_e=>!_e.disabled),jt=document.activeElement,Ve=(ye=sa.find(_e=>_e.ref.current===jt))===null||ye===void 0?void 0:ye.textValue,lt=function(_e,ut,Wt){let Cr=ut.length>1&&Array.from(ut).every(Wa=>Wa===ut[0]),Ir=Cr?ut[0]:ut,Sr=Wt?_e.indexOf(Wt):-1,ja=(Lt=_e,Rl=Math.max(Sr,0),Lt.map((Wa,Zs)=>Lt[(Rl+Zs)%Lt.length]));var Lt,Rl;Ir.length===1&&(ja=ja.filter(Wa=>Wa!==Wt));let Dl=ja.find(Wa=>Wa.toLowerCase().startsWith(Ir.toLowerCase()));return Dl!==Wt?Dl:void 0}(sa.map(_e=>_e.textValue),Dt,Ve),Sa=(yt=sa.find(_e=>_e.textValue===lt))===null||yt===void 0?void 0:yt.ref.current;(function _e(ut){P.current=ut,window.clearTimeout(M.current),ut!==""&&(M.current=window.setTimeout(()=>_e(""),1e3))})(Dt),Sa&&setTimeout(()=>Sa.focus())};(0,R.useEffect)(()=>()=>window.clearTimeout(M.current),[]),jg();let le=(0,R.useCallback)(W=>{var ye,yt;return ae.current===((ye=j.current)===null||ye===void 0?void 0:ye.side)&&function(Dt,sa){if(!sa)return!1;let jt={x:Dt.clientX,y:Dt.clientY};return function(Ve,lt){let{x:Sa,y:_e}=Ve,ut=!1;for(let Wt=0,Cr=lt.length-1;Wt_e!=Lt>_e&&Sa<(ja-Ir)*(_e-Sr)/(Lt-Sr)+Ir&&(ut=!ut)}return ut}(jt,sa)}(W,(yt=j.current)===null||yt===void 0?void 0:yt.area)},[]);return(0,R.createElement)(UI,{scope:a,searchRef:P,onItemEnter:(0,R.useCallback)(W=>{le(W)&&W.preventDefault()},[le]),onItemLeave:(0,R.useCallback)(W=>{var ye;le(W)||((ye=k.current)===null||ye===void 0||ye.focus(),I(null))},[le]),onTriggerLeave:(0,R.useCallback)(W=>{le(W)&&W.preventDefault()},[le]),pointerGraceTimerRef:U,onPointerGraceIntentChange:(0,R.useCallback)(W=>{j.current=W},[])},(0,R.createElement)(ue,Q,(0,R.createElement)(qg,{asChild:!0,trapped:o,onMountAutoFocus:q(n,W=>{var ye;W.preventDefault(),(ye=k.current)===null||ye===void 0||ye.focus()}),onUnmountAutoFocus:l},(0,R.createElement)(ds,{asChild:!0,disableOutsidePointerEvents:u,onEscapeKeyDown:i,onPointerDownOutside:d,onFocusOutside:f,onInteractOutside:p,onDismiss:v},(0,R.createElement)(fx,E({asChild:!0},h,{dir:m.dir,orientation:"vertical",loop:r,currentTabStopId:C,onCurrentTabStopIdChange:I,onEntryFocus:q(s,W=>{m.isUsingKeyboardRef.current||W.preventDefault()})}),(0,R.createElement)(sx,E({role:"menu","aria-orientation":"vertical","data-state":Ix(L.open),"data-radix-menu-content":"",dir:m.dir},c,g,{ref:S,style:w({outline:"none"},g.style),onKeyDown:q(g.onKeyDown,W=>{let ye=W.target.closest("[data-radix-menu-content]")===W.currentTarget,yt=W.ctrlKey||W.altKey||W.metaKey,Dt=W.key.length===1;ye&&(W.key==="Tab"&&W.preventDefault(),!yt&&Dt&&xe(W.key));let sa=k.current;if(W.target!==sa||!DI.includes(W.key))return;W.preventDefault();let jt=y().filter(Ve=>!Ve.disabled).map(Ve=>Ve.ref.current);mx.includes(W.key)&&jt.reverse(),function(Ve){let lt=document.activeElement;for(let Sa of Ve)if(Sa===lt||(Sa.focus(),document.activeElement!==lt))return}(jt)}),onBlur:q(e.onBlur,W=>{W.currentTarget.contains(W.target)||(window.clearTimeout(M.current),P.current="")}),onPointerMove:q(e.onPointerMove,vl(W=>{let ye=W.target,yt=Z.current!==W.clientX;if(W.currentTarget.contains(ye)&&yt){let Dt=W.clientX>Z.current?"right":"left";ae.current=Dt,Z.current=W.clientX}}))})))))))}),xx=(0,R.forwardRef)((e,t)=>{let o=e,{__scopeMenu:a}=o,r=A(o,["__scopeMenu"]);return(0,R.createElement)(oe.div,E({role:"group"},r,{ref:t}))}),jI=(0,R.forwardRef)((e,t)=>{let o=e,{__scopeMenu:a}=o,r=A(o,["__scopeMenu"]);return(0,R.createElement)(oe.div,E({},r,{ref:t}))}),Nf="MenuItem",px="menu.itemSelect",Vf=(0,R.forwardRef)((e,t)=>{let d=e,{disabled:a=!1,onSelect:r}=d,o=A(d,["disabled","onSelect"]),n=(0,R.useRef)(null),l=xl(Nf,e.__scopeMenu),u=qf(Nf,e.__scopeMenu),s=se(t,n),i=(0,R.useRef)(!1);return(0,R.createElement)(yx,E({},o,{ref:s,disabled:a,onClick:q(e.onClick,()=>{let f=n.current;if(!a&&f){let p=new CustomEvent(px,{bubbles:!0,cancelable:!0});f.addEventListener(px,v=>r==null?void 0:r(v),{once:!0}),Hr(f,p),p.defaultPrevented?i.current=!1:l.onClose()}}),onPointerDown:f=>{var p;(p=e.onPointerDown)===null||p===void 0||p.call(e,f),i.current=!0},onPointerUp:q(e.onPointerUp,f=>{var p;i.current||(p=f.currentTarget)===null||p===void 0||p.click()}),onKeyDown:q(e.onKeyDown,f=>{let p=u.searchRef.current!=="";a||p&&f.key===" "||Bf.includes(f.key)&&(f.currentTarget.click(),f.preventDefault())})}))}),yx=(0,R.forwardRef)((e,t)=>{let x=e,{__scopeMenu:a,disabled:r=!1,textValue:o}=x,n=A(x,["__scopeMenu","disabled","textValue"]),l=qf(Nf,a),u=hx(a),s=(0,R.useRef)(null),i=se(t,s),[d,f]=(0,R.useState)(!1),[p,v]=(0,R.useState)("");return(0,R.useEffect)(()=>{let g=s.current;var L;g&&v(((L=g.textContent)!==null&&L!==void 0?L:"").trim())},[n.children]),(0,R.createElement)(gl.ItemSlot,{scope:a,disabled:r,textValue:o!=null?o:p},(0,R.createElement)(cx,E({asChild:!0},u,{focusable:!r}),(0,R.createElement)(oe.div,E({role:"menuitem","data-highlighted":d?"":void 0,"aria-disabled":r||void 0,"data-disabled":r?"":void 0},n,{ref:i,onPointerMove:q(e.onPointerMove,vl(g=>{r?l.onItemLeave(g):(l.onItemEnter(g),!g.defaultPrevented&&g.currentTarget.focus())})),onPointerLeave:q(e.onPointerLeave,vl(g=>l.onItemLeave(g))),onFocus:q(e.onFocus,()=>f(!0)),onBlur:q(e.onBlur,()=>f(!1))}))))}),WI=(0,R.forwardRef)((e,t)=>{let n=e,{checked:a=!1,onCheckedChange:r}=n,o=A(n,["checked","onCheckedChange"]);return(0,R.createElement)(wx,{scope:e.__scopeMenu,checked:a},(0,R.createElement)(Vf,E({role:"menuitemcheckbox","aria-checked":_s(a)?"mixed":a},o,{ref:t,"data-state":jf(a),onSelect:q(o.onSelect,()=>r==null?void 0:r(!!_s(a)||!a),{checkForDefaultPrevented:!1})})))}),[GI,ZI]=$r("MenuRadioGroup",{value:void 0,onValueChange:()=>{}}),$I=(0,R.forwardRef)((e,t)=>{let l=e,{value:a,onValueChange:r}=l,o=A(l,["value","onValueChange"]),n=fe(r);return(0,R.createElement)(GI,{scope:e.__scopeMenu,value:a,onValueChange:n},(0,R.createElement)(xx,E({},o,{ref:t})))}),KI=(0,R.forwardRef)((e,t)=>{let l=e,{value:a}=l,r=A(l,["value"]),o=ZI("MenuRadioItem",e.__scopeMenu),n=a===o.value;return(0,R.createElement)(wx,{scope:e.__scopeMenu,checked:n},(0,R.createElement)(Vf,E({role:"menuitemradio","aria-checked":n},r,{ref:t,"data-state":jf(n),onSelect:q(r.onSelect,()=>{var u;return(u=o.onValueChange)===null||u===void 0?void 0:u.call(o,a)},{checkForDefaultPrevented:!1})})))}),Lx="MenuItemIndicator",[wx,XI]=$r(Lx,{checked:!1}),QI=(0,R.forwardRef)((e,t)=>{let l=e,{__scopeMenu:a,forceMount:r}=l,o=A(l,["__scopeMenu","forceMount"]),n=XI(Lx,a);return(0,R.createElement)(ga,{present:r||_s(n.checked)||n.checked===!0},(0,R.createElement)(oe.span,E({},o,{ref:t,"data-state":jf(n.checked)})))}),YI=(0,R.forwardRef)((e,t)=>{let o=e,{__scopeMenu:a}=o,r=A(o,["__scopeMenu"]);return(0,R.createElement)(oe.div,E({role:"separator","aria-orientation":"horizontal"},r,{ref:t}))}),JI="MenuSub",[c2,Cx]=$r(JI);var Ts="MenuSubTrigger",e0=(0,R.forwardRef)((e,t)=>{let a=rn(Ts,e.__scopeMenu),r=xl(Ts,e.__scopeMenu),o=Cx(Ts,e.__scopeMenu),n=qf(Ts,e.__scopeMenu),l=(0,R.useRef)(null),{pointerGraceTimerRef:u,onPointerGraceIntentChange:s}=n,i={__scopeMenu:e.__scopeMenu},d=(0,R.useCallback)(()=>{l.current&&window.clearTimeout(l.current),l.current=null},[]);return(0,R.useEffect)(()=>d,[d]),(0,R.useEffect)(()=>{let f=u.current;return()=>{window.clearTimeout(f),s(null)}},[u,s]),(0,R.createElement)(gx,E({asChild:!0},i),(0,R.createElement)(yx,E({id:o.triggerId,"aria-haspopup":"menu","aria-expanded":a.open,"aria-controls":o.contentId,"data-state":Ix(a.open)},e,{ref:Fa(t,o.onTriggerChange),onClick:f=>{var p;(p=e.onClick)===null||p===void 0||p.call(e,f),e.disabled||f.defaultPrevented||(f.currentTarget.focus(),a.open||a.onOpenChange(!0))},onPointerMove:q(e.onPointerMove,vl(f=>{n.onItemEnter(f),f.defaultPrevented||e.disabled||a.open||l.current||(n.onPointerGraceIntentChange(null),l.current=window.setTimeout(()=>{a.onOpenChange(!0),d()},100))})),onPointerLeave:q(e.onPointerLeave,vl(f=>{var p;d();let v=(p=a.content)===null||p===void 0?void 0:p.getBoundingClientRect();if(v){var x;let g=(x=a.content)===null||x===void 0?void 0:x.dataset.side,L=g==="right",m=L?-5:5,c=v[L?"left":"right"],h=v[L?"right":"left"];n.onPointerGraceIntentChange({area:[{x:f.clientX+m,y:f.clientY},{x:c,y:v.top},{x:h,y:v.top},{x:h,y:v.bottom},{x:c,y:v.bottom}],side:g}),window.clearTimeout(u.current),u.current=window.setTimeout(()=>n.onPointerGraceIntentChange(null),300)}else{if(n.onTriggerLeave(f),f.defaultPrevented)return;n.onPointerGraceIntentChange(null)}})),onKeyDown:q(e.onKeyDown,f=>{let p=n.searchRef.current!=="";var v;e.disabled||p&&f.key===" "||bI[r.dir].includes(f.key)&&(a.onOpenChange(!0),(v=a.content)===null||v===void 0||v.focus(),f.preventDefault())})})))}),t0=(0,R.forwardRef)((e,t)=>{let a=vx(na,e.__scopeMenu),d=e,{forceMount:r=a.forceMount}=d,o=A(d,["forceMount"]),n=rn(na,e.__scopeMenu),l=xl(na,e.__scopeMenu),u=Cx("MenuSubContent",e.__scopeMenu),s=(0,R.useRef)(null),i=se(t,s);return(0,R.createElement)(gl.Provider,{scope:e.__scopeMenu},(0,R.createElement)(ga,{present:r||n.open},(0,R.createElement)(gl.Slot,{scope:e.__scopeMenu},(0,R.createElement)(Hf,E({id:u.contentId,"aria-labelledby":u.triggerId},o,{ref:i,align:"start",side:l.dir==="rtl"?"left":"right",disableOutsidePointerEvents:!1,disableOutsideScroll:!1,trapFocus:!1,onOpenAutoFocus:f=>{var p;l.isUsingKeyboardRef.current&&((p=s.current)===null||p===void 0||p.focus()),f.preventDefault()},onCloseAutoFocus:f=>f.preventDefault(),onFocusOutside:q(e.onFocusOutside,f=>{f.target!==u.trigger&&n.onOpenChange(!1)}),onEscapeKeyDown:q(e.onEscapeKeyDown,f=>{l.onClose(),f.preventDefault()}),onKeyDown:q(e.onKeyDown,f=>{let p=f.currentTarget.contains(f.target),v=EI[l.dir].includes(f.key);var x;p&&v&&(n.onOpenChange(!1),(x=u.trigger)===null||x===void 0||x.focus(),f.preventDefault())})})))))});function Ix(e){return e?"open":"closed"}function _s(e){return e==="indeterminate"}function jf(e){return _s(e)?"indeterminate":e?"checked":"unchecked"}function vl(e){return t=>t.pointerType==="mouse"?e(t):void 0}var Sx=NI,kx=gx;var Px=qI,Mx=xx,Tx=jI,_x=Vf,Rx=WI,Dx=$I,bx=KI,Ex=QI,Ax=YI;var Ox=e0,Fx=t0;var ze=N(Ke(),1),za=N(G(),1);var ee=N(G(),1);var Bx="DropdownMenu",[a0,k2]=zt(Bx,[zf]),Tt=zf(),[r0,Nx]=a0(Bx),o0=e=>{let{__scopeDropdownMenu:t,children:a,dir:r,open:o,defaultOpen:n,onOpenChange:l,modal:u=!0}=e,s=Tt(t),i=(0,ee.useRef)(null),[d=!1,f]=Go({prop:o,defaultProp:n,onChange:l});return(0,ee.createElement)(r0,{scope:t,triggerId:gr(),triggerRef:i,contentId:gr(),open:d,onOpenChange:f,onOpenToggle:(0,ee.useCallback)(()=>f(p=>!p),[f]),modal:u},(0,ee.createElement)(Sx,E({},s,{open:d,onOpenChange:f,dir:r,modal:u}),a))},n0=(0,ee.forwardRef)((e,t)=>{let u=e,{__scopeDropdownMenu:a,disabled:r=!1}=u,o=A(u,["__scopeDropdownMenu","disabled"]),n=Nx("DropdownMenuTrigger",a),l=Tt(a);return(0,ee.createElement)(kx,E({asChild:!0},l),(0,ee.createElement)(oe.button,E({type:"button",id:n.triggerId,"aria-haspopup":"menu","aria-expanded":n.open,"aria-controls":n.open?n.contentId:void 0,"data-state":n.open?"open":"closed","data-disabled":r?"":void 0,disabled:r},o,{ref:Fa(t,n.triggerRef),onPointerDown:q(e.onPointerDown,s=>{r||s.button!==0||s.ctrlKey!==!1||(n.onOpenToggle(),n.open||s.preventDefault())}),onKeyDown:q(e.onKeyDown,s=>{r||(["Enter"," "].includes(s.key)&&n.onOpenToggle(),s.key==="ArrowDown"&&n.onOpenChange(!0),["Enter"," ","ArrowDown"].includes(s.key)&&s.preventDefault())})})))});var l0=(0,ee.forwardRef)((e,t)=>{let u=e,{__scopeDropdownMenu:a}=u,r=A(u,["__scopeDropdownMenu"]),o=Nx("DropdownMenuContent",a),n=Tt(a),l=(0,ee.useRef)(!1);return(0,ee.createElement)(Px,E({id:o.contentId,"aria-labelledby":o.triggerId},n,r,{ref:t,onCloseAutoFocus:q(e.onCloseAutoFocus,s=>{var i;l.current||(i=o.triggerRef.current)===null||i===void 0||i.focus(),l.current=!1,s.preventDefault()}),onInteractOutside:q(e.onInteractOutside,s=>{let i=s.detail.originalEvent,d=i.button===0&&i.ctrlKey===!0,f=i.button===2||d;o.modal&&!f||(l.current=!0)}),style:b(w({},e.style),{"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"})}))}),P2=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Mx,E({},o,r,{ref:t}))}),u0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Tx,E({},o,r,{ref:t}))}),s0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(_x,E({},o,r,{ref:t}))}),i0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Rx,E({},o,r,{ref:t}))}),M2=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Dx,E({},o,r,{ref:t}))}),d0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(bx,E({},o,r,{ref:t}))}),f0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Ex,E({},o,r,{ref:t}))}),c0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Ax,E({},o,r,{ref:t}))});var p0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Ox,E({},o,r,{ref:t}))}),m0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Fx,E({},o,r,{ref:t,style:b(w({},e.style),{"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"})}))}),zx=o0,Ux=n0;var Wf=l0;var Gf=u0,Zf=s0,$f=i0;var Kf=d0,Xf=f0,Qf=c0;var Yf=p0,Jf=m0;var ec=zx,tc=Ux;var qx=za.forwardRef((e,t)=>{var{className:a,inset:r,children:o}=e,n=Se(e,["className","inset","children"]);return(0,ze.jsxs)(Yf,Object.assign({ref:t,className:De("flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",r&&"pl-8",a)},n,{children:[o,(0,ze.jsx)(Tg,{className:"ml-auto h-4 w-4"})]}))});qx.displayName=Yf.displayName;var Hx=za.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,ze.jsx)(Jf,Object.assign({ref:t,className:De("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",a)},r))});Hx.displayName=Jf.displayName;var Ds=za.forwardRef((e,t)=>{var{className:a,sideOffset:r=4}=e,o=Se(e,["className","sideOffset"]);return(0,ze.jsx)(Wf,Object.assign({ref:t,sideOffset:r,className:De("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md","data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",a)},o))});Ds.displayName=Wf.displayName;var bs=za.forwardRef((e,t)=>{var{className:a,inset:r}=e,o=Se(e,["className","inset"]);return(0,ze.jsx)(Zf,Object.assign({ref:t,className:De("relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",r&&"pl-8",a)},o))});bs.displayName=Zf.displayName;var Vx=za.forwardRef((e,t)=>{var{className:a,children:r,checked:o}=e,n=Se(e,["className","children","checked"]);return(0,ze.jsxs)($f,Object.assign({ref:t,className:De("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",a),checked:o},n,{children:[(0,ze.jsx)("span",Object.assign({className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center"},{children:(0,ze.jsx)(Xf,{children:(0,ze.jsx)(Pg,{className:"h-4 w-4"})})})),r]}))});Vx.displayName=$f.displayName;var jx=za.forwardRef((e,t)=>{var{className:a,children:r}=e,o=Se(e,["className","children"]);return(0,ze.jsxs)(Kf,Object.assign({ref:t,className:De("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",a)},o,{children:[(0,ze.jsx)("span",Object.assign({className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center"},{children:(0,ze.jsx)(Xf,{children:(0,ze.jsx)(_g,{className:"h-4 w-4 fill-current"})})})),r]}))});jx.displayName=Kf.displayName;var Wx=za.forwardRef((e,t)=>{var{className:a,inset:r}=e,o=Se(e,["className","inset"]);return(0,ze.jsx)(Gf,Object.assign({ref:t,className:De("px-2 py-1.5 text-sm font-semibold",r&&"pl-8",a)},o))});Wx.displayName=Gf.displayName;var Gx=za.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,ze.jsx)(Qf,Object.assign({ref:t,className:De("-mx-1 my-1 h-px bg-muted",a)},r))});Gx.displayName=Qf.displayName;var Zx=e=>{var{className:t}=e,a=Se(e,["className"]);return(0,ze.jsx)("span",Object.assign({className:De("ml-auto text-xs tracking-widest opacity-60",t)},a))};Zx.displayName="DropdownMenuShortcut";var Es=N(G(),1);var h0=(0,Es.forwardRef)((e,t)=>(0,Es.createElement)(oe.span,E({},e,{ref:t,style:w({position:"absolute",border:0,width:1,height:1,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",wordWrap:"normal"},e.style)}))),$x=h0;var Ut=N(Ke(),1);var T=N(G(),1),Yx=N(Vo(),1);var Ll="NavigationMenu",[nc,Jx,g0]=qr(Ll),[ac,v0,x0]=qr(Ll),[lc,oM]=zt(Ll,[g0,x0]),[y0,la]=lc(Ll),[L0,w0]=lc(Ll),C0=(0,T.forwardRef)((e,t)=>{let M=e,{__scopeNavigationMenu:a,value:r,onValueChange:o,defaultValue:n,delayDuration:l=200,skipDelayDuration:u=300,orientation:s="horizontal",dir:i}=M,d=A(M,["__scopeNavigationMenu","value","onValueChange","defaultValue","delayDuration","skipDelayDuration","orientation","dir"]),[f,p]=(0,T.useState)(null),v=se(t,P=>p(P)),x=$o(i),g=(0,T.useRef)(0),L=(0,T.useRef)(0),m=(0,T.useRef)(0),[c,h]=(0,T.useState)(!0),[y="",C]=Go({prop:r,onChange:P=>{let U=u>0;P!==""?(window.clearTimeout(m.current),U&&h(!1)):(window.clearTimeout(m.current),m.current=window.setTimeout(()=>h(!0),u)),o==null||o(P)},defaultProp:n}),I=(0,T.useCallback)(()=>{window.clearTimeout(L.current),L.current=window.setTimeout(()=>C(""),150)},[C]),k=(0,T.useCallback)(P=>{window.clearTimeout(L.current),C(P)},[C]),S=(0,T.useCallback)(P=>{y===P?window.clearTimeout(L.current):g.current=window.setTimeout(()=>{window.clearTimeout(L.current),C(P)},l)},[y,C,l]);return(0,T.useEffect)(()=>()=>{window.clearTimeout(g.current),window.clearTimeout(L.current),window.clearTimeout(m.current)},[]),(0,T.createElement)(I0,{scope:a,isRootMenu:!0,value:y,dir:x,orientation:s,rootNavigationMenu:f,onTriggerEnter:P=>{window.clearTimeout(g.current),c?S(P):k(P)},onTriggerLeave:()=>{window.clearTimeout(g.current),I()},onContentEnter:()=>window.clearTimeout(L.current),onContentLeave:I,onItemSelect:P=>{C(U=>U===P?"":P)},onItemDismiss:()=>C("")},(0,T.createElement)(oe.nav,E({"aria-label":"Main","data-orientation":s,dir:x},d,{ref:v})))}),I0=e=>{let{scope:t,isRootMenu:a,rootNavigationMenu:r,dir:o,orientation:n,children:l,value:u,onItemSelect:s,onItemDismiss:i,onTriggerEnter:d,onTriggerLeave:f,onContentEnter:p,onContentLeave:v}=e,[x,g]=(0,T.useState)(null),[L,m]=(0,T.useState)(new Map),[c,h]=(0,T.useState)(null);return(0,T.createElement)(y0,{scope:t,isRootMenu:a,rootNavigationMenu:r,value:u,previousValue:xv(u),baseId:gr(),dir:o,orientation:n,viewport:x,onViewportChange:g,indicatorTrack:c,onIndicatorTrackChange:h,onTriggerEnter:fe(d),onTriggerLeave:fe(f),onContentEnter:fe(p),onContentLeave:fe(v),onItemSelect:fe(s),onItemDismiss:fe(i),onViewportContentChange:(0,T.useCallback)((y,C)=>{m(I=>(I.set(y,C),new Map(I)))},[]),onViewportContentRemove:(0,T.useCallback)(y=>{m(C=>C.has(y)?(C.delete(y),new Map(C)):C)},[])},(0,T.createElement)(nc.Provider,{scope:t},(0,T.createElement)(L0,{scope:t,items:L},l)))},S0=(0,T.forwardRef)((e,t)=>{let l=e,{__scopeNavigationMenu:a}=l,r=A(l,["__scopeNavigationMenu"]),o=la("NavigationMenuList",a),n=(0,T.createElement)(oe.ul,E({"data-orientation":o.orientation},r,{ref:t}));return(0,T.createElement)(oe.div,{style:{position:"relative"},ref:o.onIndicatorTrackChange},(0,T.createElement)(nc.Slot,{scope:a},o.isRootMenu?(0,T.createElement)(oy,{asChild:!0},n):n))}),[k0,ey]=lc("NavigationMenuItem"),P0=(0,T.forwardRef)((e,t)=>{let x=e,{__scopeNavigationMenu:a,value:r}=x,o=A(x,["__scopeNavigationMenu","value"]),n=gr(),l=r||n||"LEGACY_REACT_AUTO_VALUE",u=(0,T.useRef)(null),s=(0,T.useRef)(null),i=(0,T.useRef)(null),d=(0,T.useRef)(()=>{}),f=(0,T.useRef)(!1),p=(0,T.useCallback)((g="start")=>{if(u.current){d.current();let L=rc(u.current);L.length&&uc(g==="start"?L:L.reverse())}},[]),v=(0,T.useCallback)(()=>{if(u.current){let g=rc(u.current);g.length&&(d.current=function(L){return L.forEach(m=>{m.dataset.tabindex=m.getAttribute("tabindex")||"",m.setAttribute("tabindex","-1")}),()=>{L.forEach(m=>{let c=m.dataset.tabindex;m.setAttribute("tabindex",c)})}}(g))}},[]);return(0,T.createElement)(k0,{scope:a,value:l,triggerRef:s,contentRef:u,focusProxyRef:i,wasEscapeCloseRef:f,onEntryKeyDown:p,onFocusProxyEnter:p,onRootContentClose:v,onContentFocusOutside:v},(0,T.createElement)(oe.li,E({},o,{ref:t})))}),Kx="NavigationMenuTrigger",M0=(0,T.forwardRef)((e,t)=>{let x=e,{__scopeNavigationMenu:a,disabled:r}=x,o=A(x,["__scopeNavigationMenu","disabled"]),n=la(Kx,e.__scopeNavigationMenu),l=ey(Kx,e.__scopeNavigationMenu),u=(0,T.useRef)(null),s=se(u,l.triggerRef,t),i=ly(n.baseId,l.value),d=uy(n.baseId,l.value),f=(0,T.useRef)(!1),p=(0,T.useRef)(!1),v=l.value===n.value;return(0,T.createElement)(T.Fragment,null,(0,T.createElement)(nc.ItemSlot,{scope:a,value:l.value},(0,T.createElement)(ny,{asChild:!0},(0,T.createElement)(oe.button,E({id:i,disabled:r,"data-disabled":r?"":void 0,"data-state":sc(v),"aria-expanded":v,"aria-controls":d},o,{ref:s,onPointerEnter:q(e.onPointerEnter,()=>{p.current=!1,l.wasEscapeCloseRef.current=!1}),onPointerMove:q(e.onPointerMove,Os(()=>{r||p.current||l.wasEscapeCloseRef.current||f.current||(n.onTriggerEnter(l.value),f.current=!0)})),onPointerLeave:q(e.onPointerLeave,Os(()=>{r||(n.onTriggerLeave(),f.current=!1)})),onClick:q(e.onClick,()=>{n.onItemSelect(l.value),p.current=v}),onKeyDown:q(e.onKeyDown,g=>{let L={horizontal:"ArrowDown",vertical:n.dir==="rtl"?"ArrowLeft":"ArrowRight"}[n.orientation];v&&g.key===L&&(l.onEntryKeyDown(),g.preventDefault())})})))),v&&(0,T.createElement)(T.Fragment,null,(0,T.createElement)($x,{"aria-hidden":!0,tabIndex:0,ref:l.focusProxyRef,onFocus:g=>{let L=l.contentRef.current,m=g.relatedTarget,c=m===u.current,h=L==null?void 0:L.contains(m);!c&&h||l.onFocusProxyEnter(c?"start":"end")}}),n.viewport&&(0,T.createElement)("span",{"aria-owns":d})))}),Xx="navigationMenu.linkSelect",T0=(0,T.forwardRef)((e,t)=>{let l=e,{__scopeNavigationMenu:a,active:r,onSelect:o}=l,n=A(l,["__scopeNavigationMenu","active","onSelect"]);return(0,T.createElement)(ny,{asChild:!0},(0,T.createElement)(oe.a,E({"data-active":r?"":void 0,"aria-current":r?"page":void 0},n,{ref:t,onClick:q(e.onClick,u=>{let s=u.target,i=new CustomEvent(Xx,{bubbles:!0,cancelable:!0});if(s.addEventListener(Xx,d=>o==null?void 0:o(d),{once:!0}),Hr(s,i),!i.defaultPrevented&&!u.metaKey){let d=new CustomEvent(As,{bubbles:!0,cancelable:!0});Hr(s,d)}},{checkForDefaultPrevented:!1})})))}),ty="NavigationMenuIndicator",_0=(0,T.forwardRef)((e,t)=>{let l=e,{forceMount:a}=l,r=A(l,["forceMount"]),o=la(ty,e.__scopeNavigationMenu),n=!!o.value;return o.indicatorTrack?Yx.default.createPortal((0,T.createElement)(ga,{present:a||n},(0,T.createElement)(R0,E({},r,{ref:t}))),o.indicatorTrack):null}),R0=(0,T.forwardRef)((e,t)=>{let v=e,{__scopeNavigationMenu:a}=v,r=A(v,["__scopeNavigationMenu"]),o=la(ty,a),n=Jx(a),[l,u]=(0,T.useState)(null),[s,i]=(0,T.useState)(null),d=o.orientation==="horizontal",f=!!o.value;(0,T.useEffect)(()=>{var x;let g=(x=n().find(L=>L.value===o.value))===null||x===void 0?void 0:x.ref.current;g&&u(g)},[n,o.value]);let p=()=>{l&&i({size:d?l.offsetWidth:l.offsetHeight,offset:d?l.offsetLeft:l.offsetTop})};return oc(l,p),oc(o.indicatorTrack,p),s?(0,T.createElement)(oe.div,E({"aria-hidden":!0,"data-state":f?"visible":"hidden","data-orientation":o.orientation},r,{ref:t,style:w(w({position:"absolute"},d?{left:0,width:s.size+"px",transform:`translateX(${s.offset}px)`}:{top:0,height:s.size+"px",transform:`translateY(${s.offset}px)`}),r.style)})):null}),yl="NavigationMenuContent",D0=(0,T.forwardRef)((e,t)=>{let i=e,{forceMount:a}=i,r=A(i,["forceMount"]),o=la(yl,e.__scopeNavigationMenu),n=ey(yl,e.__scopeNavigationMenu),l=se(n.contentRef,t),u=n.value===o.value,s=w({value:n.value,triggerRef:n.triggerRef,focusProxyRef:n.focusProxyRef,wasEscapeCloseRef:n.wasEscapeCloseRef,onContentFocusOutside:n.onContentFocusOutside,onRootContentClose:n.onRootContentClose},r);return o.viewport?(0,T.createElement)(b0,E({forceMount:a},s,{ref:l})):(0,T.createElement)(ga,{present:a||u},(0,T.createElement)(ay,E({"data-state":sc(u)},s,{ref:l,onPointerEnter:q(e.onPointerEnter,o.onContentEnter),onPointerLeave:q(e.onPointerLeave,Os(o.onContentLeave)),style:w({pointerEvents:!u&&o.isRootMenu?"none":void 0},s.style)})))}),b0=(0,T.forwardRef)((e,t)=>{let a=la(yl,e.__scopeNavigationMenu),{onViewportContentChange:r,onViewportContentRemove:o}=a;return mt(()=>{r(e.value,w({ref:t},e))},[e,t,r]),mt(()=>()=>o(e.value),[e.value,o]),null}),As="navigationMenu.rootContentDismiss",ay=(0,T.forwardRef)((e,t)=>{let h=e,{__scopeNavigationMenu:a,value:r,triggerRef:o,focusProxyRef:n,wasEscapeCloseRef:l,onRootContentClose:u,onContentFocusOutside:s}=h,i=A(h,["__scopeNavigationMenu","value","triggerRef","focusProxyRef","wasEscapeCloseRef","onRootContentClose","onContentFocusOutside"]),d=la(yl,a),f=(0,T.useRef)(null),p=se(f,t),v=ly(d.baseId,r),x=uy(d.baseId,r),g=Jx(a),L=(0,T.useRef)(null),{onItemDismiss:m}=d;(0,T.useEffect)(()=>{let y=f.current;if(d.isRootMenu&&y){let C=()=>{var I;m(),u(),y.contains(document.activeElement)&&((I=o.current)===null||I===void 0||I.focus())};return y.addEventListener(As,C),()=>y.removeEventListener(As,C)}},[d.isRootMenu,e.value,o,m,u]);let c=(0,T.useMemo)(()=>{let y=g().map(P=>P.value);d.dir==="rtl"&&y.reverse();let C=y.indexOf(d.value),I=y.indexOf(d.previousValue),k=r===d.value,S=I===y.indexOf(r);if(!k&&!S)return L.current;let M=(()=>{if(C!==I){if(k&&I!==-1)return C>I?"from-end":"from-start";if(S&&C!==-1)return C>I?"to-start":"to-end"}return null})();return L.current=M,M},[d.previousValue,d.value,d.dir,g,r]);return(0,T.createElement)(oy,{asChild:!0},(0,T.createElement)(ds,E({id:x,"aria-labelledby":v,"data-motion":c,"data-orientation":d.orientation},i,{ref:p,onDismiss:()=>{var y;let C=new Event(As,{bubbles:!0,cancelable:!0});(y=f.current)===null||y===void 0||y.dispatchEvent(C)},onFocusOutside:q(e.onFocusOutside,y=>{var C;s();let I=y.target;(C=d.rootNavigationMenu)!==null&&C!==void 0&&C.contains(I)&&y.preventDefault()}),onPointerDownOutside:q(e.onPointerDownOutside,y=>{var C;let I=y.target,k=g().some(M=>{var P;return(P=M.ref.current)===null||P===void 0?void 0:P.contains(I)}),S=d.isRootMenu&&((C=d.viewport)===null||C===void 0?void 0:C.contains(I));(k||S||!d.isRootMenu)&&y.preventDefault()}),onKeyDown:q(e.onKeyDown,y=>{let C=y.altKey||y.ctrlKey||y.metaKey;if(y.key==="Tab"&&!C){let k=rc(y.currentTarget),S=document.activeElement,M=k.findIndex(P=>P===S);var I;uc(y.shiftKey?k.slice(0,M).reverse():k.slice(M+1,k.length))?y.preventDefault():(I=n.current)===null||I===void 0||I.focus()}}),onEscapeKeyDown:q(e.onEscapeKeyDown,y=>{l.current=!0})})))}),ry="NavigationMenuViewport",E0=(0,T.forwardRef)((e,t)=>{let l=e,{forceMount:a}=l,r=A(l,["forceMount"]),o=la(ry,e.__scopeNavigationMenu),n=!!o.value;return(0,T.createElement)(ga,{present:a||n},(0,T.createElement)(A0,E({},r,{ref:t})))}),A0=(0,T.forwardRef)((e,t)=>{let L=e,{__scopeNavigationMenu:a,children:r}=L,o=A(L,["__scopeNavigationMenu","children"]),n=la(ry,a),l=se(t,n.onViewportChange),u=w0(yl,e.__scopeNavigationMenu),[s,i]=(0,T.useState)(null),[d,f]=(0,T.useState)(null),p=s?(s==null?void 0:s.width)+"px":void 0,v=s?(s==null?void 0:s.height)+"px":void 0,x=!!n.value,g=x?n.value:n.previousValue;return oc(d,()=>{d&&i({width:d.offsetWidth,height:d.offsetHeight})}),(0,T.createElement)(oe.div,E({"data-state":sc(x),"data-orientation":n.orientation},o,{ref:l,style:w({pointerEvents:!x&&n.isRootMenu?"none":void 0,"--radix-navigation-menu-viewport-width":p,"--radix-navigation-menu-viewport-height":v},o.style),onPointerEnter:q(e.onPointerEnter,n.onContentEnter),onPointerLeave:q(e.onPointerLeave,Os(n.onContentLeave))}),Array.from(u.items).map(C=>{var[m,I]=C,k=I,{ref:c,forceMount:h}=k,y=A(k,["ref","forceMount"]);let S=g===m;return(0,T.createElement)(ga,{key:m,present:h||S},(0,T.createElement)(ay,E({},y,{ref:Fa(c,M=>{S&&M&&f(M)})})))}))}),oy=(0,T.forwardRef)((e,t)=>{let n=e,{__scopeNavigationMenu:a}=n,r=A(n,["__scopeNavigationMenu"]),o=la("FocusGroup",a);return(0,T.createElement)(ac.Provider,{scope:a},(0,T.createElement)(ac.Slot,{scope:a},(0,T.createElement)(oe.div,E({dir:o.dir},r,{ref:t}))))}),Qx=["ArrowRight","ArrowLeft","ArrowUp","ArrowDown"],ny=(0,T.forwardRef)((e,t)=>{let l=e,{__scopeNavigationMenu:a}=l,r=A(l,["__scopeNavigationMenu"]),o=v0(a),n=la("FocusGroupItem",a);return(0,T.createElement)(ac.ItemSlot,{scope:a},(0,T.createElement)(oe.button,E({},r,{ref:t,onKeyDown:q(e.onKeyDown,u=>{if(["Home","End",...Qx].includes(u.key)){let s=o().map(i=>i.ref.current);if([n.dir==="rtl"?"ArrowRight":"ArrowLeft","ArrowUp","End"].includes(u.key)&&s.reverse(),Qx.includes(u.key)){let i=s.indexOf(u.currentTarget);s=s.slice(i+1)}setTimeout(()=>uc(s)),u.preventDefault()}})})))});function rc(e){let t=[],a=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:r=>{let o=r.tagName==="INPUT"&&r.type==="hidden";return r.disabled||r.hidden||o?NodeFilter.FILTER_SKIP:r.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;a.nextNode();)t.push(a.currentNode);return t}function uc(e){let t=document.activeElement;return e.some(a=>a===t||(a.focus(),document.activeElement!==t))}function oc(e,t){let a=fe(t);mt(()=>{let r=0;if(e){let o=new ResizeObserver(()=>{cancelAnimationFrame(r),r=window.requestAnimationFrame(a)});return o.observe(e),()=>{window.cancelAnimationFrame(r),o.unobserve(e)}}},[e,a])}function sc(e){return e?"open":"closed"}function ly(e,t){return`${e}-trigger-${t}`}function uy(e,t){return`${e}-content-${t}`}function Os(e){return t=>t.pointerType==="mouse"?e(t):void 0}var ic=C0,dc=S0,sy=P0,fc=M0,iy=T0,cc=_0,pc=D0,mc=E0;var Kr=N(G(),1);var Fs=Kr.forwardRef((e,t)=>{var{className:a,children:r}=e,o=Se(e,["className","children"]);return(0,Ut.jsxs)(ic,Object.assign({ref:t,className:De("relative z-10 flex max-w-max flex-1 items-center justify-center",a)},o,{children:[r,(0,Ut.jsx)(vc,{})]}))});Fs.displayName=ic.displayName;var Bs=Kr.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,Ut.jsx)(dc,Object.assign({ref:t,className:De("group flex flex-1 list-none items-center justify-center space-x-1",a)},r))});Bs.displayName=dc.displayName;var hc=sy,Ns=os("group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"),dy=Kr.forwardRef((e,t)=>{var{className:a,children:r}=e,o=Se(e,["className","children"]);return(0,Ut.jsxs)(fc,Object.assign({ref:t,className:De(Ns(),"group",a),onPointerMove:n=>n.preventDefault(),onPointerLeave:n=>n.preventDefault()},o,{children:[r," ",(0,Ut.jsx)(Mg,{className:"relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180","aria-hidden":"true"})]}))});dy.displayName=fc.displayName;var fy=Kr.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,Ut.jsx)(pc,Object.assign({ref:t,className:De("left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",a),onPointerEnter:o=>o.preventDefault(),onPointerLeave:o=>o.preventDefault()},r))});fy.displayName=pc.displayName;var gc=iy,vc=Kr.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,Ut.jsx)("div",Object.assign({className:De("absolute left-0 top-full flex justify-center")},{children:(0,Ut.jsx)(mc,Object.assign({className:De("origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",a),ref:t},r))}))});vc.displayName=mc.displayName;var cy=Kr.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,Ut.jsx)(cc,Object.assign({ref:t,className:De("top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",a)},r,{children:(0,Ut.jsx)("div",{className:"relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md"})}))});cy.displayName=cc.displayName;var te;(function(e){e.assertEqual=o=>o;function t(o){}e.assertIs=t;function a(o){throw new Error}e.assertNever=a,e.arrayToEnum=o=>{let n={};for(let l of o)n[l]=l;return n},e.getValidEnumValues=o=>{let n=e.objectKeys(o).filter(u=>typeof o[o[u]]!="number"),l={};for(let u of n)l[u]=o[u];return e.objectValues(l)},e.objectValues=o=>e.objectKeys(o).map(function(n){return o[n]}),e.objectKeys=typeof Object.keys=="function"?o=>Object.keys(o):o=>{let n=[];for(let l in o)Object.prototype.hasOwnProperty.call(o,l)&&n.push(l);return n},e.find=(o,n)=>{for(let l of o)if(n(l))return l},e.isInteger=typeof Number.isInteger=="function"?o=>Number.isInteger(o):o=>typeof o=="number"&&isFinite(o)&&Math.floor(o)===o;function r(o,n=" | "){return o.map(l=>typeof l=="string"?`'${l}'`:l).join(n)}e.joinValues=r,e.jsonStringifyReplacer=(o,n)=>typeof n=="bigint"?n.toString():n})(te||(te={}));var yc;(function(e){e.mergeShapes=(t,a)=>w(w({},t),a)})(yc||(yc={}));var F=te.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),xr=e=>{switch(typeof e){case"undefined":return F.undefined;case"string":return F.string;case"number":return isNaN(e)?F.nan:F.number;case"boolean":return F.boolean;case"function":return F.function;case"bigint":return F.bigint;case"symbol":return F.symbol;case"object":return Array.isArray(e)?F.array:e===null?F.null:e.then&&typeof e.then=="function"&&e.catch&&typeof e.catch=="function"?F.promise:typeof Map!="undefined"&&e instanceof Map?F.map:typeof Set!="undefined"&&e instanceof Set?F.set:typeof Date!="undefined"&&e instanceof Date?F.date:F.object;default:return F.unknown}},D=te.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of","not_finite"]),O0=e=>JSON.stringify(e,null,2).replace(/"([^"]+)":/g,"$1:"),_t=class e extends Error{constructor(t){super(),this.issues=[],this.addIssue=r=>{this.issues=[...this.issues,r]},this.addIssues=(r=[])=>{this.issues=[...this.issues,...r]};let a=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,a):this.__proto__=a,this.name="ZodError",this.issues=t}get errors(){return this.issues}format(t){let a=t||function(n){return n.message},r={_errors:[]},o=n=>{for(let l of n.issues)if(l.code==="invalid_union")l.unionErrors.map(o);else if(l.code==="invalid_return_type")o(l.returnTypeError);else if(l.code==="invalid_arguments")o(l.argumentsError);else if(l.path.length===0)r._errors.push(a(l));else{let u=r,s=0;for(;sa.message){let a={},r=[];for(let o of this.issues)o.path.length>0?(a[o.path[0]]=a[o.path[0]]||[],a[o.path[0]].push(t(o))):r.push(t(o));return{formErrors:r,fieldErrors:a}}get formErrors(){return this.flatten()}};_t.create=e=>new _t(e);var ln=(e,t)=>{let a;switch(e.code){case D.invalid_type:e.received===F.undefined?a="Required":a=`Expected ${e.expected}, received ${e.received}`;break;case D.invalid_literal:a=`Invalid literal value, expected ${JSON.stringify(e.expected,te.jsonStringifyReplacer)}`;break;case D.unrecognized_keys:a=`Unrecognized key(s) in object: ${te.joinValues(e.keys,", ")}`;break;case D.invalid_union:a="Invalid input";break;case D.invalid_union_discriminator:a=`Invalid discriminator value. Expected ${te.joinValues(e.options)}`;break;case D.invalid_enum_value:a=`Invalid enum value. Expected ${te.joinValues(e.options)}, received '${e.received}'`;break;case D.invalid_arguments:a="Invalid function arguments";break;case D.invalid_return_type:a="Invalid function return type";break;case D.invalid_date:a="Invalid date";break;case D.invalid_string:typeof e.validation=="object"?"includes"in e.validation?(a=`Invalid input: must include "${e.validation.includes}"`,typeof e.validation.position=="number"&&(a=`${a} at one or more positions greater than or equal to ${e.validation.position}`)):"startsWith"in e.validation?a=`Invalid input: must start with "${e.validation.startsWith}"`:"endsWith"in e.validation?a=`Invalid input: must end with "${e.validation.endsWith}"`:te.assertNever(e.validation):e.validation!=="regex"?a=`Invalid ${e.validation}`:a="Invalid";break;case D.too_small:e.type==="array"?a=`Array must contain ${e.exact?"exactly":e.inclusive?"at least":"more than"} ${e.minimum} element(s)`:e.type==="string"?a=`String must contain ${e.exact?"exactly":e.inclusive?"at least":"over"} ${e.minimum} character(s)`:e.type==="number"?a=`Number must be ${e.exact?"exactly equal to ":e.inclusive?"greater than or equal to ":"greater than "}${e.minimum}`:e.type==="date"?a=`Date must be ${e.exact?"exactly equal to ":e.inclusive?"greater than or equal to ":"greater than "}${new Date(Number(e.minimum))}`:a="Invalid input";break;case D.too_big:e.type==="array"?a=`Array must contain ${e.exact?"exactly":e.inclusive?"at most":"less than"} ${e.maximum} element(s)`:e.type==="string"?a=`String must contain ${e.exact?"exactly":e.inclusive?"at most":"under"} ${e.maximum} character(s)`:e.type==="number"?a=`Number must be ${e.exact?"exactly":e.inclusive?"less than or equal to":"less than"} ${e.maximum}`:e.type==="bigint"?a=`BigInt must be ${e.exact?"exactly":e.inclusive?"less than or equal to":"less than"} ${e.maximum}`:e.type==="date"?a=`Date must be ${e.exact?"exactly":e.inclusive?"smaller than or equal to":"smaller than"} ${new Date(Number(e.maximum))}`:a="Invalid input";break;case D.custom:a="Invalid input";break;case D.invalid_intersection_types:a="Intersection results could not be merged";break;case D.not_multiple_of:a=`Number must be a multiple of ${e.multipleOf}`;break;case D.not_finite:a="Number must be finite";break;default:a=t.defaultError,te.assertNever(e)}return{message:a}},hy=ln;function F0(e){hy=e}function zs(){return hy}var Us=e=>{let{data:t,path:a,errorMaps:r,issueData:o}=e,n=[...a,...o.path||[]],l=b(w({},o),{path:n});if(o.message!==void 0)return b(w({},o),{path:n,message:o.message});let u="",s=r.filter(i=>!!i).slice().reverse();for(let i of s)u=i(l,{data:t,defaultError:u}).message;return b(w({},o),{path:n,message:u})},B0=[];function O(e,t){let a=zs(),r=Us({issueData:t,data:e.data,path:e.path,errorMaps:[e.common.contextualErrorMap,e.schemaErrorMap,a,a===ln?void 0:ln].filter(o=>!!o)});e.common.issues.push(r)}var Ye=class e{constructor(){this.value="valid"}dirty(){this.value==="valid"&&(this.value="dirty")}abort(){this.value!=="aborted"&&(this.value="aborted")}static mergeArray(t,a){let r=[];for(let o of a){if(o.status==="aborted")return V;o.status==="dirty"&&t.dirty(),r.push(o.value)}return{status:t.value,value:r}}static mergeObjectAsync(t,a){return Pe(this,null,function*(){let r=[];for(let o of a){let n=yield o.key,l=yield o.value;r.push({key:n,value:l})}return e.mergeObjectSync(t,r)})}static mergeObjectSync(t,a){let r={};for(let o of a){let{key:n,value:l}=o;if(n.status==="aborted"||l.status==="aborted")return V;n.status==="dirty"&&t.dirty(),l.status==="dirty"&&t.dirty(),n.value!=="__proto__"&&(typeof l.value!="undefined"||o.alwaysSet)&&(r[n.value]=l.value)}return{status:t.value,value:r}}},V=Object.freeze({status:"aborted"}),nn=e=>({status:"dirty",value:e}),nt=e=>({status:"valid",value:e}),Lc=e=>e.status==="aborted",wc=e=>e.status==="dirty",Il=e=>e.status==="valid",Sl=e=>typeof Promise!="undefined"&&e instanceof Promise;function qs(e,t,a,r){if(a==="a"&&!r)throw new TypeError("Private accessor was defined without a getter");if(typeof t=="function"?e!==t||!r:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return a==="m"?r:a==="a"?r.call(e):r?r.value:t.get(e)}function gy(e,t,a,r,o){if(r==="m")throw new TypeError("Private method is not writable");if(r==="a"&&!o)throw new TypeError("Private accessor was defined without a setter");if(typeof t=="function"?e!==t||!o:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return r==="a"?o.call(e,a):o?o.value=a:t.set(e,a),a}var z;(function(e){e.errToObj=t=>typeof t=="string"?{message:t}:t||{},e.toString=t=>typeof t=="string"?t:t==null?void 0:t.message})(z||(z={}));var wl,Cl,Ht=class{constructor(t,a,r,o){this._cachedPath=[],this.parent=t,this.data=a,this._path=r,this._key=o}get path(){return this._cachedPath.length||(this._key instanceof Array?this._cachedPath.push(...this._path,...this._key):this._cachedPath.push(...this._path,this._key)),this._cachedPath}},py=(e,t)=>{if(Il(t))return{success:!0,data:t.value};if(!e.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,get error(){if(this._error)return this._error;let a=new _t(e.common.issues);return this._error=a,this._error}}};function $(e){if(!e)return{};let{errorMap:t,invalid_type_error:a,required_error:r,description:o}=e;if(t&&(a||r))throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);return t?{errorMap:t,description:o}:{errorMap:(l,u)=>{var s,i;let{message:d}=e;return l.code==="invalid_enum_value"?{message:d!=null?d:u.defaultError}:typeof u.data=="undefined"?{message:(s=d!=null?d:r)!==null&&s!==void 0?s:u.defaultError}:l.code!=="invalid_type"?{message:u.defaultError}:{message:(i=d!=null?d:a)!==null&&i!==void 0?i:u.defaultError}},description:o}}var K=class{constructor(t){this.spa=this.safeParseAsync,this._def=t,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.brand=this.brand.bind(this),this.default=this.default.bind(this),this.catch=this.catch.bind(this),this.describe=this.describe.bind(this),this.pipe=this.pipe.bind(this),this.readonly=this.readonly.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this)}get description(){return this._def.description}_getType(t){return xr(t.data)}_getOrReturnCtx(t,a){return a||{common:t.parent.common,data:t.data,parsedType:xr(t.data),schemaErrorMap:this._def.errorMap,path:t.path,parent:t.parent}}_processInputParams(t){return{status:new Ye,ctx:{common:t.parent.common,data:t.data,parsedType:xr(t.data),schemaErrorMap:this._def.errorMap,path:t.path,parent:t.parent}}}_parseSync(t){let a=this._parse(t);if(Sl(a))throw new Error("Synchronous parse encountered promise.");return a}_parseAsync(t){let a=this._parse(t);return Promise.resolve(a)}parse(t,a){let r=this.safeParse(t,a);if(r.success)return r.data;throw r.error}safeParse(t,a){var r;let o={common:{issues:[],async:(r=a==null?void 0:a.async)!==null&&r!==void 0?r:!1,contextualErrorMap:a==null?void 0:a.errorMap},path:(a==null?void 0:a.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:t,parsedType:xr(t)},n=this._parseSync({data:t,path:o.path,parent:o});return py(o,n)}parseAsync(t,a){return Pe(this,null,function*(){let r=yield this.safeParseAsync(t,a);if(r.success)return r.data;throw r.error})}safeParseAsync(t,a){return Pe(this,null,function*(){let r={common:{issues:[],contextualErrorMap:a==null?void 0:a.errorMap,async:!0},path:(a==null?void 0:a.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:t,parsedType:xr(t)},o=this._parse({data:t,path:r.path,parent:r}),n=yield Sl(o)?o:Promise.resolve(o);return py(r,n)})}refine(t,a){let r=o=>typeof a=="string"||typeof a=="undefined"?{message:a}:typeof a=="function"?a(o):a;return this._refinement((o,n)=>{let l=t(o),u=()=>n.addIssue(w({code:D.custom},r(o)));return typeof Promise!="undefined"&&l instanceof Promise?l.then(s=>s?!0:(u(),!1)):l?!0:(u(),!1)})}refinement(t,a){return this._refinement((r,o)=>t(r)?!0:(o.addIssue(typeof a=="function"?a(r,o):a),!1))}_refinement(t){return new Rt({schema:this,typeName:H.ZodEffects,effect:{type:"refinement",refinement:t}})}superRefine(t){return this._refinement(t)}optional(){return qt.create(this,this._def)}nullable(){return Ia.create(this,this._def)}nullish(){return this.nullable().optional()}array(){return Ha.create(this,this._def)}promise(){return wr.create(this,this._def)}or(t){return ao.create([this,t],this._def)}and(t){return ro.create(this,t,this._def)}transform(t){return new Rt(b(w({},$(this._def)),{schema:this,typeName:H.ZodEffects,effect:{type:"transform",transform:t}}))}default(t){let a=typeof t=="function"?t:()=>t;return new so(b(w({},$(this._def)),{innerType:this,defaultValue:a,typeName:H.ZodDefault}))}brand(){return new kl(w({typeName:H.ZodBranded,type:this},$(this._def)))}catch(t){let a=typeof t=="function"?t:()=>t;return new io(b(w({},$(this._def)),{innerType:this,catchValue:a,typeName:H.ZodCatch}))}describe(t){let a=this.constructor;return new a(b(w({},this._def),{description:t}))}pipe(t){return Pl.create(this,t)}readonly(){return fo.create(this)}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}},N0=/^c[^\s-]{8,}$/i,z0=/^[0-9a-z]+$/,U0=/^[0-9A-HJKMNP-TV-Z]{26}$/,q0=/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i,H0=/^[a-z0-9_-]{21}$/i,V0=/^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/,j0=/^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i,W0="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$",xc,G0=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,Z0=/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/,$0=/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,vy="((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))",K0=new RegExp(`^${vy}$`);function xy(e){let t="([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d";return e.precision?t=`${t}\\.\\d{${e.precision}}`:e.precision==null&&(t=`${t}(\\.\\d+)?`),t}function X0(e){return new RegExp(`^${xy(e)}$`)}function yy(e){let t=`${vy}T${xy(e)}`,a=[];return a.push(e.local?"Z?":"Z"),e.offset&&a.push("([+-]\\d{2}:?\\d{2})"),t=`${t}(${a.join("|")})`,new RegExp(`^${t}$`)}function Q0(e,t){return!!((t==="v4"||!t)&&G0.test(e)||(t==="v6"||!t)&&Z0.test(e))}var yr=class e extends K{_parse(t){if(this._def.coerce&&(t.data=String(t.data)),this._getType(t)!==F.string){let n=this._getOrReturnCtx(t);return O(n,{code:D.invalid_type,expected:F.string,received:n.parsedType}),V}let r=new Ye,o;for(let n of this._def.checks)if(n.kind==="min")t.data.lengthn.value&&(o=this._getOrReturnCtx(t,o),O(o,{code:D.too_big,maximum:n.value,type:"string",inclusive:!0,exact:!1,message:n.message}),r.dirty());else if(n.kind==="length"){let l=t.data.length>n.value,u=t.data.lengtht.test(o),w({validation:a,code:D.invalid_string},z.errToObj(r)))}_addCheck(t){return new e(b(w({},this._def),{checks:[...this._def.checks,t]}))}email(t){return this._addCheck(w({kind:"email"},z.errToObj(t)))}url(t){return this._addCheck(w({kind:"url"},z.errToObj(t)))}emoji(t){return this._addCheck(w({kind:"emoji"},z.errToObj(t)))}uuid(t){return this._addCheck(w({kind:"uuid"},z.errToObj(t)))}nanoid(t){return this._addCheck(w({kind:"nanoid"},z.errToObj(t)))}cuid(t){return this._addCheck(w({kind:"cuid"},z.errToObj(t)))}cuid2(t){return this._addCheck(w({kind:"cuid2"},z.errToObj(t)))}ulid(t){return this._addCheck(w({kind:"ulid"},z.errToObj(t)))}base64(t){return this._addCheck(w({kind:"base64"},z.errToObj(t)))}ip(t){return this._addCheck(w({kind:"ip"},z.errToObj(t)))}datetime(t){var a,r;return typeof t=="string"?this._addCheck({kind:"datetime",precision:null,offset:!1,local:!1,message:t}):this._addCheck(w({kind:"datetime",precision:typeof(t==null?void 0:t.precision)=="undefined"?null:t==null?void 0:t.precision,offset:(a=t==null?void 0:t.offset)!==null&&a!==void 0?a:!1,local:(r=t==null?void 0:t.local)!==null&&r!==void 0?r:!1},z.errToObj(t==null?void 0:t.message)))}date(t){return this._addCheck({kind:"date",message:t})}time(t){return typeof t=="string"?this._addCheck({kind:"time",precision:null,message:t}):this._addCheck(w({kind:"time",precision:typeof(t==null?void 0:t.precision)=="undefined"?null:t==null?void 0:t.precision},z.errToObj(t==null?void 0:t.message)))}duration(t){return this._addCheck(w({kind:"duration"},z.errToObj(t)))}regex(t,a){return this._addCheck(w({kind:"regex",regex:t},z.errToObj(a)))}includes(t,a){return this._addCheck(w({kind:"includes",value:t,position:a==null?void 0:a.position},z.errToObj(a==null?void 0:a.message)))}startsWith(t,a){return this._addCheck(w({kind:"startsWith",value:t},z.errToObj(a)))}endsWith(t,a){return this._addCheck(w({kind:"endsWith",value:t},z.errToObj(a)))}min(t,a){return this._addCheck(w({kind:"min",value:t},z.errToObj(a)))}max(t,a){return this._addCheck(w({kind:"max",value:t},z.errToObj(a)))}length(t,a){return this._addCheck(w({kind:"length",value:t},z.errToObj(a)))}nonempty(t){return this.min(1,z.errToObj(t))}trim(){return new e(b(w({},this._def),{checks:[...this._def.checks,{kind:"trim"}]}))}toLowerCase(){return new e(b(w({},this._def),{checks:[...this._def.checks,{kind:"toLowerCase"}]}))}toUpperCase(){return new e(b(w({},this._def),{checks:[...this._def.checks,{kind:"toUpperCase"}]}))}get isDatetime(){return!!this._def.checks.find(t=>t.kind==="datetime")}get isDate(){return!!this._def.checks.find(t=>t.kind==="date")}get isTime(){return!!this._def.checks.find(t=>t.kind==="time")}get isDuration(){return!!this._def.checks.find(t=>t.kind==="duration")}get isEmail(){return!!this._def.checks.find(t=>t.kind==="email")}get isURL(){return!!this._def.checks.find(t=>t.kind==="url")}get isEmoji(){return!!this._def.checks.find(t=>t.kind==="emoji")}get isUUID(){return!!this._def.checks.find(t=>t.kind==="uuid")}get isNANOID(){return!!this._def.checks.find(t=>t.kind==="nanoid")}get isCUID(){return!!this._def.checks.find(t=>t.kind==="cuid")}get isCUID2(){return!!this._def.checks.find(t=>t.kind==="cuid2")}get isULID(){return!!this._def.checks.find(t=>t.kind==="ulid")}get isIP(){return!!this._def.checks.find(t=>t.kind==="ip")}get isBase64(){return!!this._def.checks.find(t=>t.kind==="base64")}get minLength(){let t=null;for(let a of this._def.checks)a.kind==="min"&&(t===null||a.value>t)&&(t=a.value);return t}get maxLength(){let t=null;for(let a of this._def.checks)a.kind==="max"&&(t===null||a.value{var t;return new yr(w({checks:[],typeName:H.ZodString,coerce:(t=e==null?void 0:e.coerce)!==null&&t!==void 0?t:!1},$(e)))};function Y0(e,t){let a=(e.toString().split(".")[1]||"").length,r=(t.toString().split(".")[1]||"").length,o=a>r?a:r,n=parseInt(e.toFixed(o).replace(".","")),l=parseInt(t.toFixed(o).replace(".",""));return n%l/Math.pow(10,o)}var Xr=class e extends K{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(t){if(this._def.coerce&&(t.data=Number(t.data)),this._getType(t)!==F.number){let n=this._getOrReturnCtx(t);return O(n,{code:D.invalid_type,expected:F.number,received:n.parsedType}),V}let r,o=new Ye;for(let n of this._def.checks)n.kind==="int"?te.isInteger(t.data)||(r=this._getOrReturnCtx(t,r),O(r,{code:D.invalid_type,expected:"integer",received:"float",message:n.message}),o.dirty()):n.kind==="min"?(n.inclusive?t.datan.value:t.data>=n.value)&&(r=this._getOrReturnCtx(t,r),O(r,{code:D.too_big,maximum:n.value,type:"number",inclusive:n.inclusive,exact:!1,message:n.message}),o.dirty()):n.kind==="multipleOf"?Y0(t.data,n.value)!==0&&(r=this._getOrReturnCtx(t,r),O(r,{code:D.not_multiple_of,multipleOf:n.value,message:n.message}),o.dirty()):n.kind==="finite"?Number.isFinite(t.data)||(r=this._getOrReturnCtx(t,r),O(r,{code:D.not_finite,message:n.message}),o.dirty()):te.assertNever(n);return{status:o.value,value:t.data}}gte(t,a){return this.setLimit("min",t,!0,z.toString(a))}gt(t,a){return this.setLimit("min",t,!1,z.toString(a))}lte(t,a){return this.setLimit("max",t,!0,z.toString(a))}lt(t,a){return this.setLimit("max",t,!1,z.toString(a))}setLimit(t,a,r,o){return new e(b(w({},this._def),{checks:[...this._def.checks,{kind:t,value:a,inclusive:r,message:z.toString(o)}]}))}_addCheck(t){return new e(b(w({},this._def),{checks:[...this._def.checks,t]}))}int(t){return this._addCheck({kind:"int",message:z.toString(t)})}positive(t){return this._addCheck({kind:"min",value:0,inclusive:!1,message:z.toString(t)})}negative(t){return this._addCheck({kind:"max",value:0,inclusive:!1,message:z.toString(t)})}nonpositive(t){return this._addCheck({kind:"max",value:0,inclusive:!0,message:z.toString(t)})}nonnegative(t){return this._addCheck({kind:"min",value:0,inclusive:!0,message:z.toString(t)})}multipleOf(t,a){return this._addCheck({kind:"multipleOf",value:t,message:z.toString(a)})}finite(t){return this._addCheck({kind:"finite",message:z.toString(t)})}safe(t){return this._addCheck({kind:"min",inclusive:!0,value:Number.MIN_SAFE_INTEGER,message:z.toString(t)})._addCheck({kind:"max",inclusive:!0,value:Number.MAX_SAFE_INTEGER,message:z.toString(t)})}get minValue(){let t=null;for(let a of this._def.checks)a.kind==="min"&&(t===null||a.value>t)&&(t=a.value);return t}get maxValue(){let t=null;for(let a of this._def.checks)a.kind==="max"&&(t===null||a.valuet.kind==="int"||t.kind==="multipleOf"&&te.isInteger(t.value))}get isFinite(){let t=null,a=null;for(let r of this._def.checks){if(r.kind==="finite"||r.kind==="int"||r.kind==="multipleOf")return!0;r.kind==="min"?(a===null||r.value>a)&&(a=r.value):r.kind==="max"&&(t===null||r.valuenew Xr(w({checks:[],typeName:H.ZodNumber,coerce:(e==null?void 0:e.coerce)||!1},$(e)));var Qr=class e extends K{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte}_parse(t){if(this._def.coerce&&(t.data=BigInt(t.data)),this._getType(t)!==F.bigint){let n=this._getOrReturnCtx(t);return O(n,{code:D.invalid_type,expected:F.bigint,received:n.parsedType}),V}let r,o=new Ye;for(let n of this._def.checks)n.kind==="min"?(n.inclusive?t.datan.value:t.data>=n.value)&&(r=this._getOrReturnCtx(t,r),O(r,{code:D.too_big,type:"bigint",maximum:n.value,inclusive:n.inclusive,message:n.message}),o.dirty()):n.kind==="multipleOf"?t.data%n.value!==BigInt(0)&&(r=this._getOrReturnCtx(t,r),O(r,{code:D.not_multiple_of,multipleOf:n.value,message:n.message}),o.dirty()):te.assertNever(n);return{status:o.value,value:t.data}}gte(t,a){return this.setLimit("min",t,!0,z.toString(a))}gt(t,a){return this.setLimit("min",t,!1,z.toString(a))}lte(t,a){return this.setLimit("max",t,!0,z.toString(a))}lt(t,a){return this.setLimit("max",t,!1,z.toString(a))}setLimit(t,a,r,o){return new e(b(w({},this._def),{checks:[...this._def.checks,{kind:t,value:a,inclusive:r,message:z.toString(o)}]}))}_addCheck(t){return new e(b(w({},this._def),{checks:[...this._def.checks,t]}))}positive(t){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!1,message:z.toString(t)})}negative(t){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!1,message:z.toString(t)})}nonpositive(t){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!0,message:z.toString(t)})}nonnegative(t){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!0,message:z.toString(t)})}multipleOf(t,a){return this._addCheck({kind:"multipleOf",value:t,message:z.toString(a)})}get minValue(){let t=null;for(let a of this._def.checks)a.kind==="min"&&(t===null||a.value>t)&&(t=a.value);return t}get maxValue(){let t=null;for(let a of this._def.checks)a.kind==="max"&&(t===null||a.value{var t;return new Qr(w({checks:[],typeName:H.ZodBigInt,coerce:(t=e==null?void 0:e.coerce)!==null&&t!==void 0?t:!1},$(e)))};var Yr=class extends K{_parse(t){if(this._def.coerce&&(t.data=!!t.data),this._getType(t)!==F.boolean){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.boolean,received:r.parsedType}),V}return nt(t.data)}};Yr.create=e=>new Yr(w({typeName:H.ZodBoolean,coerce:(e==null?void 0:e.coerce)||!1},$(e)));var Jr=class e extends K{_parse(t){if(this._def.coerce&&(t.data=new Date(t.data)),this._getType(t)!==F.date){let n=this._getOrReturnCtx(t);return O(n,{code:D.invalid_type,expected:F.date,received:n.parsedType}),V}if(isNaN(t.data.getTime())){let n=this._getOrReturnCtx(t);return O(n,{code:D.invalid_date}),V}let r=new Ye,o;for(let n of this._def.checks)n.kind==="min"?t.data.getTime()n.value&&(o=this._getOrReturnCtx(t,o),O(o,{code:D.too_big,message:n.message,inclusive:!0,exact:!1,maximum:n.value,type:"date"}),r.dirty()):te.assertNever(n);return{status:r.value,value:new Date(t.data.getTime())}}_addCheck(t){return new e(b(w({},this._def),{checks:[...this._def.checks,t]}))}min(t,a){return this._addCheck({kind:"min",value:t.getTime(),message:z.toString(a)})}max(t,a){return this._addCheck({kind:"max",value:t.getTime(),message:z.toString(a)})}get minDate(){let t=null;for(let a of this._def.checks)a.kind==="min"&&(t===null||a.value>t)&&(t=a.value);return t!=null?new Date(t):null}get maxDate(){let t=null;for(let a of this._def.checks)a.kind==="max"&&(t===null||a.valuenew Jr(w({checks:[],coerce:(e==null?void 0:e.coerce)||!1,typeName:H.ZodDate},$(e)));var un=class extends K{_parse(t){if(this._getType(t)!==F.symbol){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.symbol,received:r.parsedType}),V}return nt(t.data)}};un.create=e=>new un(w({typeName:H.ZodSymbol},$(e)));var eo=class extends K{_parse(t){if(this._getType(t)!==F.undefined){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.undefined,received:r.parsedType}),V}return nt(t.data)}};eo.create=e=>new eo(w({typeName:H.ZodUndefined},$(e)));var to=class extends K{_parse(t){if(this._getType(t)!==F.null){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.null,received:r.parsedType}),V}return nt(t.data)}};to.create=e=>new to(w({typeName:H.ZodNull},$(e)));var Lr=class extends K{constructor(){super(...arguments),this._any=!0}_parse(t){return nt(t.data)}};Lr.create=e=>new Lr(w({typeName:H.ZodAny},$(e)));var qa=class extends K{constructor(){super(...arguments),this._unknown=!0}_parse(t){return nt(t.data)}};qa.create=e=>new qa(w({typeName:H.ZodUnknown},$(e)));var ua=class extends K{_parse(t){let a=this._getOrReturnCtx(t);return O(a,{code:D.invalid_type,expected:F.never,received:a.parsedType}),V}};ua.create=e=>new ua(w({typeName:H.ZodNever},$(e)));var sn=class extends K{_parse(t){if(this._getType(t)!==F.undefined){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.void,received:r.parsedType}),V}return nt(t.data)}};sn.create=e=>new sn(w({typeName:H.ZodVoid},$(e)));var Ha=class e extends K{_parse(t){let{ctx:a,status:r}=this._processInputParams(t),o=this._def;if(a.parsedType!==F.array)return O(a,{code:D.invalid_type,expected:F.array,received:a.parsedType}),V;if(o.exactLength!==null){let l=a.data.length>o.exactLength.value,u=a.data.lengtho.maxLength.value&&(O(a,{code:D.too_big,maximum:o.maxLength.value,type:"array",inclusive:!0,exact:!1,message:o.maxLength.message}),r.dirty()),a.common.async)return Promise.all([...a.data].map((l,u)=>o.type._parseAsync(new Ht(a,l,a.path,u)))).then(l=>Ye.mergeArray(r,l));let n=[...a.data].map((l,u)=>o.type._parseSync(new Ht(a,l,a.path,u)));return Ye.mergeArray(r,n)}get element(){return this._def.type}min(t,a){return new e(b(w({},this._def),{minLength:{value:t,message:z.toString(a)}}))}max(t,a){return new e(b(w({},this._def),{maxLength:{value:t,message:z.toString(a)}}))}length(t,a){return new e(b(w({},this._def),{exactLength:{value:t,message:z.toString(a)}}))}nonempty(t){return this.min(1,t)}};Ha.create=(e,t)=>new Ha(w({type:e,minLength:null,maxLength:null,exactLength:null,typeName:H.ZodArray},$(t)));function on(e){if(e instanceof vt){let t={};for(let a in e.shape){let r=e.shape[a];t[a]=qt.create(on(r))}return new vt(b(w({},e._def),{shape:()=>t}))}else return e instanceof Ha?new Ha(b(w({},e._def),{type:on(e.element)})):e instanceof qt?qt.create(on(e.unwrap())):e instanceof Ia?Ia.create(on(e.unwrap())):e instanceof Ca?Ca.create(e.items.map(t=>on(t))):e}var vt=class e extends K{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=this.extend}_getCached(){if(this._cached!==null)return this._cached;let t=this._def.shape(),a=te.objectKeys(t);return this._cached={shape:t,keys:a}}_parse(t){if(this._getType(t)!==F.object){let i=this._getOrReturnCtx(t);return O(i,{code:D.invalid_type,expected:F.object,received:i.parsedType}),V}let{status:r,ctx:o}=this._processInputParams(t),{shape:n,keys:l}=this._getCached(),u=[];if(!(this._def.catchall instanceof ua&&this._def.unknownKeys==="strip"))for(let i in o.data)l.includes(i)||u.push(i);let s=[];for(let i of l){let d=n[i],f=o.data[i];s.push({key:{status:"valid",value:i},value:d._parse(new Ht(o,f,o.path,i)),alwaysSet:i in o.data})}if(this._def.catchall instanceof ua){let i=this._def.unknownKeys;if(i==="passthrough")for(let d of u)s.push({key:{status:"valid",value:d},value:{status:"valid",value:o.data[d]}});else if(i==="strict")u.length>0&&(O(o,{code:D.unrecognized_keys,keys:u}),r.dirty());else if(i!=="strip")throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{let i=this._def.catchall;for(let d of u){let f=o.data[d];s.push({key:{status:"valid",value:d},value:i._parse(new Ht(o,f,o.path,d)),alwaysSet:d in o.data})}}return o.common.async?Promise.resolve().then(()=>Pe(this,null,function*(){let i=[];for(let d of s){let f=yield d.key,p=yield d.value;i.push({key:f,value:p,alwaysSet:d.alwaysSet})}return i})).then(i=>Ye.mergeObjectSync(r,i)):Ye.mergeObjectSync(r,s)}get shape(){return this._def.shape()}strict(t){return z.errToObj,new e(w(b(w({},this._def),{unknownKeys:"strict"}),t!==void 0?{errorMap:(a,r)=>{var o,n,l,u;let s=(l=(n=(o=this._def).errorMap)===null||n===void 0?void 0:n.call(o,a,r).message)!==null&&l!==void 0?l:r.defaultError;return a.code==="unrecognized_keys"?{message:(u=z.errToObj(t).message)!==null&&u!==void 0?u:s}:{message:s}}}:{}))}strip(){return new e(b(w({},this._def),{unknownKeys:"strip"}))}passthrough(){return new e(b(w({},this._def),{unknownKeys:"passthrough"}))}extend(t){return new e(b(w({},this._def),{shape:()=>w(w({},this._def.shape()),t)}))}merge(t){return new e({unknownKeys:t._def.unknownKeys,catchall:t._def.catchall,shape:()=>w(w({},this._def.shape()),t._def.shape()),typeName:H.ZodObject})}setKey(t,a){return this.augment({[t]:a})}catchall(t){return new e(b(w({},this._def),{catchall:t}))}pick(t){let a={};return te.objectKeys(t).forEach(r=>{t[r]&&this.shape[r]&&(a[r]=this.shape[r])}),new e(b(w({},this._def),{shape:()=>a}))}omit(t){let a={};return te.objectKeys(this.shape).forEach(r=>{t[r]||(a[r]=this.shape[r])}),new e(b(w({},this._def),{shape:()=>a}))}deepPartial(){return on(this)}partial(t){let a={};return te.objectKeys(this.shape).forEach(r=>{let o=this.shape[r];t&&!t[r]?a[r]=o:a[r]=o.optional()}),new e(b(w({},this._def),{shape:()=>a}))}required(t){let a={};return te.objectKeys(this.shape).forEach(r=>{if(t&&!t[r])a[r]=this.shape[r];else{let n=this.shape[r];for(;n instanceof qt;)n=n._def.innerType;a[r]=n}}),new e(b(w({},this._def),{shape:()=>a}))}keyof(){return Ly(te.objectKeys(this.shape))}};vt.create=(e,t)=>new vt(w({shape:()=>e,unknownKeys:"strip",catchall:ua.create(),typeName:H.ZodObject},$(t)));vt.strictCreate=(e,t)=>new vt(w({shape:()=>e,unknownKeys:"strict",catchall:ua.create(),typeName:H.ZodObject},$(t)));vt.lazycreate=(e,t)=>new vt(w({shape:e,unknownKeys:"strip",catchall:ua.create(),typeName:H.ZodObject},$(t)));var ao=class extends K{_parse(t){let{ctx:a}=this._processInputParams(t),r=this._def.options;function o(n){for(let u of n)if(u.result.status==="valid")return u.result;for(let u of n)if(u.result.status==="dirty")return a.common.issues.push(...u.ctx.common.issues),u.result;let l=n.map(u=>new _t(u.ctx.common.issues));return O(a,{code:D.invalid_union,unionErrors:l}),V}if(a.common.async)return Promise.all(r.map(n=>Pe(this,null,function*(){let l=b(w({},a),{common:b(w({},a.common),{issues:[]}),parent:null});return{result:yield n._parseAsync({data:a.data,path:a.path,parent:l}),ctx:l}}))).then(o);{let n,l=[];for(let s of r){let i=b(w({},a),{common:b(w({},a.common),{issues:[]}),parent:null}),d=s._parseSync({data:a.data,path:a.path,parent:i});if(d.status==="valid")return d;d.status==="dirty"&&!n&&(n={result:d,ctx:i}),i.common.issues.length&&l.push(i.common.issues)}if(n)return a.common.issues.push(...n.ctx.common.issues),n.result;let u=l.map(s=>new _t(s));return O(a,{code:D.invalid_union,unionErrors:u}),V}}get options(){return this._def.options}};ao.create=(e,t)=>new ao(w({options:e,typeName:H.ZodUnion},$(t)));var Ua=e=>e instanceof oo?Ua(e.schema):e instanceof Rt?Ua(e.innerType()):e instanceof no?[e.value]:e instanceof lo?e.options:e instanceof uo?te.objectValues(e.enum):e instanceof so?Ua(e._def.innerType):e instanceof eo?[void 0]:e instanceof to?[null]:e instanceof qt?[void 0,...Ua(e.unwrap())]:e instanceof Ia?[null,...Ua(e.unwrap())]:e instanceof kl||e instanceof fo?Ua(e.unwrap()):e instanceof io?Ua(e._def.innerType):[],Hs=class e extends K{_parse(t){let{ctx:a}=this._processInputParams(t);if(a.parsedType!==F.object)return O(a,{code:D.invalid_type,expected:F.object,received:a.parsedType}),V;let r=this.discriminator,o=a.data[r],n=this.optionsMap.get(o);return n?a.common.async?n._parseAsync({data:a.data,path:a.path,parent:a}):n._parseSync({data:a.data,path:a.path,parent:a}):(O(a,{code:D.invalid_union_discriminator,options:Array.from(this.optionsMap.keys()),path:[r]}),V)}get discriminator(){return this._def.discriminator}get options(){return this._def.options}get optionsMap(){return this._def.optionsMap}static create(t,a,r){let o=new Map;for(let n of a){let l=Ua(n.shape[t]);if(!l.length)throw new Error(`A discriminator value for key \`${t}\` could not be extracted from all schema options`);for(let u of l){if(o.has(u))throw new Error(`Discriminator property ${String(t)} has duplicate value ${String(u)}`);o.set(u,n)}}return new e(w({typeName:H.ZodDiscriminatedUnion,discriminator:t,options:a,optionsMap:o},$(r)))}};function Cc(e,t){let a=xr(e),r=xr(t);if(e===t)return{valid:!0,data:e};if(a===F.object&&r===F.object){let o=te.objectKeys(t),n=te.objectKeys(e).filter(u=>o.indexOf(u)!==-1),l=w(w({},e),t);for(let u of n){let s=Cc(e[u],t[u]);if(!s.valid)return{valid:!1};l[u]=s.data}return{valid:!0,data:l}}else if(a===F.array&&r===F.array){if(e.length!==t.length)return{valid:!1};let o=[];for(let n=0;n{if(Lc(n)||Lc(l))return V;let u=Cc(n.value,l.value);return u.valid?((wc(n)||wc(l))&&a.dirty(),{status:a.value,value:u.data}):(O(r,{code:D.invalid_intersection_types}),V)};return r.common.async?Promise.all([this._def.left._parseAsync({data:r.data,path:r.path,parent:r}),this._def.right._parseAsync({data:r.data,path:r.path,parent:r})]).then(([n,l])=>o(n,l)):o(this._def.left._parseSync({data:r.data,path:r.path,parent:r}),this._def.right._parseSync({data:r.data,path:r.path,parent:r}))}};ro.create=(e,t,a)=>new ro(w({left:e,right:t,typeName:H.ZodIntersection},$(a)));var Ca=class e extends K{_parse(t){let{status:a,ctx:r}=this._processInputParams(t);if(r.parsedType!==F.array)return O(r,{code:D.invalid_type,expected:F.array,received:r.parsedType}),V;if(r.data.lengththis._def.items.length&&(O(r,{code:D.too_big,maximum:this._def.items.length,inclusive:!0,exact:!1,type:"array"}),a.dirty());let n=[...r.data].map((l,u)=>{let s=this._def.items[u]||this._def.rest;return s?s._parse(new Ht(r,l,r.path,u)):null}).filter(l=>!!l);return r.common.async?Promise.all(n).then(l=>Ye.mergeArray(a,l)):Ye.mergeArray(a,n)}get items(){return this._def.items}rest(t){return new e(b(w({},this._def),{rest:t}))}};Ca.create=(e,t)=>{if(!Array.isArray(e))throw new Error("You must pass an array of schemas to z.tuple([ ... ])");return new Ca(w({items:e,typeName:H.ZodTuple,rest:null},$(t)))};var Vs=class e extends K{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(t){let{status:a,ctx:r}=this._processInputParams(t);if(r.parsedType!==F.object)return O(r,{code:D.invalid_type,expected:F.object,received:r.parsedType}),V;let o=[],n=this._def.keyType,l=this._def.valueType;for(let u in r.data)o.push({key:n._parse(new Ht(r,u,r.path,u)),value:l._parse(new Ht(r,r.data[u],r.path,u)),alwaysSet:u in r.data});return r.common.async?Ye.mergeObjectAsync(a,o):Ye.mergeObjectSync(a,o)}get element(){return this._def.valueType}static create(t,a,r){return a instanceof K?new e(w({keyType:t,valueType:a,typeName:H.ZodRecord},$(r))):new e(w({keyType:yr.create(),valueType:t,typeName:H.ZodRecord},$(a)))}},dn=class extends K{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(t){let{status:a,ctx:r}=this._processInputParams(t);if(r.parsedType!==F.map)return O(r,{code:D.invalid_type,expected:F.map,received:r.parsedType}),V;let o=this._def.keyType,n=this._def.valueType,l=[...r.data.entries()].map(([u,s],i)=>({key:o._parse(new Ht(r,u,r.path,[i,"key"])),value:n._parse(new Ht(r,s,r.path,[i,"value"]))}));if(r.common.async){let u=new Map;return Promise.resolve().then(()=>Pe(this,null,function*(){for(let s of l){let i=yield s.key,d=yield s.value;if(i.status==="aborted"||d.status==="aborted")return V;(i.status==="dirty"||d.status==="dirty")&&a.dirty(),u.set(i.value,d.value)}return{status:a.value,value:u}}))}else{let u=new Map;for(let s of l){let i=s.key,d=s.value;if(i.status==="aborted"||d.status==="aborted")return V;(i.status==="dirty"||d.status==="dirty")&&a.dirty(),u.set(i.value,d.value)}return{status:a.value,value:u}}}};dn.create=(e,t,a)=>new dn(w({valueType:t,keyType:e,typeName:H.ZodMap},$(a)));var fn=class e extends K{_parse(t){let{status:a,ctx:r}=this._processInputParams(t);if(r.parsedType!==F.set)return O(r,{code:D.invalid_type,expected:F.set,received:r.parsedType}),V;let o=this._def;o.minSize!==null&&r.data.sizeo.maxSize.value&&(O(r,{code:D.too_big,maximum:o.maxSize.value,type:"set",inclusive:!0,exact:!1,message:o.maxSize.message}),a.dirty());let n=this._def.valueType;function l(s){let i=new Set;for(let d of s){if(d.status==="aborted")return V;d.status==="dirty"&&a.dirty(),i.add(d.value)}return{status:a.value,value:i}}let u=[...r.data.values()].map((s,i)=>n._parse(new Ht(r,s,r.path,i)));return r.common.async?Promise.all(u).then(s=>l(s)):l(u)}min(t,a){return new e(b(w({},this._def),{minSize:{value:t,message:z.toString(a)}}))}max(t,a){return new e(b(w({},this._def),{maxSize:{value:t,message:z.toString(a)}}))}size(t,a){return this.min(t,a).max(t,a)}nonempty(t){return this.min(1,t)}};fn.create=(e,t)=>new fn(w({valueType:e,minSize:null,maxSize:null,typeName:H.ZodSet},$(t)));var js=class e extends K{constructor(){super(...arguments),this.validate=this.implement}_parse(t){let{ctx:a}=this._processInputParams(t);if(a.parsedType!==F.function)return O(a,{code:D.invalid_type,expected:F.function,received:a.parsedType}),V;function r(u,s){return Us({data:u,path:a.path,errorMaps:[a.common.contextualErrorMap,a.schemaErrorMap,zs(),ln].filter(i=>!!i),issueData:{code:D.invalid_arguments,argumentsError:s}})}function o(u,s){return Us({data:u,path:a.path,errorMaps:[a.common.contextualErrorMap,a.schemaErrorMap,zs(),ln].filter(i=>!!i),issueData:{code:D.invalid_return_type,returnTypeError:s}})}let n={errorMap:a.common.contextualErrorMap},l=a.data;if(this._def.returns instanceof wr){let u=this;return nt(function(...s){return Pe(this,null,function*(){let i=new _t([]),d=yield u._def.args.parseAsync(s,n).catch(v=>{throw i.addIssue(r(s,v)),i}),f=yield Reflect.apply(l,this,d);return yield u._def.returns._def.type.parseAsync(f,n).catch(v=>{throw i.addIssue(o(f,v)),i})})})}else{let u=this;return nt(function(...s){let i=u._def.args.safeParse(s,n);if(!i.success)throw new _t([r(s,i.error)]);let d=Reflect.apply(l,this,i.data),f=u._def.returns.safeParse(d,n);if(!f.success)throw new _t([o(d,f.error)]);return f.data})}}parameters(){return this._def.args}returnType(){return this._def.returns}args(...t){return new e(b(w({},this._def),{args:Ca.create(t).rest(qa.create())}))}returns(t){return new e(b(w({},this._def),{returns:t}))}implement(t){return this.parse(t)}strictImplement(t){return this.parse(t)}static create(t,a,r){return new e(w({args:t||Ca.create([]).rest(qa.create()),returns:a||qa.create(),typeName:H.ZodFunction},$(r)))}},oo=class extends K{get schema(){return this._def.getter()}_parse(t){let{ctx:a}=this._processInputParams(t);return this._def.getter()._parse({data:a.data,path:a.path,parent:a})}};oo.create=(e,t)=>new oo(w({getter:e,typeName:H.ZodLazy},$(t)));var no=class extends K{_parse(t){if(t.data!==this._def.value){let a=this._getOrReturnCtx(t);return O(a,{received:a.data,code:D.invalid_literal,expected:this._def.value}),V}return{status:"valid",value:t.data}}get value(){return this._def.value}};no.create=(e,t)=>new no(w({value:e,typeName:H.ZodLiteral},$(t)));function Ly(e,t){return new lo(w({values:e,typeName:H.ZodEnum},$(t)))}var lo=class e extends K{constructor(){super(...arguments),wl.set(this,void 0)}_parse(t){if(typeof t.data!="string"){let a=this._getOrReturnCtx(t),r=this._def.values;return O(a,{expected:te.joinValues(r),received:a.parsedType,code:D.invalid_type}),V}if(qs(this,wl,"f")||gy(this,wl,new Set(this._def.values),"f"),!qs(this,wl,"f").has(t.data)){let a=this._getOrReturnCtx(t),r=this._def.values;return O(a,{received:a.data,code:D.invalid_enum_value,options:r}),V}return nt(t.data)}get options(){return this._def.values}get enum(){let t={};for(let a of this._def.values)t[a]=a;return t}get Values(){let t={};for(let a of this._def.values)t[a]=a;return t}get Enum(){let t={};for(let a of this._def.values)t[a]=a;return t}extract(t,a=this._def){return e.create(t,w(w({},this._def),a))}exclude(t,a=this._def){return e.create(this.options.filter(r=>!t.includes(r)),w(w({},this._def),a))}};wl=new WeakMap;lo.create=Ly;var uo=class extends K{constructor(){super(...arguments),Cl.set(this,void 0)}_parse(t){let a=te.getValidEnumValues(this._def.values),r=this._getOrReturnCtx(t);if(r.parsedType!==F.string&&r.parsedType!==F.number){let o=te.objectValues(a);return O(r,{expected:te.joinValues(o),received:r.parsedType,code:D.invalid_type}),V}if(qs(this,Cl,"f")||gy(this,Cl,new Set(te.getValidEnumValues(this._def.values)),"f"),!qs(this,Cl,"f").has(t.data)){let o=te.objectValues(a);return O(r,{received:r.data,code:D.invalid_enum_value,options:o}),V}return nt(t.data)}get enum(){return this._def.values}};Cl=new WeakMap;uo.create=(e,t)=>new uo(w({values:e,typeName:H.ZodNativeEnum},$(t)));var wr=class extends K{unwrap(){return this._def.type}_parse(t){let{ctx:a}=this._processInputParams(t);if(a.parsedType!==F.promise&&a.common.async===!1)return O(a,{code:D.invalid_type,expected:F.promise,received:a.parsedType}),V;let r=a.parsedType===F.promise?a.data:Promise.resolve(a.data);return nt(r.then(o=>this._def.type.parseAsync(o,{path:a.path,errorMap:a.common.contextualErrorMap})))}};wr.create=(e,t)=>new wr(w({type:e,typeName:H.ZodPromise},$(t)));var Rt=class extends K{innerType(){return this._def.schema}sourceType(){return this._def.schema._def.typeName===H.ZodEffects?this._def.schema.sourceType():this._def.schema}_parse(t){let{status:a,ctx:r}=this._processInputParams(t),o=this._def.effect||null,n={addIssue:l=>{O(r,l),l.fatal?a.abort():a.dirty()},get path(){return r.path}};if(n.addIssue=n.addIssue.bind(n),o.type==="preprocess"){let l=o.transform(r.data,n);if(r.common.async)return Promise.resolve(l).then(u=>Pe(this,null,function*(){if(a.value==="aborted")return V;let s=yield this._def.schema._parseAsync({data:u,path:r.path,parent:r});return s.status==="aborted"?V:s.status==="dirty"||a.value==="dirty"?nn(s.value):s}));{if(a.value==="aborted")return V;let u=this._def.schema._parseSync({data:l,path:r.path,parent:r});return u.status==="aborted"?V:u.status==="dirty"||a.value==="dirty"?nn(u.value):u}}if(o.type==="refinement"){let l=u=>{let s=o.refinement(u,n);if(r.common.async)return Promise.resolve(s);if(s instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return u};if(r.common.async===!1){let u=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});return u.status==="aborted"?V:(u.status==="dirty"&&a.dirty(),l(u.value),{status:a.value,value:u.value})}else return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then(u=>u.status==="aborted"?V:(u.status==="dirty"&&a.dirty(),l(u.value).then(()=>({status:a.value,value:u.value}))))}if(o.type==="transform")if(r.common.async===!1){let l=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});if(!Il(l))return l;let u=o.transform(l.value,n);if(u instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:a.value,value:u}}else return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then(l=>Il(l)?Promise.resolve(o.transform(l.value,n)).then(u=>({status:a.value,value:u})):l);te.assertNever(o)}};Rt.create=(e,t,a)=>new Rt(w({schema:e,typeName:H.ZodEffects,effect:t},$(a)));Rt.createWithPreprocess=(e,t,a)=>new Rt(w({schema:t,effect:{type:"preprocess",transform:e},typeName:H.ZodEffects},$(a)));var qt=class extends K{_parse(t){return this._getType(t)===F.undefined?nt(void 0):this._def.innerType._parse(t)}unwrap(){return this._def.innerType}};qt.create=(e,t)=>new qt(w({innerType:e,typeName:H.ZodOptional},$(t)));var Ia=class extends K{_parse(t){return this._getType(t)===F.null?nt(null):this._def.innerType._parse(t)}unwrap(){return this._def.innerType}};Ia.create=(e,t)=>new Ia(w({innerType:e,typeName:H.ZodNullable},$(t)));var so=class extends K{_parse(t){let{ctx:a}=this._processInputParams(t),r=a.data;return a.parsedType===F.undefined&&(r=this._def.defaultValue()),this._def.innerType._parse({data:r,path:a.path,parent:a})}removeDefault(){return this._def.innerType}};so.create=(e,t)=>new so(w({innerType:e,typeName:H.ZodDefault,defaultValue:typeof t.default=="function"?t.default:()=>t.default},$(t)));var io=class extends K{_parse(t){let{ctx:a}=this._processInputParams(t),r=b(w({},a),{common:b(w({},a.common),{issues:[]})}),o=this._def.innerType._parse({data:r.data,path:r.path,parent:w({},r)});return Sl(o)?o.then(n=>({status:"valid",value:n.status==="valid"?n.value:this._def.catchValue({get error(){return new _t(r.common.issues)},input:r.data})})):{status:"valid",value:o.status==="valid"?o.value:this._def.catchValue({get error(){return new _t(r.common.issues)},input:r.data})}}removeCatch(){return this._def.innerType}};io.create=(e,t)=>new io(w({innerType:e,typeName:H.ZodCatch,catchValue:typeof t.catch=="function"?t.catch:()=>t.catch},$(t)));var cn=class extends K{_parse(t){if(this._getType(t)!==F.nan){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.nan,received:r.parsedType}),V}return{status:"valid",value:t.data}}};cn.create=e=>new cn(w({typeName:H.ZodNaN},$(e)));var J0=Symbol("zod_brand"),kl=class extends K{_parse(t){let{ctx:a}=this._processInputParams(t),r=a.data;return this._def.type._parse({data:r,path:a.path,parent:a})}unwrap(){return this._def.type}},Pl=class e extends K{_parse(t){let{status:a,ctx:r}=this._processInputParams(t);if(r.common.async)return Pe(this,null,function*(){let n=yield this._def.in._parseAsync({data:r.data,path:r.path,parent:r});return n.status==="aborted"?V:n.status==="dirty"?(a.dirty(),nn(n.value)):this._def.out._parseAsync({data:n.value,path:r.path,parent:r})});{let o=this._def.in._parseSync({data:r.data,path:r.path,parent:r});return o.status==="aborted"?V:o.status==="dirty"?(a.dirty(),{status:"dirty",value:o.value}):this._def.out._parseSync({data:o.value,path:r.path,parent:r})}}static create(t,a){return new e({in:t,out:a,typeName:H.ZodPipeline})}},fo=class extends K{_parse(t){let a=this._def.innerType._parse(t),r=o=>(Il(o)&&(o.value=Object.freeze(o.value)),o);return Sl(a)?a.then(o=>r(o)):r(a)}unwrap(){return this._def.innerType}};fo.create=(e,t)=>new fo(w({innerType:e,typeName:H.ZodReadonly},$(t)));function wy(e,t={},a){return e?Lr.create().superRefine((r,o)=>{var n,l;if(!e(r)){let u=typeof t=="function"?t(r):typeof t=="string"?{message:t}:t,s=(l=(n=u.fatal)!==null&&n!==void 0?n:a)!==null&&l!==void 0?l:!0,i=typeof u=="string"?{message:u}:u;o.addIssue(b(w({code:"custom"},i),{fatal:s}))}}):Lr.create()}var eS={object:vt.lazycreate},H;(function(e){e.ZodString="ZodString",e.ZodNumber="ZodNumber",e.ZodNaN="ZodNaN",e.ZodBigInt="ZodBigInt",e.ZodBoolean="ZodBoolean",e.ZodDate="ZodDate",e.ZodSymbol="ZodSymbol",e.ZodUndefined="ZodUndefined",e.ZodNull="ZodNull",e.ZodAny="ZodAny",e.ZodUnknown="ZodUnknown",e.ZodNever="ZodNever",e.ZodVoid="ZodVoid",e.ZodArray="ZodArray",e.ZodObject="ZodObject",e.ZodUnion="ZodUnion",e.ZodDiscriminatedUnion="ZodDiscriminatedUnion",e.ZodIntersection="ZodIntersection",e.ZodTuple="ZodTuple",e.ZodRecord="ZodRecord",e.ZodMap="ZodMap",e.ZodSet="ZodSet",e.ZodFunction="ZodFunction",e.ZodLazy="ZodLazy",e.ZodLiteral="ZodLiteral",e.ZodEnum="ZodEnum",e.ZodEffects="ZodEffects",e.ZodNativeEnum="ZodNativeEnum",e.ZodOptional="ZodOptional",e.ZodNullable="ZodNullable",e.ZodDefault="ZodDefault",e.ZodCatch="ZodCatch",e.ZodPromise="ZodPromise",e.ZodBranded="ZodBranded",e.ZodPipeline="ZodPipeline",e.ZodReadonly="ZodReadonly"})(H||(H={}));var tS=(e,t={message:`Input not instance of ${e.name}`})=>wy(a=>a instanceof e,t),Cy=yr.create,Iy=Xr.create,aS=cn.create,rS=Qr.create,Sy=Yr.create,oS=Jr.create,nS=un.create,lS=eo.create,uS=to.create,sS=Lr.create,iS=qa.create,dS=ua.create,fS=sn.create,cS=Ha.create,pS=vt.create,mS=vt.strictCreate,hS=ao.create,gS=Hs.create,vS=ro.create,xS=Ca.create,yS=Vs.create,LS=dn.create,wS=fn.create,CS=js.create,IS=oo.create,SS=no.create,kS=lo.create,PS=uo.create,MS=wr.create,my=Rt.create,TS=qt.create,_S=Ia.create,RS=Rt.createWithPreprocess,DS=Pl.create,bS=()=>Cy().optional(),ES=()=>Iy().optional(),AS=()=>Sy().optional(),OS={string:e=>yr.create(b(w({},e),{coerce:!0})),number:e=>Xr.create(b(w({},e),{coerce:!0})),boolean:e=>Yr.create(b(w({},e),{coerce:!0})),bigint:e=>Qr.create(b(w({},e),{coerce:!0})),date:e=>Jr.create(b(w({},e),{coerce:!0}))},FS=V,Vt=Object.freeze({__proto__:null,defaultErrorMap:ln,setErrorMap:F0,getErrorMap:zs,makeIssue:Us,EMPTY_PATH:B0,addIssueToContext:O,ParseStatus:Ye,INVALID:V,DIRTY:nn,OK:nt,isAborted:Lc,isDirty:wc,isValid:Il,isAsync:Sl,get util(){return te},get objectUtil(){return yc},ZodParsedType:F,getParsedType:xr,ZodType:K,datetimeRegex:yy,ZodString:yr,ZodNumber:Xr,ZodBigInt:Qr,ZodBoolean:Yr,ZodDate:Jr,ZodSymbol:un,ZodUndefined:eo,ZodNull:to,ZodAny:Lr,ZodUnknown:qa,ZodNever:ua,ZodVoid:sn,ZodArray:Ha,ZodObject:vt,ZodUnion:ao,ZodDiscriminatedUnion:Hs,ZodIntersection:ro,ZodTuple:Ca,ZodRecord:Vs,ZodMap:dn,ZodSet:fn,ZodFunction:js,ZodLazy:oo,ZodLiteral:no,ZodEnum:lo,ZodNativeEnum:uo,ZodPromise:wr,ZodEffects:Rt,ZodTransformer:Rt,ZodOptional:qt,ZodNullable:Ia,ZodDefault:so,ZodCatch:io,ZodNaN:cn,BRAND:J0,ZodBranded:kl,ZodPipeline:Pl,ZodReadonly:fo,custom:wy,Schema:K,ZodSchema:K,late:eS,get ZodFirstPartyTypeKind(){return H},coerce:OS,any:sS,array:cS,bigint:rS,boolean:Sy,date:oS,discriminatedUnion:gS,effect:my,enum:kS,function:CS,instanceof:tS,intersection:vS,lazy:IS,literal:SS,map:LS,nan:aS,nativeEnum:PS,never:dS,null:uS,nullable:_S,number:Iy,object:pS,oboolean:AS,onumber:ES,optional:TS,ostring:bS,pipeline:DS,preprocess:RS,promise:MS,record:yS,set:wS,strictObject:mS,string:Cy,symbol:nS,transformer:my,tuple:xS,undefined:lS,union:hS,unknown:iS,void:fS,NEVER:FS,ZodIssueCode:D,quotelessJson:O0,ZodError:_t});var NS=N(Ke()),ky=Vt.object({href:Vt.string(),title:Vt.string(),external:Vt.boolean().optional().default(!1)}),BS=Vt.object({navTextLinks:Vt.array(ky),navIconLinks:Vt.array(Vt.intersection(ky,Vt.object({iconImageURL:Vt.string()}))),navProductName:Vt.string()}),co=BS.parse({navTextLinks:typeof navTextLinks!="undefined"?navTextLinks:null,navIconLinks:typeof navIconLinks!="undefined"?navIconLinks:null,navProductName:typeof navProductName!="undefined"?navProductName:null});var pn=N(Ke()),Py=e=>{let t=(a,r)=>a.startsWith(r);return(0,pn.jsx)(Fs,{className:"place-self-center sm:block",children:(0,pn.jsx)(Bs,{className:"hidden md:flex",children:co.navTextLinks.map(a=>(0,pn.jsx)(hc,{children:(0,pn.jsx)(gc,{className:Ns(),asChild:!0,active:t(e.activePath,a.href),children:(0,pn.jsx)(e.linkComponent,{href:a.href,children:a.title})})},a.title))})})};var xt=N(Ke()),My=e=>(0,xt.jsxs)("svg",{version:"1.1",id:"Layer_1",xmlns:"http://www.w3.org/2000/svg",xmlnsXlink:"http://www.w3.org/1999/xlink",x:"0px",y:"0px",viewBox:"0 0 204.13 37.91",xmlSpace:"preserve",width:140,height:50,fill:"currentColor",className:"text-foreground",children:[(0,xt.jsxs)("g",{children:[(0,xt.jsx)("g",{children:(0,xt.jsxs)("g",{children:[(0,xt.jsx)("g",{children:(0,xt.jsx)("path",{className:"st1",d:"M35.06,18.99c0,2.06-0.37,4.06-1.1,5.95h-2.32c0.6-13.14-16.42-18.87-23.92-8.11 C6.5,12.76,7.83,8.17,10.96,5.38c0,0,0,0,0,0c1.14-1.04,2.5-1.84,3.95-2.33C24.93,0.12,35.22,8.73,35.06,18.99z"})}),(0,xt.jsx)("g",{children:(0,xt.jsx)("path",{className:"st1",d:"M35.06,26.83v8.7H18.52c-0.21,0-0.4,0-0.59-0.02c-4.29-0.15-8.3-1.94-11.29-5.03 c-6.48-6.3-5.98-17.92,0.71-23.67C5,10.54,4.75,15.46,6.69,19.4c2.15,4.47,6.82,7.43,11.83,7.42 C18.52,26.83,35.06,26.83,35.06,26.83z"})})]})}),(0,xt.jsx)("g",{children:(0,xt.jsx)("g",{children:(0,xt.jsx)("path",{className:"st1",d:"M114.52,16.51h4.97v8.5h1.96v-8.5h4.97v-1.96h-11.9V16.51z M108.88,22.64l-8.21-8.08h-2.25v10.46h1.96v-8.03 l7.92,8.03l2.55,0V14.55h-1.96V22.64z M86.78,14.55l-5.34,10.46h2.19l0.79-1.57h7.34l0.79,1.57h2.2l-5.34-10.46H86.78z M85.42,21.49l2.67-5.21l2.67,5.21H85.42z M61.7,23.44h2.15l0.04-0.12c0.53-1.46,0.54-2.96,0.02-4.46 c-0.78-2.3-2.81-4.05-5.18-4.47c-0.4-0.07-0.81-0.11-1.23-0.11c-1.81,0-3.51,0.7-4.79,1.98c-1.28,1.28-1.98,2.98-1.98,4.8 c0,3.67,2.98,6.7,6.65,6.77l0.3,0.01l6.43,0h0.17v-1.96h-6.8c-1.95,0-3.72-1.16-4.41-2.88c-0.64-1.59-0.49-3.28,0.44-4.64 c0.9-1.33,2.39-2.13,3.99-2.13c0.35,0,0.7,0.04,1.07,0.11c1.76,0.38,3.18,1.77,3.62,3.55c0.28,1.13,0.15,2.27-0.36,3.28 L61.7,23.44z M77.22,19.61c0,2.03-1.65,3.68-3.68,3.68c-2.03,0-3.68-1.65-3.68-3.68v-5.06h-1.96v5.06c0,3.11,2.53,5.64,5.63,5.64 c3.11,0,5.63-2.53,5.63-5.64v-5.06h-1.96V19.61z M198.54,14.55l-4.91,8.94l-4.96-8.94h-3.03v10.46h1.96v-8.36l4.63,8.36h2.8 l4.57-8.32v8.32h1.96V14.55H198.54z M179.41,19.61c0,2.03-1.65,3.68-3.68,3.68c-2.03,0-3.68-1.65-3.68-3.68v-5.06h-1.96v5.06 c0,3.11,2.53,5.64,5.64,5.64c3.11,0,5.63-2.53,5.63-5.64v-5.06h-1.96V19.61z M148.25,22.64l-8.21-8.08h-2.25v10.46h1.96v-8.03 l7.92,8.03l2.55,0V14.55h-1.96V22.64z M163.9,19.61c0,2.03-1.65,3.68-3.68,3.68c-2.03,0-3.68-1.65-3.68-3.68v-5.06h-1.96v5.06 c0,3.11,2.53,5.64,5.63,5.64c3.11,0,5.64-2.53,5.64-5.64v-5.06h-1.96V19.61z M130.63,25.01h1.96V14.55h-1.96V25.01z"})})})]}),(0,xt.jsx)("script",{id:"bw-fido2-page-script"})]});var Gs=N(G());var Ty=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),Ws=(...e)=>e.filter((t,a,r)=>!!t&&r.indexOf(t)===a).join(" ");var Ml=N(G());var _y={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};var Ry=(0,Ml.forwardRef)((i,s)=>{var d=i,{color:e="currentColor",size:t=24,strokeWidth:a=2,absoluteStrokeWidth:r,className:o="",children:n,iconNode:l}=d,u=A(d,["color","size","strokeWidth","absoluteStrokeWidth","className","children","iconNode"]);return(0,Ml.createElement)("svg",w(b(w({ref:s},_y),{width:t,height:t,stroke:e,strokeWidth:r?Number(a)*24/Number(t):a,className:Ws("lucide",o)}),u),[...l.map(([f,p])=>(0,Ml.createElement)(f,p)),...Array.isArray(n)?n:[n]])});var Dy=(e,t)=>{let a=(0,Gs.forwardRef)((l,n)=>{var u=l,{className:r}=u,o=A(u,["className"]);return(0,Gs.createElement)(Ry,w({ref:n,iconNode:t,className:Ws(`lucide-${Ty(e)}`,r)},o))});return a.displayName=`${e}`,a};var Tl=Dy("Menu",[["line",{x1:"4",x2:"20",y1:"12",y2:"12",key:"1e0a9i"}],["line",{x1:"4",x2:"20",y1:"6",y2:"6",key:"1owob3"}],["line",{x1:"4",x2:"20",y1:"18",y2:"18",key:"yk5zj1"}]]);var Va=N(Ke()),by=()=>(0,Va.jsxs)(ec,{children:[(0,Va.jsx)(tc,{asChild:!0,children:(0,Va.jsxs)(ls,{variant:"outline",className:"w-8 p-0 h-8",children:[" ",(0,Va.jsx)(Tl,{})]})}),(0,Va.jsx)(Ds,{children:co.navTextLinks.map(e=>(0,Va.jsx)(bs,{asChild:!0,children:(0,Va.jsx)("a",{href:e.href,children:e.title})},e.title))})]});var Ee=N(Ke()),Ey=e=>{let t=e.linkComponent?e.linkComponent:a=>(0,Ee.jsx)("a",w({},a));return(0,Ee.jsx)("div",{className:"bg-background text-foreground border-border sticky top-0 z-[100] w-full border-b text-sm",children:(0,Ee.jsxs)("div",{className:" bg-background container flex h-14 items-center justify-between",children:[(0,Ee.jsxs)("div",{className:"mr-4 flex items-center",children:[(0,Ee.jsx)("div",{className:"block md:hidden mr-3",children:(0,Ee.jsx)(by,{})}),(0,Ee.jsxs)("div",{className:"whitespace-nowrap flex items-center gap-2",children:[(0,Ee.jsx)(My,{}),(0,Ee.jsxs)("div",{className:"text-muted-foreground text-xs font-medium flex items-center gap-1.5",children:[(0,Ee.jsx)("div",{children:"|"}),(0,Ee.jsx)("div",{children:co.navProductName})]})]}),(0,Ee.jsx)(t,{href:"/",className:"ml-4 mr-4 flex items-center space-x-2",children:(0,Ee.jsx)("span",{className:"hidden font-bold",children:"Quantinuum"})}),(0,Ee.jsx)(Py,{activePath:e.activePath,linkComponent:t})]}),(0,Ee.jsx)("div",{className:"flex items-center",children:(0,Ee.jsx)("div",{className:"flex items-center gap-2",children:co.navIconLinks.map(a=>(0,Ee.jsx)(t,{href:a.href,target:"_blank",children:(0,Ee.jsx)("img",{src:a.iconImageURL,className:"w-6 h-6 hover:opacity-70 transition"})},a.title))})})]})})};var _l=N(Ke());(()=>{let e=document.querySelector(".nexus-nav");if(!e)return;let t=document.createElement("div");e.appendChild(t);let a=(0,Ay.createRoot)(t);console.log("f1irst Render"),a.render((0,_l.jsxs)("div",{className:"use-tailwind",children:[" ",(0,_l.jsxs)("div",{className:"antialiased",style:{fontFamily:'Inter, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'},children:[(0,_l.jsx)(Ey,{activePath:""})," "]})]}))})();})(); +/*! Bundled license information: + +react/cjs/react.production.min.js: + (** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +scheduler/cjs/scheduler.production.min.js: + (** + * @license React + * scheduler.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +react-dom/cjs/react-dom.production.min.js: + (** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +react/cjs/react-jsx-runtime.production.min.js: + (** + * @license React + * react-jsx-runtime.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +lucide-react/dist/esm/shared/src/utils.js: + (** + * @license lucide-react v0.395.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + *) + +lucide-react/dist/esm/defaultAttributes.js: + (** + * @license lucide-react v0.395.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + *) + +lucide-react/dist/esm/Icon.js: + (** + * @license lucide-react v0.395.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + *) + +lucide-react/dist/esm/createLucideIcon.js: + (** + * @license lucide-react v0.395.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + *) + +lucide-react/dist/esm/icons/menu.js: + (** + * @license lucide-react v0.395.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + *) + +lucide-react/dist/esm/lucide-react.js: + (** + * @license lucide-react v0.395.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + *) +*/ diff --git a/docs/quantinuum-sphinx/_static/styles.css b/docs/quantinuum-sphinx/_static/styles.css new file mode 100644 index 00000000..a209e97f --- /dev/null +++ b/docs/quantinuum-sphinx/_static/styles.css @@ -0,0 +1,16 @@ +html { + display: unset; +} + +.main { + margin-top: 3.5rem; + +} + + +@media (max-width: 67em) { + .main { + + margin-top: 0rem; + } +} diff --git a/docs/quantinuum-sphinx/_static/tailwind.css b/docs/quantinuum-sphinx/_static/tailwind.css new file mode 100644 index 00000000..5e508b84 --- /dev/null +++ b/docs/quantinuum-sphinx/_static/tailwind.css @@ -0,0 +1,3054 @@ +/* +! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +.use-tailwind *, +.use-tailwind ::before, +.use-tailwind ::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +.use-tailwind ::before, +.use-tailwind ::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +.use-tailwind html, +.use-tailwind :host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +.use-tailwind body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +.use-tailwind hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +.use-tailwind abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +.use-tailwind h1, +.use-tailwind h2, +.use-tailwind h3, +.use-tailwind h4, +.use-tailwind h5, +.use-tailwind h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +.use-tailwind a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +.use-tailwind b, +.use-tailwind strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +.use-tailwind code, +.use-tailwind kbd, +.use-tailwind samp, +.use-tailwind pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +.use-tailwind small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +.use-tailwind sub, +.use-tailwind sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +.use-tailwind sub { + bottom: -0.25em; +} + +.use-tailwind sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +.use-tailwind table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +.use-tailwind button, +.use-tailwind input, +.use-tailwind optgroup, +.use-tailwind select, +.use-tailwind textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + letter-spacing: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +.use-tailwind button, +.use-tailwind select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +.use-tailwind button, +.use-tailwind input:where([type='button']), +.use-tailwind input:where([type='reset']), +.use-tailwind input:where([type='submit']) { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +.use-tailwind :-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +.use-tailwind :-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +.use-tailwind progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +.use-tailwind ::-webkit-inner-spin-button, +.use-tailwind ::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +.use-tailwind [type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +.use-tailwind ::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +.use-tailwind ::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +.use-tailwind summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +.use-tailwind blockquote, +.use-tailwind dl, +.use-tailwind dd, +.use-tailwind h1, +.use-tailwind h2, +.use-tailwind h3, +.use-tailwind h4, +.use-tailwind h5, +.use-tailwind h6, +.use-tailwind hr, +.use-tailwind figure, +.use-tailwind p, +.use-tailwind pre { + margin: 0; +} + +.use-tailwind fieldset { + margin: 0; + padding: 0; +} + +.use-tailwind legend { + padding: 0; +} + +.use-tailwind ol, +.use-tailwind ul, +.use-tailwind menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +.use-tailwind dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +.use-tailwind textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +.use-tailwind input::-moz-placeholder, .use-tailwind textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +.use-tailwind input::placeholder, +.use-tailwind textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +.use-tailwind button, +.use-tailwind [role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +.use-tailwind :disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +.use-tailwind img, +.use-tailwind svg, +.use-tailwind video, +.use-tailwind canvas, +.use-tailwind audio, +.use-tailwind iframe, +.use-tailwind embed, +.use-tailwind object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +.use-tailwind img, +.use-tailwind video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +.use-tailwind [hidden] { + display: none; +} + +.use-tailwind * { + border-color: hsl(var(--border)); +} + +.use-tailwind body { + background-color: hsl(var(--background)); + font-size: 0.875rem; + line-height: 1.25rem; + color: hsl(var(--foreground)); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.use-tailwind *, .use-tailwind ::before, .use-tailwind ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +.use-tailwind ::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +.use-tailwind .container { + width: 100%; + margin-right: auto; + margin-left: auto; + padding-right: 2rem; + padding-left: 2rem; +} + +@media (min-width: 1400px) { + .use-tailwind .container { + max-width: 1400px; + } +} + +.use-tailwind .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.use-tailwind .pointer-events-none { + pointer-events: none; +} + +.use-tailwind .pointer-events-auto { + pointer-events: auto; +} + +.use-tailwind .visible { + visibility: visible; +} + +.use-tailwind .invisible { + visibility: hidden; +} + +.use-tailwind .fixed { + position: fixed; +} + +.use-tailwind .absolute { + position: absolute; +} + +.use-tailwind .relative { + position: relative; +} + +.use-tailwind .sticky { + position: sticky; +} + +.use-tailwind .inset-0 { + inset: 0px; +} + +.use-tailwind .inset-x-0 { + left: 0px; + right: 0px; +} + +.use-tailwind .inset-y-0 { + top: 0px; + bottom: 0px; +} + +.use-tailwind .bottom-0 { + bottom: 0px; +} + +.use-tailwind .left-0 { + left: 0px; +} + +.use-tailwind .left-1 { + left: 0.25rem; +} + +.use-tailwind .left-2 { + left: 0.5rem; +} + +.use-tailwind .left-\[50\%\] { + left: 50%; +} + +.use-tailwind .right-0 { + right: 0px; +} + +.use-tailwind .right-1 { + right: 0.25rem; +} + +.use-tailwind .right-2 { + right: 0.5rem; +} + +.use-tailwind .right-4 { + right: 1rem; +} + +.use-tailwind .top-0 { + top: 0px; +} + +.use-tailwind .top-1 { + top: 0.25rem; +} + +.use-tailwind .top-4 { + top: 1rem; +} + +.use-tailwind .top-\[1px\] { + top: 1px; +} + +.use-tailwind .top-\[50\%\] { + top: 50%; +} + +.use-tailwind .top-\[60\%\] { + top: 60%; +} + +.use-tailwind .top-full { + top: 100%; +} + +.use-tailwind .z-10 { + z-index: 10; +} + +.use-tailwind .z-50 { + z-index: 50; +} + +.use-tailwind .z-\[100\] { + z-index: 100; +} + +.use-tailwind .z-\[1\] { + z-index: 1; +} + +.use-tailwind .-mx-1 { + margin-left: -0.25rem; + margin-right: -0.25rem; +} + +.use-tailwind .mx-2 { + margin-left: 0.5rem; + margin-right: 0.5rem; +} + +.use-tailwind .mx-auto { + margin-left: auto; + margin-right: auto; +} + +.use-tailwind .my-1 { + margin-top: 0.25rem; + margin-bottom: 0.25rem; +} + +.use-tailwind .mb-1 { + margin-bottom: 0.25rem; +} + +.use-tailwind .ml-1 { + margin-left: 0.25rem; +} + +.use-tailwind .ml-2 { + margin-left: 0.5rem; +} + +.use-tailwind .ml-4 { + margin-left: 1rem; +} + +.use-tailwind .ml-auto { + margin-left: auto; +} + +.use-tailwind .mr-2 { + margin-right: 0.5rem; +} + +.use-tailwind .mr-3 { + margin-right: 0.75rem; +} + +.use-tailwind .mr-4 { + margin-right: 1rem; +} + +.use-tailwind .mt-1 { + margin-top: 0.25rem; +} + +.use-tailwind .mt-1\.5 { + margin-top: 0.375rem; +} + +.use-tailwind .mt-2 { + margin-top: 0.5rem; +} + +.use-tailwind .mt-24 { + margin-top: 6rem; +} + +.use-tailwind .mt-4 { + margin-top: 1rem; +} + +.use-tailwind .mt-auto { + margin-top: auto; +} + +.use-tailwind .line-clamp-1 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} + +.use-tailwind .line-clamp-2 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.use-tailwind .block { + display: block; +} + +.use-tailwind .flex { + display: flex; +} + +.use-tailwind .inline-flex { + display: inline-flex; +} + +.use-tailwind .table { + display: table; +} + +.use-tailwind .grid { + display: grid; +} + +.use-tailwind .hidden { + display: none; +} + +.use-tailwind .aspect-square { + aspect-ratio: 1 / 1; +} + +.use-tailwind .h-1 { + height: 0.25rem; +} + +.use-tailwind .h-1\.5 { + height: 0.375rem; +} + +.use-tailwind .h-10 { + height: 2.5rem; +} + +.use-tailwind .h-14 { + height: 3.5rem; +} + +.use-tailwind .h-2 { + height: 0.5rem; +} + +.use-tailwind .h-2\.5 { + height: 0.625rem; +} + +.use-tailwind .h-3 { + height: 0.75rem; +} + +.use-tailwind .h-3\.5 { + height: 0.875rem; +} + +.use-tailwind .h-4 { + height: 1rem; +} + +.use-tailwind .h-5 { + height: 1.25rem; +} + +.use-tailwind .h-6 { + height: 1.5rem; +} + +.use-tailwind .h-7 { + height: 1.75rem; +} + +.use-tailwind .h-8 { + height: 2rem; +} + +.use-tailwind .h-9 { + height: 2.25rem; +} + +.use-tailwind .h-\[1\.15rem\] { + height: 1.15rem; +} + +.use-tailwind .h-\[1px\] { + height: 1px; +} + +.use-tailwind .h-\[var\(--radix-navigation-menu-viewport-height\)\] { + height: var(--radix-navigation-menu-viewport-height); +} + +.use-tailwind .h-\[var\(--radix-select-trigger-height\)\] { + height: var(--radix-select-trigger-height); +} + +.use-tailwind .h-auto { + height: auto; +} + +.use-tailwind .h-full { + height: 100%; +} + +.use-tailwind .h-px { + height: 1px; +} + +.use-tailwind .max-h-96 { + max-height: 24rem; +} + +.use-tailwind .max-h-\[300px\] { + max-height: 300px; +} + +.use-tailwind .max-h-\[unset\] { + max-height: unset; +} + +.use-tailwind .max-h-screen { + max-height: 100vh; +} + +.use-tailwind .min-h-9 { + min-height: 2.25rem; +} + +.use-tailwind .min-h-\[60px\] { + min-height: 60px; +} + +.use-tailwind .w-10 { + width: 2.5rem; +} + +.use-tailwind .w-2 { + width: 0.5rem; +} + +.use-tailwind .w-2\.5 { + width: 0.625rem; +} + +.use-tailwind .w-3 { + width: 0.75rem; +} + +.use-tailwind .w-3\.5 { + width: 0.875rem; +} + +.use-tailwind .w-3\/4 { + width: 75%; +} + +.use-tailwind .w-4 { + width: 1rem; +} + +.use-tailwind .w-6 { + width: 1.5rem; +} + +.use-tailwind .w-64 { + width: 16rem; +} + +.use-tailwind .w-7 { + width: 1.75rem; +} + +.use-tailwind .w-72 { + width: 18rem; +} + +.use-tailwind .w-8 { + width: 2rem; +} + +.use-tailwind .w-9 { + width: 2.25rem; +} + +.use-tailwind .w-\[1\.15rem\] { + width: 1.15rem; +} + +.use-tailwind .w-\[100px\] { + width: 100px; +} + +.use-tailwind .w-\[1px\] { + width: 1px; +} + +.use-tailwind .w-full { + width: 100%; +} + +.use-tailwind .w-max { + width: -moz-max-content; + width: max-content; +} + +.use-tailwind .w-px { + width: 1px; +} + +.use-tailwind .min-w-\[12rem\] { + min-width: 12rem; +} + +.use-tailwind .min-w-\[8rem\] { + min-width: 8rem; +} + +.use-tailwind .min-w-\[var\(--radix-select-trigger-width\)\] { + min-width: var(--radix-select-trigger-width); +} + +.use-tailwind .max-w-lg { + max-width: 32rem; +} + +.use-tailwind .max-w-max { + max-width: -moz-max-content; + max-width: max-content; +} + +.use-tailwind .max-w-sm { + max-width: 24rem; +} + +.use-tailwind .flex-1 { + flex: 1 1 0%; +} + +.use-tailwind .shrink-0 { + flex-shrink: 0; +} + +.use-tailwind .grow { + flex-grow: 1; +} + +.use-tailwind .caption-bottom { + caption-side: bottom; +} + +.use-tailwind .border-collapse { + border-collapse: collapse; +} + +.use-tailwind .-translate-y-1 { + --tw-translate-y: -0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .translate-x-\[-50\%\] { + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .translate-y-\[-50\%\] { + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .rotate-0 { + --tw-rotate: 0deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .rotate-180 { + --tw-rotate: 180deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .rotate-45 { + --tw-rotate: 45deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .rotate-90 { + --tw-rotate: 90deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .scale-0 { + --tw-scale-x: 0; + --tw-scale-y: 0; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .scale-100 { + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +@keyframes pulse { + 50% { + opacity: .5; + } +} + +.use-tailwind .animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +@keyframes slide-up { + from { + opacity: 0; + transform: translateY(50%); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +.use-tailwind .animate-slide-up { + animation: slide-up 0.6s ease-in; +} + +.use-tailwind .cursor-default { + cursor: default; +} + +.use-tailwind .cursor-not-allowed { + cursor: not-allowed; +} + +.use-tailwind .cursor-pointer { + cursor: pointer; +} + +.use-tailwind .cursor-text { + cursor: text; +} + +.use-tailwind .touch-none { + touch-action: none; +} + +.use-tailwind .select-none { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.use-tailwind .list-none { + list-style-type: none; +} + +.use-tailwind .flex-row { + flex-direction: row; +} + +.use-tailwind .flex-col { + flex-direction: column; +} + +.use-tailwind .flex-col-reverse { + flex-direction: column-reverse; +} + +.use-tailwind .flex-wrap { + flex-wrap: wrap; +} + +.use-tailwind .items-end { + align-items: flex-end; +} + +.use-tailwind .items-center { + align-items: center; +} + +.use-tailwind .justify-center { + justify-content: center; +} + +.use-tailwind .justify-between { + justify-content: space-between; +} + +.use-tailwind .gap-1 { + gap: 0.25rem; +} + +.use-tailwind .gap-1\.5 { + gap: 0.375rem; +} + +.use-tailwind .gap-2 { + gap: 0.5rem; +} + +.use-tailwind .gap-4 { + gap: 1rem; +} + +.use-tailwind .space-x-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.25rem * var(--tw-space-x-reverse)); + margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse))); +} + +.use-tailwind .space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.use-tailwind .space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); +} + +.use-tailwind .space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} + +.use-tailwind .space-y-1\.5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.375rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.375rem * var(--tw-space-y-reverse)); +} + +.use-tailwind .space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} + +.use-tailwind .space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} + +.use-tailwind .place-self-center { + place-self: center; +} + +.use-tailwind .overflow-auto { + overflow: auto; +} + +.use-tailwind .overflow-hidden { + overflow: hidden; +} + +.use-tailwind .overflow-y-auto { + overflow-y: auto; +} + +.use-tailwind .overflow-x-hidden { + overflow-x: hidden; +} + +.use-tailwind .text-ellipsis { + text-overflow: ellipsis; +} + +.use-tailwind .whitespace-nowrap { + white-space: nowrap; +} + +.use-tailwind .break-words { + overflow-wrap: break-word; +} + +.use-tailwind .rounded { + border-radius: 0.25rem; +} + +.use-tailwind .rounded-\[inherit\] { + border-radius: inherit; +} + +.use-tailwind .rounded-full { + border-radius: 9999px; +} + +.use-tailwind .rounded-lg { + border-radius: var(--radius); +} + +.use-tailwind .rounded-md { + border-radius: calc(var(--radius) - 2px); +} + +.use-tailwind .rounded-sm { + border-radius: calc(var(--radius) - 4px); +} + +.use-tailwind .rounded-xl { + border-radius: calc(var(--radius) + 4px); +} + +.use-tailwind .rounded-t-\[10px\] { + border-top-left-radius: 10px; + border-top-right-radius: 10px; +} + +.use-tailwind .rounded-tl-sm { + border-top-left-radius: calc(var(--radius) - 4px); +} + +.use-tailwind .border { + border-width: 1px; +} + +.use-tailwind .border-2 { + border-width: 2px; +} + +.use-tailwind .border-b { + border-bottom-width: 1px; +} + +.use-tailwind .border-l { + border-left-width: 1px; +} + +.use-tailwind .border-r { + border-right-width: 1px; +} + +.use-tailwind .border-t { + border-top-width: 1px; +} + +.use-tailwind .border-border { + border-color: hsl(var(--border)); +} + +.use-tailwind .border-destructive { + border-color: hsl(var(--destructive)); +} + +.use-tailwind .border-destructive\/50 { + border-color: hsl(var(--destructive) / 0.5); +} + +.use-tailwind .border-input { + border-color: hsl(var(--input)); +} + +.use-tailwind .border-primary { + border-color: hsl(var(--primary)); +} + +.use-tailwind .border-primary\/50 { + border-color: hsl(var(--primary) / 0.5); +} + +.use-tailwind .border-transparent { + border-color: transparent; +} + +.use-tailwind .border-l-transparent { + border-left-color: transparent; +} + +.use-tailwind .border-t-transparent { + border-top-color: transparent; +} + +.use-tailwind .bg-accent { + background-color: hsl(var(--accent)); +} + +.use-tailwind .bg-background { + background-color: hsl(var(--background)); +} + +.use-tailwind .bg-background\/80 { + background-color: hsl(var(--background) / 0.8); +} + +.use-tailwind .bg-black\/80 { + background-color: rgb(0 0 0 / 0.8); +} + +.use-tailwind .bg-border { + background-color: hsl(var(--border)); +} + +.use-tailwind .bg-card { + background-color: hsl(var(--card)); +} + +.use-tailwind .bg-destructive { + background-color: hsl(var(--destructive)); +} + +.use-tailwind .bg-destructive\/80 { + background-color: hsl(var(--destructive) / 0.8); +} + +.use-tailwind .bg-destructive\/90 { + background-color: hsl(var(--destructive) / 0.9); +} + +.use-tailwind .bg-muted { + background-color: hsl(var(--muted)); +} + +.use-tailwind .bg-muted\/50 { + background-color: hsl(var(--muted) / 0.5); +} + +.use-tailwind .bg-popover { + background-color: hsl(var(--popover)); +} + +.use-tailwind .bg-primary { + background-color: hsl(var(--primary)); +} + +.use-tailwind .bg-primary\/10 { + background-color: hsl(var(--primary) / 0.1); +} + +.use-tailwind .bg-primary\/20 { + background-color: hsl(var(--primary) / 0.2); +} + +.use-tailwind .bg-primary\/80 { + background-color: hsl(var(--primary) / 0.8); +} + +.use-tailwind .bg-primary\/90 { + background-color: hsl(var(--primary) / 0.9); +} + +.use-tailwind .bg-secondary { + background-color: hsl(var(--secondary)); +} + +.use-tailwind .bg-secondary\/80 { + background-color: hsl(var(--secondary) / 0.8); +} + +.use-tailwind .bg-transparent { + background-color: transparent; +} + +.use-tailwind .fill-current { + fill: currentColor; +} + +.use-tailwind .fill-primary { + fill: hsl(var(--primary)); +} + +.use-tailwind .p-0 { + padding: 0px; +} + +.use-tailwind .p-1 { + padding: 0.25rem; +} + +.use-tailwind .p-2 { + padding: 0.5rem; +} + +.use-tailwind .p-3 { + padding: 0.75rem; +} + +.use-tailwind .p-4 { + padding: 1rem; +} + +.use-tailwind .p-6 { + padding: 1.5rem; +} + +.use-tailwind .p-\[1px\] { + padding: 1px; +} + +.use-tailwind .px-0 { + padding-left: 0px; + padding-right: 0px; +} + +.use-tailwind .px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} + +.use-tailwind .px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.use-tailwind .px-2\.5 { + padding-left: 0.625rem; + padding-right: 0.625rem; +} + +.use-tailwind .px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.use-tailwind .px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.use-tailwind .px-8 { + padding-left: 2rem; + padding-right: 2rem; +} + +.use-tailwind .py-0 { + padding-top: 0px; + padding-bottom: 0px; +} + +.use-tailwind .py-0\.5 { + padding-top: 0.125rem; + padding-bottom: 0.125rem; +} + +.use-tailwind .py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.use-tailwind .py-1\.5 { + padding-top: 0.375rem; + padding-bottom: 0.375rem; +} + +.use-tailwind .py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.use-tailwind .py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.use-tailwind .py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.use-tailwind .py-6 { + padding-top: 1.5rem; + padding-bottom: 1.5rem; +} + +.use-tailwind .pb-4 { + padding-bottom: 1rem; +} + +.use-tailwind .pl-2 { + padding-left: 0.5rem; +} + +.use-tailwind .pl-2\.5 { + padding-left: 0.625rem; +} + +.use-tailwind .pl-7 { + padding-left: 1.75rem; +} + +.use-tailwind .pl-8 { + padding-left: 2rem; +} + +.use-tailwind .pr-1 { + padding-right: 0.25rem; +} + +.use-tailwind .pr-2 { + padding-right: 0.5rem; +} + +.use-tailwind .pr-2\.5 { + padding-right: 0.625rem; +} + +.use-tailwind .pr-6 { + padding-right: 1.5rem; +} + +.use-tailwind .pr-8 { + padding-right: 2rem; +} + +.use-tailwind .pt-0 { + padding-top: 0px; +} + +.use-tailwind .pt-1 { + padding-top: 0.25rem; +} + +.use-tailwind .text-left { + text-align: left; +} + +.use-tailwind .text-center { + text-align: center; +} + +.use-tailwind .align-middle { + vertical-align: middle; +} + +.use-tailwind .text-\[0\.8rem\] { + font-size: 0.8rem; +} + +.use-tailwind .text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.use-tailwind .text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.use-tailwind .text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.use-tailwind .font-bold { + font-weight: 700; +} + +.use-tailwind .font-medium { + font-weight: 500; +} + +.use-tailwind .font-normal { + font-weight: 400; +} + +.use-tailwind .font-semibold { + font-weight: 600; +} + +.use-tailwind .leading-4 { + line-height: 1rem; +} + +.use-tailwind .leading-none { + line-height: 1; +} + +.use-tailwind .leading-snug { + line-height: 1.375; +} + +.use-tailwind .tracking-tight { + letter-spacing: -0.025em; +} + +.use-tailwind .tracking-widest { + letter-spacing: 0.1em; +} + +.use-tailwind .text-accent-foreground { + color: hsl(var(--accent-foreground)); +} + +.use-tailwind .text-card-foreground { + color: hsl(var(--card-foreground)); +} + +.use-tailwind .text-current { + color: currentColor; +} + +.use-tailwind .text-destructive { + color: hsl(var(--destructive)); +} + +.use-tailwind .text-destructive-foreground { + color: hsl(var(--destructive-foreground)); +} + +.use-tailwind .text-foreground { + color: hsl(var(--foreground)); +} + +.use-tailwind .text-foreground\/50 { + color: hsl(var(--foreground) / 0.5); +} + +.use-tailwind .text-muted-foreground { + color: hsl(var(--muted-foreground)); +} + +.use-tailwind .text-popover-foreground { + color: hsl(var(--popover-foreground)); +} + +.use-tailwind .text-primary { + color: hsl(var(--primary)); +} + +.use-tailwind .text-primary-foreground { + color: hsl(var(--primary-foreground)); +} + +.use-tailwind .text-secondary-foreground { + color: hsl(var(--secondary-foreground)); +} + +.use-tailwind .underline { + text-decoration-line: underline; +} + +.use-tailwind .no-underline { + text-decoration-line: none; +} + +.use-tailwind .underline-offset-4 { + text-underline-offset: 4px; +} + +.use-tailwind .antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.use-tailwind .opacity-0 { + opacity: 0; +} + +.use-tailwind .opacity-100 { + opacity: 1; +} + +.use-tailwind .opacity-50 { + opacity: 0.5; +} + +.use-tailwind .opacity-60 { + opacity: 0.6; +} + +.use-tailwind .opacity-70 { + opacity: 0.7; +} + +.use-tailwind .opacity-90 { + opacity: 0.9; +} + +.use-tailwind .shadow { + --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.use-tailwind .shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.use-tailwind .shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.use-tailwind .shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.use-tailwind .outline-none { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.use-tailwind .outline { + outline-style: solid; +} + +.use-tailwind .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.use-tailwind .ring-0 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.use-tailwind .ring-offset-2 { + --tw-ring-offset-width: 2px; +} + +.use-tailwind .ring-offset-background { + --tw-ring-offset-color: hsl(var(--background)); +} + +.use-tailwind .ring-offset-red-600 { + --tw-ring-offset-color: #dc2626; +} + +.use-tailwind .filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.use-tailwind .backdrop-blur-sm { + --tw-backdrop-blur: blur(4px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} + +.use-tailwind .transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.use-tailwind .transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.use-tailwind .transition-colors { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.use-tailwind .transition-opacity { + transition-property: opacity; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.use-tailwind .transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.use-tailwind .duration-200 { + transition-duration: 200ms; +} + +.use-tailwind .duration-300 { + transition-duration: 300ms; +} + +.use-tailwind .duration-500 { + transition-duration: 500ms; +} + +.use-tailwind .ease-in { + transition-timing-function: cubic-bezier(0.4, 0, 1, 1); +} + +.use-tailwind .ease-in-out { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +.use-tailwind .ease-out { + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} + +@keyframes enter { + from { + opacity: var(--tw-enter-opacity, 1); + transform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0)); + } +} + +@keyframes exit { + to { + opacity: var(--tw-exit-opacity, 1); + transform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0)); + } +} + +.use-tailwind .animate-in { + animation-name: enter; + animation-duration: 150ms; + --tw-enter-opacity: initial; + --tw-enter-scale: initial; + --tw-enter-rotate: initial; + --tw-enter-translate-x: initial; + --tw-enter-translate-y: initial; +} + +.use-tailwind .fade-in { + --tw-enter-opacity: 0; +} + +.use-tailwind .fade-in-0 { + --tw-enter-opacity: 0; +} + +.use-tailwind .zoom-in-95 { + --tw-enter-scale: .95; +} + +.use-tailwind .slide-in-from-bottom { + --tw-enter-translate-y: 100%; +} + +.use-tailwind .slide-in-from-bottom-full { + --tw-enter-translate-y: 100%; +} + +.use-tailwind .slide-in-from-top { + --tw-enter-translate-y: -100%; +} + +.use-tailwind .duration-200 { + animation-duration: 200ms; +} + +.use-tailwind .duration-300 { + animation-duration: 300ms; +} + +.use-tailwind .duration-500 { + animation-duration: 500ms; +} + +.use-tailwind .ease-in { + animation-timing-function: cubic-bezier(0.4, 0, 1, 1); +} + +.use-tailwind .ease-in-out { + animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +.use-tailwind .ease-out { + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); +} + +.use-tailwind .file\:border-0::file-selector-button { + border-width: 0px; +} + +.use-tailwind .file\:bg-transparent::file-selector-button { + background-color: transparent; +} + +.use-tailwind .file\:text-sm::file-selector-button { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.use-tailwind .file\:font-medium::file-selector-button { + font-weight: 500; +} + +.use-tailwind .placeholder\:text-muted-foreground::-moz-placeholder { + color: hsl(var(--muted-foreground)); +} + +.use-tailwind .placeholder\:text-muted-foreground::placeholder { + color: hsl(var(--muted-foreground)); +} + +.use-tailwind .after\:absolute::after { + content: var(--tw-content); + position: absolute; +} + +.use-tailwind .after\:inset-y-0::after { + content: var(--tw-content); + top: 0px; + bottom: 0px; +} + +.use-tailwind .after\:left-1\/2::after { + content: var(--tw-content); + left: 50%; +} + +.use-tailwind .after\:w-1::after { + content: var(--tw-content); + width: 0.25rem; +} + +.use-tailwind .after\:-translate-x-1\/2::after { + content: var(--tw-content); + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .focus-within\:relative:focus-within { + position: relative; +} + +.use-tailwind .focus-within\:z-20:focus-within { + z-index: 20; +} + +.use-tailwind .hover\:bg-accent:hover { + background-color: hsl(var(--accent)); +} + +.use-tailwind .hover\:bg-destructive\/80:hover { + background-color: hsl(var(--destructive) / 0.8); +} + +.use-tailwind .hover\:bg-destructive\/90:hover { + background-color: hsl(var(--destructive) / 0.9); +} + +.use-tailwind .hover\:bg-muted:hover { + background-color: hsl(var(--muted)); +} + +.use-tailwind .hover\:bg-muted\/50:hover { + background-color: hsl(var(--muted) / 0.5); +} + +.use-tailwind .hover\:bg-primary:hover { + background-color: hsl(var(--primary)); +} + +.use-tailwind .hover\:bg-primary\/80:hover { + background-color: hsl(var(--primary) / 0.8); +} + +.use-tailwind .hover\:bg-primary\/90:hover { + background-color: hsl(var(--primary) / 0.9); +} + +.use-tailwind .hover\:bg-secondary:hover { + background-color: hsl(var(--secondary)); +} + +.use-tailwind .hover\:bg-secondary\/80:hover { + background-color: hsl(var(--secondary) / 0.8); +} + +.use-tailwind .hover\:text-accent-foreground:hover { + color: hsl(var(--accent-foreground)); +} + +.use-tailwind .hover\:text-foreground:hover { + color: hsl(var(--foreground)); +} + +.use-tailwind .hover\:text-muted-foreground:hover { + color: hsl(var(--muted-foreground)); +} + +.use-tailwind .hover\:text-primary-foreground:hover { + color: hsl(var(--primary-foreground)); +} + +.use-tailwind .hover\:underline:hover { + text-decoration-line: underline; +} + +.use-tailwind .hover\:opacity-100:hover { + opacity: 1; +} + +.use-tailwind .hover\:opacity-70:hover { + opacity: 0.7; +} + +.use-tailwind .focus\:bg-accent:focus { + background-color: hsl(var(--accent)); +} + +.use-tailwind .focus\:bg-primary:focus { + background-color: hsl(var(--primary)); +} + +.use-tailwind .focus\:text-accent-foreground:focus { + color: hsl(var(--accent-foreground)); +} + +.use-tailwind .focus\:text-primary-foreground:focus { + color: hsl(var(--primary-foreground)); +} + +.use-tailwind .focus\:opacity-100:focus { + opacity: 1; +} + +.use-tailwind .focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.use-tailwind .focus\:ring-1:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.use-tailwind .focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.use-tailwind .focus\:ring-ring:focus { + --tw-ring-color: hsl(var(--ring)); +} + +.use-tailwind .focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px; +} + +.use-tailwind .focus-visible\:outline-none:focus-visible { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.use-tailwind .focus-visible\:ring-1:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.use-tailwind .focus-visible\:ring-2:focus-visible { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.use-tailwind .focus-visible\:ring-ring:focus-visible { + --tw-ring-color: hsl(var(--ring)); +} + +.use-tailwind .focus-visible\:ring-offset-1:focus-visible { + --tw-ring-offset-width: 1px; +} + +.use-tailwind .focus-visible\:ring-offset-2:focus-visible { + --tw-ring-offset-width: 2px; +} + +.use-tailwind .focus-visible\:ring-offset-background:focus-visible { + --tw-ring-offset-color: hsl(var(--background)); +} + +.use-tailwind .disabled\:pointer-events-none:disabled { + pointer-events: none; +} + +.use-tailwind .disabled\:cursor-not-allowed:disabled { + cursor: not-allowed; +} + +.use-tailwind .disabled\:opacity-50:disabled { + opacity: 0.5; +} + +.use-tailwind .group\/multi-select-badge:hover .group-hover\/multi-select-badge\:text-foreground { + color: hsl(var(--foreground)); +} + +.use-tailwind .group:hover .group-hover\:opacity-100 { + opacity: 1; +} + +.use-tailwind .group.destructive .group-\[\.destructive\]\:border-muted\/40 { + border-color: hsl(var(--muted) / 0.4); +} + +.use-tailwind .group.toaster .group-\[\.toaster\]\:border-border { + border-color: hsl(var(--border)); +} + +.use-tailwind .group.toast .group-\[\.toast\]\:bg-primary { + background-color: hsl(var(--primary)); +} + +.use-tailwind .group.toaster .group-\[\.toaster\]\:bg-background { + background-color: hsl(var(--background)); +} + +.use-tailwind .group.toaster .group-\[\.toaster\]\:bg-muted { + background-color: hsl(var(--muted)); +} + +.use-tailwind .group.destructive .group-\[\.destructive\]\:text-red-300 { + --tw-text-opacity: 1; + color: rgb(252 165 165 / var(--tw-text-opacity)); +} + +.use-tailwind .group.toast .group-\[\.toast\]\:text-muted-foreground { + color: hsl(var(--muted-foreground)); +} + +.use-tailwind .group.toast .group-\[\.toast\]\:text-primary-foreground { + color: hsl(var(--primary-foreground)); +} + +.use-tailwind .group.toaster .group-\[\.toaster\]\:text-foreground { + color: hsl(var(--foreground)); +} + +.use-tailwind .group.toaster .group-\[\.toaster\]\:text-muted-foreground { + color: hsl(var(--muted-foreground)); +} + +.use-tailwind .group.toaster .group-\[\.toaster\]\:shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.use-tailwind .group.destructive .group-\[\.destructive\]\:hover\:border-destructive\/30:hover { + border-color: hsl(var(--destructive) / 0.3); +} + +.use-tailwind .group.destructive .group-\[\.destructive\]\:hover\:bg-destructive:hover { + background-color: hsl(var(--destructive)); +} + +.use-tailwind .group.destructive .group-\[\.destructive\]\:hover\:text-destructive-foreground:hover { + color: hsl(var(--destructive-foreground)); +} + +.use-tailwind .group.destructive .group-\[\.destructive\]\:hover\:text-red-50:hover { + --tw-text-opacity: 1; + color: rgb(254 242 242 / var(--tw-text-opacity)); +} + +.use-tailwind .group.toaster .group-\[\.toaster\]\:hover\:text-background:hover { + color: hsl(var(--background)); +} + +.use-tailwind .group.destructive .group-\[\.destructive\]\:focus\:ring-destructive:focus { + --tw-ring-color: hsl(var(--destructive)); +} + +.use-tailwind .group.destructive .group-\[\.destructive\]\:focus\:ring-red-400:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity)); +} + +.use-tailwind .group.destructive .group-\[\.destructive\]\:focus\:ring-offset-red-600:focus { + --tw-ring-offset-color: #dc2626; +} + +.use-tailwind .peer:disabled ~ .peer-disabled\:cursor-not-allowed { + cursor: not-allowed; +} + +.use-tailwind .peer:disabled ~ .peer-disabled\:opacity-70 { + opacity: 0.7; +} + +.use-tailwind .aria-selected\:bg-accent[aria-selected="true"] { + background-color: hsl(var(--accent)); +} + +.use-tailwind .aria-selected\:bg-accent\/50[aria-selected="true"] { + background-color: hsl(var(--accent) / 0.5); +} + +.use-tailwind .aria-selected\:text-accent-foreground[aria-selected="true"] { + color: hsl(var(--accent-foreground)); +} + +.use-tailwind .aria-selected\:text-muted-foreground[aria-selected="true"] { + color: hsl(var(--muted-foreground)); +} + +.use-tailwind .aria-selected\:opacity-100[aria-selected="true"] { + opacity: 1; +} + +.use-tailwind .aria-selected\:opacity-30[aria-selected="true"] { + opacity: 0.3; +} + +.use-tailwind .data-\[disabled\=true\]\:pointer-events-none[data-disabled=true] { + pointer-events: none; +} + +.use-tailwind .data-\[disabled\]\:pointer-events-none[data-disabled] { + pointer-events: none; +} + +.use-tailwind .data-\[panel-group-direction\=vertical\]\:h-px[data-panel-group-direction=vertical] { + height: 1px; +} + +.use-tailwind .data-\[panel-group-direction\=vertical\]\:w-full[data-panel-group-direction=vertical] { + width: 100%; +} + +.use-tailwind .data-\[side\=bottom\]\:translate-y-1[data-side=bottom] { + --tw-translate-y: 0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .data-\[side\=left\]\:-translate-x-1[data-side=left] { + --tw-translate-x: -0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .data-\[side\=right\]\:translate-x-1[data-side=right] { + --tw-translate-x: 0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .data-\[side\=top\]\:-translate-y-1[data-side=top] { + --tw-translate-y: -0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .data-\[state\=checked\]\:translate-x-4[data-state=checked] { + --tw-translate-x: 1rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked] { + --tw-translate-x: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel] { + --tw-translate-x: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end] { + --tw-translate-x: var(--radix-toast-swipe-end-x); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move] { + --tw-translate-x: var(--radix-toast-swipe-move-x); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +@keyframes accordion-up { + from { + height: var(--radix-accordion-content-height); + } + + to { + height: 0; + } +} + +.use-tailwind .data-\[state\=closed\]\:animate-accordion-up[data-state=closed] { + animation: accordion-up 0.2s ease-out; +} + +@keyframes accordion-down { + from { + height: 0; + } + + to { + height: var(--radix-accordion-content-height); + } +} + +.use-tailwind .data-\[state\=open\]\:animate-accordion-down[data-state=open] { + animation: accordion-down 0.2s ease-out; +} + +.use-tailwind .data-\[panel-group-direction\=vertical\]\:flex-col[data-panel-group-direction=vertical] { + flex-direction: column; +} + +.use-tailwind .data-\[active\]\:bg-accent\/50[data-active] { + background-color: hsl(var(--accent) / 0.5); +} + +.use-tailwind .data-\[state\=active\]\:bg-background[data-state=active] { + background-color: hsl(var(--background)); +} + +.use-tailwind .data-\[state\=checked\]\:bg-primary[data-state=checked] { + background-color: hsl(var(--primary)); +} + +.use-tailwind .data-\[state\=on\]\:bg-accent[data-state=on] { + background-color: hsl(var(--accent)); +} + +.use-tailwind .data-\[state\=open\]\:bg-accent[data-state=open] { + background-color: hsl(var(--accent)); +} + +.use-tailwind .data-\[state\=open\]\:bg-accent\/50[data-state=open] { + background-color: hsl(var(--accent) / 0.5); +} + +.use-tailwind .data-\[state\=open\]\:bg-secondary[data-state=open] { + background-color: hsl(var(--secondary)); +} + +.use-tailwind .data-\[state\=selected\]\:bg-muted[data-state=selected] { + background-color: hsl(var(--muted)); +} + +.use-tailwind .data-\[state\=unchecked\]\:bg-input[data-state=unchecked] { + background-color: hsl(var(--input)); +} + +.use-tailwind .data-\[state\=active\]\:text-foreground[data-state=active] { + color: hsl(var(--foreground)); +} + +.use-tailwind .data-\[state\=checked\]\:text-primary-foreground[data-state=checked] { + color: hsl(var(--primary-foreground)); +} + +.use-tailwind .data-\[state\=on\]\:text-accent-foreground[data-state=on] { + color: hsl(var(--accent-foreground)); +} + +.use-tailwind .data-\[state\=open\]\:text-accent-foreground[data-state=open] { + color: hsl(var(--accent-foreground)); +} + +.use-tailwind .data-\[state\=open\]\:text-muted-foreground[data-state=open] { + color: hsl(var(--muted-foreground)); +} + +.use-tailwind .data-\[disabled\=true\]\:opacity-50[data-disabled=true] { + opacity: 0.5; +} + +.use-tailwind .data-\[disabled\]\:opacity-50[data-disabled] { + opacity: 0.5; +} + +.use-tailwind .data-\[state\=active\]\:shadow[data-state=active] { + --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.use-tailwind .data-\[swipe\=move\]\:transition-none[data-swipe=move] { + transition-property: none; +} + +.use-tailwind .data-\[state\=closed\]\:duration-300[data-state=closed] { + transition-duration: 300ms; +} + +.use-tailwind .data-\[state\=open\]\:duration-500[data-state=open] { + transition-duration: 500ms; +} + +.use-tailwind .data-\[motion\^\=from-\]\:animate-in[data-motion^=from-] { + animation-name: enter; + animation-duration: 150ms; + --tw-enter-opacity: initial; + --tw-enter-scale: initial; + --tw-enter-rotate: initial; + --tw-enter-translate-x: initial; + --tw-enter-translate-y: initial; +} + +.use-tailwind .data-\[state\=open\]\:animate-in[data-state=open] { + animation-name: enter; + animation-duration: 150ms; + --tw-enter-opacity: initial; + --tw-enter-scale: initial; + --tw-enter-rotate: initial; + --tw-enter-translate-x: initial; + --tw-enter-translate-y: initial; +} + +.use-tailwind .data-\[state\=visible\]\:animate-in[data-state=visible] { + animation-name: enter; + animation-duration: 150ms; + --tw-enter-opacity: initial; + --tw-enter-scale: initial; + --tw-enter-rotate: initial; + --tw-enter-translate-x: initial; + --tw-enter-translate-y: initial; +} + +.use-tailwind .data-\[motion\^\=to-\]\:animate-out[data-motion^=to-] { + animation-name: exit; + animation-duration: 150ms; + --tw-exit-opacity: initial; + --tw-exit-scale: initial; + --tw-exit-rotate: initial; + --tw-exit-translate-x: initial; + --tw-exit-translate-y: initial; +} + +.use-tailwind .data-\[state\=closed\]\:animate-out[data-state=closed] { + animation-name: exit; + animation-duration: 150ms; + --tw-exit-opacity: initial; + --tw-exit-scale: initial; + --tw-exit-rotate: initial; + --tw-exit-translate-x: initial; + --tw-exit-translate-y: initial; +} + +.use-tailwind .data-\[state\=hidden\]\:animate-out[data-state=hidden] { + animation-name: exit; + animation-duration: 150ms; + --tw-exit-opacity: initial; + --tw-exit-scale: initial; + --tw-exit-rotate: initial; + --tw-exit-translate-x: initial; + --tw-exit-translate-y: initial; +} + +.use-tailwind .data-\[swipe\=end\]\:animate-out[data-swipe=end] { + animation-name: exit; + animation-duration: 150ms; + --tw-exit-opacity: initial; + --tw-exit-scale: initial; + --tw-exit-rotate: initial; + --tw-exit-translate-x: initial; + --tw-exit-translate-y: initial; +} + +.use-tailwind .data-\[motion\^\=from-\]\:fade-in[data-motion^=from-] { + --tw-enter-opacity: 0; +} + +.use-tailwind .data-\[motion\^\=to-\]\:fade-out[data-motion^=to-] { + --tw-exit-opacity: 0; +} + +.use-tailwind .data-\[state\=closed\]\:fade-out-0[data-state=closed] { + --tw-exit-opacity: 0; +} + +.use-tailwind .data-\[state\=closed\]\:fade-out-80[data-state=closed] { + --tw-exit-opacity: 0.8; +} + +.use-tailwind .data-\[state\=hidden\]\:fade-out[data-state=hidden] { + --tw-exit-opacity: 0; +} + +.use-tailwind .data-\[state\=open\]\:fade-in-0[data-state=open] { + --tw-enter-opacity: 0; +} + +.use-tailwind .data-\[state\=visible\]\:fade-in[data-state=visible] { + --tw-enter-opacity: 0; +} + +.use-tailwind .data-\[state\=closed\]\:zoom-out-95[data-state=closed] { + --tw-exit-scale: .95; +} + +.use-tailwind .data-\[state\=open\]\:zoom-in-90[data-state=open] { + --tw-enter-scale: .9; +} + +.use-tailwind .data-\[state\=open\]\:zoom-in-95[data-state=open] { + --tw-enter-scale: .95; +} + +.use-tailwind .data-\[motion\=from-end\]\:slide-in-from-right-52[data-motion=from-end] { + --tw-enter-translate-x: 13rem; +} + +.use-tailwind .data-\[motion\=from-start\]\:slide-in-from-left-52[data-motion=from-start] { + --tw-enter-translate-x: -13rem; +} + +.use-tailwind .data-\[motion\=to-end\]\:slide-out-to-right-52[data-motion=to-end] { + --tw-exit-translate-x: 13rem; +} + +.use-tailwind .data-\[motion\=to-start\]\:slide-out-to-left-52[data-motion=to-start] { + --tw-exit-translate-x: -13rem; +} + +.use-tailwind .data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom] { + --tw-enter-translate-y: -0.5rem; +} + +.use-tailwind .data-\[side\=left\]\:slide-in-from-right-2[data-side=left] { + --tw-enter-translate-x: 0.5rem; +} + +.use-tailwind .data-\[side\=right\]\:slide-in-from-left-2[data-side=right] { + --tw-enter-translate-x: -0.5rem; +} + +.use-tailwind .data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top] { + --tw-enter-translate-y: 0.5rem; +} + +.use-tailwind .data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed] { + --tw-exit-translate-y: 100%; +} + +.use-tailwind .data-\[state\=closed\]\:slide-out-to-left[data-state=closed] { + --tw-exit-translate-x: -100%; +} + +.use-tailwind .data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed] { + --tw-exit-translate-x: -50%; +} + +.use-tailwind .data-\[state\=closed\]\:slide-out-to-right[data-state=closed] { + --tw-exit-translate-x: 100%; +} + +.use-tailwind .data-\[state\=closed\]\:slide-out-to-right-full[data-state=closed] { + --tw-exit-translate-x: 100%; +} + +.use-tailwind .data-\[state\=closed\]\:slide-out-to-top[data-state=closed] { + --tw-exit-translate-y: -100%; +} + +.use-tailwind .data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed] { + --tw-exit-translate-y: -48%; +} + +.use-tailwind .data-\[state\=open\]\:slide-in-from-bottom[data-state=open] { + --tw-enter-translate-y: 100%; +} + +.use-tailwind .data-\[state\=open\]\:slide-in-from-left[data-state=open] { + --tw-enter-translate-x: -100%; +} + +.use-tailwind .data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open] { + --tw-enter-translate-x: -50%; +} + +.use-tailwind .data-\[state\=open\]\:slide-in-from-right[data-state=open] { + --tw-enter-translate-x: 100%; +} + +.use-tailwind .data-\[state\=open\]\:slide-in-from-top[data-state=open] { + --tw-enter-translate-y: -100%; +} + +.use-tailwind .data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open] { + --tw-enter-translate-y: -48%; +} + +.use-tailwind .data-\[state\=open\]\:slide-in-from-top-full[data-state=open] { + --tw-enter-translate-y: -100%; +} + +.use-tailwind .data-\[state\=closed\]\:duration-300[data-state=closed] { + animation-duration: 300ms; +} + +.use-tailwind .data-\[state\=open\]\:duration-500[data-state=open] { + animation-duration: 500ms; +} + +.use-tailwind .data-\[panel-group-direction\=vertical\]\:after\:left-0[data-panel-group-direction=vertical]::after { + content: var(--tw-content); + left: 0px; +} + +.use-tailwind .data-\[panel-group-direction\=vertical\]\:after\:h-1[data-panel-group-direction=vertical]::after { + content: var(--tw-content); + height: 0.25rem; +} + +.use-tailwind .data-\[panel-group-direction\=vertical\]\:after\:w-full[data-panel-group-direction=vertical]::after { + content: var(--tw-content); + width: 100%; +} + +.use-tailwind .data-\[panel-group-direction\=vertical\]\:after\:-translate-y-1\/2[data-panel-group-direction=vertical]::after { + content: var(--tw-content); + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .data-\[panel-group-direction\=vertical\]\:after\:translate-x-0[data-panel-group-direction=vertical]::after { + content: var(--tw-content); + --tw-translate-x: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .data-\[state\=inactive\]\:hover\:brightness-50:hover[data-state=inactive] { + --tw-brightness: brightness(.5); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.use-tailwind .group[data-state=open] .group-data-\[state\=open\]\:rotate-180 { + --tw-rotate: 180deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .dark\:-rotate-90:is(.theme-mode-dark *) { + --tw-rotate: -90deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .dark\:rotate-0:is(.theme-mode-dark *) { + --tw-rotate: 0deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .dark\:scale-0:is(.theme-mode-dark *) { + --tw-scale-x: 0; + --tw-scale-y: 0; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .dark\:scale-100:is(.theme-mode-dark *) { + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .dark\:border-destructive:is(.theme-mode-dark *) { + border-color: hsl(var(--destructive)); +} + +.use-tailwind .data-\[state\=inactive\]\:dark\:hover\:brightness-150:hover:is(.theme-mode-dark *)[data-state=inactive] { + --tw-brightness: brightness(1.5); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +@media (min-width: 640px) { + .use-tailwind .sm\:bottom-0 { + bottom: 0px; + } + + .use-tailwind .sm\:right-0 { + right: 0px; + } + + .use-tailwind .sm\:top-auto { + top: auto; + } + + .use-tailwind .sm\:mt-0 { + margin-top: 0px; + } + + .use-tailwind .sm\:block { + display: block; + } + + .use-tailwind .sm\:max-w-sm { + max-width: 24rem; + } + + .use-tailwind .sm\:flex-row { + flex-direction: row; + } + + .use-tailwind .sm\:flex-col { + flex-direction: column; + } + + .use-tailwind .sm\:justify-end { + justify-content: flex-end; + } + + .use-tailwind .sm\:gap-2 { + gap: 0.5rem; + } + + .use-tailwind .sm\:gap-2\.5 { + gap: 0.625rem; + } + + .use-tailwind .sm\:space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); + } + + .use-tailwind .sm\:space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + } + + .use-tailwind .sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); + } + + .use-tailwind .sm\:rounded-lg { + border-radius: var(--radius); + } + + .use-tailwind .sm\:text-left { + text-align: left; + } + + .use-tailwind .data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open] { + --tw-enter-translate-y: 100%; + } +} + +@media (min-width: 768px) { + .use-tailwind .md\:absolute { + position: absolute; + } + + .use-tailwind .md\:flex { + display: flex; + } + + .use-tailwind .md\:hidden { + display: none; + } + + .use-tailwind .md\:w-\[var\(--radix-navigation-menu-viewport-width\)\] { + width: var(--radix-navigation-menu-viewport-width); + } + + .use-tailwind .md\:w-auto { + width: auto; + } + + .use-tailwind .md\:max-w-\[420px\] { + max-width: 420px; + } +} + +.use-tailwind .\[\&\+div\]\:text-xs+div { + font-size: 0.75rem; + line-height: 1rem; +} + +.use-tailwind .\[\&\:has\(\>\.day-range-end\)\]\:rounded-r-md:has(>.day-range-end) { + border-top-right-radius: calc(var(--radius) - 2px); + border-bottom-right-radius: calc(var(--radius) - 2px); +} + +.use-tailwind .\[\&\:has\(\>\.day-range-start\)\]\:rounded-l-md:has(>.day-range-start) { + border-top-left-radius: calc(var(--radius) - 2px); + border-bottom-left-radius: calc(var(--radius) - 2px); +} + +.use-tailwind .\[\&\:has\(\[aria-selected\]\)\]\:rounded-md:has([aria-selected]) { + border-radius: calc(var(--radius) - 2px); +} + +.use-tailwind .\[\&\:has\(\[aria-selected\]\)\]\:bg-accent:has([aria-selected]) { + background-color: hsl(var(--accent)); +} + +.use-tailwind .first\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-l-md:has([aria-selected]):first-child { + border-top-left-radius: calc(var(--radius) - 2px); + border-bottom-left-radius: calc(var(--radius) - 2px); +} + +.use-tailwind .last\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-r-md:has([aria-selected]):last-child { + border-top-right-radius: calc(var(--radius) - 2px); + border-bottom-right-radius: calc(var(--radius) - 2px); +} + +.use-tailwind .\[\&\:has\(\[aria-selected\]\.day-outside\)\]\:bg-accent\/50:has([aria-selected].day-outside) { + background-color: hsl(var(--accent) / 0.5); +} + +.use-tailwind .\[\&\:has\(\[aria-selected\]\.day-range-end\)\]\:rounded-r-md:has([aria-selected].day-range-end) { + border-top-right-radius: calc(var(--radius) - 2px); + border-bottom-right-radius: calc(var(--radius) - 2px); +} + +.use-tailwind .\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]) { + padding-right: 0px; +} + +.use-tailwind .\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\]>[role=checkbox] { + --tw-translate-y: 2px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .\[\&\>span\]\:line-clamp-1>span { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} + +.use-tailwind .\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div { + --tw-translate-y: -3px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .\[\&\>svg\]\:absolute>svg { + position: absolute; +} + +.use-tailwind .\[\&\>svg\]\:left-4>svg { + left: 1rem; +} + +.use-tailwind .\[\&\>svg\]\:top-4>svg { + top: 1rem; +} + +.use-tailwind .\[\&\>svg\]\:size-3\.5>svg { + width: 0.875rem; + height: 0.875rem; +} + +.use-tailwind .\[\&\>svg\]\:text-destructive>svg { + color: hsl(var(--destructive)); +} + +.use-tailwind .\[\&\>svg\]\:text-foreground>svg { + color: hsl(var(--foreground)); +} + +.use-tailwind .\[\&\>svg\~\*\]\:pl-7>svg~* { + padding-left: 1.75rem; +} + +.use-tailwind .\[\&\>tr\]\:last\:border-b-0:last-child>tr { + border-bottom-width: 0px; +} + +.use-tailwind .\[\&\[data-panel-group-direction\=vertical\]\>div\]\:rotate-90[data-panel-group-direction=vertical]>div { + --tw-rotate: 90deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg { + --tw-rotate: 180deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.use-tailwind .\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading] { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.use-tailwind .\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading] { + padding-top: 0.375rem; + padding-bottom: 0.375rem; +} + +.use-tailwind .\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading] { + font-size: 0.75rem; + line-height: 1rem; +} + +.use-tailwind .\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading] { + font-weight: 500; +} + +.use-tailwind .\[\&_\[cmdk-group-heading\]\]\:text-muted-foreground [cmdk-group-heading] { + color: hsl(var(--muted-foreground)); +} + +.use-tailwind .\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 [cmdk-group]:not([hidden]) ~[cmdk-group] { + padding-top: 0px; +} + +.use-tailwind .\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group] { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.use-tailwind .\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg { + height: 1.25rem; +} + +.use-tailwind .\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg { + width: 1.25rem; +} + +.use-tailwind .\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input] { + height: 3rem; +} + +.use-tailwind .\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item] { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.use-tailwind .\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item] { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.use-tailwind .\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg { + height: 1.25rem; +} + +.use-tailwind .\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg { + width: 1.25rem; +} + +.use-tailwind .\[\&_p\]\:leading-relaxed p { + line-height: 1.625; +} + +.use-tailwind .\[\&_tr\:last-child\]\:border-0 tr:last-child { + border-width: 0px; +} + +.use-tailwind .\[\&_tr\]\:border-b tr { + border-bottom-width: 1px; +} diff --git a/docs/quantinuum-sphinx/_static/tokens.css b/docs/quantinuum-sphinx/_static/tokens.css new file mode 100644 index 00000000..6fbfa246 --- /dev/null +++ b/docs/quantinuum-sphinx/_static/tokens.css @@ -0,0 +1,63 @@ +:root { + color-scheme: light; + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; + + --card: 0 0% 100%; + --card-foreground: 240 10% 3.9%; + + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; + + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 10% 3.9%; + + --radius: 0.5rem; +} + +:root.theme-mode-dark { + color-scheme: dark; + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 84% 60%; + --destructive-foreground: 240 10% 3.9%; + + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; +} diff --git a/docs/quantinuum-sphinx/_templates/page.html b/docs/quantinuum-sphinx/_templates/page.html new file mode 100644 index 00000000..2bebea6c --- /dev/null +++ b/docs/quantinuum-sphinx/_templates/page.html @@ -0,0 +1,313 @@ +{% extends "furo/base.html" %} + +{% block extrahead %} + + + + + + + + +{% endblock %} + +{% block body -%} + +{{ super() }} +{% include "partials/icons.html" %} + + + + + + + + + + + + + + + {%- trans -%} + Skip to content + {%- endtrans -%} + + +{% if theme_announcement -%} +
+ +
+{%- endif %} + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + {% trans %}Back to top{% endtrans %} + +
+ {% if theme_top_of_page_button != "edit" -%} + {{ warning("Got configuration for 'top_of_page_button': this is deprecated.") }} + {%- endif -%} + + {%- if theme_top_of_page_buttons == "" -%} + {% if theme_top_of_page_button == None -%} + {#- We respect the old configuration of disabling all the buttons -#} + {%- set theme_top_of_page_buttons = [] -%} + {% else %} + {%- set theme_top_of_page_buttons = ["view", "edit"] -%} + {%- endif -%} + {% else -%} + {% if theme_top_of_page_button != "edit" -%} + {%- set theme_top_of_page_buttons = [] -%} + {{ warning("Got configuration for both 'top_of_page_button' and 'top_of_page_buttons', ignoring both and removing all top of page buttons.") }} + {%- endif -%} + {%- endif -%} + {% for button in theme_top_of_page_buttons -%} + {% if button == "view" %} + {%- include "components/view-this-page.html" with context -%} + {% elif button == "edit" %} + {%- include "components/edit-this-page.html" with context -%} + {% else %} + {{ warning("Got an unsupported value in 'top_of_page_buttons' for theme configuration") }} + {% endif %} + {%- endfor -%} + {#- Theme toggle -#} +
+ +
+ +
+
+ {% block content %}{{ body }}{% endblock %} +
+
+
+ {% block footer %} + +
+
+ {%- if show_copyright %} + + {%- endif %} + {% trans %}Made with {% endtrans -%} + {%- if show_sphinx -%} + {% trans %}Sphinx and {% endtrans -%} + @pradyunsg's + {% endif -%} + {% trans %} + Furo + {% endtrans %} + {%- if last_updated -%} +
+ {% trans last_updated=last_updated|e -%} + Last updated on {{ last_updated }} + {%- endtrans -%} +
+ {%- endif %} +
+
+ {% if theme_footer_icons or READTHEDOCS -%} +
+ {% if theme_footer_icons -%} + {% for icon_dict in theme_footer_icons -%} + + {{- icon_dict.html -}} + + {% endfor %} + {%- else -%} + {#- Show Read the Docs project -#} + {%- if READTHEDOCS and slug -%} + + + + + + {%- endif -%} + {#- Show GitHub repository home -#} + {%- if READTHEDOCS and display_github and github_user != "None" and github_repo != "None" -%} + + + + + + {%- endif -%} + {%- endif %} +
+ {%- endif %} +
+
+ {% endblock footer %} +
+
+ +
+
+{%- endblock %} + + + + From bea0a708bccaf2c2dc4674bd265126bf3f7d9908 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 4 Jul 2024 15:46:25 +0100 Subject: [PATCH 28/76] add furo dependency --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 694c4190..9c9441cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,10 @@ quimb = "^1.8.1" openfermion = "^1.6.1" ipyparallel = "^8.8.0" myst-nb = "^1.1.0" +sphinx-book-theme = "^1.1.3" +nbsphinx = "^0.9.4" +quantinuum-docs-theme = {git = "https://github.com/aidanCQ/qui-sphinx.git", rev = "dist"} +furo = "^2024.5.6" [build-system] From f5489fa301ae9c534706ab6d0fa1895a332c7a12 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 4 Jul 2024 15:47:17 +0100 Subject: [PATCH 29/76] New index page structure --- docs/examples/Getting_started.rst | 36 ----------------------- docs/index.rst | 49 +++++++++++++++++++++++++++++-- docs/manual/manual_index.rst | 22 -------------- 3 files changed, 46 insertions(+), 61 deletions(-) delete mode 100644 docs/examples/Getting_started.rst delete mode 100644 docs/manual/manual_index.rst diff --git a/docs/examples/Getting_started.rst b/docs/examples/Getting_started.rst deleted file mode 100644 index 618dfcbd..00000000 --- a/docs/examples/Getting_started.rst +++ /dev/null @@ -1,36 +0,0 @@ -Getting Started -=============== - -.. toctree:: - :caption: Example Notebooks: - :maxdepth: 2 - - ansatz_sequence_example - circuit_analysis_example - circuit_generation_example - compilation_example - conditional_gate_example - contextual_optimization - creating_backends - measurement_reduction_example - mapping_example - symbolics_example - phase_estimation - pytket-qujax_qaoa - ucc_vqe - CONTRIBUTING.md - - - - - -.. jupyter-execute:: - - from pytket import Circuit - - ghz_circ = Circuit(3) - ghz_circ.H(0) - ghz_circ.CX(0, 1) - ghz_circ.CX(1, 2) - ghz_circ.add_barrier(ghz_circ.qubits) - ghz_circ.measure_all() \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 358acbe9..ba4e2e10 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,6 +1,49 @@ +Getting Started +=============== + + +.. jupyter-execute:: + + from pytket import Circuit + + ghz_circ = Circuit(3) + ghz_circ.H(0) + ghz_circ.CX(0, 1) + ghz_circ.CX(1, 2) + ghz_circ.add_barrier(ghz_circ.qubits) + ghz_circ.measure_all() + + + .. toctree:: - :caption: pytket docs + :caption: Manual :maxdepth: 2 - manual/manual_index.rst - examples/Getting_started.ipynb \ No newline at end of file + manual/manual_intro.rst + manual/manual_circuit.rst + manual/manual_backend.rst + manual/manual_compiler.rst + manual/manual_noise.rst + manual/manual_assertion.rst + manual/manual_zx.rst + +.. toctree:: + :caption: Examples + :maxdepth: 2 + + examples/ansatz_sequence_example + examples/circuit_analysis_example + examples/circuit_generation_example + examples/compilation_example + examples/conditional_gate_example + examples/contextual_optimization + examples/creating_backends + examples/measurement_reduction_example + examples/mapping_example + examples/symbolics_example + examples/phase_estimation + examples/pytket-qujax_qaoa + examples/ucc_vqe + + + diff --git a/docs/manual/manual_index.rst b/docs/manual/manual_index.rst deleted file mode 100644 index cf0041fc..00000000 --- a/docs/manual/manual_index.rst +++ /dev/null @@ -1,22 +0,0 @@ -Manual -====== - -.. toctree:: - :caption: Manual Sections: - :maxdepth: 2 - - manual_intro.rst - manual_circuit.rst - manual_backend.rst - manual_compiler.rst - manual_noise.rst - manual_assertion.rst - manual_zx.rst - - -Indices and Tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` From 137493c25a4b2d2c0862aee64cc79e93b4194058 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 4 Jul 2024 15:48:02 +0100 Subject: [PATCH 30/76] fix maths rendering problems --- docs/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 536abaf4..db30f765 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,6 +20,8 @@ "myst_nb", ] +myst_enable_extensions = ["dollarmath", "html_image", "attrs_inline"] + html_theme = "furo" templates_path = ["./quantinuum-sphinx/_templates/"] html_static_path = ["./quantinuum-sphinx/_static/", "_static/"] From 87084cdb1d5193655021859c32130ebfd3b0bfa6 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:08:51 +0100 Subject: [PATCH 31/76] remove jupyterbook build workflow --- .github/workflows/build_jupyterbook.yml | 41 ------------------ .github/workflows/check-manual.yml | 55 ------------------------- 2 files changed, 96 deletions(-) delete mode 100644 .github/workflows/build_jupyterbook.yml delete mode 100644 .github/workflows/check-manual.yml diff --git a/.github/workflows/build_jupyterbook.yml b/.github/workflows/build_jupyterbook.yml deleted file mode 100644 index c2534ba7..00000000 --- a/.github/workflows/build_jupyterbook.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: build-book - -# Only run this when the main branch changes -on: - push: - branches: - - main - - 'jupyterbook/**' - - pull_request: - branches: - - main - -# This job installs dependencies and builds the jupyterbook -jobs: - check-jupyterbook-build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - # Install dependencies - - name: Set up Python 3.10.12 - uses: actions/setup-python@v5 - with: - python-version: 3.10.12 - - - name: Install dependencies - run: | - cd examples - python -m pip install --upgrade pip wheel - python -m pip install -r example_requirements.txt - python -m pip install jupyter-book - - # Build the book ensuring that the build treats warnings as errors (-W flag) - # CLI Reference: https://jupyterbook.org/en/stable/reference/cli.html - - name: Build the book - run: | - cd examples - jupyter-book build -W . - - # Only the build is tested on CI in the pytket repository. The built pages are deployed from the tket-site repository. diff --git a/.github/workflows/check-manual.yml b/.github/workflows/check-manual.yml deleted file mode 100644 index 771788c8..00000000 --- a/.github/workflows/check-manual.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: check manual - -on: - pull_request: - branches: - - main - schedule: - # 03:00 every Saturday morning - - cron: '0 3 * * 6' - -jobs: - - changes: - runs-on: ubuntu-22.04 - outputs: - manual: ${{ steps.filter.outputs.manual }} - steps: - - uses: actions/checkout@v4 - - uses: dorny/paths-filter@v3 - id: filter - with: - base: ${{ github.ref }} - filters: | - manual: - - 'manual/**' - - 'manual_requirements.txt' - - 'manual_constraints.txt' - - '.github/**' - - check: - name: check manual - needs: changes - if: github.event_name == 'schedule' || needs.changes.outputs.manual == 'true' - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: '0' - - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/* - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: 3.11 - - name: install python requirements for manual - run: | - python -m pip install poetry - poetry install - poetry shell - - name: install graphviz - run: | - sudo apt-get update - sudo apt-get install graphviz - - name: build manual - run: | - ./build-docs From 3cdaa660d77d9bd6ac450e7184c8e8935c3f807d Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:27:24 +0100 Subject: [PATCH 32/76] rename build workflow --- .github/workflows/check-docs-build.yml | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/check-docs-build.yml diff --git a/.github/workflows/check-docs-build.yml b/.github/workflows/check-docs-build.yml new file mode 100644 index 00000000..33053bb7 --- /dev/null +++ b/.github/workflows/check-docs-build.yml @@ -0,0 +1,55 @@ +name: check docs build + +on: + pull_request: + branches: + - main + schedule: + # 03:00 every Saturday morning + - cron: '0 3 * * 6' + +jobs: + + changes: + runs-on: ubuntu-22.04 + outputs: + manual: ${{ steps.filter.outputs.manual }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + base: ${{ github.ref }} + filters: | + manual: + - 'manual/**' + - 'manual_requirements.txt' + - 'manual_constraints.txt' + - '.github/**' + + check: + name: check manual + needs: changes + if: github.event_name == 'schedule' || needs.changes.outputs.manual == 'true' + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: '0' + - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/* + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: 3.11 + - name: install python requirements for manual + run: | + python -m pip install poetry + poetry install + poetry shell + - name: install graphviz + run: | + sudo apt-get update + sudo apt-get install graphviz + - name: build manual + run: | + ./build-docs.sh From f65441466376c87aaea81fe4c3b20a5cc6acc186 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:36:03 +0100 Subject: [PATCH 33/76] simplify poetry dependencies and regenerate lock file --- poetry.lock | 2059 ++++++++++++++++++++---------------------------- pyproject.toml | 6 +- 2 files changed, 850 insertions(+), 1215 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8637d957..309003ee 100644 --- a/poetry.lock +++ b/poetry.lock @@ -158,24 +158,24 @@ css = ["tinycss2 (>=1.1.0,<1.3)"] [[package]] name = "cachetools" -version = "5.3.3" +version = "5.4.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, - {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, + {file = "cachetools-5.4.0-py3-none-any.whl", hash = "sha256:3ae3b49a3d5e28a77a0be2b37dbcb89005058959cb2323858c2657c4a8cab474"}, + {file = "cachetools-5.4.0.tar.gz", hash = "sha256:b8adc2e7c07f105ced7bc56dbb6dfbe7c4a00acce20e2227b3f355be89bc6827"}, ] [[package]] name = "certifi" -version = "2024.6.2" +version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, - {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] @@ -383,20 +383,6 @@ google-api-core = {version = ">=1.14.0", extras = ["grpc"]} proto-plus = ">=1.20.0" protobuf = ">=3.15.0" -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - [[package]] name = "colorama" version = "0.4.6" @@ -509,43 +495,38 @@ test = ["altair", "baytune", "chocolate", "dask", "distributed", "kahypar", "mat [[package]] name = "cryptography" -version = "42.0.8" +version = "43.0.0" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, - {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, - {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, - {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, - {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, - {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, - {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, - {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, + {file = "cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf"}, + {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55"}, + {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431"}, + {file = "cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc"}, + {file = "cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778"}, + {file = "cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f"}, + {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0"}, + {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b"}, + {file = "cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf"}, + {file = "cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1"}, + {file = "cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e"}, ] [package.dependencies] @@ -558,7 +539,7 @@ nox = ["nox"] pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "cryptography-vectors (==43.0.0)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] @@ -697,33 +678,33 @@ cython = ["cython"] [[package]] name = "debugpy" -version = "1.8.1" +version = "1.8.2" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, - {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, - {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, - {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, - {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, - {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, - {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, - {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, - {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, - {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, - {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, - {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, - {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, - {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, - {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, - {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, - {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, - {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, - {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, - {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, - {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, - {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, + {file = "debugpy-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7ee2e1afbf44b138c005e4380097d92532e1001580853a7cb40ed84e0ef1c3d2"}, + {file = "debugpy-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f8c3f7c53130a070f0fc845a0f2cee8ed88d220d6b04595897b66605df1edd6"}, + {file = "debugpy-1.8.2-cp310-cp310-win32.whl", hash = "sha256:f179af1e1bd4c88b0b9f0fa153569b24f6b6f3de33f94703336363ae62f4bf47"}, + {file = "debugpy-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:0600faef1d0b8d0e85c816b8bb0cb90ed94fc611f308d5fde28cb8b3d2ff0fe3"}, + {file = "debugpy-1.8.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8a13417ccd5978a642e91fb79b871baded925d4fadd4dfafec1928196292aa0a"}, + {file = "debugpy-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdf39855f65c48ac9667b2801234fc64d46778021efac2de7e50907ab90c634"}, + {file = "debugpy-1.8.2-cp311-cp311-win32.whl", hash = "sha256:2cbd4d9a2fc5e7f583ff9bf11f3b7d78dfda8401e8bb6856ad1ed190be4281ad"}, + {file = "debugpy-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:d3408fddd76414034c02880e891ea434e9a9cf3a69842098ef92f6e809d09afa"}, + {file = "debugpy-1.8.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:5d3ccd39e4021f2eb86b8d748a96c766058b39443c1f18b2dc52c10ac2757835"}, + {file = "debugpy-1.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62658aefe289598680193ff655ff3940e2a601765259b123dc7f89c0239b8cd3"}, + {file = "debugpy-1.8.2-cp312-cp312-win32.whl", hash = "sha256:bd11fe35d6fd3431f1546d94121322c0ac572e1bfb1f6be0e9b8655fb4ea941e"}, + {file = "debugpy-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:15bc2f4b0f5e99bf86c162c91a74c0631dbd9cef3c6a1d1329c946586255e859"}, + {file = "debugpy-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:5a019d4574afedc6ead1daa22736c530712465c0c4cd44f820d803d937531b2d"}, + {file = "debugpy-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40f062d6877d2e45b112c0bbade9a17aac507445fd638922b1a5434df34aed02"}, + {file = "debugpy-1.8.2-cp38-cp38-win32.whl", hash = "sha256:c78ba1680f1015c0ca7115671fe347b28b446081dada3fedf54138f44e4ba031"}, + {file = "debugpy-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf327316ae0c0e7dd81eb92d24ba8b5e88bb4d1b585b5c0d32929274a66a5210"}, + {file = "debugpy-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1523bc551e28e15147815d1397afc150ac99dbd3a8e64641d53425dba57b0ff9"}, + {file = "debugpy-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e24ccb0cd6f8bfaec68d577cb49e9c680621c336f347479b3fce060ba7c09ec1"}, + {file = "debugpy-1.8.2-cp39-cp39-win32.whl", hash = "sha256:7f8d57a98c5a486c5c7824bc0b9f2f11189d08d73635c326abef268f83950326"}, + {file = "debugpy-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:16c8dcab02617b75697a0a925a62943e26a0330da076e2a10437edd9f0bf3755"}, + {file = "debugpy-1.8.2-py2.py3-none-any.whl", hash = "sha256:16e16df3a98a35c63c3ab1e4d19be4cbc7fdda92d9ddc059294f18910928e0ca"}, + {file = "debugpy-1.8.2.zip", hash = "sha256:95378ed08ed2089221896b9b3a8d021e642c24edc8fef20e5d4342ca8be65c00"}, ] [[package]] @@ -815,13 +796,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.1" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -843,13 +824,13 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "fastjsonschema" -version = "2.19.1" +version = "2.20.0" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, - {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, + {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, + {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, ] [package.extras] @@ -857,53 +838,53 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "fonttools" -version = "4.53.0" +version = "4.53.1" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.53.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:52a6e0a7a0bf611c19bc8ec8f7592bdae79c8296c70eb05917fd831354699b20"}, - {file = "fonttools-4.53.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:099634631b9dd271d4a835d2b2a9e042ccc94ecdf7e2dd9f7f34f7daf333358d"}, - {file = "fonttools-4.53.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e40013572bfb843d6794a3ce076c29ef4efd15937ab833f520117f8eccc84fd6"}, - {file = "fonttools-4.53.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:715b41c3e231f7334cbe79dfc698213dcb7211520ec7a3bc2ba20c8515e8a3b5"}, - {file = "fonttools-4.53.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74ae2441731a05b44d5988d3ac2cf784d3ee0a535dbed257cbfff4be8bb49eb9"}, - {file = "fonttools-4.53.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:95db0c6581a54b47c30860d013977b8a14febc206c8b5ff562f9fe32738a8aca"}, - {file = "fonttools-4.53.0-cp310-cp310-win32.whl", hash = "sha256:9cd7a6beec6495d1dffb1033d50a3f82dfece23e9eb3c20cd3c2444d27514068"}, - {file = "fonttools-4.53.0-cp310-cp310-win_amd64.whl", hash = "sha256:daaef7390e632283051e3cf3e16aff2b68b247e99aea916f64e578c0449c9c68"}, - {file = "fonttools-4.53.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a209d2e624ba492df4f3bfad5996d1f76f03069c6133c60cd04f9a9e715595ec"}, - {file = "fonttools-4.53.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f520d9ac5b938e6494f58a25c77564beca7d0199ecf726e1bd3d56872c59749"}, - {file = "fonttools-4.53.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eceef49f457253000e6a2d0f7bd08ff4e9fe96ec4ffce2dbcb32e34d9c1b8161"}, - {file = "fonttools-4.53.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1f3e34373aa16045484b4d9d352d4c6b5f9f77ac77a178252ccbc851e8b2ee"}, - {file = "fonttools-4.53.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:28d072169fe8275fb1a0d35e3233f6df36a7e8474e56cb790a7258ad822b6fd6"}, - {file = "fonttools-4.53.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a2a6ba400d386e904fd05db81f73bee0008af37799a7586deaa4aef8cd5971e"}, - {file = "fonttools-4.53.0-cp311-cp311-win32.whl", hash = "sha256:bb7273789f69b565d88e97e9e1da602b4ee7ba733caf35a6c2affd4334d4f005"}, - {file = "fonttools-4.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:9fe9096a60113e1d755e9e6bda15ef7e03391ee0554d22829aa506cdf946f796"}, - {file = "fonttools-4.53.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d8f191a17369bd53a5557a5ee4bab91d5330ca3aefcdf17fab9a497b0e7cff7a"}, - {file = "fonttools-4.53.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93156dd7f90ae0a1b0e8871032a07ef3178f553f0c70c386025a808f3a63b1f4"}, - {file = "fonttools-4.53.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bff98816cb144fb7b85e4b5ba3888a33b56ecef075b0e95b95bcd0a5fbf20f06"}, - {file = "fonttools-4.53.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:973d030180eca8255b1bce6ffc09ef38a05dcec0e8320cc9b7bcaa65346f341d"}, - {file = "fonttools-4.53.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4ee5a24e281fbd8261c6ab29faa7fd9a87a12e8c0eed485b705236c65999109"}, - {file = "fonttools-4.53.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bd5bc124fae781a4422f61b98d1d7faa47985f663a64770b78f13d2c072410c2"}, - {file = "fonttools-4.53.0-cp312-cp312-win32.whl", hash = "sha256:a239afa1126b6a619130909c8404070e2b473dd2b7fc4aacacd2e763f8597fea"}, - {file = "fonttools-4.53.0-cp312-cp312-win_amd64.whl", hash = "sha256:45b4afb069039f0366a43a5d454bc54eea942bfb66b3fc3e9a2c07ef4d617380"}, - {file = "fonttools-4.53.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:93bc9e5aaa06ff928d751dc6be889ff3e7d2aa393ab873bc7f6396a99f6fbb12"}, - {file = "fonttools-4.53.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2367d47816cc9783a28645bc1dac07f8ffc93e0f015e8c9fc674a5b76a6da6e4"}, - {file = "fonttools-4.53.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:907fa0b662dd8fc1d7c661b90782ce81afb510fc4b7aa6ae7304d6c094b27bce"}, - {file = "fonttools-4.53.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e0ad3c6ea4bd6a289d958a1eb922767233f00982cf0fe42b177657c86c80a8f"}, - {file = "fonttools-4.53.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:73121a9b7ff93ada888aaee3985a88495489cc027894458cb1a736660bdfb206"}, - {file = "fonttools-4.53.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ee595d7ba9bba130b2bec555a40aafa60c26ce68ed0cf509983e0f12d88674fd"}, - {file = "fonttools-4.53.0-cp38-cp38-win32.whl", hash = "sha256:fca66d9ff2ac89b03f5aa17e0b21a97c21f3491c46b583bb131eb32c7bab33af"}, - {file = "fonttools-4.53.0-cp38-cp38-win_amd64.whl", hash = "sha256:31f0e3147375002aae30696dd1dc596636abbd22fca09d2e730ecde0baad1d6b"}, - {file = "fonttools-4.53.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7d6166192dcd925c78a91d599b48960e0a46fe565391c79fe6de481ac44d20ac"}, - {file = "fonttools-4.53.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef50ec31649fbc3acf6afd261ed89d09eb909b97cc289d80476166df8438524d"}, - {file = "fonttools-4.53.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f193f060391a455920d61684a70017ef5284ccbe6023bb056e15e5ac3de11d1"}, - {file = "fonttools-4.53.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba9f09ff17f947392a855e3455a846f9855f6cf6bec33e9a427d3c1d254c712f"}, - {file = "fonttools-4.53.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0c555e039d268445172b909b1b6bdcba42ada1cf4a60e367d68702e3f87e5f64"}, - {file = "fonttools-4.53.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5a4788036201c908079e89ae3f5399b33bf45b9ea4514913f4dbbe4fac08efe0"}, - {file = "fonttools-4.53.0-cp39-cp39-win32.whl", hash = "sha256:d1a24f51a3305362b94681120c508758a88f207fa0a681c16b5a4172e9e6c7a9"}, - {file = "fonttools-4.53.0-cp39-cp39-win_amd64.whl", hash = "sha256:1e677bfb2b4bd0e5e99e0f7283e65e47a9814b0486cb64a41adf9ef110e078f2"}, - {file = "fonttools-4.53.0-py3-none-any.whl", hash = "sha256:6b4f04b1fbc01a3569d63359f2227c89ab294550de277fd09d8fca6185669fa4"}, - {file = "fonttools-4.53.0.tar.gz", hash = "sha256:c93ed66d32de1559b6fc348838c7572d5c0ac1e4a258e76763a5caddd8944002"}, + {file = "fonttools-4.53.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0679a30b59d74b6242909945429dbddb08496935b82f91ea9bf6ad240ec23397"}, + {file = "fonttools-4.53.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8bf06b94694251861ba7fdeea15c8ec0967f84c3d4143ae9daf42bbc7717fe3"}, + {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b96cd370a61f4d083c9c0053bf634279b094308d52fdc2dd9a22d8372fdd590d"}, + {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1c7c5aa18dd3b17995898b4a9b5929d69ef6ae2af5b96d585ff4005033d82f0"}, + {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e013aae589c1c12505da64a7d8d023e584987e51e62006e1bb30d72f26522c41"}, + {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9efd176f874cb6402e607e4cc9b4a9cd584d82fc34a4b0c811970b32ba62501f"}, + {file = "fonttools-4.53.1-cp310-cp310-win32.whl", hash = "sha256:c8696544c964500aa9439efb6761947393b70b17ef4e82d73277413f291260a4"}, + {file = "fonttools-4.53.1-cp310-cp310-win_amd64.whl", hash = "sha256:8959a59de5af6d2bec27489e98ef25a397cfa1774b375d5787509c06659b3671"}, + {file = "fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1"}, + {file = "fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923"}, + {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719"}, + {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3"}, + {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb"}, + {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2"}, + {file = "fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88"}, + {file = "fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02"}, + {file = "fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58"}, + {file = "fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8"}, + {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60"}, + {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f"}, + {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2"}, + {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f"}, + {file = "fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670"}, + {file = "fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab"}, + {file = "fonttools-4.53.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c818c058404eb2bba05e728d38049438afd649e3c409796723dfc17cd3f08749"}, + {file = "fonttools-4.53.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:651390c3b26b0c7d1f4407cad281ee7a5a85a31a110cbac5269de72a51551ba2"}, + {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54f1bba2f655924c1138bbc7fa91abd61f45c68bd65ab5ed985942712864bbb"}, + {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9cd19cf4fe0595ebdd1d4915882b9440c3a6d30b008f3cc7587c1da7b95be5f"}, + {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2af40ae9cdcb204fc1d8f26b190aa16534fcd4f0df756268df674a270eab575d"}, + {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:35250099b0cfb32d799fb5d6c651220a642fe2e3c7d2560490e6f1d3f9ae9169"}, + {file = "fonttools-4.53.1-cp38-cp38-win32.whl", hash = "sha256:f08df60fbd8d289152079a65da4e66a447efc1d5d5a4d3f299cdd39e3b2e4a7d"}, + {file = "fonttools-4.53.1-cp38-cp38-win_amd64.whl", hash = "sha256:7b6b35e52ddc8fb0db562133894e6ef5b4e54e1283dff606fda3eed938c36fc8"}, + {file = "fonttools-4.53.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75a157d8d26c06e64ace9df037ee93a4938a4606a38cb7ffaf6635e60e253b7a"}, + {file = "fonttools-4.53.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4824c198f714ab5559c5be10fd1adf876712aa7989882a4ec887bf1ef3e00e31"}, + {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:becc5d7cb89c7b7afa8321b6bb3dbee0eec2b57855c90b3e9bf5fb816671fa7c"}, + {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ec3fb43befb54be490147b4a922b5314e16372a643004f182babee9f9c3407"}, + {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:73379d3ffdeecb376640cd8ed03e9d2d0e568c9d1a4e9b16504a834ebadc2dfb"}, + {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02569e9a810f9d11f4ae82c391ebc6fb5730d95a0657d24d754ed7763fb2d122"}, + {file = "fonttools-4.53.1-cp39-cp39-win32.whl", hash = "sha256:aae7bd54187e8bf7fd69f8ab87b2885253d3575163ad4d669a262fe97f0136cb"}, + {file = "fonttools-4.53.1-cp39-cp39-win_amd64.whl", hash = "sha256:e5b708073ea3d684235648786f5f6153a48dc8762cdfe5563c57e80787c29fbb"}, + {file = "fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d"}, + {file = "fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4"}, ] [package.extras] @@ -920,39 +901,63 @@ ufo = ["fs (>=2.2.0,<3)"] unicode = ["unicodedata2 (>=15.1.0)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] +[[package]] +name = "furo" +version = "2024.7.18" +description = "A clean customisable Sphinx documentation theme." +optional = false +python-versions = ">=3.8" +files = [ + {file = "furo-2024.7.18-py3-none-any.whl", hash = "sha256:b192c7c1f59805494c8ed606d9375fdac6e6ba8178e747e72bc116745fb7e13f"}, + {file = "furo-2024.7.18.tar.gz", hash = "sha256:37b08c5fccc95d46d8712c8be97acd46043963895edde05b0f4f135d58325c83"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7" +sphinx = ">=6.0,<8.0" +sphinx-basic-ng = ">=1.0.0.beta2" + [[package]] name = "google-api-core" -version = "2.8.0" +version = "2.19.1" description = "Google API client core library" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "google-api-core-2.8.0.tar.gz", hash = "sha256:065bb8e11c605fd232707ae50963dc1c8af5b3c95b4568887515985e6c1156b3"}, - {file = "google_api_core-2.8.0-py3-none-any.whl", hash = "sha256:1b9f59236ce1bae9a687c1d4f22957e79a2669e53d032893f6bf0fca54f6931d"}, + {file = "google-api-core-2.19.1.tar.gz", hash = "sha256:f4695f1e3650b316a795108a76a1c416e6afb036199d1c1f1f110916df479ffd"}, + {file = "google_api_core-2.19.1-py3-none-any.whl", hash = "sha256:f12a9b8309b5e21d92483bbd47ce2c445861ec7d269ef6784ecc0ea8c1fa6125"}, ] [package.dependencies] -google-auth = ">=1.25.0,<3.0dev" -googleapis-common-protos = ">=1.52.0,<2.0dev" -grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} -grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=3.12.0" -requests = ">=2.18.0,<3.0.0dev" +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = [ + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, + {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, +] +grpcio-status = [ + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, + {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, +] +proto-plus = ">=1.22.3,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" [package.extras] -grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)"] -grpcgcp = ["grpcio-gcp (>=0.2.2)"] -grpcio-gcp = ["grpcio-gcp (>=0.2.2)"] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.30.0" +version = "2.32.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.30.0.tar.gz", hash = "sha256:ab630a1320f6720909ad76a7dbdb6841cdf5c66b328d690027e4867bdfb16688"}, - {file = "google_auth-2.30.0-py2.py3-none-any.whl", hash = "sha256:8df7da660f62757388b8a7f249df13549b3373f24388cb5d2f1dd91cc18180b5"}, + {file = "google_auth-2.32.0-py2.py3-none-any.whl", hash = "sha256:53326ea2ebec768070a94bee4e1b9194c9646ea0c2bd72422785bd0f9abfad7b"}, + {file = "google_auth-2.32.0.tar.gz", hash = "sha256:49315be72c55a6a37d62819e3573f6b416aca00721f7e3e31a008d928bf64022"}, ] [package.dependencies] @@ -969,17 +974,17 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "googleapis-common-protos" -version = "1.63.1" +version = "1.63.2" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.63.1.tar.gz", hash = "sha256:c6442f7a0a6b2a80369457d79e6672bb7dcbaab88e0848302497e3ec80780a6a"}, - {file = "googleapis_common_protos-1.63.1-py2.py3-none-any.whl", hash = "sha256:0e1c2cdfcbc354b76e4a211a35ea35d6926a835cba1377073c4861db904a1877"}, + {file = "googleapis-common-protos-1.63.2.tar.gz", hash = "sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87"}, + {file = "googleapis_common_protos-1.63.2-py2.py3-none-any.whl", hash = "sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945"}, ] [package.dependencies] -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" [package.extras] grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] @@ -1000,149 +1005,78 @@ dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"] -[[package]] -name = "greenlet" -version = "3.0.3" -description = "Lightweight in-process concurrent programming" -optional = false -python-versions = ">=3.7" -files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, -] - -[package.extras] -docs = ["Sphinx", "furo"] -test = ["objgraph", "psutil"] - [[package]] name = "grpcio" -version = "1.64.1" +version = "1.65.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.64.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:55697ecec192bc3f2f3cc13a295ab670f51de29884ca9ae6cd6247df55df2502"}, - {file = "grpcio-1.64.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3b64ae304c175671efdaa7ec9ae2cc36996b681eb63ca39c464958396697daff"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:bac71b4b28bc9af61efcdc7630b166440bbfbaa80940c9a697271b5e1dabbc61"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c024ffc22d6dc59000faf8ad781696d81e8e38f4078cb0f2630b4a3cf231a90"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cd5c1325f6808b8ae31657d281aadb2a51ac11ab081ae335f4f7fc44c1721d"}, - {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0a2813093ddb27418a4c99f9b1c223fab0b053157176a64cc9db0f4557b69bd9"}, - {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2981c7365a9353f9b5c864595c510c983251b1ab403e05b1ccc70a3d9541a73b"}, - {file = "grpcio-1.64.1-cp310-cp310-win32.whl", hash = "sha256:1262402af5a511c245c3ae918167eca57342c72320dffae5d9b51840c4b2f86d"}, - {file = "grpcio-1.64.1-cp310-cp310-win_amd64.whl", hash = "sha256:19264fc964576ddb065368cae953f8d0514ecc6cb3da8903766d9fb9d4554c33"}, - {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, - {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, - {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, - {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, - {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, - {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, - {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, - {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, - {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, - {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, - {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, - {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, - {file = "grpcio-1.64.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:a011ac6c03cfe162ff2b727bcb530567826cec85eb8d4ad2bfb4bd023287a52d"}, - {file = "grpcio-1.64.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4d6dab6124225496010bd22690f2d9bd35c7cbb267b3f14e7a3eb05c911325d4"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a5e771d0252e871ce194d0fdcafd13971f1aae0ddacc5f25615030d5df55c3a2"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3c1b90ab93fed424e454e93c0ed0b9d552bdf1b0929712b094f5ecfe7a23ad"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20405cb8b13fd779135df23fabadc53b86522d0f1cba8cca0e87968587f50650"}, - {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cc79c982ccb2feec8aad0e8fb0d168bcbca85bc77b080d0d3c5f2f15c24ea8f"}, - {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a3a035c37ce7565b8f4f35ff683a4db34d24e53dc487e47438e434eb3f701b2a"}, - {file = "grpcio-1.64.1-cp38-cp38-win32.whl", hash = "sha256:1257b76748612aca0f89beec7fa0615727fd6f2a1ad580a9638816a4b2eb18fd"}, - {file = "grpcio-1.64.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a12ddb1678ebc6a84ec6b0487feac020ee2b1659cbe69b80f06dbffdb249122"}, - {file = "grpcio-1.64.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:75dbbf415026d2862192fe1b28d71f209e2fd87079d98470db90bebe57b33179"}, - {file = "grpcio-1.64.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e3d9f8d1221baa0ced7ec7322a981e28deb23749c76eeeb3d33e18b72935ab62"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f8b75f64d5d324c565b263c67dbe4f0af595635bbdd93bb1a88189fc62ed2e5"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84ad903d0d94311a2b7eea608da163dace97c5fe9412ea311e72c3684925602"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940e3ec884520155f68a3b712d045e077d61c520a195d1a5932c531f11883489"}, - {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f10193c69fc9d3d726e83bbf0f3d316f1847c3071c8c93d8090cf5f326b14309"}, - {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac15b6c2c80a4d1338b04d42a02d376a53395ddf0ec9ab157cbaf44191f3ffdd"}, - {file = "grpcio-1.64.1-cp39-cp39-win32.whl", hash = "sha256:03b43d0ccf99c557ec671c7dede64f023c7da9bb632ac65dbc57f166e4970040"}, - {file = "grpcio-1.64.1-cp39-cp39-win_amd64.whl", hash = "sha256:ed6091fa0adcc7e4ff944090cf203a52da35c37a130efa564ded02b7aff63bcd"}, - {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, + {file = "grpcio-1.65.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:3dc5f928815b8972fb83b78d8db5039559f39e004ec93ebac316403fe031a062"}, + {file = "grpcio-1.65.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:8333ca46053c35484c9f2f7e8d8ec98c1383a8675a449163cea31a2076d93de8"}, + {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7af64838b6e615fff0ec711960ed9b6ee83086edfa8c32670eafb736f169d719"}, + {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb64b4166362d9326f7efbf75b1c72106c1aa87f13a8c8b56a1224fac152f5c"}, + {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8422dc13ad93ec8caa2612b5032a2b9cd6421c13ed87f54db4a3a2c93afaf77"}, + {file = "grpcio-1.65.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4effc0562b6c65d4add6a873ca132e46ba5e5a46f07c93502c37a9ae7f043857"}, + {file = "grpcio-1.65.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a6c71575a2fedf259724981fd73a18906513d2f306169c46262a5bae956e6364"}, + {file = "grpcio-1.65.1-cp310-cp310-win32.whl", hash = "sha256:34966cf526ef0ea616e008d40d989463e3db157abb213b2f20c6ce0ae7928875"}, + {file = "grpcio-1.65.1-cp310-cp310-win_amd64.whl", hash = "sha256:ca931de5dd6d9eb94ff19a2c9434b23923bce6f767179fef04dfa991f282eaad"}, + {file = "grpcio-1.65.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:bbb46330cc643ecf10bd9bd4ca8e7419a14b6b9dedd05f671c90fb2c813c6037"}, + {file = "grpcio-1.65.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d827a6fb9215b961eb73459ad7977edb9e748b23e3407d21c845d1d8ef6597e5"}, + {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:6e71aed8835f8d9fbcb84babc93a9da95955d1685021cceb7089f4f1e717d719"}, + {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a1c84560b3b2d34695c9ba53ab0264e2802721c530678a8f0a227951f453462"}, + {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27adee2338d697e71143ed147fe286c05810965d5d30ec14dd09c22479bfe48a"}, + {file = "grpcio-1.65.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f62652ddcadc75d0e7aa629e96bb61658f85a993e748333715b4ab667192e4e8"}, + {file = "grpcio-1.65.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:71a05fd814700dd9cb7d9a507f2f6a1ef85866733ccaf557eedacec32d65e4c2"}, + {file = "grpcio-1.65.1-cp311-cp311-win32.whl", hash = "sha256:b590f1ad056294dfaeac0b7e1b71d3d5ace638d8dd1f1147ce4bd13458783ba8"}, + {file = "grpcio-1.65.1-cp311-cp311-win_amd64.whl", hash = "sha256:12e9bdf3b5fd48e5fbe5b3da382ad8f97c08b47969f3cca81dd9b36b86ed39e2"}, + {file = "grpcio-1.65.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:54cb822e177374b318b233e54b6856c692c24cdbd5a3ba5335f18a47396bac8f"}, + {file = "grpcio-1.65.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:aaf3c54419a28d45bd1681372029f40e5bfb58e5265e3882eaf21e4a5f81a119"}, + {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:557de35bdfbe8bafea0a003dbd0f4da6d89223ac6c4c7549d78e20f92ead95d9"}, + {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8bfd95ef3b097f0cc86ade54eafefa1c8ed623aa01a26fbbdcd1a3650494dd11"}, + {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e6a8f3d6c41e6b642870afe6cafbaf7b61c57317f9ec66d0efdaf19db992b90"}, + {file = "grpcio-1.65.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1faaf7355ceed07ceaef0b9dcefa4c98daf1dd8840ed75c2de128c3f4a4d859d"}, + {file = "grpcio-1.65.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:60f1f38eed830488ad2a1b11579ef0f345ff16fffdad1d24d9fbc97ba31804ff"}, + {file = "grpcio-1.65.1-cp312-cp312-win32.whl", hash = "sha256:e75acfa52daf5ea0712e8aa82f0003bba964de7ae22c26d208cbd7bc08500177"}, + {file = "grpcio-1.65.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff5a84907e51924973aa05ed8759210d8cdae7ffcf9e44fd17646cf4a902df59"}, + {file = "grpcio-1.65.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:1fbd6331f18c3acd7e09d17fd840c096f56eaf0ef830fbd50af45ae9dc8dfd83"}, + {file = "grpcio-1.65.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:de5b6be29116e094c5ef9d9e4252e7eb143e3d5f6bd6d50a78075553ab4930b0"}, + {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:e4a3cdba62b2d6aeae6027ae65f350de6dc082b72e6215eccf82628e79efe9ba"}, + {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941c4869aa229d88706b78187d60d66aca77fe5c32518b79e3c3e03fc26109a2"}, + {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f40cebe5edb518d78b8131e87cb83b3ee688984de38a232024b9b44e74ee53d3"}, + {file = "grpcio-1.65.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2ca684ba331fb249d8a1ce88db5394e70dbcd96e58d8c4b7e0d7b141a453dce9"}, + {file = "grpcio-1.65.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8558f0083ddaf5de64a59c790bffd7568e353914c0c551eae2955f54ee4b857f"}, + {file = "grpcio-1.65.1-cp38-cp38-win32.whl", hash = "sha256:8d8143a3e3966f85dce6c5cc45387ec36552174ba5712c5dc6fcc0898fb324c0"}, + {file = "grpcio-1.65.1-cp38-cp38-win_amd64.whl", hash = "sha256:76e81a86424d6ca1ce7c16b15bdd6a964a42b40544bf796a48da241fdaf61153"}, + {file = "grpcio-1.65.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:cb5175f45c980ff418998723ea1b3869cce3766d2ab4e4916fbd3cedbc9d0ed3"}, + {file = "grpcio-1.65.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b12c1aa7b95abe73b3e04e052c8b362655b41c7798da69f1eaf8d186c7d204df"}, + {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:3019fb50128b21a5e018d89569ffaaaa361680e1346c2f261bb84a91082eb3d3"}, + {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ae15275ed98ea267f64ee9ddedf8ecd5306a5b5bb87972a48bfe24af24153e8"}, + {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f096ffb881f37e8d4f958b63c74bfc400c7cebd7a944b027357cd2fb8d91a57"}, + {file = "grpcio-1.65.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2f56b5a68fdcf17a0a1d524bf177218c3c69b3947cb239ea222c6f1867c3ab68"}, + {file = "grpcio-1.65.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:941596d419b9736ab548aa0feb5bbba922f98872668847bf0720b42d1d227b9e"}, + {file = "grpcio-1.65.1-cp39-cp39-win32.whl", hash = "sha256:5fd7337a823b890215f07d429f4f193d24b80d62a5485cf88ee06648591a0c57"}, + {file = "grpcio-1.65.1-cp39-cp39-win_amd64.whl", hash = "sha256:1bceeec568372cbebf554eae1b436b06c2ff24cfaf04afade729fb9035408c6c"}, + {file = "grpcio-1.65.1.tar.gz", hash = "sha256:3c492301988cd720cd145d84e17318d45af342e29ef93141228f9cd73222368b"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.64.1)"] +protobuf = ["grpcio-tools (>=1.65.1)"] [[package]] name = "grpcio-status" -version = "1.64.1" +version = "1.65.1" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio_status-1.64.1-py3-none-any.whl", hash = "sha256:2ec6e0777958831484a517e32b6ffe0a4272242eae81bff2f5c3707fa58b40e3"}, - {file = "grpcio_status-1.64.1.tar.gz", hash = "sha256:c50bd14eb6506d8580a6c553bea463d7c08499b2c0e93f6d1864c5e8eabb1066"}, + {file = "grpcio_status-1.65.1-py3-none-any.whl", hash = "sha256:0ec2070f7dbcc2fe78a7b34233a2a00f8ced727d2f1dec1af422d628cf86b92c"}, + {file = "grpcio_status-1.65.1.tar.gz", hash = "sha256:740d68d4a1824e59063f394df05171886262d5367b82256d54aac8aa7c5c79bf"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.64.1" +grpcio = ">=1.65.1" protobuf = ">=5.26.1,<6.0dev" [[package]] @@ -1180,12 +1114,12 @@ numpy = ">=1.17.3" [[package]] name = "ibm-cloud-sdk-core" -version = "3.20.0" +version = "3.20.3" description = "Core library used by SDKs for IBM Cloud Services" optional = false python-versions = "*" files = [ - {file = "ibm-cloud-sdk-core-3.20.0.tar.gz", hash = "sha256:0aa6d97043f589a9ef451a71e76eca0634138ef181ce54a6a0a6353dee4c5d10"}, + {file = "ibm-cloud-sdk-core-3.20.3.tar.gz", hash = "sha256:1f95d2dfe4140c259ac24611b2e7f5e95370d01eaa2d5be129d06ca2c99c7e8a"}, ] [package.dependencies] @@ -1196,16 +1130,16 @@ urllib3 = ">=2.1.0,<3.0.0" [[package]] name = "ibm-platform-services" -version = "0.53.7" +version = "0.55.1" description = "Python client library for IBM Cloud Platform Services" optional = false python-versions = "*" files = [ - {file = "ibm-platform-services-0.53.7.tar.gz", hash = "sha256:1f1dd0b282757be546eb39c470417b99a9fc3184cdc245872c5b1e8346113e39"}, + {file = "ibm-platform-services-0.55.1.tar.gz", hash = "sha256:2554683df328bd490002dbd1fb5ff5c30a134183562041bfa352300ed3ca64a9"}, ] [package.dependencies] -ibm_cloud_sdk_core = ">=3.19.2,<4.0.0" +ibm_cloud_sdk_core = ">=3.20.3,<4.0.0" [[package]] name = "idna" @@ -1229,25 +1163,6 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -[[package]] -name = "importlib-metadata" -version = "7.1.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, - {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] - [[package]] name = "iniconfig" version = "2.0.0" @@ -1261,13 +1176,13 @@ files = [ [[package]] name = "ipykernel" -version = "6.29.4" +version = "6.29.5" description = "IPython Kernel for Jupyter" optional = false python-versions = ">=3.8" files = [ - {file = "ipykernel-6.29.4-py3-none-any.whl", hash = "sha256:1181e653d95c6808039c509ef8e67c4126b3b3af7781496c7cbfb5ed938a27da"}, - {file = "ipykernel-6.29.4.tar.gz", hash = "sha256:3d44070060f9475ac2092b760123fadf105d2e2493c24848b6691a7c4f42af5c"}, + {file = "ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5"}, + {file = "ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215"}, ] [package.dependencies] @@ -1326,13 +1241,13 @@ test = ["ipython[test]", "pytest", "pytest-asyncio", "pytest-cov", "testpath"] [[package]] name = "ipython" -version = "8.25.0" +version = "8.26.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" files = [ - {file = "ipython-8.25.0-py3-none-any.whl", hash = "sha256:53eee7ad44df903a06655871cbab66d156a051fd86f3ec6750470ac9604ac1ab"}, - {file = "ipython-8.25.0.tar.gz", hash = "sha256:c6ed726a140b6e725b911528f80439c534fac915246af3efc39440a6b0f9d716"}, + {file = "ipython-8.26.0-py3-none-any.whl", hash = "sha256:e6b347c27bdf9c32ee9d31ae85defc525755a1869f14057e900675b9e8d6e6ff"}, + {file = "ipython-8.26.0.tar.gz", hash = "sha256:1cec0fbba8404af13facebe83d04436a7434c7400e59f47acf467c64abd0956c"}, ] [package.dependencies] @@ -1359,7 +1274,7 @@ nbformat = ["nbformat"] notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] +test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] [[package]] @@ -1385,80 +1300,75 @@ test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] [[package]] name = "jax" -version = "0.4.28" +version = "0.4.30" description = "Differentiate, compile, and transform Numpy code." optional = false python-versions = ">=3.9" files = [ - {file = "jax-0.4.28-py3-none-any.whl", hash = "sha256:6a181e6b5a5b1140e19cdd2d5c4aa779e4cb4ec627757b918be322d8e81035ba"}, - {file = "jax-0.4.28.tar.gz", hash = "sha256:dcf0a44aff2e1713f0a2b369281cd5b79d8c18fc1018905c4125897cb06b37e9"}, + {file = "jax-0.4.30-py3-none-any.whl", hash = "sha256:289b30ae03b52f7f4baf6ef082a9f4e3e29c1080e22d13512c5ecf02d5f1a55b"}, + {file = "jax-0.4.30.tar.gz", hash = "sha256:94d74b5b2db0d80672b61d83f1f63ebf99d2ab7398ec12b2ca0c9d1e97afe577"}, ] [package.dependencies] +jaxlib = ">=0.4.27,<=0.4.30" ml-dtypes = ">=0.2.0" numpy = [ + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.22", markers = "python_version < \"3.11\""}, {version = ">=1.23.2", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] opt-einsum = "*" scipy = [ - {version = ">=1.9", markers = "python_version < \"3.12\""}, {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, + {version = ">=1.9", markers = "python_version < \"3.12\""}, ] [package.extras] -australis = ["protobuf (>=3.13,<4)"] -ci = ["jaxlib (==0.4.27)"] -cpu = ["jaxlib (==0.4.28)"] -cuda = ["jaxlib (==0.4.28+cuda12.cudnn89)"] -cuda12 = ["jax-cuda12-plugin (==0.4.28)", "jaxlib (==0.4.28)", "nvidia-cublas-cu12 (>=12.1.3.1)", "nvidia-cuda-cupti-cu12 (>=12.1.105)", "nvidia-cuda-nvcc-cu12 (>=12.1.105)", "nvidia-cuda-runtime-cu12 (>=12.1.105)", "nvidia-cudnn-cu12 (>=8.9.2.26,<9.0)", "nvidia-cufft-cu12 (>=11.0.2.54)", "nvidia-cusolver-cu12 (>=11.4.5.107)", "nvidia-cusparse-cu12 (>=12.1.0.106)", "nvidia-nccl-cu12 (>=2.18.1)", "nvidia-nvjitlink-cu12 (>=12.1.105)"] -cuda12-cudnn89 = ["jaxlib (==0.4.28+cuda12.cudnn89)"] -cuda12-local = ["jaxlib (==0.4.28+cuda12.cudnn89)"] -cuda12-pip = ["jaxlib (==0.4.28+cuda12.cudnn89)", "nvidia-cublas-cu12 (>=12.1.3.1)", "nvidia-cuda-cupti-cu12 (>=12.1.105)", "nvidia-cuda-nvcc-cu12 (>=12.1.105)", "nvidia-cuda-runtime-cu12 (>=12.1.105)", "nvidia-cudnn-cu12 (>=8.9.2.26,<9.0)", "nvidia-cufft-cu12 (>=11.0.2.54)", "nvidia-cusolver-cu12 (>=11.4.5.107)", "nvidia-cusparse-cu12 (>=12.1.0.106)", "nvidia-nccl-cu12 (>=2.18.1)", "nvidia-nvjitlink-cu12 (>=12.1.105)"] +ci = ["jaxlib (==0.4.29)"] +cuda = ["jax-cuda12-plugin[with-cuda] (==0.4.30)", "jaxlib (==0.4.30)"] +cuda12 = ["jax-cuda12-plugin[with-cuda] (==0.4.30)", "jaxlib (==0.4.30)"] +cuda12-local = ["jax-cuda12-plugin (==0.4.30)", "jaxlib (==0.4.30)"] +cuda12-pip = ["jax-cuda12-plugin[with-cuda] (==0.4.30)", "jaxlib (==0.4.30)"] minimum-jaxlib = ["jaxlib (==0.4.27)"] -tpu = ["jaxlib (==0.4.28)", "libtpu-nightly (==0.1.dev20240508)", "requests"] +tpu = ["jaxlib (==0.4.30)", "libtpu-nightly (==0.1.dev20240617)", "requests"] [[package]] name = "jaxlib" -version = "0.4.28" +version = "0.4.30" description = "XLA library for JAX" optional = false python-versions = ">=3.9" files = [ - {file = "jaxlib-0.4.28-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:a421d237f8c25d2850166d334603c673ddb9b6c26f52bc496704b8782297bd66"}, - {file = "jaxlib-0.4.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f038e68bd10d1a3554722b0bbe36e6a448384437a75aa9d283f696f0ed9f8c09"}, - {file = "jaxlib-0.4.28-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:fabe77c174e9e196e9373097cefbb67e00c7e5f9d864583a7cfcf9dabd2429b6"}, - {file = "jaxlib-0.4.28-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:e3bcdc6f8e60f8554f415c14d930134e602e3ca33c38e546274fd545f875769b"}, - {file = "jaxlib-0.4.28-cp310-cp310-win_amd64.whl", hash = "sha256:a8b31c0e5eea36b7915696b9be40ea8646edc395a3e5437bf7ef26b7239a567a"}, - {file = "jaxlib-0.4.28-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:2ff8290edc7b92c7eae52517f65492633e267b2e9067bad3e4c323d213e77cf5"}, - {file = "jaxlib-0.4.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:793857faf37f371cafe752fea5fc811f435e43b8fb4b502058444a7f5eccf829"}, - {file = "jaxlib-0.4.28-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b41a6b0d506c09f86a18ecc05bd376f072b548af89c333107e49bb0c09c1a3f8"}, - {file = "jaxlib-0.4.28-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:45ce0f3c840cff8236cff26c37f26c9ff078695f93e0c162c320c281f5041275"}, - {file = "jaxlib-0.4.28-cp311-cp311-win_amd64.whl", hash = "sha256:d4d762c3971d74e610a0e85a7ee063cea81a004b365b2a7dc65133f08b04fac5"}, - {file = "jaxlib-0.4.28-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:d6c09a545329722461af056e735146d2c8c74c22ac7426a845eb69f326b4f7a0"}, - {file = "jaxlib-0.4.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8dd8bffe3853702f63cd924da0ee25734a4d19cd5c926be033d772ba7d1c175d"}, - {file = "jaxlib-0.4.28-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:de2e8521eb51e16e85093a42cb51a781773fa1040dcf9245d7ea160a14ee5a5b"}, - {file = "jaxlib-0.4.28-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:46a1aa857f4feee8a43fcba95c0e0ab62d40c26cc9730b6c69655908ba359f8d"}, - {file = "jaxlib-0.4.28-cp312-cp312-win_amd64.whl", hash = "sha256:eee428eac31697a070d655f1f24f6ab39ced76750d93b1de862377a52dcc2401"}, - {file = "jaxlib-0.4.28-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:4f98cc837b2b6c6dcfe0ab7ff9eb109314920946119aa3af9faa139718ff2787"}, - {file = "jaxlib-0.4.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b01562ec8ad75719b7d0389752489e97eb6b4dcb4c8c113be491634d5282ad3c"}, - {file = "jaxlib-0.4.28-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:aa77a9360a395ba9faf6932df637686fb0c14ddcf4fdc1d2febe04bc88a580a6"}, - {file = "jaxlib-0.4.28-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:4a56ebf05b4a4c1791699d874e072f3f808f0986b4010b14fb549a69c90ca9dc"}, - {file = "jaxlib-0.4.28-cp39-cp39-win_amd64.whl", hash = "sha256:459a4ddcc3e120904b9f13a245430d7801d707bca48925981cbdc59628057dc8"}, + {file = "jaxlib-0.4.30-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:c40856e28f300938c6824ab1a615166193d6997dec946578823f6d402ad454e5"}, + {file = "jaxlib-0.4.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4bdfda6a3c7a2b0cc0a7131009eb279e98ca4a6f25679fabb5302dd135a5e349"}, + {file = "jaxlib-0.4.30-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:28e032c9b394ab7624d89b0d9d3bbcf4d1d71694fe8b3e09d3fe64122eda7b0c"}, + {file = "jaxlib-0.4.30-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:d83f36ef42a403bbf7c7f2da526b34ba286988e170f4df5e58b3bb735417868c"}, + {file = "jaxlib-0.4.30-cp310-cp310-win_amd64.whl", hash = "sha256:a56678b28f96b524ded6da8ef4b38e72a532356d139cfd434da804abf4234e14"}, + {file = "jaxlib-0.4.30-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:bfb5d85b69c29c3c6e8051a0ea715ac1e532d6e54494c8d9c3813dcc00deac30"}, + {file = "jaxlib-0.4.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:974998cd8a78550402e6c09935c1f8d850cad9cc19ccd7488bde45b6f7f99c12"}, + {file = "jaxlib-0.4.30-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:e93eb0646b41ba213252b51b0b69096b9cd1d81a35ea85c9d06663b5d11efe45"}, + {file = "jaxlib-0.4.30-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:16b2ab18ea90d2e15941bcf45de37afc2f289a029129c88c8d7aba0404dd0043"}, + {file = "jaxlib-0.4.30-cp311-cp311-win_amd64.whl", hash = "sha256:3a2e2c11c179f8851a72249ba1ae40ae817dfaee9877d23b3b8f7c6b7a012f76"}, + {file = "jaxlib-0.4.30-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:7704db5962b32a2be3cc07185433cbbcc94ed90ee50c84021a3f8a1ecfd66ee3"}, + {file = "jaxlib-0.4.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:57090d33477fd0f0c99dc686274882ea75c44c7d712ae42dd2460b10f896131d"}, + {file = "jaxlib-0.4.30-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:0a3850e76278038e21685975a62b622bcf3708485f13125757a0561ee4512940"}, + {file = "jaxlib-0.4.30-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:c58a8071c4e00898282118169f6a5a97eb15a79c2897858f3a732b17891c99ab"}, + {file = "jaxlib-0.4.30-cp312-cp312-win_amd64.whl", hash = "sha256:b7079a5b1ab6864a7d4f2afaa963841451186d22c90f39719a3ff85735ce3915"}, + {file = "jaxlib-0.4.30-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:ea3a00005faafbe3c18b178d3b534208b3b4027b2be6230227e7b87ce399fc29"}, + {file = "jaxlib-0.4.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d31e01191ce8052bd611aaf16ff967d8d0ec0b63f1ea4b199020cecb248d667"}, + {file = "jaxlib-0.4.30-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:11602d5556e8baa2f16314c36518e9be4dfae0c2c256a361403fb29dc9dc79a4"}, + {file = "jaxlib-0.4.30-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:f74a6b0e09df4b5e2ee399ebb9f0e01190e26e84ccb0a758fadb516415c07f18"}, + {file = "jaxlib-0.4.30-cp39-cp39-win_amd64.whl", hash = "sha256:54987e97a22db70f3829b437b9329e4799d653634bacc8b398554d3b90c76b2a"}, ] [package.dependencies] ml-dtypes = ">=0.2.0" numpy = ">=1.22" scipy = [ - {version = ">=1.9", markers = "python_version < \"3.12\""}, {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, + {version = ">=1.9", markers = "python_version < \"3.12\""}, ] -[package.extras] -cuda12-pip = ["nvidia-cublas-cu12 (>=12.1.3.1)", "nvidia-cuda-cupti-cu12 (>=12.1.105)", "nvidia-cuda-nvcc-cu12 (>=12.1.105)", "nvidia-cuda-runtime-cu12 (>=12.1.105)", "nvidia-cudnn-cu12 (>=8.9.2.26,<9.0)", "nvidia-cufft-cu12 (>=11.0.2.54)", "nvidia-cusolver-cu12 (>=11.4.5.107)", "nvidia-cusparse-cu12 (>=12.1.0.106)", "nvidia-nccl-cu12 (>=2.18.1)", "nvidia-nvjitlink-cu12 (>=12.1.105)"] - [[package]] name = "jedi" version = "0.19.1" @@ -1497,13 +1407,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jsonschema" -version = "4.22.0" +version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.22.0-py3-none-any.whl", hash = "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802"}, - {file = "jsonschema-4.22.0.tar.gz", hash = "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7"}, + {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, + {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, ] [package.dependencies] @@ -1514,7 +1424,7 @@ rpds-py = ">=0.7.1" [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] [[package]] name = "jsonschema-specifications" @@ -1530,33 +1440,6 @@ files = [ [package.dependencies] referencing = ">=0.31.0" -[[package]] -name = "jupyter-cache" -version = "1.0.0" -description = "A defined interface for working with a cache of jupyter notebooks." -optional = false -python-versions = ">=3.9" -files = [ - {file = "jupyter_cache-1.0.0-py3-none-any.whl", hash = "sha256:594b1c4e29b488b36547e12477645f489dbdc62cc939b2408df5679f79245078"}, - {file = "jupyter_cache-1.0.0.tar.gz", hash = "sha256:d0fa7d7533cd5798198d8889318269a8c1382ed3b22f622c09a9356521f48687"}, -] - -[package.dependencies] -attrs = "*" -click = "*" -importlib-metadata = "*" -nbclient = ">=0.2" -nbformat = "*" -pyyaml = "*" -sqlalchemy = ">=1.3.12,<3" -tabulate = "*" - -[package.extras] -cli = ["click-log"] -code-style = ["pre-commit (>=2.12)"] -rtd = ["ipykernel", "jupytext", "myst-nb", "nbdime", "sphinx-book-theme", "sphinx-copybutton"] -testing = ["coverage", "ipykernel", "jupytext", "matplotlib", "nbdime", "nbformat (>=5.1)", "numpy", "pandas", "pytest (>=6,<8)", "pytest-cov", "pytest-regressions", "sympy"] - [[package]] name = "jupyter-client" version = "8.6.2" @@ -1798,58 +1681,34 @@ regex = ["regex"] [[package]] name = "llvmlite" -version = "0.42.0" +version = "0.43.0" description = "lightweight wrapper around basic LLVM functionality" optional = false python-versions = ">=3.9" files = [ - {file = "llvmlite-0.42.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3366938e1bf63d26c34fbfb4c8e8d2ded57d11e0567d5bb243d89aab1eb56098"}, - {file = "llvmlite-0.42.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c35da49666a21185d21b551fc3caf46a935d54d66969d32d72af109b5e7d2b6f"}, - {file = "llvmlite-0.42.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70f44ccc3c6220bd23e0ba698a63ec2a7d3205da0d848804807f37fc243e3f77"}, - {file = "llvmlite-0.42.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:763f8d8717a9073b9e0246998de89929071d15b47f254c10eef2310b9aac033d"}, - {file = "llvmlite-0.42.0-cp310-cp310-win_amd64.whl", hash = "sha256:8d90edf400b4ceb3a0e776b6c6e4656d05c7187c439587e06f86afceb66d2be5"}, - {file = "llvmlite-0.42.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ae511caed28beaf1252dbaf5f40e663f533b79ceb408c874c01754cafabb9cbf"}, - {file = "llvmlite-0.42.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81e674c2fe85576e6c4474e8c7e7aba7901ac0196e864fe7985492b737dbab65"}, - {file = "llvmlite-0.42.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb3975787f13eb97629052edb5017f6c170eebc1c14a0433e8089e5db43bcce6"}, - {file = "llvmlite-0.42.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5bece0cdf77f22379f19b1959ccd7aee518afa4afbd3656c6365865f84903f9"}, - {file = "llvmlite-0.42.0-cp311-cp311-win_amd64.whl", hash = "sha256:7e0c4c11c8c2aa9b0701f91b799cb9134a6a6de51444eff5a9087fc7c1384275"}, - {file = "llvmlite-0.42.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:08fa9ab02b0d0179c688a4216b8939138266519aaa0aa94f1195a8542faedb56"}, - {file = "llvmlite-0.42.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b2fce7d355068494d1e42202c7aff25d50c462584233013eb4470c33b995e3ee"}, - {file = "llvmlite-0.42.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebe66a86dc44634b59a3bc860c7b20d26d9aaffcd30364ebe8ba79161a9121f4"}, - {file = "llvmlite-0.42.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d47494552559e00d81bfb836cf1c4d5a5062e54102cc5767d5aa1e77ccd2505c"}, - {file = "llvmlite-0.42.0-cp312-cp312-win_amd64.whl", hash = "sha256:05cb7e9b6ce69165ce4d1b994fbdedca0c62492e537b0cc86141b6e2c78d5888"}, - {file = "llvmlite-0.42.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bdd3888544538a94d7ec99e7c62a0cdd8833609c85f0c23fcb6c5c591aec60ad"}, - {file = "llvmlite-0.42.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d0936c2067a67fb8816c908d5457d63eba3e2b17e515c5fe00e5ee2bace06040"}, - {file = "llvmlite-0.42.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a78ab89f1924fc11482209f6799a7a3fc74ddc80425a7a3e0e8174af0e9e2301"}, - {file = "llvmlite-0.42.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7599b65c7af7abbc978dbf345712c60fd596aa5670496561cc10e8a71cebfb2"}, - {file = "llvmlite-0.42.0-cp39-cp39-win_amd64.whl", hash = "sha256:43d65cc4e206c2e902c1004dd5418417c4efa6c1d04df05c6c5675a27e8ca90e"}, - {file = "llvmlite-0.42.0.tar.gz", hash = "sha256:f92b09243c0cc3f457da8b983f67bd8e1295d0f5b3746c7a1861d7a99403854a"}, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -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"}, + {file = "llvmlite-0.43.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a289af9a1687c6cf463478f0fa8e8aa3b6fb813317b0d70bf1ed0759eab6f761"}, + {file = "llvmlite-0.43.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d4fd101f571a31acb1559ae1af30f30b1dc4b3186669f92ad780e17c81e91bc"}, + {file = "llvmlite-0.43.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d434ec7e2ce3cc8f452d1cd9a28591745de022f931d67be688a737320dfcead"}, + {file = "llvmlite-0.43.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6912a87782acdff6eb8bf01675ed01d60ca1f2551f8176a300a886f09e836a6a"}, + {file = "llvmlite-0.43.0-cp310-cp310-win_amd64.whl", hash = "sha256:14f0e4bf2fd2d9a75a3534111e8ebeb08eda2f33e9bdd6dfa13282afacdde0ed"}, + {file = "llvmlite-0.43.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8d0618cb9bfe40ac38a9633f2493d4d4e9fcc2f438d39a4e854f39cc0f5f98"}, + {file = "llvmlite-0.43.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0a9a1a39d4bf3517f2af9d23d479b4175ead205c592ceeb8b89af48a327ea57"}, + {file = "llvmlite-0.43.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1da416ab53e4f7f3bc8d4eeba36d801cc1894b9fbfbf2022b29b6bad34a7df2"}, + {file = "llvmlite-0.43.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977525a1e5f4059316b183fb4fd34fa858c9eade31f165427a3977c95e3ee749"}, + {file = "llvmlite-0.43.0-cp311-cp311-win_amd64.whl", hash = "sha256:d5bd550001d26450bd90777736c69d68c487d17bf371438f975229b2b8241a91"}, + {file = "llvmlite-0.43.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f99b600aa7f65235a5a05d0b9a9f31150c390f31261f2a0ba678e26823ec38f7"}, + {file = "llvmlite-0.43.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:35d80d61d0cda2d767f72de99450766250560399edc309da16937b93d3b676e7"}, + {file = "llvmlite-0.43.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eccce86bba940bae0d8d48ed925f21dbb813519169246e2ab292b5092aba121f"}, + {file = "llvmlite-0.43.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df6509e1507ca0760787a199d19439cc887bfd82226f5af746d6977bd9f66844"}, + {file = "llvmlite-0.43.0-cp312-cp312-win_amd64.whl", hash = "sha256:7a2872ee80dcf6b5dbdc838763d26554c2a18aa833d31a2635bff16aafefb9c9"}, + {file = "llvmlite-0.43.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cd2a7376f7b3367019b664c21f0c61766219faa3b03731113ead75107f3b66c"}, + {file = "llvmlite-0.43.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18e9953c748b105668487b7c81a3e97b046d8abf95c4ddc0cd3c94f4e4651ae8"}, + {file = "llvmlite-0.43.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74937acd22dc11b33946b67dca7680e6d103d6e90eeaaaf932603bec6fe7b03a"}, + {file = "llvmlite-0.43.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9efc739cc6ed760f795806f67889923f7274276f0eb45092a1473e40d9b867"}, + {file = "llvmlite-0.43.0-cp39-cp39-win_amd64.whl", hash = "sha256:47e147cdda9037f94b399bf03bfd8a6b6b1f2f90be94a454e3386f006455a9b4"}, + {file = "llvmlite-0.43.0.tar.gz", hash = "sha256:ae2b5b5c3ef67354824fb75517c8db5fbe93bc02cd9671f3c62271626bc041d5"}, ] -[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" @@ -1921,40 +1780,40 @@ files = [ [[package]] name = "matplotlib" -version = "3.9.0" +version = "3.9.1" description = "Python plotting package" optional = false python-versions = ">=3.9" files = [ - {file = "matplotlib-3.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2bcee1dffaf60fe7656183ac2190bd630842ff87b3153afb3e384d966b57fe56"}, - {file = "matplotlib-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f988bafb0fa39d1074ddd5bacd958c853e11def40800c5824556eb630f94d3b"}, - {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe428e191ea016bb278758c8ee82a8129c51d81d8c4bc0846c09e7e8e9057241"}, - {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf3978060a106fab40c328778b148f590e27f6fa3cd15a19d6892575bce387d"}, - {file = "matplotlib-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e7f03e5cbbfacdd48c8ea394d365d91ee8f3cae7e6ec611409927b5ed997ee4"}, - {file = "matplotlib-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:13beb4840317d45ffd4183a778685e215939be7b08616f431c7795276e067463"}, - {file = "matplotlib-3.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:063af8587fceeac13b0936c42a2b6c732c2ab1c98d38abc3337e430e1ff75e38"}, - {file = "matplotlib-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a2fa6d899e17ddca6d6526cf6e7ba677738bf2a6a9590d702c277204a7c6152"}, - {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550cdda3adbd596078cca7d13ed50b77879104e2e46392dcd7c75259d8f00e85"}, - {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cce0f31b351e3551d1f3779420cf8f6ec0d4a8cf9c0237a3b549fd28eb4abb"}, - {file = "matplotlib-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c53aeb514ccbbcbab55a27f912d79ea30ab21ee0531ee2c09f13800efb272674"}, - {file = "matplotlib-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5be985db2596d761cdf0c2eaf52396f26e6a64ab46bd8cd810c48972349d1be"}, - {file = "matplotlib-3.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c79f3a585f1368da6049318bdf1f85568d8d04b2e89fc24b7e02cc9b62017382"}, - {file = "matplotlib-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdd1ecbe268eb3e7653e04f451635f0fb0f77f07fd070242b44c076c9106da84"}, - {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e85a1a6d732f645f1403ce5e6727fd9418cd4574521d5803d3d94911038e5"}, - {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a490715b3b9984fa609116481b22178348c1a220a4499cda79132000a79b4db"}, - {file = "matplotlib-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8146ce83cbc5dc71c223a74a1996d446cd35cfb6a04b683e1446b7e6c73603b7"}, - {file = "matplotlib-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:d91a4ffc587bacf5c4ce4ecfe4bcd23a4b675e76315f2866e588686cc97fccdf"}, - {file = "matplotlib-3.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:616fabf4981a3b3c5a15cd95eba359c8489c4e20e03717aea42866d8d0465956"}, - {file = "matplotlib-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd53c79fd02f1c1808d2cfc87dd3cf4dbc63c5244a58ee7944497107469c8d8a"}, - {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06a478f0d67636554fa78558cfbcd7b9dba85b51f5c3b5a0c9be49010cf5f321"}, - {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81c40af649d19c85f8073e25e5806926986806fa6d54be506fbf02aef47d5a89"}, - {file = "matplotlib-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52146fc3bd7813cc784562cb93a15788be0b2875c4655e2cc6ea646bfa30344b"}, - {file = "matplotlib-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:0fc51eaa5262553868461c083d9adadb11a6017315f3a757fc45ec6ec5f02888"}, - {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bd4f2831168afac55b881db82a7730992aa41c4f007f1913465fb182d6fb20c0"}, - {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:290d304e59be2b33ef5c2d768d0237f5bd132986bdcc66f80bc9bcc300066a03"}, - {file = "matplotlib-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff2e239c26be4f24bfa45860c20ffccd118d270c5b5d081fa4ea409b5469fcd"}, - {file = "matplotlib-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:af4001b7cae70f7eaacfb063db605280058246de590fa7874f00f62259f2df7e"}, - {file = "matplotlib-3.9.0.tar.gz", hash = "sha256:e6d29ea6c19e34b30fb7d88b7081f869a03014f66fe06d62cc77d5a6ea88ed7a"}, + {file = "matplotlib-3.9.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7ccd6270066feb9a9d8e0705aa027f1ff39f354c72a87efe8fa07632f30fc6bb"}, + {file = "matplotlib-3.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:591d3a88903a30a6d23b040c1e44d1afdd0d778758d07110eb7596f811f31842"}, + {file = "matplotlib-3.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd2a59ff4b83d33bca3b5ec58203cc65985367812cb8c257f3e101632be86d92"}, + {file = "matplotlib-3.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fc001516ffcf1a221beb51198b194d9230199d6842c540108e4ce109ac05cc0"}, + {file = "matplotlib-3.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:83c6a792f1465d174c86d06f3ae85a8fe36e6f5964633ae8106312ec0921fdf5"}, + {file = "matplotlib-3.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:421851f4f57350bcf0811edd754a708d2275533e84f52f6760b740766c6747a7"}, + {file = "matplotlib-3.9.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b3fce58971b465e01b5c538f9d44915640c20ec5ff31346e963c9e1cd66fa812"}, + {file = "matplotlib-3.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a973c53ad0668c53e0ed76b27d2eeeae8799836fd0d0caaa4ecc66bf4e6676c0"}, + {file = "matplotlib-3.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82cd5acf8f3ef43f7532c2f230249720f5dc5dd40ecafaf1c60ac8200d46d7eb"}, + {file = "matplotlib-3.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab38a4f3772523179b2f772103d8030215b318fef6360cb40558f585bf3d017f"}, + {file = "matplotlib-3.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2315837485ca6188a4b632c5199900e28d33b481eb083663f6a44cfc8987ded3"}, + {file = "matplotlib-3.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0c977c5c382f6696caf0bd277ef4f936da7e2aa202ff66cad5f0ac1428ee15b"}, + {file = "matplotlib-3.9.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:565d572efea2b94f264dd86ef27919515aa6d629252a169b42ce5f570db7f37b"}, + {file = "matplotlib-3.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d397fd8ccc64af2ec0af1f0efc3bacd745ebfb9d507f3f552e8adb689ed730a"}, + {file = "matplotlib-3.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26040c8f5121cd1ad712abffcd4b5222a8aec3a0fe40bc8542c94331deb8780d"}, + {file = "matplotlib-3.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12cb1837cffaac087ad6b44399d5e22b78c729de3cdae4629e252067b705e2b"}, + {file = "matplotlib-3.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0e835c6988edc3d2d08794f73c323cc62483e13df0194719ecb0723b564e0b5c"}, + {file = "matplotlib-3.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:44a21d922f78ce40435cb35b43dd7d573cf2a30138d5c4b709d19f00e3907fd7"}, + {file = "matplotlib-3.9.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0c584210c755ae921283d21d01f03a49ef46d1afa184134dd0f95b0202ee6f03"}, + {file = "matplotlib-3.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11fed08f34fa682c2b792942f8902e7aefeed400da71f9e5816bea40a7ce28fe"}, + {file = "matplotlib-3.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0000354e32efcfd86bda75729716b92f5c2edd5b947200be9881f0a671565c33"}, + {file = "matplotlib-3.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4db17fea0ae3aceb8e9ac69c7e3051bae0b3d083bfec932240f9bf5d0197a049"}, + {file = "matplotlib-3.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:208cbce658b72bf6a8e675058fbbf59f67814057ae78165d8a2f87c45b48d0ff"}, + {file = "matplotlib-3.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:dc23f48ab630474264276be156d0d7710ac6c5a09648ccdf49fef9200d8cbe80"}, + {file = "matplotlib-3.9.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3fda72d4d472e2ccd1be0e9ccb6bf0d2eaf635e7f8f51d737ed7e465ac020cb3"}, + {file = "matplotlib-3.9.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:84b3ba8429935a444f1fdc80ed930babbe06725bcf09fbeb5c8757a2cd74af04"}, + {file = "matplotlib-3.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b918770bf3e07845408716e5bbda17eadfc3fcbd9307dc67f37d6cf834bb3d98"}, + {file = "matplotlib-3.9.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f1f2e5d29e9435c97ad4c36fb6668e89aee13d48c75893e25cef064675038ac9"}, + {file = "matplotlib-3.9.1.tar.gz", hash = "sha256:de06b19b8db95dd33d0dc17c926c7c9ebed9f572074b6fac4f65068a6814d010"}, ] [package.dependencies] @@ -1985,36 +1844,6 @@ files = [ [package.dependencies] traitlets = "*" -[[package]] -name = "mdit-py-plugins" -version = "0.4.1" -description = "Collection of plugins for markdown-it-py" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, - {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, -] - -[package.dependencies] -markdown-it-py = ">=1.0.0,<4.0.0" - -[package.extras] -code-style = ["pre-commit"] -rtd = ["myst-parser", "sphinx-book-theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -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 = "mistune" version = "3.0.2" @@ -2054,9 +1883,9 @@ files = [ [package.dependencies] numpy = [ + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.21.2", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, {version = ">=1.23.3", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] [package.extras] @@ -2079,60 +1908,6 @@ docs = ["sphinx"] gmpy = ["gmpy2 (>=2.1.0a4)"] tests = ["pytest (>=4.6)"] -[[package]] -name = "myst-nb" -version = "1.1.0" -description = "A Jupyter Notebook Sphinx reader built on top of the MyST markdown parser." -optional = false -python-versions = ">=3.9" -files = [ - {file = "myst_nb-1.1.0-py3-none-any.whl", hash = "sha256:0ac29b2a346f9a1257edbfb5d6c47d528728a37e6b9438903c2821f69fda9235"}, - {file = "myst_nb-1.1.0.tar.gz", hash = "sha256:9278840e844f5d780b5acc5400cbf63d97caaccf8eb442a55ebd9a03e2522d5e"}, -] - -[package.dependencies] -importlib_metadata = "*" -ipykernel = "*" -ipython = "*" -jupyter-cache = ">=0.5" -myst-parser = ">=1.0.0" -nbclient = "*" -nbformat = ">=5.0" -pyyaml = "*" -sphinx = ">=5" -typing-extensions = "*" - -[package.extras] -code-style = ["pre-commit"] -rtd = ["alabaster", "altair", "bokeh", "coconut (>=1.4.3,<3.1.0)", "ipykernel (>=5.5,<7.0)", "ipywidgets", "jupytext (>=1.11.2,<1.16.0)", "matplotlib", "numpy", "pandas", "plotly", "sphinx-book-theme (>=0.3)", "sphinx-copybutton", "sphinx-design (>=0.4.0,<0.5.0)", "sphinxcontrib-bibtex", "sympy"] -testing = ["beautifulsoup4", "coverage (>=6.4,<8.0)", "ipykernel (>=5.5,<7.0)", "ipython (!=8.1.0,<8.17)", "ipywidgets (>=8)", "jupytext (>=1.11.2,<1.16.0)", "matplotlib (==3.7.*)", "nbdime", "numpy", "pandas (==1.5.*)", "pyarrow", "pytest (>=7.1,<8.0)", "pytest-cov (>=3,<5)", "pytest-param-files (>=0.3.3,<0.4.0)", "pytest-regressions", "sympy (>=1.10.1)"] - -[[package]] -name = "myst-parser" -version = "3.0.1" -description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," -optional = false -python-versions = ">=3.8" -files = [ - {file = "myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1"}, - {file = "myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87"}, -] - -[package.dependencies] -docutils = ">=0.18,<0.22" -jinja2 = "*" -markdown-it-py = ">=3.0,<4.0" -mdit-py-plugins = ">=0.4,<1.0" -pyyaml = "*" -sphinx = ">=6,<8" - -[package.extras] -code-style = ["pre-commit (>=3.0,<4.0)"] -linkify = ["linkify-it-py (>=2.0,<3.0)"] -rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] -testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"] -testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"] - [[package]] name = "nbclient" version = "0.10.0" @@ -2244,37 +2019,37 @@ test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] [[package]] name = "numba" -version = "0.59.1" +version = "0.60.0" description = "compiling Python code using LLVM" optional = false python-versions = ">=3.9" files = [ - {file = "numba-0.59.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97385a7f12212c4f4bc28f648720a92514bee79d7063e40ef66c2d30600fd18e"}, - {file = "numba-0.59.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0b77aecf52040de2a1eb1d7e314497b9e56fba17466c80b457b971a25bb1576d"}, - {file = "numba-0.59.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3476a4f641bfd58f35ead42f4dcaf5f132569c4647c6f1360ccf18ee4cda3990"}, - {file = "numba-0.59.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:525ef3f820931bdae95ee5379c670d5c97289c6520726bc6937a4a7d4230ba24"}, - {file = "numba-0.59.1-cp310-cp310-win_amd64.whl", hash = "sha256:990e395e44d192a12105eca3083b61307db7da10e093972ca285c85bef0963d6"}, - {file = "numba-0.59.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43727e7ad20b3ec23ee4fc642f5b61845c71f75dd2825b3c234390c6d8d64051"}, - {file = "numba-0.59.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:411df625372c77959570050e861981e9d196cc1da9aa62c3d6a836b5cc338966"}, - {file = "numba-0.59.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2801003caa263d1e8497fb84829a7ecfb61738a95f62bc05693fcf1733e978e4"}, - {file = "numba-0.59.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dd2842fac03be4e5324ebbbd4d2d0c8c0fc6e0df75c09477dd45b288a0777389"}, - {file = "numba-0.59.1-cp311-cp311-win_amd64.whl", hash = "sha256:0594b3dfb369fada1f8bb2e3045cd6c61a564c62e50cf1f86b4666bc721b3450"}, - {file = "numba-0.59.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1cce206a3b92836cdf26ef39d3a3242fec25e07f020cc4feec4c4a865e340569"}, - {file = "numba-0.59.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c8b4477763cb1fbd86a3be7050500229417bf60867c93e131fd2626edb02238"}, - {file = "numba-0.59.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d80bce4ef7e65bf895c29e3889ca75a29ee01da80266a01d34815918e365835"}, - {file = "numba-0.59.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7ad1d217773e89a9845886401eaaab0a156a90aa2f179fdc125261fd1105096"}, - {file = "numba-0.59.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bf68f4d69dd3a9f26a9b23548fa23e3bcb9042e2935257b471d2a8d3c424b7f"}, - {file = "numba-0.59.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4e0318ae729de6e5dbe64c75ead1a95eb01fabfe0e2ebed81ebf0344d32db0ae"}, - {file = "numba-0.59.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0f68589740a8c38bb7dc1b938b55d1145244c8353078eea23895d4f82c8b9ec1"}, - {file = "numba-0.59.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:649913a3758891c77c32e2d2a3bcbedf4a69f5fea276d11f9119677c45a422e8"}, - {file = "numba-0.59.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9712808e4545270291d76b9a264839ac878c5eb7d8b6e02c970dc0ac29bc8187"}, - {file = "numba-0.59.1-cp39-cp39-win_amd64.whl", hash = "sha256:8d51ccd7008a83105ad6a0082b6a2b70f1142dc7cfd76deb8c5a862367eb8c86"}, - {file = "numba-0.59.1.tar.gz", hash = "sha256:76f69132b96028d2774ed20415e8c528a34e3299a40581bae178f0994a2f370b"}, + {file = "numba-0.60.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d761de835cd38fb400d2c26bb103a2726f548dc30368853121d66201672e651"}, + {file = "numba-0.60.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:159e618ef213fba758837f9837fb402bbe65326e60ba0633dbe6c7f274d42c1b"}, + {file = "numba-0.60.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1527dc578b95c7c4ff248792ec33d097ba6bef9eda466c948b68dfc995c25781"}, + {file = "numba-0.60.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe0b28abb8d70f8160798f4de9d486143200f34458d34c4a214114e445d7124e"}, + {file = "numba-0.60.0-cp310-cp310-win_amd64.whl", hash = "sha256:19407ced081d7e2e4b8d8c36aa57b7452e0283871c296e12d798852bc7d7f198"}, + {file = "numba-0.60.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a17b70fc9e380ee29c42717e8cc0bfaa5556c416d94f9aa96ba13acb41bdece8"}, + {file = "numba-0.60.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fb02b344a2a80efa6f677aa5c40cd5dd452e1b35f8d1c2af0dfd9ada9978e4b"}, + {file = "numba-0.60.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f4fde652ea604ea3c86508a3fb31556a6157b2c76c8b51b1d45eb40c8598703"}, + {file = "numba-0.60.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4142d7ac0210cc86432b818338a2bc368dc773a2f5cf1e32ff7c5b378bd63ee8"}, + {file = "numba-0.60.0-cp311-cp311-win_amd64.whl", hash = "sha256:cac02c041e9b5bc8cf8f2034ff6f0dbafccd1ae9590dc146b3a02a45e53af4e2"}, + {file = "numba-0.60.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7da4098db31182fc5ffe4bc42c6f24cd7d1cb8a14b59fd755bfee32e34b8404"}, + {file = "numba-0.60.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38d6ea4c1f56417076ecf8fc327c831ae793282e0ff51080c5094cb726507b1c"}, + {file = "numba-0.60.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:62908d29fb6a3229c242e981ca27e32a6e606cc253fc9e8faeb0e48760de241e"}, + {file = "numba-0.60.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ebaa91538e996f708f1ab30ef4d3ddc344b64b5227b67a57aa74f401bb68b9d"}, + {file = "numba-0.60.0-cp312-cp312-win_amd64.whl", hash = "sha256:f75262e8fe7fa96db1dca93d53a194a38c46da28b112b8a4aca168f0df860347"}, + {file = "numba-0.60.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:01ef4cd7d83abe087d644eaa3d95831b777aa21d441a23703d649e06b8e06b74"}, + {file = "numba-0.60.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:819a3dfd4630d95fd574036f99e47212a1af41cbcb019bf8afac63ff56834449"}, + {file = "numba-0.60.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b983bd6ad82fe868493012487f34eae8bf7dd94654951404114f23c3466d34b"}, + {file = "numba-0.60.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c151748cd269ddeab66334bd754817ffc0cabd9433acb0f551697e5151917d25"}, + {file = "numba-0.60.0-cp39-cp39-win_amd64.whl", hash = "sha256:3031547a015710140e8c87226b4cfe927cac199835e5bf7d4fe5cb64e814e3ab"}, + {file = "numba-0.60.0.tar.gz", hash = "sha256:5df6158e5584eece5fc83294b949fd30b9f1125df7708862205217e068aabf16"}, ] [package.dependencies] -llvmlite = "==0.42.*" -numpy = ">=1.22,<1.27" +llvmlite = "==0.43.*" +numpy = ">=1.22,<2.1" [[package]] name = "numpy" @@ -2415,9 +2190,9 @@ files = [ [package.dependencies] numpy = [ + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, {version = ">=1.23.2", markers = "python_version == \"3.11\""}, - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -2501,84 +2276,95 @@ ptyprocess = ">=0.5" [[package]] name = "pillow" -version = "10.3.0" +version = "10.4.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ - {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, - {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, - {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, - {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, - {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, - {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, - {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, - {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, - {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, - {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, - {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, - {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, - {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, - {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, - {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, - {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, - {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, - {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, - {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, - {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, - {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, - {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, - {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, - {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, - {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, - {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, - {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, - {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, - {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, - {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, - {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, - {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, - {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, - {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, - {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, + {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, + {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, + {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, + {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, + {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, + {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, + {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, + {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, + {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, + {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, + {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, + {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, + {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, + {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, + {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, + {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, + {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, + {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, ] [package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] @@ -2632,64 +2418,65 @@ wcwidth = "*" [[package]] name = "proto-plus" -version = "1.20.4" +version = "1.24.0" description = "Beautiful, Pythonic protocol buffers." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "proto-plus-1.20.4.tar.gz", hash = "sha256:6653541c2f1209e4d5268d3e6302791f72a95cc5f8bdcf3e60d943edc657e70a"}, - {file = "proto_plus-1.20.4-py3-none-any.whl", hash = "sha256:3cfaac30676793d5ee764a0982bc30481beb5059f315e2a2422d7c73ded5b601"}, + {file = "proto-plus-1.24.0.tar.gz", hash = "sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445"}, + {file = "proto_plus-1.24.0-py3-none-any.whl", hash = "sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12"}, ] [package.dependencies] -protobuf = ">=3.19.0" +protobuf = ">=3.19.0,<6.0.0dev" [package.extras] -testing = ["google-api-core[grpc] (>=1.22.2)"] +testing = ["google-api-core (>=1.31.5)"] [[package]] name = "protobuf" -version = "5.27.1" +version = "5.27.2" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.27.1-cp310-abi3-win32.whl", hash = "sha256:3adc15ec0ff35c5b2d0992f9345b04a540c1e73bfee3ff1643db43cc1d734333"}, - {file = "protobuf-5.27.1-cp310-abi3-win_amd64.whl", hash = "sha256:25236b69ab4ce1bec413fd4b68a15ef8141794427e0b4dc173e9d5d9dffc3bcd"}, - {file = "protobuf-5.27.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4e38fc29d7df32e01a41cf118b5a968b1efd46b9c41ff515234e794011c78b17"}, - {file = "protobuf-5.27.1-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:917ed03c3eb8a2d51c3496359f5b53b4e4b7e40edfbdd3d3f34336e0eef6825a"}, - {file = "protobuf-5.27.1-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:ee52874a9e69a30271649be88ecbe69d374232e8fd0b4e4b0aaaa87f429f1631"}, - {file = "protobuf-5.27.1-cp38-cp38-win32.whl", hash = "sha256:7a97b9c5aed86b9ca289eb5148df6c208ab5bb6906930590961e08f097258107"}, - {file = "protobuf-5.27.1-cp38-cp38-win_amd64.whl", hash = "sha256:f6abd0f69968792da7460d3c2cfa7d94fd74e1c21df321eb6345b963f9ec3d8d"}, - {file = "protobuf-5.27.1-cp39-cp39-win32.whl", hash = "sha256:dfddb7537f789002cc4eb00752c92e67885badcc7005566f2c5de9d969d3282d"}, - {file = "protobuf-5.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:39309898b912ca6febb0084ea912e976482834f401be35840a008da12d189340"}, - {file = "protobuf-5.27.1-py3-none-any.whl", hash = "sha256:4ac7249a1530a2ed50e24201d6630125ced04b30619262f06224616e0030b6cf"}, - {file = "protobuf-5.27.1.tar.gz", hash = "sha256:df5e5b8e39b7d1c25b186ffdf9f44f40f810bbcc9d2b71d9d3156fee5a9adf15"}, + {file = "protobuf-5.27.2-cp310-abi3-win32.whl", hash = "sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38"}, + {file = "protobuf-5.27.2-cp310-abi3-win_amd64.whl", hash = "sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505"}, + {file = "protobuf-5.27.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5"}, + {file = "protobuf-5.27.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b"}, + {file = "protobuf-5.27.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e"}, + {file = "protobuf-5.27.2-cp38-cp38-win32.whl", hash = "sha256:4fadd8d83e1992eed0248bc50a4a6361dc31bcccc84388c54c86e530b7f58863"}, + {file = "protobuf-5.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:610e700f02469c4a997e58e328cac6f305f649826853813177e6290416e846c6"}, + {file = "protobuf-5.27.2-cp39-cp39-win32.whl", hash = "sha256:9e8f199bf7f97bd7ecebffcae45ebf9527603549b2b562df0fbc6d4d688f14ca"}, + {file = "protobuf-5.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:7fc3add9e6003e026da5fc9e59b131b8f22b428b991ccd53e2af8071687b4fce"}, + {file = "protobuf-5.27.2-py3-none-any.whl", hash = "sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470"}, + {file = "protobuf-5.27.2.tar.gz", hash = "sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714"}, ] [[package]] name = "psutil" -version = "5.9.8" +version = "6.0.0" description = "Cross-platform lib for process and system monitoring in Python." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, - {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, - {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, - {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, - {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, - {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, - {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, - {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, - {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, - {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, + {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, + {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, + {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, + {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, + {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, + {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, + {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, + {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, + {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, ] [package.extras] @@ -2721,13 +2508,13 @@ pandas = ["pandas"] [[package]] name = "pure-eval" -version = "0.2.2" +version = "0.2.3" description = "Safely evaluate AST nodes without side effects" optional = false python-versions = "*" files = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, + {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, + {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, ] [package.extras] @@ -2771,109 +2558,122 @@ files = [ [[package]] name = "pydantic" -version = "2.7.3" +version = "2.8.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"}, - {file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"}, + {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, + {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.18.4" -typing-extensions = ">=4.6.1" +pydantic-core = "2.20.1" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] [package.extras] email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.4" +version = "2.20.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, - {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, - {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, - {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, - {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, - {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, - {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, - {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, - {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, - {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, - {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, - {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, - {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, - {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, + {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, + {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, + {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, + {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, + {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, + {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, + {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, + {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"}, + {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"}, + {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"}, + {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"}, + {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, + {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, ] [package.dependencies] @@ -2881,13 +2681,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydata-sphinx-theme" -version = "0.15.3" +version = "0.15.4" description = "Bootstrap-based Sphinx theme from the PyData community" optional = false python-versions = ">=3.9" files = [ - {file = "pydata_sphinx_theme-0.15.3-py3-none-any.whl", hash = "sha256:a48ee049dc9b0f7064dbb8f7064b1cf3ae48aa193faafe14abd403a1b7102810"}, - {file = "pydata_sphinx_theme-0.15.3.tar.gz", hash = "sha256:f26ed9b676f61d1b2ae9289f3d7e496e8678dd56f2568b27a66fa4ad1f164efd"}, + {file = "pydata_sphinx_theme-0.15.4-py3-none-any.whl", hash = "sha256:2136ad0e9500d0949f96167e63f3e298620040aea8f9c74621959eda5d4cf8e6"}, + {file = "pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d"}, ] [package.dependencies] @@ -2903,7 +2703,7 @@ typing-extensions = "*" [package.extras] a11y = ["pytest-playwright"] dev = ["pandoc", "pre-commit", "pydata-sphinx-theme[doc,test]", "pyyaml", "sphinx-theme-builder[cli]", "tox"] -doc = ["ablog (>=0.11.8)", "colorama", "ipykernel", "ipyleaflet", "ipywidgets", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (>=3.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap", "sphinx-togglebutton", "sphinxcontrib-youtube (<1.4)", "sphinxext-rediraffe", "xarray"] +doc = ["ablog (>=0.11.8)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "ipywidgets", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (>=3.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap", "sphinx-togglebutton", "sphinxcontrib-youtube (>=1.4.1)", "sphinxext-rediraffe", "xarray"] i18n = ["Babel", "jinja2"] test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] @@ -2954,13 +2754,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyspnego" -version = "0.10.2" +version = "0.11.1" description = "Windows Negotiate Authentication Client and Server" optional = false python-versions = ">=3.8" files = [ - {file = "pyspnego-0.10.2-py3-none-any.whl", hash = "sha256:3d5c5c28dbd0cd6a679acf45219630254db3c0e5ad4a16de521caa0585b088c0"}, - {file = "pyspnego-0.10.2.tar.gz", hash = "sha256:9a22c23aeae7b4424fdb2482450d3f8302ac012e2644e1cfe735cf468fcd12ed"}, + {file = "pyspnego-0.11.1-py3-none-any.whl", hash = "sha256:129a4294f2c4d681d5875240ef87accc6f1d921e8983737fb0b59642b397951e"}, + {file = "pyspnego-0.11.1.tar.gz", hash = "sha256:e92ed8b0a62765b9d6abbb86a48cf871228ddb97678598dc01c9c39a626823f6"}, ] [package.dependencies] @@ -2973,13 +2773,13 @@ yaml = ["ruamel.yaml"] [[package]] name = "pytest" -version = "8.2.2" +version = "8.3.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, - {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, + {file = "pytest-8.3.1-py3-none-any.whl", hash = "sha256:e9600ccf4f563976e2c99fa02c7624ab938296551f280835ee6516df8bc4ae8c"}, + {file = "pytest-8.3.1.tar.gz", hash = "sha256:7e8e5c5abd6e93cb1cc151f23e57adc31fcf8cfd2a3ff2da63e23f732de35db6"}, ] [package.dependencies] @@ -2987,7 +2787,7 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.5,<2.0" +pluggy = ">=1.5,<2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] @@ -3009,44 +2809,45 @@ six = ">=1.5" [[package]] name = "pytket" -version = "1.28.0" +version = "1.30.0" description = "Quantum computing toolkit and interface to the TKET compiler" optional = false python-versions = ">=3.10" files = [ - {file = "pytket-1.28.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5cea8b772c51d3189c353d027b4d2c3dc2f265cca39345fdfaaea2dd6f8c4970"}, - {file = "pytket-1.28.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:1d5c5ab5f3dff39f048cacdc494aa7eea6099d96b0d7455ba411c0f630a46dbe"}, - {file = "pytket-1.28.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91b993a9991050fc8e47279e229cd35fd77acce53f720e8e465a9616ff1727aa"}, - {file = "pytket-1.28.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6276b5735f0c266d18daa7d212dca2cae3fd8cd0697009cd577d01b6fbd2ad35"}, - {file = "pytket-1.28.0-cp310-cp310-win_amd64.whl", hash = "sha256:acad2ddb5f6787e4ad2430c728a30cb77f30c37401c0ca9fea241e3b695241cb"}, - {file = "pytket-1.28.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:01d029c3d6168fea9b2d1c83f346461b393798745b9f6ca6aa05dc59913f21f2"}, - {file = "pytket-1.28.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ef49b12a3bdb5b276da501b70854f0dea26125dfdfe74ad6bdeb041df736f421"}, - {file = "pytket-1.28.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99c2ef5b42a8ab8a11235821b23c98b59fb3dee389f8f26ac9f93977d3cd958d"}, - {file = "pytket-1.28.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e12c24030ee1e94c1f68bed4abbb5ae474a7cdbbe4af7defe2bac0b270e45727"}, - {file = "pytket-1.28.0-cp311-cp311-win_amd64.whl", hash = "sha256:4052e83e252875a1b49a07c59acc61cb3d8e89cc06f9d43ffbbd2d5055455a61"}, - {file = "pytket-1.28.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9b1458433fc3c94ae20ae845ab501ddb692044e1d6d06e05ef0e614e99deff89"}, - {file = "pytket-1.28.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ba0c03a2294c38ed2e75d036f9885755725f0ecc8aa0f285ce9cf3e092414c22"}, - {file = "pytket-1.28.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4628753dacc5e0aa948900510c58c82e50d3efdc59772debc7dcf08e873385b0"}, - {file = "pytket-1.28.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e02fc7362f6ce7aba7a1d94dcc6d89071ce891c0598eec01892352d3046e33f2"}, - {file = "pytket-1.28.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cbe60b5affc5a9fba82639956c465073ccd56e1358fb33a44d195002b9c1975"}, + {file = "pytket-1.30.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d111e978f027c65363d019408d76f69ea584e8fc878d2302d5f867091e769327"}, + {file = "pytket-1.30.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:85d99f81b955573c13fe1e166400e9553549f6a38151a3097c4143d9883f9d5c"}, + {file = "pytket-1.30.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19bb8268dc7d28bc33482750330549f7f7ebe546abcfc22170fa6f299241d918"}, + {file = "pytket-1.30.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce598114ff9674503b61a15b2dd42dd74068ed74e662492256ad78a425f9a569"}, + {file = "pytket-1.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:977b17c573ab5f3f0771448e42415f16595331699d63eeabaf17b72e6ddc06c6"}, + {file = "pytket-1.30.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:87cc5f77f189f39f354f2df31061e1f58b949a514c6c90bad80044f4c0e37410"}, + {file = "pytket-1.30.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:c78076b66d4b8102ac7538a227415f75639b0445e629de82a5d451b52658ef6d"}, + {file = "pytket-1.30.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f845ab325f3747776a080a1addc3236c00f6099c1d0de1b0f21af98e4d059aed"}, + {file = "pytket-1.30.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:06d308ef53121afc89f7111748f2776bd6161e1a4f7b3febea9949d058f4e5da"}, + {file = "pytket-1.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:4267c45e8d21655d47dbf2e85f56bde03ff00e402bb97a356e208073cbc5b929"}, + {file = "pytket-1.30.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:4010feed278ec363c1a06d462c3e547fc991de85954af9a083ca0b925bc48054"}, + {file = "pytket-1.30.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:2eeff1016a667a69997af2904ae35c74a6954dea6b0e840f2087e806e3fd9801"}, + {file = "pytket-1.30.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97cdb52d1fb440da2452c8a295aa67d44e5e70e2ed9113d48f34b3c948dbcc69"}, + {file = "pytket-1.30.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eebdb1879bc8fd57d2267b704902d37ba7403119e3aa80cbe2e99897f8a91576"}, + {file = "pytket-1.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:95bc56d7e8570af43df2b86ac7501623dba9dbd725df353de02daef3c7d29f8d"}, ] [package.dependencies] -autoray = {version = ">=0.6.1", optional = true, markers = "extra == \"zx\""} -graphviz = ">=0.14,<1.0" -jinja2 = ">=3.0,<4.0" -lark = ">=1.1,<2.0" +autoray = {version = ">=0.6.12", optional = true, markers = "extra == \"zx\""} +graphviz = ">=0.20.3" +jinja2 = ">=3.1.4" +lark = ">=1.1.9" networkx = ">=2.8.8" -numpy = ">=1.21.4,<2.0" -quimb = {version = ">=1.8,<2.0", optional = true, markers = "extra == \"zx\""} -qwasm = ">=1.0,<2.0" -scipy = ">=1.13,<2.0" -sympy = ">=1.6,<2.0" -types-pkg-resources = "*" -typing-extensions = ">=4.2,<5.0" +numba = {version = ">=0.60.0", optional = true, markers = "extra == \"zx\""} +numpy = ">=1.26.4" +quimb = {version = ">=1.8.2", optional = true, markers = "extra == \"zx\""} +qwasm = ">=1.0.1" +scipy = ">=1.13.1" +sympy = ">=1.12.1" +types-pkg-resources = ">=0.1.3" +typing-extensions = ">=4.12.2" [package.extras] -zx = ["autoray (>=0.6.1)", "quimb (>=1.8,<2.0)"] +zx = ["autoray (>=0.6.12)", "numba (>=0.60.0)", "quimb (>=1.8.2)"] [[package]] name = "pytket-cirq" @@ -3131,66 +2932,6 @@ files = [ {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, ] -[[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - [[package]] name = "pyzmq" version = "26.0.3" @@ -3293,22 +3034,22 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "qiskit" -version = "1.1.0" +version = "1.1.1" description = "An open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives." optional = false python-versions = ">=3.8" files = [ - {file = "qiskit-1.1.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:1abad8ac96dceb838e279fecae713f26f5debbd85b476e0a52ea829cd823da95"}, - {file = "qiskit-1.1.0-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c2345eaa770a0112a667c6bcbcd9694dc7eed274f1ceea0732ddb6fb42336ccc"}, - {file = "qiskit-1.1.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:3133d90bda74ec487f205c68ba501cb5f27a23a146ff5f3de70eb93084a906b8"}, - {file = "qiskit-1.1.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:135f8dcd4759ea6d5989abb68d494d47d86a8068deaef4addb365e8fd89d87a8"}, - {file = "qiskit-1.1.0-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06d7081b8c4fb51cd613822d6365643fb0673cdaf4e0fa40b59602f61d9e1229"}, - {file = "qiskit-1.1.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b4f0e946d703f2bfb53d97fe7156444e55ad2d3cfb853a84b756c50877dc368"}, - {file = "qiskit-1.1.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fda2001a3d87c85a3166e660cb8f9f8b2842bd2aa2867e679cc3a8b9ca8785a"}, - {file = "qiskit-1.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01da5ebdcf907435d1ab47ea024bb791b4dea8245c148b89bc68e606c45eb478"}, - {file = "qiskit-1.1.0-cp38-abi3-win32.whl", hash = "sha256:3d9cf7fa7d0a17a500b0181eae07c08d38e9a6a67789e09736b2de334159edfe"}, - {file = "qiskit-1.1.0-cp38-abi3-win_amd64.whl", hash = "sha256:26eb1b391ac36e831bec0f7dd3c78b426ea5b0963b124a749b8da38f03c2e875"}, - {file = "qiskit-1.1.0.tar.gz", hash = "sha256:d4d0310029659711dca6b9c0cc593e1b33daf059e52ba221267f85bb47f1bf4e"}, + {file = "qiskit-1.1.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:98f1cc5ad460ef8815eab7fa8405daaacc262a4fc18f042ebfc09690b5b6c717"}, + {file = "qiskit-1.1.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:dcb1f719beb9b01c5cd73173708c3743723c447f5396c2bb87e727926bcdf685"}, + {file = "qiskit-1.1.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:540cb61230aca537b2b748e99001e7b7a9f49449f28f9514c3544395c54fbbe0"}, + {file = "qiskit-1.1.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c2b26aa70d20aa95dbb28772a1735718603b6cb8e2c6b460679c1e0faffde6"}, + {file = "qiskit-1.1.1-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95177938035da0d88e68ab2cdc144953518fdfce2d657b1c89442b7580776b13"}, + {file = "qiskit-1.1.1-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d01aff14b0480de9232b358aebd4c48bb4d460fc776c90de224d38bd08455af"}, + {file = "qiskit-1.1.1-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c88ee6afa941eeb34577668e60c78ee053612b66b8c9e6315b62c310054ec597"}, + {file = "qiskit-1.1.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aac52542651867f301a6cd1c6c55a02de494f915b4c9f5885dc7549058b6bc35"}, + {file = "qiskit-1.1.1-cp38-abi3-win32.whl", hash = "sha256:e3ae6399174b7931a0c6b41f6650ab732436f41afbb5625563857047087ee4d3"}, + {file = "qiskit-1.1.1-cp38-abi3-win_amd64.whl", hash = "sha256:88c98fbd3314f1961ebd10901df262251dbe12c512ed6152d2f578bac33e2d72"}, + {file = "qiskit-1.1.1.tar.gz", hash = "sha256:5f1af6a1d94b92aa8c15007b626b76aeb5873a59ff53cb40d1f3662abd50c2cc"}, ] [package.dependencies] @@ -3446,20 +3187,38 @@ requests-ntlm = ">=1.1.0" urllib3 = ">=1.21.1" websocket-client = ">=1.5.1" +[[package]] +name = "quantinuum-docs-theme" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.dependencies] +sphinx-book-theme = "^1.1.3" + +[package.source] +type = "git" +url = "https://github.com/aidanCQ/qui-sphinx.git" +reference = "dist" +resolved_reference = "20b578e090b7413c8779a44b56dc3b50087b8258" + [[package]] name = "quimb" -version = "1.8.1" +version = "1.8.4" description = "Quantum information and many-body library." optional = false python-versions = ">=3.8" files = [ - {file = "quimb-1.8.1-py3-none-any.whl", hash = "sha256:b01c6f1e8470fc76b54216eaa78cd276f394011992d29c780443087f7656a690"}, - {file = "quimb-1.8.1.tar.gz", hash = "sha256:5fea3e07b076c717fa354505a11ccb9b3627712fa3d6e722ee9b293ba8d643ea"}, + {file = "quimb-1.8.4-py3-none-any.whl", hash = "sha256:357c42c5c1a696ba8234e1d7e1361aabc182ac76ff046a85fabb37f63d989d2d"}, + {file = "quimb-1.8.4.tar.gz", hash = "sha256:51b7b6f09a451f44468d7b41ac990635b2e718b437aee8db992f892c3e5d30b5"}, ] [package.dependencies] -autoray = ">=0.6.7" -cotengra = ">=0.5.6" +autoray = ">=0.6.12" +cotengra = ">=0.6.1" cytoolz = ">=0.8.0" numba = ">=0.39" numpy = ">=1.17" @@ -3469,7 +3228,7 @@ tqdm = ">=4" [package.extras] advanced-solvers = ["mpi4py", "petsc4py", "slepc4py"] -docs = ["astroid (<3.0.0)", "autoray (>=0.6.7)", "cotengra (>=0.5.3)", "doc2dash (>=2.4.1)", "furo", "ipython (!=8.7.0)", "myst-nb", "setuptools-scm", "sphinx (>=2.0)", "sphinx-autoapi", "sphinx-copybutton", "sphinx-design"] +docs = ["astroid (<3.0.0)", "autoray (>=0.6.12)", "cotengra (>=0.6.1)", "doc2dash (>=2.4.1)", "furo", "ipython (!=8.7.0)", "myst-nb", "setuptools-scm", "sphinx (>=2.0)", "sphinx-autoapi", "sphinx-copybutton", "sphinx-design"] tensor = ["matplotlib (>=2.0)", "networkx (>=2.3)"] tests = ["coverage", "pytest", "pytest-cov"] @@ -3555,110 +3314,114 @@ requests = ">=2.0.0" [[package]] name = "rpds-py" -version = "0.18.1" +version = "0.19.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53"}, - {file = "rpds_py-0.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d"}, - {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60"}, - {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da"}, - {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1"}, - {file = "rpds_py-0.18.1-cp310-none-win32.whl", hash = "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333"}, - {file = "rpds_py-0.18.1-cp310-none-win_amd64.whl", hash = "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a"}, - {file = "rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8"}, - {file = "rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8"}, - {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7"}, - {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e"}, - {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88"}, - {file = "rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb"}, - {file = "rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2"}, - {file = "rpds_py-0.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3"}, - {file = "rpds_py-0.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac"}, - {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c"}, - {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac"}, - {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a"}, - {file = "rpds_py-0.18.1-cp312-none-win32.whl", hash = "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6"}, - {file = "rpds_py-0.18.1-cp312-none-win_amd64.whl", hash = "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72"}, - {file = "rpds_py-0.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74"}, - {file = "rpds_py-0.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0"}, - {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d"}, - {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e"}, - {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc"}, - {file = "rpds_py-0.18.1-cp38-none-win32.whl", hash = "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9"}, - {file = "rpds_py-0.18.1-cp38-none-win_amd64.whl", hash = "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2"}, - {file = "rpds_py-0.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93"}, - {file = "rpds_py-0.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c"}, - {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338"}, - {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b"}, - {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26"}, - {file = "rpds_py-0.18.1-cp39-none-win32.whl", hash = "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360"}, - {file = "rpds_py-0.18.1-cp39-none-win_amd64.whl", hash = "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e"}, - {file = "rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f"}, + {file = "rpds_py-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:aaf71f95b21f9dc708123335df22e5a2fef6307e3e6f9ed773b2e0938cc4d491"}, + {file = "rpds_py-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca0dda0c5715efe2ab35bb83f813f681ebcd2840d8b1b92bfc6fe3ab382fae4a"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81db2e7282cc0487f500d4db203edc57da81acde9e35f061d69ed983228ffe3b"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a8dfa125b60ec00c7c9baef945bb04abf8ac772d8ebefd79dae2a5f316d7850"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271accf41b02687cef26367c775ab220372ee0f4925591c6796e7c148c50cab5"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9bc4161bd3b970cd6a6fcda70583ad4afd10f2750609fb1f3ca9505050d4ef3"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0cf2a0dbb5987da4bd92a7ca727eadb225581dd9681365beba9accbe5308f7d"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b5e28e56143750808c1c79c70a16519e9bc0a68b623197b96292b21b62d6055c"}, + {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c7af6f7b80f687b33a4cdb0a785a5d4de1fb027a44c9a049d8eb67d5bfe8a687"}, + {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e429fc517a1c5e2a70d576077231538a98d59a45dfc552d1ac45a132844e6dfb"}, + {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d2dbd8f4990d4788cb122f63bf000357533f34860d269c1a8e90ae362090ff3a"}, + {file = "rpds_py-0.19.1-cp310-none-win32.whl", hash = "sha256:e0f9d268b19e8f61bf42a1da48276bcd05f7ab5560311f541d22557f8227b866"}, + {file = "rpds_py-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:df7c841813f6265e636fe548a49664c77af31ddfa0085515326342a751a6ba51"}, + {file = "rpds_py-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:902cf4739458852fe917104365ec0efbea7d29a15e4276c96a8d33e6ed8ec137"}, + {file = "rpds_py-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3d73022990ab0c8b172cce57c69fd9a89c24fd473a5e79cbce92df87e3d9c48"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3837c63dd6918a24de6c526277910e3766d8c2b1627c500b155f3eecad8fad65"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cdb7eb3cf3deb3dd9e7b8749323b5d970052711f9e1e9f36364163627f96da58"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26ab43b6d65d25b1a333c8d1b1c2f8399385ff683a35ab5e274ba7b8bb7dc61c"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75130df05aae7a7ac171b3b5b24714cffeabd054ad2ebc18870b3aa4526eba23"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c34f751bf67cab69638564eee34023909380ba3e0d8ee7f6fe473079bf93f09b"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2671cb47e50a97f419a02cd1e0c339b31de017b033186358db92f4d8e2e17d8"}, + {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c73254c256081704dba0a333457e2fb815364018788f9b501efe7c5e0ada401"}, + {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4383beb4a29935b8fa28aca8fa84c956bf545cb0c46307b091b8d312a9150e6a"}, + {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dbceedcf4a9329cc665452db1aaf0845b85c666e4885b92ee0cddb1dbf7e052a"}, + {file = "rpds_py-0.19.1-cp311-none-win32.whl", hash = "sha256:f0a6d4a93d2a05daec7cb885157c97bbb0be4da739d6f9dfb02e101eb40921cd"}, + {file = "rpds_py-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:c149a652aeac4902ecff2dd93c3b2681c608bd5208c793c4a99404b3e1afc87c"}, + {file = "rpds_py-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:56313be667a837ff1ea3508cebb1ef6681d418fa2913a0635386cf29cff35165"}, + {file = "rpds_py-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d1d7539043b2b31307f2c6c72957a97c839a88b2629a348ebabe5aa8b626d6b"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1dc59a5e7bc7f44bd0c048681f5e05356e479c50be4f2c1a7089103f1621d5"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8f78398e67a7227aefa95f876481485403eb974b29e9dc38b307bb6eb2315ea"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef07a0a1d254eeb16455d839cef6e8c2ed127f47f014bbda64a58b5482b6c836"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8124101e92c56827bebef084ff106e8ea11c743256149a95b9fd860d3a4f331f"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08ce9c95a0b093b7aec75676b356a27879901488abc27e9d029273d280438505"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b02dd77a2de6e49078c8937aadabe933ceac04b41c5dde5eca13a69f3cf144e"}, + {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4dd02e29c8cbed21a1875330b07246b71121a1c08e29f0ee3db5b4cfe16980c4"}, + {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9c7042488165f7251dc7894cd533a875d2875af6d3b0e09eda9c4b334627ad1c"}, + {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f809a17cc78bd331e137caa25262b507225854073fd319e987bd216bed911b7c"}, + {file = "rpds_py-0.19.1-cp312-none-win32.whl", hash = "sha256:3ddab996807c6b4227967fe1587febade4e48ac47bb0e2d3e7858bc621b1cace"}, + {file = "rpds_py-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:32e0db3d6e4f45601b58e4ac75c6f24afbf99818c647cc2066f3e4b192dabb1f"}, + {file = "rpds_py-0.19.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:747251e428406b05fc86fee3904ee19550c4d2d19258cef274e2151f31ae9d38"}, + {file = "rpds_py-0.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dc733d35f861f8d78abfaf54035461e10423422999b360966bf1c443cbc42705"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbda75f245caecff8faa7e32ee94dfaa8312a3367397975527f29654cd17a6ed"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd04d8cab16cab5b0a9ffc7d10f0779cf1120ab16c3925404428f74a0a43205a"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2d66eb41ffca6cc3c91d8387509d27ba73ad28371ef90255c50cb51f8953301"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdf4890cda3b59170009d012fca3294c00140e7f2abe1910e6a730809d0f3f9b"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1fa67ef839bad3815124f5f57e48cd50ff392f4911a9f3cf449d66fa3df62a5"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b82c9514c6d74b89a370c4060bdb80d2299bc6857e462e4a215b4ef7aa7b090e"}, + {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c7b07959866a6afb019abb9564d8a55046feb7a84506c74a6f197cbcdf8a208e"}, + {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4f580ae79d0b861dfd912494ab9d477bea535bfb4756a2269130b6607a21802e"}, + {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c6d20c8896c00775e6f62d8373aba32956aa0b850d02b5ec493f486c88e12859"}, + {file = "rpds_py-0.19.1-cp313-none-win32.whl", hash = "sha256:afedc35fe4b9e30ab240b208bb9dc8938cb4afe9187589e8d8d085e1aacb8309"}, + {file = "rpds_py-0.19.1-cp313-none-win_amd64.whl", hash = "sha256:1d4af2eb520d759f48f1073ad3caef997d1bfd910dc34e41261a595d3f038a94"}, + {file = "rpds_py-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:34bca66e2e3eabc8a19e9afe0d3e77789733c702c7c43cd008e953d5d1463fde"}, + {file = "rpds_py-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:24f8ae92c7fae7c28d0fae9b52829235df83f34847aa8160a47eb229d9666c7b"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71157f9db7f6bc6599a852852f3389343bea34315b4e6f109e5cbc97c1fb2963"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d494887d40dc4dd0d5a71e9d07324e5c09c4383d93942d391727e7a40ff810b"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3661e6d4ba63a094138032c1356d557de5b3ea6fd3cca62a195f623e381c76"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97fbb77eaeb97591efdc654b8b5f3ccc066406ccfb3175b41382f221ecc216e8"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cc4bc73e53af8e7a42c8fd7923bbe35babacfa7394ae9240b3430b5dcf16b2a"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:35af5e4d5448fa179fd7fff0bba0fba51f876cd55212f96c8bbcecc5c684ae5c"}, + {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3511f6baf8438326e351097cecd137eb45c5f019944fe0fd0ae2fea2fd26be39"}, + {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:57863d16187995c10fe9cf911b897ed443ac68189179541734502353af33e693"}, + {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9e318e6786b1e750a62f90c6f7fa8b542102bdcf97c7c4de2a48b50b61bd36ec"}, + {file = "rpds_py-0.19.1-cp38-none-win32.whl", hash = "sha256:53dbc35808c6faa2ce3e48571f8f74ef70802218554884787b86a30947842a14"}, + {file = "rpds_py-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:8df1c283e57c9cb4d271fdc1875f4a58a143a2d1698eb0d6b7c0d7d5f49c53a1"}, + {file = "rpds_py-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e76c902d229a3aa9d5ceb813e1cbcc69bf5bda44c80d574ff1ac1fa3136dea71"}, + {file = "rpds_py-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de1f7cd5b6b351e1afd7568bdab94934d656abe273d66cda0ceea43bbc02a0c2"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fc5a84777cb61692d17988989690d6f34f7f95968ac81398d67c0d0994a897"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74129d5ffc4cde992d89d345f7f7d6758320e5d44a369d74d83493429dad2de5"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e360188b72f8080fefa3adfdcf3618604cc8173651c9754f189fece068d2a45"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13e6d4840897d4e4e6b2aa1443e3a8eca92b0402182aafc5f4ca1f5e24f9270a"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f09529d2332264a902688031a83c19de8fda5eb5881e44233286b9c9ec91856d"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0d4b52811dcbc1aba08fd88d475f75b4f6db0984ba12275d9bed1a04b2cae9b5"}, + {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dd635c2c4043222d80d80ca1ac4530a633102a9f2ad12252183bcf338c1b9474"}, + {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f35b34a5184d5e0cc360b61664c1c06e866aab077b5a7c538a3e20c8fcdbf90b"}, + {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d4ec0046facab83012d821b33cead742a35b54575c4edfb7ed7445f63441835f"}, + {file = "rpds_py-0.19.1-cp39-none-win32.whl", hash = "sha256:f5b8353ea1a4d7dfb59a7f45c04df66ecfd363bb5b35f33b11ea579111d4655f"}, + {file = "rpds_py-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:1fb93d3486f793d54a094e2bfd9cd97031f63fcb5bc18faeb3dd4b49a1c06523"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7d5c7e32f3ee42f77d8ff1a10384b5cdcc2d37035e2e3320ded909aa192d32c3"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:89cc8921a4a5028d6dd388c399fcd2eef232e7040345af3d5b16c04b91cf3c7e"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca34e913d27401bda2a6f390d0614049f5a95b3b11cd8eff80fe4ec340a1208"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5953391af1405f968eb5701ebbb577ebc5ced8d0041406f9052638bafe52209d"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:840e18c38098221ea6201f091fc5d4de6128961d2930fbbc96806fb43f69aec1"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d8b735c4d162dc7d86a9cf3d717f14b6c73637a1f9cd57fe7e61002d9cb1972"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce757c7c90d35719b38fa3d4ca55654a76a40716ee299b0865f2de21c146801c"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9421b23c85f361a133aa7c5e8ec757668f70343f4ed8fdb5a4a14abd5437244"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3b823be829407393d84ee56dc849dbe3b31b6a326f388e171555b262e8456cc1"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:5e58b61dcbb483a442c6239c3836696b79f2cd8e7eec11e12155d3f6f2d886d1"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39d67896f7235b2c886fb1ee77b1491b77049dcef6fbf0f401e7b4cbed86bbd4"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8b32cd4ab6db50c875001ba4f5a6b30c0f42151aa1fbf9c2e7e3674893fb1dc4"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c32e41de995f39b6b315d66c27dea3ef7f7c937c06caab4c6a79a5e09e2c415"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a129c02b42d46758c87faeea21a9f574e1c858b9f358b6dd0bbd71d17713175"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:346557f5b1d8fd9966059b7a748fd79ac59f5752cd0e9498d6a40e3ac1c1875f"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31e450840f2f27699d014cfc8865cc747184286b26d945bcea6042bb6aa4d26e"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01227f8b3e6c8961490d869aa65c99653df80d2f0a7fde8c64ebddab2b9b02fd"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69084fd29bfeff14816666c93a466e85414fe6b7d236cfc108a9c11afa6f7301"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d2b88efe65544a7d5121b0c3b003ebba92bfede2ea3577ce548b69c5235185"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ea961a674172ed2235d990d7edf85d15d8dfa23ab8575e48306371c070cda67"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:5beffdbe766cfe4fb04f30644d822a1080b5359df7db3a63d30fa928375b2720"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:720f3108fb1bfa32e51db58b832898372eb5891e8472a8093008010911e324c5"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c2087dbb76a87ec2c619253e021e4fb20d1a72580feeaa6892b0b3d955175a71"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ddd50f18ebc05ec29a0d9271e9dbe93997536da3546677f8ca00b76d477680c"}, + {file = "rpds_py-0.19.1.tar.gz", hash = "sha256:31dd5794837f00b46f4096aa8ccaa5972f73a938982e32ed817bb520c465e520"}, ] [[package]] @@ -3677,76 +3440,27 @@ pyasn1 = ">=0.1.3" [[package]] name = "rustworkx" -version = "0.14.2" +version = "0.15.1" description = "A python graph library implemented in Rust" optional = false python-versions = ">=3.8" files = [ - {file = "rustworkx-0.14.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a28a972dc7e0faf03f9f90c5be89328af8a71e609f311840e1a6abc6385edb79"}, - {file = "rustworkx-0.14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:50e682b8fd2f11f9e99c309a01f7ed88a09ad32cda35b92c49835b1c9536ec65"}, - {file = "rustworkx-0.14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6e1c3cf3d265835429074a1ecaa8f9bff327b188e1496a120bf8be8260a46453"}, - {file = "rustworkx-0.14.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a22c02f74bf391b48ae92f633083d068055f3ed85050e35fe6cda967ff8a825"}, - {file = "rustworkx-0.14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:996bad21eacbe124dd1e6abca47dd69ade9db0d4df5dd29197694f5d8e0a8258"}, - {file = "rustworkx-0.14.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95c4647461f05fd9f99bae52002a929e8628d4e5a2e732dbfd7abd00ae5257b7"}, - {file = "rustworkx-0.14.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:829444876bba1940fa3109998f3b6c9184256d91eea5f0e09d9e9f8f26bb4704"}, - {file = "rustworkx-0.14.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:987b430dce1351a0c761bd6eedb8f6999f48983c9d4b06bf4b0b9dc45d08be8d"}, - {file = "rustworkx-0.14.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:18ef16f9b6b4f1c0d458fde3f213b78436ac810d61cae60385696b411aa80e1d"}, - {file = "rustworkx-0.14.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fe30f1e22e69cbab4182d0017e21c345bf75f142a7b66a828227dd3c654d524c"}, - {file = "rustworkx-0.14.2-cp310-cp310-win32.whl", hash = "sha256:c1fe9f9ed18e270074d3632f6c70cc75c461535d9e76db39d1c0ab712bf64a7a"}, - {file = "rustworkx-0.14.2-cp310-cp310-win_amd64.whl", hash = "sha256:271b36412421d622e9e8cd27e2c6e1bd356e452f979edd41bb32d308df936f47"}, - {file = "rustworkx-0.14.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c52e34ff4b08d1eaedd2ec906bca4317f4f852b36e4615d372b1ff2bb435ff26"}, - {file = "rustworkx-0.14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c97dc0cf7efef033ce50fa570887f97896b0f449c841ec3b127ecb70b3c16c84"}, - {file = "rustworkx-0.14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:114bec1606ae31c089ecf52aa511551c545c6ce0746d3e8766082ad450377a2c"}, - {file = "rustworkx-0.14.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:950fa4ffc1691081587c87c4e869a8f5c7d0672d35ce1ba7c69f758f90bfe8c0"}, - {file = "rustworkx-0.14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2134aa9c2065ab6c934017b6909e224e860003eb5dbaa5d2c4e87fff1187459a"}, - {file = "rustworkx-0.14.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bec8f1f1a6fed3ffbf5348a2b9d700f0b840fed2faa6a5198838d0fa9674a781"}, - {file = "rustworkx-0.14.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8046991499df7aa984b3d9092e4f013597901c919aaf6fa43147e8550685734"}, - {file = "rustworkx-0.14.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acb4256fba2c4f5c4ec009f383623b6a7c0a2dbeed1b529d22a193113927364a"}, - {file = "rustworkx-0.14.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:be7be125f9313b58829f7202a66dc166b61bf3c4bbe0c509b8d6902ed0d2da45"}, - {file = "rustworkx-0.14.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5d00f87fce0e48c6d7af4b63ee635188178e91462b52ac900d36ec3184ce92fc"}, - {file = "rustworkx-0.14.2-cp311-cp311-win32.whl", hash = "sha256:521e0f432a94ac9a4c92f30a746b971f7e49476fd128d83d94d4b15a2c17245d"}, - {file = "rustworkx-0.14.2-cp311-cp311-win_amd64.whl", hash = "sha256:8fd20776c0f543340ef96450ba5d9d670b8d74396315f7191303a392844271e0"}, - {file = "rustworkx-0.14.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7bb37e877653ae4b4d505fc7e5f7847ae06e6822b91cec56e9e851941a6a0ae7"}, - {file = "rustworkx-0.14.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:230808e3878236464ac00001d8b440382aa6230f0073554ec627580863e380cc"}, - {file = "rustworkx-0.14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a7cb7103ba88e12e3dd8e3b28365cbe971a8c158c1ee770646b2f3fd5cedab0"}, - {file = "rustworkx-0.14.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2637d0e496f34bac45f926b0aa12fb2e143581208f29a424cfb0eb5a7b5c3bfa"}, - {file = "rustworkx-0.14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692f78ee7f7a60d9c7082a5a26b4eefb697526f195172798389d7009510d84f3"}, - {file = "rustworkx-0.14.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d5513e93b7c10fbce954771a74fc86d551eb33b9eb318eaa35d7668f9929da"}, - {file = "rustworkx-0.14.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26076523a1c43e903c633f2375afac28fdbb83b9668bee00fae24d8c672bf6c9"}, - {file = "rustworkx-0.14.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6de5e2df15c415dfb6e5cb7175239d0862568cb10d028f451d358d101be5d8bf"}, - {file = "rustworkx-0.14.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:21c86c240628abc2123d7d1317647073a738bdfe143c55728261b66bc32806e2"}, - {file = "rustworkx-0.14.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0aa0277b931ca3fdfae07f8999b6a63dc9b89622b2fab820fa6bd95dd1e2e2eb"}, - {file = "rustworkx-0.14.2-cp312-cp312-win32.whl", hash = "sha256:fdc632673d4cd7f1cffe8ce13ea17dc361cf9d0d9f37dfa0888d94bdd5e6c159"}, - {file = "rustworkx-0.14.2-cp312-cp312-win_amd64.whl", hash = "sha256:47768f985f32ac1cd807af816fbd5f6e2433889793afdd838891ae516a95c8a6"}, - {file = "rustworkx-0.14.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:bfeee5a5be9eb71635a7897a6d2c034b1c01bf876fd15007b8bd4c6eaa8921e2"}, - {file = "rustworkx-0.14.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4a20434c77f3daab043ab2f96386b5da871ebf15a5495f9ad5b916c3edf03e5c"}, - {file = "rustworkx-0.14.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d856549e874e064af136f2ce304eb896d32d8865c3e98f8d9e83b577f4c57f1d"}, - {file = "rustworkx-0.14.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2521a223fb5aab2a14351205456d02bd851e0ec6b0c028f5598fe14f292e881b"}, - {file = "rustworkx-0.14.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc9e7718eee8295cd5c11a5cf1c0fc7772e9c1dcc3d110edba4c77aad47e7f07"}, - {file = "rustworkx-0.14.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c23ef82b1d373e07c280b8b6927dbad3953597e34c752e14843ac3df722a621"}, - {file = "rustworkx-0.14.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a46e0d1398138a75fb909369ffe6dfdcec6bab4d21794e80a9abf45fd2823f68"}, - {file = "rustworkx-0.14.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16fb941e8f48aea96ee38471a1ae770ec68623864a9b0e4760aabf82c41fc2b"}, - {file = "rustworkx-0.14.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fa92a97e5d35c6901553a812f31ca18305922c0ef06c2d7a9d20fbcc0769b4d1"}, - {file = "rustworkx-0.14.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2e14b2956f2d06f5bb196bcd95f73008245eb6ffa9ee08f86ec369acf0cc04be"}, - {file = "rustworkx-0.14.2-cp38-cp38-win32.whl", hash = "sha256:816d33f69f4189376e1bb8132dea1deef1cd019b25bd281f01b7f394fcadbdad"}, - {file = "rustworkx-0.14.2-cp38-cp38-win_amd64.whl", hash = "sha256:4163f9c2c2d2158e053b30a39f74b0382b4c5a8a43f192c13b736e200b5e2025"}, - {file = "rustworkx-0.14.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a9b55a8f97799b159da96087176a0e97679dca0b6b5a14b3140aeda7e1050777"}, - {file = "rustworkx-0.14.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:edb2d67870e41d5a1e16288bca0758580fb6961e8b4dfc337557bdaab81ff016"}, - {file = "rustworkx-0.14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f058bdb50c5b0be731b96ffd789c6cec2a99e7f757a57763b2cc56004ed95af6"}, - {file = "rustworkx-0.14.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff900cb6ae2d4028ffe5a3075cfefa21b14929270844b172595e6de0d2f183eb"}, - {file = "rustworkx-0.14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630177a80c68823fb2dd94733298377bd52c2ce3f66758ea0a63966fc2d7c08f"}, - {file = "rustworkx-0.14.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a79c177a9e4f1c623554e01319fcb7b2a062ae26def7b85dc1f0539b7cdd874"}, - {file = "rustworkx-0.14.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff956ee6c8224b8225478bb72103d4fc6dd4a247c066da30927776e1b05690"}, - {file = "rustworkx-0.14.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae61a4c58186b4e428947b92ac2aa0557bcd5071fe8102a542c4337f64091766"}, - {file = "rustworkx-0.14.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5a96b6f96e1bb4e8ee337618d8af0a1aec16c2eda6ffd9968e16d161850d1e77"}, - {file = "rustworkx-0.14.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49bc143729e0d64a51b0ec6d745665f067116db78ce958d5cbe0389e69c6e73c"}, - {file = "rustworkx-0.14.2-cp39-cp39-win32.whl", hash = "sha256:f11e858a1804d5e276d18d6fc197f797adf5da82cd3382550abeef50196c5a7e"}, - {file = "rustworkx-0.14.2-cp39-cp39-win_amd64.whl", hash = "sha256:b55e75ea35a225d6b0afbdd449665e3b907684347be6a38648bdbfd50e177bf0"}, - {file = "rustworkx-0.14.2.tar.gz", hash = "sha256:bd649322c0649b71fa18cc70a9af027b549560415fa860d6894736029c277b13"}, + {file = "rustworkx-0.15.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6cd4496d3298cd3205c03545e48cc37d21e0455d57752af801d3fb250452d590"}, + {file = "rustworkx-0.15.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:cb518f5649e62d753e29ca1e57290c8f58adbebcd154dc3159f4a36ebfa1e2b7"}, + {file = "rustworkx-0.15.1-cp38-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac68ae2515ece22ba3ef56f3d16ad6bf707955f650d623190b2e7d706c6dc92"}, + {file = "rustworkx-0.15.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b903edec1d803704b499959f9d6f6119cdda63b9b64194a4b4307e506b112f0"}, + {file = "rustworkx-0.15.1-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2c97a56ff8a0f6c273a83e26e627c72207442b4252aa550acad0bff42caac40"}, + {file = "rustworkx-0.15.1-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:241c502532e348ba89200823326dba30de4df4b886cb2fd5a140b359ff124bb3"}, + {file = "rustworkx-0.15.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e5f4156f46fa03177c9b0580450eab87786063495d48b457762a5bdd20c55e2"}, + {file = "rustworkx-0.15.1-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7834ab34748db6214ec3b3836b996b23882dc83184234e6d346d6bb85fd58ae5"}, + {file = "rustworkx-0.15.1-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ce53f173fed16e1d51d9df9f23475a16c981b03bf1a412d991c75a70db6b1dc1"}, + {file = "rustworkx-0.15.1-cp38-abi3-win32.whl", hash = "sha256:308bc76a01bcae9af4602d8b9ed58021df37dd0bb5a7b2e3831ae53c5e234ff0"}, + {file = "rustworkx-0.15.1-cp38-abi3-win_amd64.whl", hash = "sha256:89077382633e918d2392772f53b9d6d30eee51eb536f8d38ee195c212b2f0427"}, + {file = "rustworkx-0.15.1.tar.gz", hash = "sha256:0e0cc86599f979285b2ab9c357276f3272f3fcb3b2df5651a6bf9704c570d4c1"}, ] [package.dependencies] -numpy = ">=1.16.0,<2" +numpy = ">=1.16.0,<3" [package.extras] all = ["matplotlib (>=3.0)", "pillow (>=5.4)"] @@ -3755,60 +3469,61 @@ mpl = ["matplotlib (>=3.0)"] [[package]] name = "scipy" -version = "1.13.1" +version = "1.14.0" description = "Fundamental algorithms for scientific computing in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, - {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, - {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, - {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, - {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, - {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, - {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, - {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, - {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, - {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, - {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, - {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, - {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, - {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, - {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, - {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, - {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, - {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, - {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, - {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, - {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, - {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, - {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, - {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, - {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, + {file = "scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484"}, + {file = "scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6"}, + {file = "scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7"}, + {file = "scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1"}, + {file = "scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0"}, + {file = "scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0"}, + {file = "scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d"}, + {file = "scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359"}, + {file = "scipy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6d056a8709ccda6cf36cdd2eac597d13bc03dba38360f418560a93050c76a16e"}, + {file = "scipy-1.14.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f0a50da861a7ec4573b7c716b2ebdcdf142b66b756a0d392c236ae568b3a93fb"}, + {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:94c164a9e2498e68308e6e148646e486d979f7fcdb8b4cf34b5441894bdb9caf"}, + {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a7d46c3e0aea5c064e734c3eac5cf9eb1f8c4ceee756262f2c7327c4c2691c86"}, + {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eee2989868e274aae26125345584254d97c56194c072ed96cb433f32f692ed8"}, + {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3154691b9f7ed73778d746da2df67a19d046a6c8087c8b385bc4cdb2cfca74"}, + {file = "scipy-1.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c40003d880f39c11c1edbae8144e3813904b10514cd3d3d00c277ae996488cdb"}, + {file = "scipy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:5b083c8940028bb7e0b4172acafda6df762da1927b9091f9611b0bcd8676f2bc"}, + {file = "scipy-1.14.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff2438ea1330e06e53c424893ec0072640dac00f29c6a43a575cbae4c99b2b9"}, + {file = "scipy-1.14.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:bbc0471b5f22c11c389075d091d3885693fd3f5e9a54ce051b46308bc787e5d4"}, + {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:64b2ff514a98cf2bb734a9f90d32dc89dc6ad4a4a36a312cd0d6327170339eb0"}, + {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:7d3da42fbbbb860211a811782504f38ae7aaec9de8764a9bef6b262de7a2b50f"}, + {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d91db2c41dd6c20646af280355d41dfa1ec7eead235642178bd57635a3f82209"}, + {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a01cc03bcdc777c9da3cfdcc74b5a75caffb48a6c39c8450a9a05f82c4250a14"}, + {file = "scipy-1.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65df4da3c12a2bb9ad52b86b4dcf46813e869afb006e58be0f516bc370165159"}, + {file = "scipy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:4c4161597c75043f7154238ef419c29a64ac4a7c889d588ea77690ac4d0d9b20"}, + {file = "scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b"}, ] [package.dependencies] -numpy = ">=1.22.4,<2.3" +numpy = ">=1.23.5,<2.3" [package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] -doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] -test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] +test = ["Cython", "array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] name = "setuptools" -version = "70.0.0" +version = "71.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, - {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, + {file = "setuptools-71.1.0-py3-none-any.whl", hash = "sha256:33874fdc59b3188304b2e7c80d9029097ea31627180896fb549c578ceb8a0855"}, + {file = "setuptools-71.1.0.tar.gz", hash = "sha256:032d42ee9fb536e33087fb66cac5f840eb9391ed05637b3f2a76a7c8fb477936"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -3856,26 +3571,26 @@ files = [ [[package]] name = "sphinx" -version = "7.3.7" +version = "7.4.7" description = "Python documentation generator" optional = false python-versions = ">=3.9" files = [ - {file = "sphinx-7.3.7-py3-none-any.whl", hash = "sha256:413f75440be4cacf328f580b4274ada4565fb2187d696a84970c23f77b64d8c3"}, - {file = "sphinx-7.3.7.tar.gz", hash = "sha256:a4a7db75ed37531c05002d56ed6948d4c42f473a36f46e1382b0bd76ca9627bc"}, + {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"}, + {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"}, ] [package.dependencies] alabaster = ">=0.7.14,<0.8.0" -babel = ">=2.9" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.18.1,<0.22" +babel = ">=2.13" +colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} +docutils = ">=0.20,<0.22" imagesize = ">=1.3" -Jinja2 = ">=3.0" -packaging = ">=21.0" -Pygments = ">=2.14" -requests = ">=2.25.0" -snowballstemmer = ">=2.0" +Jinja2 = ">=3.1" +packaging = ">=23.0" +Pygments = ">=2.17" +requests = ">=2.30.0" +snowballstemmer = ">=2.2" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" @@ -3886,8 +3601,45 @@ tomli = {version = ">=2", markers = "python_version < \"3.11\""} [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "importlib_metadata", "mypy (==1.9.0)", "pytest (>=6.0)", "ruff (==0.3.7)", "sphinx-lint", "tomli", "types-docutils", "types-requests"] -test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=6.0)", "setuptools (>=67.0)"] +lint = ["flake8 (>=6.0)", "importlib-metadata (>=6.0)", "mypy (==1.10.1)", "pytest (>=6.0)", "ruff (==0.5.2)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-docutils (==0.21.0.20240711)", "types-requests (>=2.30.0)"] +test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b2" +description = "A modern skeleton for Sphinx themes." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, + {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, +] + +[package.dependencies] +sphinx = ">=4.0" + +[package.extras] +docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] + +[[package]] +name = "sphinx-book-theme" +version = "1.1.3" +description = "A clean book theme for scientific explanations and documentation with Sphinx" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinx_book_theme-1.1.3-py3-none-any.whl", hash = "sha256:a554a9a7ac3881979a87a2b10f633aa2a5706e72218a10f71be38b3c9e831ae9"}, + {file = "sphinx_book_theme-1.1.3.tar.gz", hash = "sha256:1f25483b1846cb3d353a6bc61b3b45b031f4acf845665d7da90e01ae0aef5b4d"}, +] + +[package.dependencies] +pydata-sphinx-theme = ">=0.15.2" +sphinx = ">=5" + +[package.extras] +code-style = ["pre-commit"] +doc = ["ablog", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex", "sphinxcontrib-youtube", "sphinxext-opengraph"] +test = ["beautifulsoup4", "coverage", "defusedxml", "myst-nb", "pytest", "pytest-cov", "pytest-regressions", "sphinx_thebe"] [[package]] name = "sphinx-copybutton" @@ -3941,13 +3693,13 @@ test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" -version = "2.0.5" +version = "2.0.6" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, - {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, + {file = "sphinxcontrib_htmlhelp-2.0.6-py3-none-any.whl", hash = "sha256:1b9af5a2671a61410a868fce050cab7ca393c218e6205cbc7f590136f207395c"}, + {file = "sphinxcontrib_htmlhelp-2.0.6.tar.gz", hash = "sha256:c6597da06185f0e3b4dc952777a04200611ef563882e0c244d27a15ee22afa73"}, ] [package.extras] @@ -3971,19 +3723,19 @@ test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" -version = "1.0.7" +version = "1.0.8" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, - {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, + {file = "sphinxcontrib_qthelp-1.0.8-py3-none-any.whl", hash = "sha256:323d6acc4189af76dfe94edd2a27d458902319b60fcca2aeef3b2180c106a75f"}, + {file = "sphinxcontrib_qthelp-1.0.8.tar.gz", hash = "sha256:db3f8fa10789c7a8e76d173c23364bdf0ebcd9449969a9e6a3dd31b8b7469f03"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] -test = ["pytest"] +test = ["defusedxml (>=0.7.1)", "pytest"] [[package]] name = "sphinxcontrib-serializinghtml" @@ -4001,93 +3753,6 @@ lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] -[[package]] -name = "sqlalchemy" -version = "2.0.30" -description = "Database Abstraction Library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-win32.whl", hash = "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"}, - {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"}, - {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} -typing-extensions = ">=4.6.0" - -[package.extras] -aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] -aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910)"] -mysql = ["mysqlclient (>=1.4.0)"] -mysql-connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=8)"] -oracle-oracledb = ["oracledb (>=1.0.1)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.29.1)"] -postgresql-psycopg = ["psycopg (>=3.0.7)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] -pymysql = ["pymysql"] -sqlcipher = ["sqlcipher3_binary"] - [[package]] name = "sspilib" version = "0.1.0" @@ -4206,31 +3871,20 @@ files = [ [[package]] name = "sympy" -version = "1.12.1" +version = "1.13.1" description = "Computer algebra system (CAS) in Python" optional = false python-versions = ">=3.8" files = [ - {file = "sympy-1.12.1-py3-none-any.whl", hash = "sha256:9b2cbc7f1a640289430e13d2a56f02f867a1da0190f2f99d8968c2f74da0e515"}, - {file = "sympy-1.12.1.tar.gz", hash = "sha256:2877b03f998cd8c08f07cd0de5b767119cd3ef40d09f41c30d722f6686b0fb88"}, + {file = "sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8"}, + {file = "sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f"}, ] [package.dependencies] -mpmath = ">=1.1.0,<1.4.0" - -[[package]] -name = "tabulate" -version = "0.9.0" -description = "Pretty-print tabular data" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, - {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, -] +mpmath = ">=1.1.0,<1.4" [package.extras] -widechars = ["wcwidth"] +dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] [[package]] name = "tinycss2" @@ -4362,13 +4016,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -4507,22 +4161,7 @@ files = [ {file = "widgetsnbextension-4.0.11.tar.gz", hash = "sha256:8b22a8f1910bfd188e596fe7fc05dcbd87e810c8a4ba010bdb3da86637398474"}, ] -[[package]] -name = "zipp" -version = "3.19.2" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, - {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, -] - -[package.extras] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] - [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "9af018e64f3a9df6f063d8299f7f0235152ef7b26756c7e19d3a0b95d076408d" +content-hash = "c10e172a0789a5360eedb13be2ba8f28212d23c9e5d2bbc049152b758441495a" diff --git a/pyproject.toml b/pyproject.toml index 9c9441cc..512eeb8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,6 @@ pytket-qiskit = "^0.53.0" pytket-cirq = "^0.36.0" pytket-qujax = "0.19.0" pytest = "^8.2.2" -pydata-sphinx-theme = "^0.15.3" qiskit-algorithms = "^0.3.0" ipykernel = "^6.29.4" kahypar = "^1.3.5" @@ -22,10 +21,7 @@ jupyter-sphinx = "^0.5.3" quimb = "^1.8.1" openfermion = "^1.6.1" ipyparallel = "^8.8.0" -myst-nb = "^1.1.0" -sphinx-book-theme = "^1.1.3" -nbsphinx = "^0.9.4" -quantinuum-docs-theme = {git = "https://github.com/aidanCQ/qui-sphinx.git", rev = "dist"} +quantinuum-docs-theme = { git = "https://github.com/aidanCQ/qui-sphinx.git", rev = "dist" } furo = "^2024.5.6" From ede0492b461442c58beaa6dd6b454be8290008a7 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:37:43 +0100 Subject: [PATCH 34/76] try --no-update flag --- .github/workflows/check-docs-build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check-docs-build.yml b/.github/workflows/check-docs-build.yml index 33053bb7..2a0bba6c 100644 --- a/.github/workflows/check-docs-build.yml +++ b/.github/workflows/check-docs-build.yml @@ -43,7 +43,8 @@ jobs: python-version: 3.11 - name: install python requirements for manual run: | - python -m pip install poetry + python -m pip install poetry + poetry lock [--no-update] poetry install poetry shell - name: install graphviz From 0f6d5e7e5e48d38950c660f1ba128bcd2b73b9fb Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:40:18 +0100 Subject: [PATCH 35/76] fix typo in workflow --- .github/workflows/check-docs-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-docs-build.yml b/.github/workflows/check-docs-build.yml index 2a0bba6c..358a8b79 100644 --- a/.github/workflows/check-docs-build.yml +++ b/.github/workflows/check-docs-build.yml @@ -44,7 +44,7 @@ jobs: - name: install python requirements for manual run: | python -m pip install poetry - poetry lock [--no-update] + poetry lock --no-update poetry install poetry shell - name: install graphviz From d3b421abc9a7bac33091c7c27d04b27d27b4ae33 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:49:29 +0100 Subject: [PATCH 36/76] remove outdated quantinuum-shinx dependency --- poetry.lock | 86 +------------------------------------------------- pyproject.toml | 1 - 2 files changed, 1 insertion(+), 86 deletions(-) diff --git a/poetry.lock b/poetry.lock index 309003ee..8f10131b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,23 +1,5 @@ # This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. -[[package]] -name = "accessible-pygments" -version = "0.0.5" -description = "A collection of accessible pygments styles" -optional = false -python-versions = ">=3.9" -files = [ - {file = "accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7"}, - {file = "accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872"}, -] - -[package.dependencies] -pygments = ">=1.5" - -[package.extras] -dev = ["pillow", "pkginfo (>=1.10)", "playwright", "pre-commit", "setuptools", "twine (>=5.0)"] -tests = ["hypothesis", "pytest"] - [[package]] name = "alabaster" version = "0.7.16" @@ -2679,34 +2661,6 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" -[[package]] -name = "pydata-sphinx-theme" -version = "0.15.4" -description = "Bootstrap-based Sphinx theme from the PyData community" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pydata_sphinx_theme-0.15.4-py3-none-any.whl", hash = "sha256:2136ad0e9500d0949f96167e63f3e298620040aea8f9c74621959eda5d4cf8e6"}, - {file = "pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d"}, -] - -[package.dependencies] -accessible-pygments = "*" -Babel = "*" -beautifulsoup4 = "*" -docutils = "!=0.17.0" -packaging = "*" -pygments = ">=2.7" -sphinx = ">=5" -typing-extensions = "*" - -[package.extras] -a11y = ["pytest-playwright"] -dev = ["pandoc", "pre-commit", "pydata-sphinx-theme[doc,test]", "pyyaml", "sphinx-theme-builder[cli]", "tox"] -doc = ["ablog (>=0.11.8)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "ipywidgets", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (>=3.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap", "sphinx-togglebutton", "sphinxcontrib-youtube (>=1.4.1)", "sphinxext-rediraffe", "xarray"] -i18n = ["Babel", "jinja2"] -test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] - [[package]] name = "pygments" version = "2.18.0" @@ -3187,24 +3141,6 @@ requests-ntlm = ">=1.1.0" urllib3 = ">=1.21.1" websocket-client = ">=1.5.1" -[[package]] -name = "quantinuum-docs-theme" -version = "0.1.0" -description = "" -optional = false -python-versions = "^3.10" -files = [] -develop = false - -[package.dependencies] -sphinx-book-theme = "^1.1.3" - -[package.source] -type = "git" -url = "https://github.com/aidanCQ/qui-sphinx.git" -reference = "dist" -resolved_reference = "20b578e090b7413c8779a44b56dc3b50087b8258" - [[package]] name = "quimb" version = "1.8.4" @@ -3621,26 +3557,6 @@ sphinx = ">=4.0" [package.extras] docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] -[[package]] -name = "sphinx-book-theme" -version = "1.1.3" -description = "A clean book theme for scientific explanations and documentation with Sphinx" -optional = false -python-versions = ">=3.9" -files = [ - {file = "sphinx_book_theme-1.1.3-py3-none-any.whl", hash = "sha256:a554a9a7ac3881979a87a2b10f633aa2a5706e72218a10f71be38b3c9e831ae9"}, - {file = "sphinx_book_theme-1.1.3.tar.gz", hash = "sha256:1f25483b1846cb3d353a6bc61b3b45b031f4acf845665d7da90e01ae0aef5b4d"}, -] - -[package.dependencies] -pydata-sphinx-theme = ">=0.15.2" -sphinx = ">=5" - -[package.extras] -code-style = ["pre-commit"] -doc = ["ablog", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex", "sphinxcontrib-youtube", "sphinxext-opengraph"] -test = ["beautifulsoup4", "coverage", "defusedxml", "myst-nb", "pytest", "pytest-cov", "pytest-regressions", "sphinx_thebe"] - [[package]] name = "sphinx-copybutton" version = "0.5.2" @@ -4164,4 +4080,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "c10e172a0789a5360eedb13be2ba8f28212d23c9e5d2bbc049152b758441495a" +content-hash = "781d472b949423807c34b0aff904ebe36ea1e688eb3a99226d569a993c27f58d" diff --git a/pyproject.toml b/pyproject.toml index 512eeb8e..d9f027d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,6 @@ jupyter-sphinx = "^0.5.3" quimb = "^1.8.1" openfermion = "^1.6.1" ipyparallel = "^8.8.0" -quantinuum-docs-theme = { git = "https://github.com/aidanCQ/qui-sphinx.git", rev = "dist" } furo = "^2024.5.6" From 031a68405b23b206bbe568babbcaf096f12df5a7 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:49:52 +0100 Subject: [PATCH 37/76] remove legacy contributing file --- docs/examples/CONTRIBUTING.md | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 docs/examples/CONTRIBUTING.md diff --git a/docs/examples/CONTRIBUTING.md b/docs/examples/CONTRIBUTING.md deleted file mode 100644 index c7cac86f..00000000 --- a/docs/examples/CONTRIBUTING.md +++ /dev/null @@ -1,25 +0,0 @@ -# Contributing new notebooks - -The sources for all these notebooks are the Python scripts in the `python` -directory. The notebook files are generated from them with the `gen-nb` script -(which requires `p2j`). Do not edit the notebooks directly; instead edit the -Python scripts and regenerate the notebooks by running `gen-nb`. - -The notebooks are now deployed as html pages using [jupyterbook](https://jupyterbook.org/en/stable/intro.html) tool. To make changes to the notebooks, make changes to the corresponding python files in `examples/python` and then use the `gen-nb` script to generate the notebook files. - -Currently some notebooks are excluded from execution. See the `examples/_config.yml` file. These notebooks are still deployed as html pages but they are not in executed form. - -The `check-examples` script (which is run regularly on the CI and can also be -run locally) first checks that the notebooks match what is generated by `p2j` -from the Python scripts; and then attempts to run all the scripts. For those -scripts that require credentials, it first tries to load them, and skips the -test if it fails. For those that require containers to be running, it first -tries to start them, and skips the test if it fails. - -To add a new script, you can either create the Python file and convert it using -`gen-nb` or create a notebook file, convert it to Python using `p2j -r`, and -then run `gen-nb` to convert back. In either case, please check that the -formatting in the notebook is satisfactory. Then add the name of the new -notebook to `maintained-notebooks.txt`. If the new script requires special -set-up, such as local credentials, make sure it is covered by the checks in -`check-examples`. From 1479596a7f5744813da5af349bf2a0d61c3ed7be Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:54:26 +0100 Subject: [PATCH 38/76] use poetry run instead of poetry shell --- .github/workflows/check-docs-build.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/check-docs-build.yml b/.github/workflows/check-docs-build.yml index 358a8b79..aee74e9c 100644 --- a/.github/workflows/check-docs-build.yml +++ b/.github/workflows/check-docs-build.yml @@ -41,16 +41,11 @@ jobs: uses: actions/setup-python@v5 with: python-version: 3.11 - - name: install python requirements for manual + - name: install python requirements for docs run: | python -m pip install poetry poetry lock --no-update poetry install - poetry shell - - name: install graphviz + - name: build manual and examples run: | - sudo apt-get update - sudo apt-get install graphviz - - name: build manual - run: | - ./build-docs.sh + poetry run ./build-docs.sh From 0bba02412873f6ef000767e34993e9bdc210a06e Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 22:18:13 +0100 Subject: [PATCH 39/76] remove unused sphinx extension --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index db30f765..927b4498 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,7 +17,6 @@ "jupyter_sphinx", "sphinx_copybutton", "sphinx.ext.autosectionlabel", - "myst_nb", ] myst_enable_extensions = ["dollarmath", "html_image", "attrs_inline"] From 2962f8de0ab8fe5be6e7975f73a3b7e63b55409d Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 22:23:53 +0100 Subject: [PATCH 40/76] install graphviz in docs build --- .github/workflows/check-docs-build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/check-docs-build.yml b/.github/workflows/check-docs-build.yml index aee74e9c..0be91c1f 100644 --- a/.github/workflows/check-docs-build.yml +++ b/.github/workflows/check-docs-build.yml @@ -46,6 +46,10 @@ jobs: python -m pip install poetry poetry lock --no-update poetry install + - name: install graphviz + run: | + sudo apt-get update + sudo apt-get install graphviz - name: build manual and examples run: | poetry run ./build-docs.sh From 30a77c7b683943f43369c18817e278445477b60b Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 22:30:19 +0100 Subject: [PATCH 41/76] delete unused _static files --- docs/_static/Quantinuum_logo_black.png | Bin 18245 -> 0 bytes docs/_static/Quantinuum_logo_white.png | Bin 16704 -> 0 bytes docs/_static/custom.css | 57 ------------------------- 3 files changed, 57 deletions(-) delete mode 100644 docs/_static/Quantinuum_logo_black.png delete mode 100644 docs/_static/Quantinuum_logo_white.png delete mode 100644 docs/_static/custom.css diff --git a/docs/_static/Quantinuum_logo_black.png b/docs/_static/Quantinuum_logo_black.png deleted file mode 100644 index 5569581b8e420f14eee561cbf1bf78ca0c96882f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18245 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 diff --git a/docs/_static/custom.css b/docs/_static/custom.css deleted file mode 100644 index 4beccdbe..00000000 --- a/docs/_static/custom.css +++ /dev/null @@ -1,57 +0,0 @@ -.wy-side-nav-search, -.wy-nav-top { - background: #5A46BE; -} - -.wy-grid-for-nav, -.wy-body-for-nav, -.wy-nav-side, -.wy-side-scroll, -.wy-menu, -.wy-menu-vertical { - background-color: #FFFFFF; -} - -.wy-menu-vertical a:hover { - background-color: #d9d9d9; -} - -.btn-link:visited, -.btn-link, -a:visited, -.a.reference.external, -.a.reference.internal, -.wy-menu-vertical a, -.wy-menu-vertical li, -.wy-menu-vertical ul, -.span.pre, -.sig-param, -.std.std-ref, - - -html[data-theme=light] { - --pst-color-inline-code: rgb(199, 37, 78) !important; -} - -.sig-name { - font-size: 1.25rem; -} - -.navbar .navbar-brand img { - height: 100%; - max-width: 100%; - width: auto; -} - -.navbar .navbar-brand { - align-items: center; - display: flex; - flex-shrink: 0; - gap: .5rem; - height: var(--pst-header-height); - margin: 0; - max-height: var(--pst-header-height); - padding: .5rem 0; - position: relative; - width: auto; -} \ No newline at end of file From 95ca70b7b7d0f43178ae280eb3c6b0ceadc2bbf4 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 22:30:51 +0100 Subject: [PATCH 42/76] remove --no-update flag --- .github/workflows/check-docs-build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/check-docs-build.yml b/.github/workflows/check-docs-build.yml index 0be91c1f..4d80ba32 100644 --- a/.github/workflows/check-docs-build.yml +++ b/.github/workflows/check-docs-build.yml @@ -44,7 +44,6 @@ jobs: - name: install python requirements for docs run: | python -m pip install poetry - poetry lock --no-update poetry install - name: install graphviz run: | From d3d224b52dafc43bbde2f1c62dc2c257f3bdf365 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Jul 2024 23:41:04 +0100 Subject: [PATCH 43/76] add favicon --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 927b4498..5d3153b6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,7 +24,7 @@ html_theme = "furo" templates_path = ["./quantinuum-sphinx/_templates/"] html_static_path = ["./quantinuum-sphinx/_static/", "_static/"] - +html_favicon = "quantinuum-sphinx/_static/assets/quantinuum_favicon.svg" pytketdoc_base = "https://tket.quantinuum.com/api-docs/" From 4bcf70db0cc08d7f17443b3fa493db9dbe7ec674 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:47:35 +0100 Subject: [PATCH 44/76] add myst_nb extension again to fix notebook build --- docs/conf.py | 2 +- poetry.lock | 433 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 426 insertions(+), 10 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5d3153b6..859588da 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,6 +17,7 @@ "jupyter_sphinx", "sphinx_copybutton", "sphinx.ext.autosectionlabel", + "myst_nb", ] myst_enable_extensions = ["dollarmath", "html_image", "attrs_inline"] @@ -24,7 +25,6 @@ html_theme = "furo" templates_path = ["./quantinuum-sphinx/_templates/"] html_static_path = ["./quantinuum-sphinx/_static/", "_static/"] -html_favicon = "quantinuum-sphinx/_static/assets/quantinuum_favicon.svg" pytketdoc_base = "https://tket.quantinuum.com/api-docs/" diff --git a/poetry.lock b/poetry.lock index 8f10131b..cbae9d23 100644 --- a/poetry.lock +++ b/poetry.lock @@ -365,6 +365,20 @@ google-api-core = {version = ">=1.14.0", extras = ["grpc"]} proto-plus = ">=1.20.0" protobuf = ">=3.15.0" +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -915,12 +929,12 @@ files = [ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = [ - {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] grpcio-status = [ - {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" @@ -987,6 +1001,77 @@ dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"] +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + [[package]] name = "grpcio" version = "1.65.1" @@ -1145,6 +1230,25 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] +[[package]] +name = "importlib-metadata" +version = "8.2.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, + {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -1295,14 +1399,14 @@ files = [ jaxlib = ">=0.4.27,<=0.4.30" ml-dtypes = ">=0.2.0" numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.22", markers = "python_version < \"3.11\""}, {version = ">=1.23.2", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] opt-einsum = "*" scipy = [ - {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, {version = ">=1.9", markers = "python_version < \"3.12\""}, + {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, ] [package.extras] @@ -1347,8 +1451,8 @@ files = [ ml-dtypes = ">=0.2.0" numpy = ">=1.22" scipy = [ - {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, {version = ">=1.9", markers = "python_version < \"3.12\""}, + {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, ] [[package]] @@ -1422,6 +1526,33 @@ files = [ [package.dependencies] referencing = ">=0.31.0" +[[package]] +name = "jupyter-cache" +version = "1.0.0" +description = "A defined interface for working with a cache of jupyter notebooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "jupyter_cache-1.0.0-py3-none-any.whl", hash = "sha256:594b1c4e29b488b36547e12477645f489dbdc62cc939b2408df5679f79245078"}, + {file = "jupyter_cache-1.0.0.tar.gz", hash = "sha256:d0fa7d7533cd5798198d8889318269a8c1382ed3b22f622c09a9356521f48687"}, +] + +[package.dependencies] +attrs = "*" +click = "*" +importlib-metadata = "*" +nbclient = ">=0.2" +nbformat = "*" +pyyaml = "*" +sqlalchemy = ">=1.3.12,<3" +tabulate = "*" + +[package.extras] +cli = ["click-log"] +code-style = ["pre-commit (>=2.12)"] +rtd = ["ipykernel", "jupytext", "myst-nb", "nbdime", "sphinx-book-theme", "sphinx-copybutton"] +testing = ["coverage", "ipykernel", "jupytext", "matplotlib", "nbdime", "nbformat (>=5.1)", "numpy", "pandas", "pytest (>=6,<8)", "pytest-cov", "pytest-regressions", "sympy"] + [[package]] name = "jupyter-client" version = "8.6.2" @@ -1691,6 +1822,30 @@ files = [ {file = "llvmlite-0.43.0.tar.gz", hash = "sha256:ae2b5b5c3ef67354824fb75517c8db5fbe93bc02cd9671f3c62271626bc041d5"}, ] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +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" @@ -1826,6 +1981,36 @@ files = [ [package.dependencies] traitlets = "*" +[[package]] +name = "mdit-py-plugins" +version = "0.4.1" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, + {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +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 = "mistune" version = "3.0.2" @@ -1865,9 +2050,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.21.2", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, {version = ">=1.23.3", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] [package.extras] @@ -1890,6 +2075,60 @@ docs = ["sphinx"] gmpy = ["gmpy2 (>=2.1.0a4)"] tests = ["pytest (>=4.6)"] +[[package]] +name = "myst-nb" +version = "1.1.1" +description = "A Jupyter Notebook Sphinx reader built on top of the MyST markdown parser." +optional = false +python-versions = ">=3.9" +files = [ + {file = "myst_nb-1.1.1-py3-none-any.whl", hash = "sha256:8b8f9085287d948eef46cb3764aafc21915e0e981882b8c742719f5b1a84c36f"}, + {file = "myst_nb-1.1.1.tar.gz", hash = "sha256:74227c11f76d03494f43b7788659b161b94f4dedef230a2912412bc8c3c9e553"}, +] + +[package.dependencies] +importlib_metadata = "*" +ipykernel = "*" +ipython = "*" +jupyter-cache = ">=0.5" +myst-parser = ">=1.0.0" +nbclient = "*" +nbformat = ">=5.0" +pyyaml = "*" +sphinx = ">=5" +typing-extensions = "*" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["alabaster", "altair", "bokeh", "coconut (>=1.4.3,<3.1.0)", "ipykernel (>=5.5,<7.0)", "ipywidgets", "jupytext (>=1.11.2,<1.16.0)", "matplotlib", "numpy", "pandas", "plotly", "sphinx-book-theme (>=0.3)", "sphinx-copybutton", "sphinx-design (>=0.4.0,<0.5.0)", "sphinxcontrib-bibtex", "sympy"] +testing = ["beautifulsoup4", "coverage (>=6.4,<8.0)", "ipykernel (>=5.5,<7.0)", "ipython (!=8.1.0,<8.17)", "ipywidgets (>=8)", "jupytext (>=1.11.2,<1.16.0)", "matplotlib (==3.7.*)", "nbdime", "numpy", "pandas (==1.5.*)", "pyarrow", "pytest (>=7.1,<8.0)", "pytest-cov (>=3,<5)", "pytest-param-files (>=0.3.3,<0.4.0)", "pytest-regressions", "sympy (>=1.10.1)"] + +[[package]] +name = "myst-parser" +version = "3.0.1" +description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," +optional = false +python-versions = ">=3.8" +files = [ + {file = "myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1"}, + {file = "myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87"}, +] + +[package.dependencies] +docutils = ">=0.18,<0.22" +jinja2 = "*" +markdown-it-py = ">=3.0,<4.0" +mdit-py-plugins = ">=0.4,<1.0" +pyyaml = "*" +sphinx = ">=6,<8" + +[package.extras] +code-style = ["pre-commit (>=3.0,<4.0)"] +linkify = ["linkify-it-py (>=2.0,<3.0)"] +rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] +testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"] +testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"] + [[package]] name = "nbclient" version = "0.10.0" @@ -2172,9 +2411,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -2553,8 +2792,8 @@ files = [ annotated-types = ">=0.4.0" pydantic-core = "2.20.1" typing-extensions = [ - {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, {version = ">=4.6.1", markers = "python_version < \"3.13\""}, + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, ] [package.extras] @@ -2886,6 +3125,66 @@ files = [ {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, ] +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + [[package]] name = "pyzmq" version = "26.0.3" @@ -3669,6 +3968,93 @@ lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] +[[package]] +name = "sqlalchemy" +version = "2.0.31" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2a213c1b699d3f5768a7272de720387ae0122f1becf0901ed6eaa1abd1baf6c"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9fea3d0884e82d1e33226935dac990b967bef21315cbcc894605db3441347443"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ad7f221d8a69d32d197e5968d798217a4feebe30144986af71ada8c548e9fa"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2bee229715b6366f86a95d497c347c22ddffa2c7c96143b59a2aa5cc9eebbc"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cd5b94d4819c0c89280b7c6109c7b788a576084bf0a480ae17c227b0bc41e109"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:750900a471d39a7eeba57580b11983030517a1f512c2cb287d5ad0fcf3aebd58"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-win32.whl", hash = "sha256:7bd112be780928c7f493c1a192cd8c5fc2a2a7b52b790bc5a84203fb4381c6be"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:5a48ac4d359f058474fadc2115f78a5cdac9988d4f99eae44917f36aa1476327"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f68470edd70c3ac3b6cd5c2a22a8daf18415203ca1b036aaeb9b0fb6f54e8298"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e2c38c2a4c5c634fe6c3c58a789712719fa1bf9b9d6ff5ebfce9a9e5b89c1ca"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd15026f77420eb2b324dcb93551ad9c5f22fab2c150c286ef1dc1160f110203"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2196208432deebdfe3b22185d46b08f00ac9d7b01284e168c212919891289396"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:352b2770097f41bff6029b280c0e03b217c2dcaddc40726f8f53ed58d8a85da4"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56d51ae825d20d604583f82c9527d285e9e6d14f9a5516463d9705dab20c3740"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-win32.whl", hash = "sha256:6e2622844551945db81c26a02f27d94145b561f9d4b0c39ce7bfd2fda5776dac"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:ccaf1b0c90435b6e430f5dd30a5aede4764942a695552eb3a4ab74ed63c5b8d3"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3b74570d99126992d4b0f91fb87c586a574a5872651185de8297c6f90055ae42"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f77c4f042ad493cb8595e2f503c7a4fe44cd7bd59c7582fd6d78d7e7b8ec52c"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1591329333daf94467e699e11015d9c944f44c94d2091f4ac493ced0119449"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74afabeeff415e35525bf7a4ecdab015f00e06456166a2eba7590e49f8db940e"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b9c01990d9015df2c6f818aa8f4297d42ee71c9502026bb074e713d496e26b67"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66f63278db425838b3c2b1c596654b31939427016ba030e951b292e32b99553e"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-win32.whl", hash = "sha256:0b0f658414ee4e4b8cbcd4a9bb0fd743c5eeb81fc858ca517217a8013d282c96"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-win_amd64.whl", hash = "sha256:fa4b1af3e619b5b0b435e333f3967612db06351217c58bfb50cee5f003db2a5a"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f43e93057cf52a227eda401251c72b6fbe4756f35fa6bfebb5d73b86881e59b0"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d337bf94052856d1b330d5fcad44582a30c532a2463776e1651bd3294ee7e58b"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06fb43a51ccdff3b4006aafee9fcf15f63f23c580675f7734245ceb6b6a9e05"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:b6e22630e89f0e8c12332b2b4c282cb01cf4da0d26795b7eae16702a608e7ca1"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:79a40771363c5e9f3a77f0e28b3302801db08040928146e6808b5b7a40749c88"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-win32.whl", hash = "sha256:501ff052229cb79dd4c49c402f6cb03b5a40ae4771efc8bb2bfac9f6c3d3508f"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:597fec37c382a5442ffd471f66ce12d07d91b281fd474289356b1a0041bdf31d"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dc6d69f8829712a4fd799d2ac8d79bdeff651c2301b081fd5d3fe697bd5b4ab9"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23b9fbb2f5dd9e630db70fbe47d963c7779e9c81830869bd7d137c2dc1ad05fb"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21c97efcbb9f255d5c12a96ae14da873233597dfd00a3a0c4ce5b3e5e79704"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a6a9837589c42b16693cf7bf836f5d42218f44d198f9343dd71d3164ceeeac"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc251477eae03c20fae8db9c1c23ea2ebc47331bcd73927cdcaecd02af98d3c3"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2fd17e3bb8058359fa61248c52c7b09a97cf3c820e54207a50af529876451808"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-win32.whl", hash = "sha256:c76c81c52e1e08f12f4b6a07af2b96b9b15ea67ccdd40ae17019f1c373faa227"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:4b600e9a212ed59355813becbcf282cfda5c93678e15c25a0ef896b354423238"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b6cf796d9fcc9b37011d3f9936189b3c8074a02a4ed0c0fbbc126772c31a6d4"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78fe11dbe37d92667c2c6e74379f75746dc947ee505555a0197cfba9a6d4f1a4"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc47dc6185a83c8100b37acda27658fe4dbd33b7d5e7324111f6521008ab4fe"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a41514c1a779e2aa9a19f67aaadeb5cbddf0b2b508843fcd7bafdf4c6864005"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:afb6dde6c11ea4525318e279cd93c8734b795ac8bb5dda0eedd9ebaca7fa23f1"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3f9faef422cfbb8fd53716cd14ba95e2ef655400235c3dfad1b5f467ba179c8c"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-win32.whl", hash = "sha256:fc6b14e8602f59c6ba893980bea96571dd0ed83d8ebb9c4479d9ed5425d562e9"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:3cb8a66b167b033ec72c3812ffc8441d4e9f5f78f5e31e54dcd4c90a4ca5bebc"}, + {file = "SQLAlchemy-2.0.31-py3-none-any.whl", hash = "sha256:69f3e3c08867a8e4856e92d7afb618b95cdee18e0bc1647b77599722c9a28911"}, + {file = "SQLAlchemy-2.0.31.tar.gz", hash = "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + [[package]] name = "sspilib" version = "0.1.0" @@ -3802,6 +4188,20 @@ mpmath = ">=1.1.0,<1.4" [package.extras] dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + [[package]] name = "tinycss2" version = "1.3.0" @@ -4077,7 +4477,22 @@ files = [ {file = "widgetsnbextension-4.0.11.tar.gz", hash = "sha256:8b22a8f1910bfd188e596fe7fc05dcbd87e810c8a4ba010bdb3da86637398474"}, ] +[[package]] +name = "zipp" +version = "3.19.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, +] + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "781d472b949423807c34b0aff904ebe36ea1e688eb3a99226d569a993c27f58d" +content-hash = "757edf95621f51b3a5b49d6fcc0161d19d97ff1308ad3b3c9b69abbe7fa2fba9" diff --git a/pyproject.toml b/pyproject.toml index d9f027d7..5cc1ca69 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ quimb = "^1.8.1" openfermion = "^1.6.1" ipyparallel = "^8.8.0" furo = "^2024.5.6" +myst-nb = "^1.1.1" [build-system] From 70337bdd90568311116afd1d04480a63e3a162ef Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:47:59 +0100 Subject: [PATCH 45/76] fail sphinx build on warnings --- build-docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-docs.sh b/build-docs.sh index 8b8c32a0..99023de9 100755 --- a/build-docs.sh +++ b/build-docs.sh @@ -3,5 +3,5 @@ cd docs/ rm -rf build/ -sphinx-build -b html . build +sphinx-build -b html . build -W From 33e898fc3420fd0811b02802ca88a3a932c527e4 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:34:09 +0100 Subject: [PATCH 46/76] fix problems with qujax example headings --- docs/examples/pytket-qujax_qaoa.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/pytket-qujax_qaoa.ipynb b/docs/examples/pytket-qujax_qaoa.ipynb index b294cd59..1b8167c9 100644 --- a/docs/examples/pytket-qujax_qaoa.ipynb +++ b/docs/examples/pytket-qujax_qaoa.ipynb @@ -1 +1 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Symbolic circuits with `pytket-qujax`\n","In this notebook we will show how to manipulate symbolic circuits with the `pytket-qujax` extension. In particular, we will consider a QAOA and an Ising Hamiltonian."]},{"cell_type":"markdown","metadata":{},"source":["See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter\n","from jax import numpy as jnp, random, value_and_grad, jit\n","from sympy import Symbol\n","import matplotlib.pyplot as plt"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import qujax\n","from pytket.extensions.qujax import tk_to_qujax"]},{"cell_type":"markdown","metadata":{},"source":["# QAOA\n","The Quantum Approximate Optimization Algorithm (QAOA), first introduced by [Farhi et al.](https://arxiv.org/pdf/1411.4028.pdf), is a quantum variational algorithm used to solve optimization problems. It consists of a unitary $U(\\beta, \\gamma)$ formed by alternate repetitions of $U(\\beta)=e^{-i\\beta H_B}$ and $U(\\gamma)=e^{-i\\gamma H_P}$, where $H_B$ is the mixing Hamiltonian and $H_P$ the problem Hamiltonian. The goal is to find the optimal parameters that minimize $H_P$.\n","Given a depth $d$, the expression of the final unitary is $U(\\beta, \\gamma) = U(\\beta_d)U(\\gamma_d)\\cdots U(\\beta_1)U(\\gamma_1)$. Notice that for each repetition the parameters are different.\n","\n","## Problem Hamiltonian\n","QAOA uses a problem dependent ansatz. Therefore, we first need to know the problem that we want to solve. In this case we will consider an Ising Hamiltonian with only $Z$ interactions. Given a set of pairs (or qubit indices) $E$, the problem Hamiltonian will be:\n","\n","$$\n","\\begin{equation}\n","H_P = \\sum_{(i, j) \\in E}\\alpha_{ij}Z_iZ_j,\n","\\end{equation}\n","$$\n","\n","where $\\alpha_{ij}$ are the coefficients.\n","Let's build our problem Hamiltonian with random coefficients and a set of pairs for a given number of qubits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_qubits = 4\n","hamiltonian_qubit_inds = [(0, 1), (1, 2), (0, 2), (1, 3)]\n","hamiltonian_gates = [[\"Z\", \"Z\"]] * (len(hamiltonian_qubit_inds))"]},{"cell_type":"markdown","metadata":{},"source":["Notice that in order to use the random package from jax we first need to define a seeded key"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["seed = 13\n","key = random.PRNGKey(seed)\n","coefficients = random.uniform(key, shape=(len(hamiltonian_qubit_inds),))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"Gates:\\t\", hamiltonian_gates)\n","print(\"Qubits:\\t\", hamiltonian_qubit_inds)\n","print(\"Coefficients:\\t\", coefficients)"]},{"cell_type":"markdown","metadata":{},"source":["## Variational Circuit\n","Before constructing the circuit, we still need to select the mixing Hamiltonian. In our case, we will be using $X$ gates in each qubit, so $H_B = \\sum_{i=1}^{n}X_i$, where $n$ is the number of qubits. Notice that the unitary $U(\\beta)$, given this mixing Hamiltonian, is an $X$ rotation in each qubit with angle $\\beta$.\n","As for the unitary corresponding to the problem Hamiltonian, $U(\\gamma)$, it has the following form:\n","\n","$$\n","\\begin{equation}\n","U(\\gamma)=\\prod_{(i, j) \\in E}e^{-i\\gamma\\alpha_{ij}Z_i Z_j}\n","\\end{equation}\n","$$\n","\n","The operation $e^{-i\\gamma\\alpha_{ij}Z_iZ_j}$ can be performed using two CNOT gates with qubit $i$ as control and qubit $j$ as target and a $Z$ rotation in qubit $j$ in between them, with angle $\\gamma\\alpha_{ij}$.\n","Finally, the initial state used, in general, with the QAOA is an equal superposition of all the basis states. This can be achieved adding a first layer of Hadamard gates in each qubit at the beginning of the circuit."]},{"cell_type":"markdown","metadata":{},"source":["With all the building blocks, let's construct the symbolic circuit using tket. Notice that in order to define the parameters, we use the ```Symbol``` object from the `sympy` package. More info can be found in this [documentation](https://tket.quantinuum.com/user-manual/manual_circuit.html#symbolic-circuits). In order to later convert the circuit to qujax, we need to return the list of symbolic parameters as well."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qaoa_circuit(n_qubits, depth):\n"," circuit = Circuit(n_qubits)\n"," p_keys = []\n","\n"," # Initial State\n"," for i in range(n_qubits):\n"," circuit.H(i)\n"," for d in range(depth):\n"," # Hamiltonian unitary\n"," gamma_d = Symbol(f\"γ_{d}\")\n"," for index in range(len(hamiltonian_qubit_inds)):\n"," pair = hamiltonian_qubit_inds[index]\n"," coef = coefficients[index]\n"," circuit.CX(pair[0], pair[1])\n"," circuit.Rz(gamma_d * coef, pair[1])\n"," circuit.CX(pair[0], pair[1])\n"," circuit.add_barrier(range(0, n_qubits))\n"," p_keys.append(gamma_d)\n","\n"," # Mixing unitary\n"," beta_d = Symbol(f\"β_{d}\")\n"," for i in range(n_qubits):\n"," circuit.Rx(beta_d, i)\n"," p_keys.append(beta_d)\n"," return circuit, p_keys"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["depth = 3\n","circuit, keys = qaoa_circuit(n_qubits, depth)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["keys"]},{"cell_type":"markdown","metadata":{},"source":["Let's check the circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circuit)"]},{"cell_type":"markdown","metadata":{},"source":["# Now for `qujax`\n","The `pytket.extensions.qujax.tk_to_qujax` function will generate a parameters -> statetensor function for us. However, in order to convert a symbolic circuit we first need to define the `symbol_map`. This object maps each symbol key to their corresponding index. In our case, since the object `keys` contains the symbols in the correct order, we can simply construct the dictionary as follows:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol_map = {keys[i]: i for i in range(len(keys))}"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol_map"]},{"cell_type":"markdown","metadata":{},"source":["Then, we invoke the `tk_to_qujax` with both the circuit and the symbolic map."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_st = tk_to_qujax(circuit, symbol_map=symbol_map)"]},{"cell_type":"markdown","metadata":{},"source":["And we also construct the expectation map using the problem Hamiltonian via qujax:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["st_to_expectation = qujax.get_statetensor_to_expectation_func(\n"," hamiltonian_gates, hamiltonian_qubit_inds, coefficients\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_expectation = lambda param: st_to_expectation(param_to_st(param))"]},{"cell_type":"markdown","metadata":{},"source":["# Training process\n","We construct a function that, given a parameter vector, returns the value of the cost function and the gradient.\n","We also `jit` to avoid recompilation, this means that the expensive `cost_and_grad` function is compiled once into a very fast XLA (C++) function which is then executed at each iteration. Alternatively, we could get the same speedup by replacing our `for` loop with `jax.lax.scan`. You can read more about JIT compilation in the [JAX documentation](https://jax.readthedocs.io/en/latest/jax-101/02-jitting.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_and_grad = jit(value_and_grad(param_to_expectation))"]},{"cell_type":"markdown","metadata":{},"source":["For the training process we'll use vanilla gradient descent with a constant stepsize:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["seed = 123\n","key = random.PRNGKey(seed)\n","init_param = random.uniform(key, shape=(len(symbol_map),))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_steps = 150\n","stepsize = 0.01"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param = init_param"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_vals = jnp.zeros(n_steps)\n","cost_vals = cost_vals.at[0].set(param_to_expectation(init_param))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for step in range(1, n_steps):\n"," cost_val, cost_grad = cost_and_grad(param)\n"," cost_vals = cost_vals.at[step].set(cost_val)\n"," param = param - stepsize * cost_grad\n"," print(\"Iteration:\", step, \"\\tCost:\", cost_val, end=\"\\r\")"]},{"cell_type":"markdown","metadata":{},"source":["Let's visualise the gradient descent"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.plot(cost_vals)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Symbolic circuits with `pytket-qujax`\n","In this notebook we will show how to manipulate symbolic circuits with the `pytket-qujax` extension. In particular, we will consider a QAOA and an Ising Hamiltonian."]},{"cell_type":"markdown","metadata":{},"source":["See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter\n","from jax import numpy as jnp, random, value_and_grad, jit\n","from sympy import Symbol\n","import matplotlib.pyplot as plt"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import qujax\n","from pytket.extensions.qujax import tk_to_qujax"]},{"cell_type":"markdown","metadata":{},"source":["## QAOA\n","The Quantum Approximate Optimization Algorithm (QAOA), first introduced by [Farhi et al.](https://arxiv.org/pdf/1411.4028.pdf), is a quantum variational algorithm used to solve optimization problems. It consists of a unitary $U(\\beta, \\gamma)$ formed by alternate repetitions of $U(\\beta)=e^{-i\\beta H_B}$ and $U(\\gamma)=e^{-i\\gamma H_P}$, where $H_B$ is the mixing Hamiltonian and $H_P$ the problem Hamiltonian. The goal is to find the optimal parameters that minimize $H_P$.\n","Given a depth $d$, the expression of the final unitary is $U(\\beta, \\gamma) = U(\\beta_d)U(\\gamma_d)\\cdots U(\\beta_1)U(\\gamma_1)$. Notice that for each repetition the parameters are different.\n","\n","## Problem Hamiltonian\n","QAOA uses a problem dependent ansatz. Therefore, we first need to know the problem that we want to solve. In this case we will consider an Ising Hamiltonian with only $Z$ interactions. Given a set of pairs (or qubit indices) $E$, the problem Hamiltonian will be:\n","\n","$$\n","\\begin{equation}\n","H_P = \\sum_{(i, j) \\in E}\\alpha_{ij}Z_iZ_j,\n","\\end{equation}\n","$$\n","\n","where $\\alpha_{ij}$ are the coefficients.\n","Let's build our problem Hamiltonian with random coefficients and a set of pairs for a given number of qubits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_qubits = 4\n","hamiltonian_qubit_inds = [(0, 1), (1, 2), (0, 2), (1, 3)]\n","hamiltonian_gates = [[\"Z\", \"Z\"]] * (len(hamiltonian_qubit_inds))"]},{"cell_type":"markdown","metadata":{},"source":["Notice that in order to use the random package from jax we first need to define a seeded key"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["seed = 13\n","key = random.PRNGKey(seed)\n","coefficients = random.uniform(key, shape=(len(hamiltonian_qubit_inds),))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"Gates:\\t\", hamiltonian_gates)\n","print(\"Qubits:\\t\", hamiltonian_qubit_inds)\n","print(\"Coefficients:\\t\", coefficients)"]},{"cell_type":"markdown","metadata":{},"source":["## Variational Circuit\n","Before constructing the circuit, we still need to select the mixing Hamiltonian. In our case, we will be using $X$ gates in each qubit, so $H_B = \\sum_{i=1}^{n}X_i$, where $n$ is the number of qubits. Notice that the unitary $U(\\beta)$, given this mixing Hamiltonian, is an $X$ rotation in each qubit with angle $\\beta$.\n","As for the unitary corresponding to the problem Hamiltonian, $U(\\gamma)$, it has the following form:\n","\n","$$\n","\\begin{equation}\n","U(\\gamma)=\\prod_{(i, j) \\in E}e^{-i\\gamma\\alpha_{ij}Z_i Z_j}\n","\\end{equation}\n","$$\n","\n","The operation $e^{-i\\gamma\\alpha_{ij}Z_iZ_j}$ can be performed using two CNOT gates with qubit $i$ as control and qubit $j$ as target and a $Z$ rotation in qubit $j$ in between them, with angle $\\gamma\\alpha_{ij}$.\n","Finally, the initial state used, in general, with the QAOA is an equal superposition of all the basis states. This can be achieved adding a first layer of Hadamard gates in each qubit at the beginning of the circuit."]},{"cell_type":"markdown","metadata":{},"source":["With all the building blocks, let's construct the symbolic circuit using tket. Notice that in order to define the parameters, we use the ```Symbol``` object from the `sympy` package. More info can be found in this [documentation](https://tket.quantinuum.com/user-manual/manual_circuit.html#symbolic-circuits). In order to later convert the circuit to qujax, we need to return the list of symbolic parameters as well."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qaoa_circuit(n_qubits, depth):\n"," circuit = Circuit(n_qubits)\n"," p_keys = []\n","\n"," # Initial State\n"," for i in range(n_qubits):\n"," circuit.H(i)\n"," for d in range(depth):\n"," # Hamiltonian unitary\n"," gamma_d = Symbol(f\"γ_{d}\")\n"," for index in range(len(hamiltonian_qubit_inds)):\n"," pair = hamiltonian_qubit_inds[index]\n"," coef = coefficients[index]\n"," circuit.CX(pair[0], pair[1])\n"," circuit.Rz(gamma_d * coef, pair[1])\n"," circuit.CX(pair[0], pair[1])\n"," circuit.add_barrier(range(0, n_qubits))\n"," p_keys.append(gamma_d)\n","\n"," # Mixing unitary\n"," beta_d = Symbol(f\"β_{d}\")\n"," for i in range(n_qubits):\n"," circuit.Rx(beta_d, i)\n"," p_keys.append(beta_d)\n"," return circuit, p_keys"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["depth = 3\n","circuit, keys = qaoa_circuit(n_qubits, depth)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["keys"]},{"cell_type":"markdown","metadata":{},"source":["Let's check the circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circuit)"]},{"cell_type":"markdown","metadata":{},"source":["## Now for `qujax`\n","The `pytket.extensions.qujax.tk_to_qujax` function will generate a parameters -> statetensor function for us. However, in order to convert a symbolic circuit we first need to define the `symbol_map`. This object maps each symbol key to their corresponding index. In our case, since the object `keys` contains the symbols in the correct order, we can simply construct the dictionary as follows:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol_map = {keys[i]: i for i in range(len(keys))}"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol_map"]},{"cell_type":"markdown","metadata":{},"source":["Then, we invoke the `tk_to_qujax` with both the circuit and the symbolic map."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_st = tk_to_qujax(circuit, symbol_map=symbol_map)"]},{"cell_type":"markdown","metadata":{},"source":["And we also construct the expectation map using the problem Hamiltonian via qujax:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["st_to_expectation = qujax.get_statetensor_to_expectation_func(\n"," hamiltonian_gates, hamiltonian_qubit_inds, coefficients\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_expectation = lambda param: st_to_expectation(param_to_st(param))"]},{"cell_type":"markdown","metadata":{},"source":["## Training process\n","We construct a function that, given a parameter vector, returns the value of the cost function and the gradient.\n","We also `jit` to avoid recompilation, this means that the expensive `cost_and_grad` function is compiled once into a very fast XLA (C++) function which is then executed at each iteration. Alternatively, we could get the same speedup by replacing our `for` loop with `jax.lax.scan`. You can read more about JIT compilation in the [JAX documentation](https://jax.readthedocs.io/en/latest/jax-101/02-jitting.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_and_grad = jit(value_and_grad(param_to_expectation))"]},{"cell_type":"markdown","metadata":{},"source":["For the training process we'll use vanilla gradient descent with a constant stepsize:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["seed = 123\n","key = random.PRNGKey(seed)\n","init_param = random.uniform(key, shape=(len(symbol_map),))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_steps = 150\n","stepsize = 0.01"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param = init_param"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_vals = jnp.zeros(n_steps)\n","cost_vals = cost_vals.at[0].set(param_to_expectation(init_param))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for step in range(1, n_steps):\n"," cost_val, cost_grad = cost_and_grad(param)\n"," cost_vals = cost_vals.at[step].set(cost_val)\n"," param = param - stepsize * cost_grad\n"," print(\"Iteration:\", step, \"\\tCost:\", cost_val, end=\"\\r\")"]},{"cell_type":"markdown","metadata":{},"source":["Let's visualise the gradient descent"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.plot(cost_vals)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} From dc3d84d3c1b54e08ed6f171a42f034371cc368a9 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:34:35 +0100 Subject: [PATCH 47/76] add getting started guide to index page --- docs/index.rst | 141 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 123 insertions(+), 18 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index ba4e2e10..3e711d6b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,11 +1,16 @@ Getting Started =============== +Building a circuit with the ``Circuit`` class +--------------------------------------------- -.. jupyter-execute:: +You can create a circuit by creating an instance of the ``Circuit`` +class and adding gates manually. - from pytket import Circuit +.. jupyter-execute:: + from pytket import Circuit + ghz_circ = Circuit(3) ghz_circ.H(0) ghz_circ.CX(0, 1) @@ -13,6 +18,109 @@ Getting Started ghz_circ.add_barrier(ghz_circ.qubits) ghz_circ.measure_all() +Now let’s draw a nice picture of the circuit with the circuit renderer + +.. jupyter-execute:: + + from pytket.circuit.display import render_circuit_jupyter + + render_circuit_jupyter(ghz_circ) + +See also the `Circuit +construction `__ +section of the user manual. + +Build a ``Circuit`` from a QASM file +------------------------------------ + +Alternatively we can import a circuit from a QASM file using +`pytket.qasm `__. There +are also functions for generating a circuit from a QASM string or +exporting to a qasm file. + +Note that its also possible to import a circuit from quipper using +`pytket.quipper `__ +module. + +.. jupyter-execute:: + + from pytket.qasm import circuit_from_qasm + + w_state_circ = circuit_from_qasm("examples/qasm/W-state.qasm") + render_circuit_jupyter(w_state_circ) + +Import a circuit from qiskit (or other SDK) +------------------------------------------- + +Its possible to generate a circuit directly from a qiskit +``QuantumCircuit`` using the +`qiskit_to_tk `__ +function. + +.. jupyter-execute:: + + from qiskit import QuantumCircuit + + qiskit_circ = QuantumCircuit(3) + qiskit_circ.h(range(3)) + qiskit_circ.ccx(2, 1 ,0) + qiskit_circ.cx(0, 1) + print(qiskit_circ) + +.. jupyter-execute:: + + from pytket.extensions.qiskit import qiskit_to_tk + + tket_circ = qiskit_to_tk(qiskit_circ) + + render_circuit_jupyter(tket_circ) + +Note that pytket and qiskit use opposite qubit ordering conventions. So +circuits which look identical may correspond to different unitary +operations. + +Circuit conversion functions are also available for +`pytket-cirq `_, +`pytket-pennylane `_, +`pytket-braket `_ +and more. + +Using Backends +-------------- + +In pytket a ``Backend`` represents an interface to a quantum device or +simulator. + +We will show a simple example of running the ``ghz_circ`` defined above +on the ``AerBackend`` simulator. + +.. jupyter-execute:: + + render_circuit_jupyter(ghz_circ) + +.. jupyter-execute:: + + from pytket.extensions.qiskit import AerBackend + + backend = AerBackend() + result = backend.run_circuit(ghz_circ) + print(result.get_counts()) + +The ``AerBackend`` simulator is highly idealised having a broad gateset, +and no restrictive connectivity or device noise. + +The Hadamard and CX gate are supported operations of the simulator so we +can run the GHZ circuit without changing any of the operations. For more +realistic cases a compiler will have to solve for the limited gateset of +the target backend as well as other backend requirements. + +See the `Running on +Backends `__ +section of the user manual and the `backends example +notebook `__ +for more. + + .. toctree:: @@ -31,19 +139,16 @@ Getting Started :caption: Examples :maxdepth: 2 - examples/ansatz_sequence_example - examples/circuit_analysis_example - examples/circuit_generation_example - examples/compilation_example - examples/conditional_gate_example - examples/contextual_optimization - examples/creating_backends - examples/measurement_reduction_example - examples/mapping_example - examples/symbolics_example - examples/phase_estimation - examples/pytket-qujax_qaoa - examples/ucc_vqe - - - + examples/ansatz_sequence_example.ipynb + examples/circuit_analysis_example.ipynb + examples/circuit_generation_example.ipynb + examples/compilation_example.ipynb + examples/conditional_gate_example.ipynb + examples/contextual_optimization.ipynb + examples/creating_backends.ipynb + examples/measurement_reduction_example.ipynb + examples/mapping_example.ipynb + examples/symbolics_example.ipynb + examples/phase_estimation.ipynb + examples/pytket-qujax_qaoa.ipynb + examples/ucc_vqe.ipynb From 7d943eaaf65e94d1a31acae3cfd360069e67a213 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:47:01 +0100 Subject: [PATCH 48/76] correctly exclude notebooks non-executable notebooks from build --- docs/conf.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 859588da..0ecbcacc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,7 +7,7 @@ author = "Quantinuum" -html_title = "User guide" +html_title = "pytket user guide" extensions = [ "sphinx.ext.autodoc", @@ -43,20 +43,20 @@ nb_execution_mode = "cache" -exclude_patterns = [ +nb_execution_excludepatterns = [ "_build", "Thumbs.db", ".DS_Store", ".venv/*", - "examples/Forest_portability_example.ipynb", - "examples/backends_example.ipynb", - "examples/qiskit_integration.ipynb", - "examples/comparing_simulators.ipynb", - "examples/expectation_value_example.ipynb", - "examples/pytket-qujax_heisenberg_vqe.ipynb", - "examples/spam_example.ipynb", - "examples/entanglement_swapping.ipynb", - "examples/pytket-qujax-classification.ipynb", + "examples/backends/Forest_portability_example.ipynb", + "examples/backends/backends_example.ipynb", + "examples/backends/qiskit_integration.ipynb", + "examples/backends/comparing_simulators.ipynb", + "examples/algorithms_and_protocols/expectation_value_example.ipynb", + "examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb", + "examples/algorithms_and_protocols/spam_example.ipynb", + "examples/algorithms_and_protocols/entanglement_swapping.ipynb", + "examples/algorithms_and_protocols/pytket-qujax-classification.ipynb", "jupyter_execute/*", "manual/README.md", ".jupyter_cache", From c6aff9f96b99beec3bad6b6b81788cc0cc3e0eff Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 25 Jul 2024 16:01:19 +0100 Subject: [PATCH 49/76] fix another duplicate execution bug --- docs/conf.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0ecbcacc..e1fdafee 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,10 +44,6 @@ nb_execution_mode = "cache" nb_execution_excludepatterns = [ - "_build", - "Thumbs.db", - ".DS_Store", - ".venv/*", "examples/backends/Forest_portability_example.ipynb", "examples/backends/backends_example.ipynb", "examples/backends/qiskit_integration.ipynb", @@ -57,7 +53,6 @@ "examples/algorithms_and_protocols/spam_example.ipynb", "examples/algorithms_and_protocols/entanglement_swapping.ipynb", "examples/algorithms_and_protocols/pytket-qujax-classification.ipynb", - "jupyter_execute/*", - "manual/README.md", - ".jupyter_cache", ] + +exclude_patterns = ["jupyter_execute/*", ".jupyter_cache", "manual/README.md"] From 383f4754d1803ec8acd5cb6274e268ca27d56ec8 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 25 Jul 2024 16:02:18 +0100 Subject: [PATCH 50/76] new file sturcture for example notebook categories --- build-docs.sh | 2 +- .../ansatz_sequence_example.ipynb | 0 .../entanglement_swapping.ipynb | 0 .../expectation_value_example.ipynb | 0 .../images/phase_est.png | Bin .../images/qft.png | Bin .../measurement_reduction_example.ipynb | 0 .../phase_estimation.ipynb | 0 .../pytket-qujax-classification.ipynb | 0 .../pytket-qujax_heisenberg_vqe.ipynb | 0 .../pytket-qujax_qaoa.ipynb | 0 .../spam_example.ipynb | 0 .../ucc_vqe.ipynb | 0 .../Forest_portability_example.ipynb | 0 .../{ => backends}/backends_example.ipynb | 0 .../{ => backends}/comparing_simulators.ipynb | 0 .../{ => backends}/creating_backends.ipynb | 0 .../{ => backends}/qiskit_integration.ipynb | 0 .../compilation_example.ipynb | 0 .../contextual_optimization.ipynb | 0 .../mapping_example.ipynb | 0 .../symbolics_example.ipynb | 0 docs/examples/circuit_construction/c.quip | 14 ++++++++++++ .../circuit_analysis_example.ipynb | 0 .../circuit_generation_example.ipynb | 1 + .../conditional_gate_example.ipynb | 0 .../examples/circuit_generation_example.ipynb | 1 - docs/examples/constraints.txt | 2 -- docs/examples/qasm/c.qasm | 10 +++++++++ docs/index.rst | 20 ++++++------------ 30 files changed, 32 insertions(+), 18 deletions(-) rename docs/examples/{ => algorithms_and_protocols}/ansatz_sequence_example.ipynb (100%) rename docs/examples/{ => algorithms_and_protocols}/entanglement_swapping.ipynb (100%) rename docs/examples/{ => algorithms_and_protocols}/expectation_value_example.ipynb (100%) rename docs/examples/{ => algorithms_and_protocols}/images/phase_est.png (100%) rename docs/examples/{ => algorithms_and_protocols}/images/qft.png (100%) rename docs/examples/{ => algorithms_and_protocols}/measurement_reduction_example.ipynb (100%) rename docs/examples/{ => algorithms_and_protocols}/phase_estimation.ipynb (100%) rename docs/examples/{ => algorithms_and_protocols}/pytket-qujax-classification.ipynb (100%) rename docs/examples/{ => algorithms_and_protocols}/pytket-qujax_heisenberg_vqe.ipynb (100%) rename docs/examples/{ => algorithms_and_protocols}/pytket-qujax_qaoa.ipynb (100%) rename docs/examples/{ => algorithms_and_protocols}/spam_example.ipynb (100%) rename docs/examples/{ => algorithms_and_protocols}/ucc_vqe.ipynb (100%) rename docs/examples/{ => backends}/Forest_portability_example.ipynb (100%) rename docs/examples/{ => backends}/backends_example.ipynb (100%) rename docs/examples/{ => backends}/comparing_simulators.ipynb (100%) rename docs/examples/{ => backends}/creating_backends.ipynb (100%) rename docs/examples/{ => backends}/qiskit_integration.ipynb (100%) rename docs/examples/{ => circuit_compilation}/compilation_example.ipynb (100%) rename docs/examples/{ => circuit_compilation}/contextual_optimization.ipynb (100%) rename docs/examples/{ => circuit_compilation}/mapping_example.ipynb (100%) rename docs/examples/{ => circuit_compilation}/symbolics_example.ipynb (100%) create mode 100644 docs/examples/circuit_construction/c.quip rename docs/examples/{ => circuit_construction}/circuit_analysis_example.ipynb (100%) create mode 100644 docs/examples/circuit_construction/circuit_generation_example.ipynb rename docs/examples/{ => circuit_construction}/conditional_gate_example.ipynb (100%) delete mode 100644 docs/examples/circuit_generation_example.ipynb delete mode 100644 docs/examples/constraints.txt create mode 100644 docs/examples/qasm/c.qasm diff --git a/build-docs.sh b/build-docs.sh index 99023de9..33f7ee33 100755 --- a/build-docs.sh +++ b/build-docs.sh @@ -3,5 +3,5 @@ cd docs/ rm -rf build/ -sphinx-build -b html . build -W +sphinx-build -b html . build diff --git a/docs/examples/ansatz_sequence_example.ipynb b/docs/examples/algorithms_and_protocols/ansatz_sequence_example.ipynb similarity index 100% rename from docs/examples/ansatz_sequence_example.ipynb rename to docs/examples/algorithms_and_protocols/ansatz_sequence_example.ipynb diff --git a/docs/examples/entanglement_swapping.ipynb b/docs/examples/algorithms_and_protocols/entanglement_swapping.ipynb similarity index 100% rename from docs/examples/entanglement_swapping.ipynb rename to docs/examples/algorithms_and_protocols/entanglement_swapping.ipynb diff --git a/docs/examples/expectation_value_example.ipynb b/docs/examples/algorithms_and_protocols/expectation_value_example.ipynb similarity index 100% rename from docs/examples/expectation_value_example.ipynb rename to docs/examples/algorithms_and_protocols/expectation_value_example.ipynb diff --git a/docs/examples/images/phase_est.png b/docs/examples/algorithms_and_protocols/images/phase_est.png similarity index 100% rename from docs/examples/images/phase_est.png rename to docs/examples/algorithms_and_protocols/images/phase_est.png diff --git a/docs/examples/images/qft.png b/docs/examples/algorithms_and_protocols/images/qft.png similarity index 100% rename from docs/examples/images/qft.png rename to docs/examples/algorithms_and_protocols/images/qft.png diff --git a/docs/examples/measurement_reduction_example.ipynb b/docs/examples/algorithms_and_protocols/measurement_reduction_example.ipynb similarity index 100% rename from docs/examples/measurement_reduction_example.ipynb rename to docs/examples/algorithms_and_protocols/measurement_reduction_example.ipynb diff --git a/docs/examples/phase_estimation.ipynb b/docs/examples/algorithms_and_protocols/phase_estimation.ipynb similarity index 100% rename from docs/examples/phase_estimation.ipynb rename to docs/examples/algorithms_and_protocols/phase_estimation.ipynb diff --git a/docs/examples/pytket-qujax-classification.ipynb b/docs/examples/algorithms_and_protocols/pytket-qujax-classification.ipynb similarity index 100% rename from docs/examples/pytket-qujax-classification.ipynb rename to docs/examples/algorithms_and_protocols/pytket-qujax-classification.ipynb diff --git a/docs/examples/pytket-qujax_heisenberg_vqe.ipynb b/docs/examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb similarity index 100% rename from docs/examples/pytket-qujax_heisenberg_vqe.ipynb rename to docs/examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb diff --git a/docs/examples/pytket-qujax_qaoa.ipynb b/docs/examples/algorithms_and_protocols/pytket-qujax_qaoa.ipynb similarity index 100% rename from docs/examples/pytket-qujax_qaoa.ipynb rename to docs/examples/algorithms_and_protocols/pytket-qujax_qaoa.ipynb diff --git a/docs/examples/spam_example.ipynb b/docs/examples/algorithms_and_protocols/spam_example.ipynb similarity index 100% rename from docs/examples/spam_example.ipynb rename to docs/examples/algorithms_and_protocols/spam_example.ipynb diff --git a/docs/examples/ucc_vqe.ipynb b/docs/examples/algorithms_and_protocols/ucc_vqe.ipynb similarity index 100% rename from docs/examples/ucc_vqe.ipynb rename to docs/examples/algorithms_and_protocols/ucc_vqe.ipynb diff --git a/docs/examples/Forest_portability_example.ipynb b/docs/examples/backends/Forest_portability_example.ipynb similarity index 100% rename from docs/examples/Forest_portability_example.ipynb rename to docs/examples/backends/Forest_portability_example.ipynb diff --git a/docs/examples/backends_example.ipynb b/docs/examples/backends/backends_example.ipynb similarity index 100% rename from docs/examples/backends_example.ipynb rename to docs/examples/backends/backends_example.ipynb diff --git a/docs/examples/comparing_simulators.ipynb b/docs/examples/backends/comparing_simulators.ipynb similarity index 100% rename from docs/examples/comparing_simulators.ipynb rename to docs/examples/backends/comparing_simulators.ipynb diff --git a/docs/examples/creating_backends.ipynb b/docs/examples/backends/creating_backends.ipynb similarity index 100% rename from docs/examples/creating_backends.ipynb rename to docs/examples/backends/creating_backends.ipynb diff --git a/docs/examples/qiskit_integration.ipynb b/docs/examples/backends/qiskit_integration.ipynb similarity index 100% rename from docs/examples/qiskit_integration.ipynb rename to docs/examples/backends/qiskit_integration.ipynb diff --git a/docs/examples/compilation_example.ipynb b/docs/examples/circuit_compilation/compilation_example.ipynb similarity index 100% rename from docs/examples/compilation_example.ipynb rename to docs/examples/circuit_compilation/compilation_example.ipynb diff --git a/docs/examples/contextual_optimization.ipynb b/docs/examples/circuit_compilation/contextual_optimization.ipynb similarity index 100% rename from docs/examples/contextual_optimization.ipynb rename to docs/examples/circuit_compilation/contextual_optimization.ipynb diff --git a/docs/examples/mapping_example.ipynb b/docs/examples/circuit_compilation/mapping_example.ipynb similarity index 100% rename from docs/examples/mapping_example.ipynb rename to docs/examples/circuit_compilation/mapping_example.ipynb diff --git a/docs/examples/symbolics_example.ipynb b/docs/examples/circuit_compilation/symbolics_example.ipynb similarity index 100% rename from docs/examples/symbolics_example.ipynb rename to docs/examples/circuit_compilation/symbolics_example.ipynb diff --git a/docs/examples/circuit_construction/c.quip b/docs/examples/circuit_construction/c.quip new file mode 100644 index 00000000..196936ea --- /dev/null +++ b/docs/examples/circuit_construction/c.quip @@ -0,0 +1,14 @@ +Inputs: 0:Qbit, 1:Qbit, 2:Qbit +QGate["H"](0) +Subroutine(x2)["sub", shape "([Q,Q],())"] (2,1) -> (2,1) +QGate["H"](1) +Outputs: 0:Qbit, 1:Qbit, 2:Qbit + +Subroutine: "sub" +Shape: "([Q,Q],())" +Controllable: no +Inputs: 0:Qbit, 1:Qbit +QGate["Y"](0) +QGate["not"](1) with controls=[+0] +QGate["Z"](1) +Outputs: 0:Qbit, 1:Qbit diff --git a/docs/examples/circuit_analysis_example.ipynb b/docs/examples/circuit_construction/circuit_analysis_example.ipynb similarity index 100% rename from docs/examples/circuit_analysis_example.ipynb rename to docs/examples/circuit_construction/circuit_analysis_example.ipynb diff --git a/docs/examples/circuit_construction/circuit_generation_example.ipynb b/docs/examples/circuit_construction/circuit_generation_example.ipynb new file mode 100644 index 00000000..aefe356a --- /dev/null +++ b/docs/examples/circuit_construction/circuit_generation_example.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Circuit generation"]},{"cell_type":"markdown","metadata":{},"source":["This notebook will provide a brief introduction to some of the more advanced methods of circuit generation available in `pytket`, including:
\n","* how to address wires and registers;
\n","* reading in circuits from QASM and Quipper ASCII files;
\n","* various types of 'boxes';
\n","* composition of circuits (both 'horizontally' and 'vertically');
\n","* use of symbolic gate parameters;
\n","* representation of classically controlled gates."]},{"cell_type":"markdown","metadata":{},"source":["## Wires, unit IDs and registers"]},{"cell_type":"markdown","metadata":{},"source":["Let's get started by constructing a circuit with 3 qubits and 2 classical bits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit\n","from pytket.circuit.display import render_circuit_jupyter as draw"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(1, 2)\n","print(circ.qubits)\n","print(circ.bits)"]},{"cell_type":"markdown","metadata":{},"source":["The qubits have automatically been assigned to a register with name `q` and indices 0, 1 and 2, while the bits have been assigned to a register with name `c` and indices 0 and 1.
\n","
\n","We can give these units arbitrary names and indices of arbitrary dimension:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Qubit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["new_q1 = Qubit(\"alpha\", 0)\n","new_q2 = Qubit(\"beta\", 2, 1)\n","circ.add_qubit(new_q1)\n","circ.add_qubit(new_q2)\n","print(circ.qubits)"]},{"cell_type":"markdown","metadata":{},"source":["We can also add a new register of qubits in one go:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["delta_reg = circ.add_q_register(\"delta\", 2)\n","print(circ.qubits)"]},{"cell_type":"markdown","metadata":{},"source":["Similar commands are available for classical bits.
\n","
\n","We can add gates to the circuit as follows:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.CX(delta_reg[0], delta_reg[1])"]},{"cell_type":"markdown","metadata":{},"source":["This command appends a CX gate with control `q[0]` and target `q[1]`. Note that the integer arguments are automatically converted to the default unit IDs. For simple circuits it is often easiest to stick to the default register and refer to the qubits by integers. To add gates to our own named units, we simply pass the `Qubit` (or classical `Bit`) as an argument. (We can't mix the two conventions in one command, however.)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.H(new_q1)\n","circ.CX(Qubit(\"q\", 0), new_q2)\n","circ.Rz(0.5, new_q2)"]},{"cell_type":"markdown","metadata":{},"source":["Let's have a look at our circuit using the interactive circuit renderer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit.display import render_circuit_jupyter as draw"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(circ)"]},{"cell_type":"markdown","metadata":{},"source":["## Exporting to and importing from standard formats"]},{"cell_type":"markdown","metadata":{},"source":["We can export a `Circuit` to a file in QASM format. Conversely, if we have such a file we can import it into `pytket`. There are some limitations on the circuits that can be converted: for example, multi-dimensional indices (as in `beta` and `gamma` above) are not allowed.
\n","
\n","Here is a simple example:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.qasm import circuit_from_qasm, circuit_to_qasm"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(3, 1)\n","circ.H(0)\n","circ.CX(0, 1)\n","circ.CX(1, 2)\n","circ.Rz(0.25, 2)\n","circ.Measure(2, 0)\n","draw(circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qasmfile = \"../qasm/c.qasm\"\n","circuit_to_qasm(circ, qasmfile)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["with open(qasmfile, encoding=\"utf-8\") as f:\n"," print(f.read())"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = circuit_from_qasm(qasmfile)\n","circ == c1"]},{"cell_type":"markdown","metadata":{},"source":["We can also import files in the Quipper ASCII format:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.quipper import circuit_from_quipper"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["quipfile = \"c.quip\"\n","with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n"," f.write(\n"," \"\"\"Inputs: 0:Qbit, 1:Qbit\n","QGate[\"W\"](0,1)\n","QGate[\"omega\"](1)\n","QGate[\"swap\"](0,1)\n","QGate[\"W\"]*(1,0)\n","Outputs: 0:Qbit, 1:Qbit\n","\"\"\"\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = circuit_from_quipper(quipfile)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the Quipper gates that are not supported directly in `pytket` (`W` and `omega`) are translated into equivalent sequences of `pytket` gates. See the [pytket.quipper](https://tket.quantinuum.com/api-docs/quipper.html) docs for more.
\n","
\n","Quipper subroutines are also supported, corresponding to `CircBox` operations in `pytket`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n"," f.write(\n"," \"\"\"Inputs: 0:Qbit, 1:Qbit, 2:Qbit\n","QGate[\"H\"](0)\n","Subroutine(x2)[\"sub\", shape \"([Q,Q],())\"] (2,1) -> (2,1)\n","QGate[\"H\"](1)\n","Outputs: 0:Qbit, 1:Qbit, 2:Qbit \\n\n","Subroutine: \"sub\"\n","Shape: \"([Q,Q],())\"\n","Controllable: no\n","Inputs: 0:Qbit, 1:Qbit\n","QGate[\"Y\"](0)\n","QGate[\"not\"](1) with controls=[+0]\n","QGate[\"Z\"](1)\n","Outputs: 0:Qbit, 1:Qbit\n","\"\"\"\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = circuit_from_quipper(quipfile)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Boxes in `pytket`"]},{"cell_type":"markdown","metadata":{},"source":["The `CircBox` is an example of a `pytket` 'box', which is a reusable encapsulation of a circuit inside another. We can recover the circuit 'inside' the box using the `get_circuit()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cmds = c.get_commands()\n","boxed_circuit = cmds[1].op.get_circuit()\n","draw(boxed_circuit)"]},{"cell_type":"markdown","metadata":{},"source":["The `CircBox` is the most general type of box, implementing an arbitrary circuit. But `pytket` supports several other useful box types:
\n","* [Unitary1qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary1qBox) (implementing an arbitrary $2 \\times 2$ unitary matrix);
\n","* [Unitary2qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary2qBox) (implementing an arbitrary $4 \\times 4$ unitary matrix);
\n","* [ExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.ExpBox) (implementing $e^{itA}$ for an arbitrary $4 \\times 4$ hermitian matrix $A$ and parameter $t$);
\n","* [PauliExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.PauliExpBox) (implementing $e^{-\\frac{1}{2} i \\pi t (\\sigma_0 \\otimes \\sigma_1 \\otimes \\cdots)}$ for arbitrary Pauli operators $\\sigma_i \\in \\{\\mathrm{I}, \\mathrm{X}, \\mathrm{Y}, \\mathrm{Z}\\}$ and parameter $t$)."]},{"cell_type":"markdown","metadata":{},"source":["An example will illustrate how these various box types are added to a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from math import sqrt"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import numpy as np\n","from pytket.circuit import CircBox, ExpBox, PauliExpBox, Unitary1qBox, Unitary2qBox\n","from pytket.pauli import Pauli"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["boxycirc = Circuit(3)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `CircBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["subcirc = Circuit(2, name=\"MY BOX\")\n","subcirc.X(0).Y(1).CZ(0, 1)\n","cbox = CircBox(subcirc)\n","boxycirc.add_gate(cbox, args=[Qubit(0), Qubit(1)])"]},{"cell_type":"markdown","metadata":{},"source":["Add a `Unitary1qBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["m1 = np.asarray([[1 / 2, sqrt(3) / 2], [sqrt(3) / 2, -1 / 2]])\n","m1box = Unitary1qBox(m1)\n","boxycirc.add_unitary1qbox(m1box, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `Unitary2qBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["m2 = np.asarray([[0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0]])\n","m2box = Unitary2qBox(m2)\n","boxycirc.add_unitary2qbox(m2box, 1, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Add an `ExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["A = np.asarray(\n"," [[1, 2, 3, 4 + 1j], [2, 0, 1j, -1], [3, -1j, 2, 1j], [4 - 1j, -1, -1j, 1]]\n",")\n","ebox = ExpBox(A, 0.5)\n","boxycirc.add_expbox(ebox, 0, 1)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `PauliExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pbox = PauliExpBox([Pauli.X, Pauli.Z, Pauli.X], 0.75)\n","boxycirc.add_gate(pbox, [0, 1, 2])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(boxycirc)"]},{"cell_type":"markdown","metadata":{},"source":["Try clicking on boxes in the diagram above to get information about the underlying subroutine."]},{"cell_type":"markdown","metadata":{},"source":["The `get_circuit()` method is available for all box types, and returns a `Circuit` object. For example if we look inside the `ExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(ebox.get_circuit())"]},{"cell_type":"markdown","metadata":{},"source":["## Circuit composition"]},{"cell_type":"markdown","metadata":{},"source":["For more discussion of circuit composition see the corresponding section of the [manual](https://tket.quantinuum.com/user-manual/manual_circuit.html#composing-circuits)."]},{"cell_type":"markdown","metadata":{},"source":["Circuits can be composed either serially, whereby wires are joined together, or in parallel, using the `append()` command.
\n","
\n","For a simple illustration of serial composition, let's create two circuits with compatible set of wires, and append the second to the first:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(2)\n","c.CX(0, 1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = Circuit(2)\n","c1.CZ(1, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["In the above example, there was a one-to-one match between the unit IDs in the two circuits, and they were matched up accordingly. The same applied with named unit IDs:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["x, y = Qubit(\"x\"), Qubit(\"y\")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","c.add_qubit(x)\n","c.add_qubit(y)\n","c.CX(x, y)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = Circuit()\n","c1.add_qubit(x)\n","c1.add_qubit(y)\n","c1.CZ(y, x)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["If either circuit contains wires not matching any wires in the other, those are added to the other circuit before composition:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["z = Qubit(\"z\")\n","c1.add_qubit(z)\n","c1.CY(y, z)\n","c.append(c1)\n","print(c.qubits)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["If the sets of unit IDs for the two circuits are disjoint, then the composition is entirely parallel.
\n","
\n","What if we want to serially compose two circuits having different sets of `Qubit`? In that case, we can use the `rename_units()` method on one or other of them to bring them into line. This method takes a dictionary mapping current unit IDs to new one:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c2 = Circuit()\n","c2.add_q_register(\"w\", 3)\n","w = [Qubit(\"w\", i) for i in range(3)]\n","c2.H(w[0]).CX(w[0], w[1]).CRz(0.25, w[1], w[2])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.rename_units({x: w[0], y: w[1], z: w[2]})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c2)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Symbolic parameters"]},{"cell_type":"markdown","metadata":{},"source":["Many of the gates supported by `pytket` are parametrized by one or more phase parameters, which represent rotations in multiples of $\\pi$. For example, $\\mathrm{Rz}(\\frac{1}{2})$ represents a quarter turn, i.e. a rotation of $\\pi/2$, about the Z axis. If we know the values of these parameters we can add the gates directly to our circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(1)\n","c.Rz(0.5, 0)"]},{"cell_type":"markdown","metadata":{},"source":["However, we may wish to construct and manipulate circuits containing such parametrized gates without specifying the values. This allows us to do calculations in a general setting, only later substituting values for the parameters.
\n","
\n","Thus `pytket` allows us to specify any of the parameters as symbols. All manipulations (such as combination and cancellation of gates) are performed on the symbolic representation:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from sympy import Symbol"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = Symbol(\"a\")\n","c.Rz(a, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["When we apply any transformation to this circuit, the symbolic parameter is preserved in the result:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.transform import Transform"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.RemoveRedundancies().apply(c)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["To substitute values for symbols, we use the `symbol_substitution()` method, supplying a dictionary from symbols to values:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.symbol_substitution({a: 0.75})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can also substitute symbols for other symbols:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b = Symbol(\"b\")\n","c = Circuit(1)\n","c.Rz(a + b, 0)\n","c.symbol_substitution({b: 2 * a})\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Custom gates"]},{"cell_type":"markdown","metadata":{},"source":["We can define custom parametrized gates in `pytket` by first setting up a circuit containing symbolic parameters and then converting this to a parametrized operation type:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import CustomGateDef"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = Symbol(\"a\")\n","b = Symbol(\"b\")\n","setup = Circuit(3)\n","setup.CX(0, 1)\n","setup.Rz(a + 0.5, 2)\n","setup.CRz(b, 0, 2)\n","my_gate = CustomGateDef.define(\"g\", setup, [a, b])\n","c = Circuit(4)\n","c.add_custom_gate(my_gate, [0.2, 1.3], [0, 3, 1])\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Custom gates can also receive symbolic parameters:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["x = Symbol(\"x\")\n","c.add_custom_gate(my_gate, [x, 1.0], [0, 1, 2])\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Decomposing boxes and custom gates"]},{"cell_type":"markdown","metadata":{},"source":["Having defined a circuit containing custom gates, we may now want to decompose it into elementary gates. The `DecomposeBoxes()` transform allows us to do this:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.DecomposeBoxes().apply(c)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["The same transform works on circuits composed of arbitrary boxes. Let's try it on a copy of the circuit we built up earlier out of various box types."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = boxycirc.copy()\n","Transform.DecomposeBoxes().apply(c)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the unitaries have been decomposed into elementary gates."]},{"cell_type":"markdown","metadata":{},"source":["## Classical controls"]},{"cell_type":"markdown","metadata":{},"source":["Most of the examples above involve only pure quantum gates. However, `pytket` can also represent gates whose operation is conditional on one or more classical inputs.
\n","
\n","For example, suppose we want to run the complex circuit `c` we've just constructed, then measure qubits 0 and 1, and finally apply an $\\mathrm{Rz}(\\frac{1}{2})$ rotation to qubit 2 if and only if the measurements were 0 and 1 respectively.
\n","
\n","First, we'll add two classical wires to the circuit to store the measurement results:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Bit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.add_c_register(\"m\", 2)\n","m = [Bit(\"m\", i) for i in range(2)]"]},{"cell_type":"markdown","metadata":{},"source":["Classically conditioned operations depend on all their inputs being 1. Since we want to condition on `m[0]` being 0, we must first apply an X gate to its qubit, and then measure:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["q = [Qubit(\"q\", i) for i in range(3)]\n","c.X(q[0])\n","c.Measure(q[0], m[0])\n","c.Measure(q[1], m[1])"]},{"cell_type":"markdown","metadata":{},"source":["Finally we add the classically conditioned Rz operation, using the `add_gate()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import OpType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.add_gate(OpType.Rz, [0.5], [q[2]], condition_bits=[m[0], m[1]], condition_value=3)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that many of the transforms and compilation passes will not accept circuits that contain classical controls."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/conditional_gate_example.ipynb b/docs/examples/circuit_construction/conditional_gate_example.ipynb similarity index 100% rename from docs/examples/conditional_gate_example.ipynb rename to docs/examples/circuit_construction/conditional_gate_example.ipynb diff --git a/docs/examples/circuit_generation_example.ipynb b/docs/examples/circuit_generation_example.ipynb deleted file mode 100644 index 4381865e..00000000 --- a/docs/examples/circuit_generation_example.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Circuit generation"]},{"cell_type":"markdown","metadata":{},"source":["This notebook will provide a brief introduction to some of the more advanced methods of circuit generation available in `pytket`, including:
\n","* how to address wires and registers;
\n","* reading in circuits from QASM and Quipper ASCII files;
\n","* various types of 'boxes';
\n","* composition of circuits (both 'horizontally' and 'vertically');
\n","* use of symbolic gate parameters;
\n","* representation of classically controlled gates."]},{"cell_type":"markdown","metadata":{},"source":["## Wires, unit IDs and registers"]},{"cell_type":"markdown","metadata":{},"source":["Let's get started by constructing a circuit with 3 qubits and 2 classical bits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit\n","from pytket.circuit.display import render_circuit_jupyter as draw"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(1, 2)\n","print(circ.qubits)\n","print(circ.bits)"]},{"cell_type":"markdown","metadata":{},"source":["The qubits have automatically been assigned to a register with name `q` and indices 0, 1 and 2, while the bits have been assigned to a register with name `c` and indices 0 and 1.
\n","
\n","We can give these units arbitrary names and indices of arbitrary dimension:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Qubit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["new_q1 = Qubit(\"alpha\", 0)\n","new_q2 = Qubit(\"beta\", 2, 1)\n","circ.add_qubit(new_q1)\n","circ.add_qubit(new_q2)\n","print(circ.qubits)"]},{"cell_type":"markdown","metadata":{},"source":["We can also add a new register of qubits in one go:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["delta_reg = circ.add_q_register(\"delta\", 2)\n","print(circ.qubits)"]},{"cell_type":"markdown","metadata":{},"source":["Similar commands are available for classical bits.
\n","
\n","We can add gates to the circuit as follows:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.CX(delta_reg[0], delta_reg[1])"]},{"cell_type":"markdown","metadata":{},"source":["This command appends a CX gate with control `q[0]` and target `q[1]`. Note that the integer arguments are automatically converted to the default unit IDs. For simple circuits it is often easiest to stick to the default register and refer to the qubits by integers. To add gates to our own named units, we simply pass the `Qubit` (or classical `Bit`) as an argument. (We can't mix the two conventions in one command, however.)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.H(new_q1)\n","circ.CX(Qubit(\"q\", 0), new_q2)\n","circ.Rz(0.5, new_q2)"]},{"cell_type":"markdown","metadata":{},"source":["Let's have a look at our circuit using the interactive circuit renderer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit.display import render_circuit_jupyter as draw"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(circ)"]},{"cell_type":"markdown","metadata":{},"source":["## Exporting to and importing from standard formats"]},{"cell_type":"markdown","metadata":{},"source":["We can export a `Circuit` to a file in QASM format. Conversely, if we have such a file we can import it into `pytket`. There are some limitations on the circuits that can be converted: for example, multi-dimensional indices (as in `beta` and `gamma` above) are not allowed.
\n","
\n","Here is a simple example:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.qasm import circuit_from_qasm, circuit_to_qasm"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(3, 1)\n","circ.H(0)\n","circ.CX(0, 1)\n","circ.CX(1, 2)\n","circ.Rz(0.25, 2)\n","circ.Measure(2, 0)\n","draw(circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qasmfile = \"c.qasm\"\n","circuit_to_qasm(circ, qasmfile)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["with open(qasmfile, encoding=\"utf-8\") as f:\n"," print(f.read())"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = circuit_from_qasm(qasmfile)\n","circ == c1"]},{"cell_type":"markdown","metadata":{},"source":["We can also import files in the Quipper ASCII format:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.quipper import circuit_from_quipper"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["quipfile = \"c.quip\"\n","with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n"," f.write(\n"," \"\"\"Inputs: 0:Qbit, 1:Qbit\n","QGate[\"W\"](0,1)\n","QGate[\"omega\"](1)\n","QGate[\"swap\"](0,1)\n","QGate[\"W\"]*(1,0)\n","Outputs: 0:Qbit, 1:Qbit\n","\"\"\"\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = circuit_from_quipper(quipfile)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the Quipper gates that are not supported directly in `pytket` (`W` and `omega`) are translated into equivalent sequences of `pytket` gates. See the [pytket.quipper](https://tket.quantinuum.com/api-docs/quipper.html) docs for more.
\n","
\n","Quipper subroutines are also supported, corresponding to `CircBox` operations in `pytket`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n"," f.write(\n"," \"\"\"Inputs: 0:Qbit, 1:Qbit, 2:Qbit\n","QGate[\"H\"](0)\n","Subroutine(x2)[\"sub\", shape \"([Q,Q],())\"] (2,1) -> (2,1)\n","QGate[\"H\"](1)\n","Outputs: 0:Qbit, 1:Qbit, 2:Qbit \\n\n","Subroutine: \"sub\"\n","Shape: \"([Q,Q],())\"\n","Controllable: no\n","Inputs: 0:Qbit, 1:Qbit\n","QGate[\"Y\"](0)\n","QGate[\"not\"](1) with controls=[+0]\n","QGate[\"Z\"](1)\n","Outputs: 0:Qbit, 1:Qbit\n","\"\"\"\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = circuit_from_quipper(quipfile)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Boxes in `pytket`"]},{"cell_type":"markdown","metadata":{},"source":["The `CircBox` is an example of a `pytket` 'box', which is a reusable encapsulation of a circuit inside another. We can recover the circuit 'inside' the box using the `get_circuit()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cmds = c.get_commands()\n","boxed_circuit = cmds[1].op.get_circuit()\n","draw(boxed_circuit)"]},{"cell_type":"markdown","metadata":{},"source":["The `CircBox` is the most general type of box, implementing an arbitrary circuit. But `pytket` supports several other useful box types:
\n","* [Unitary1qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary1qBox) (implementing an arbitrary $2 \\times 2$ unitary matrix);
\n","* [Unitary2qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary2qBox) (implementing an arbitrary $4 \\times 4$ unitary matrix);
\n","* [ExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.ExpBox) (implementing $e^{itA}$ for an arbitrary $4 \\times 4$ hermitian matrix $A$ and parameter $t$);
\n","* [PauliExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.PauliExpBox) (implementing $e^{-\\frac{1}{2} i \\pi t (\\sigma_0 \\otimes \\sigma_1 \\otimes \\cdots)}$ for arbitrary Pauli operators $\\sigma_i \\in \\{\\mathrm{I}, \\mathrm{X}, \\mathrm{Y}, \\mathrm{Z}\\}$ and parameter $t$)."]},{"cell_type":"markdown","metadata":{},"source":["An example will illustrate how these various box types are added to a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from math import sqrt"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import numpy as np\n","from pytket.circuit import CircBox, ExpBox, PauliExpBox, Unitary1qBox, Unitary2qBox\n","from pytket.pauli import Pauli"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["boxycirc = Circuit(3)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `CircBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["subcirc = Circuit(2, name=\"MY BOX\")\n","subcirc.X(0).Y(1).CZ(0, 1)\n","cbox = CircBox(subcirc)\n","boxycirc.add_gate(cbox, args=[Qubit(0), Qubit(1)])"]},{"cell_type":"markdown","metadata":{},"source":["Add a `Unitary1qBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["m1 = np.asarray([[1 / 2, sqrt(3) / 2], [sqrt(3) / 2, -1 / 2]])\n","m1box = Unitary1qBox(m1)\n","boxycirc.add_unitary1qbox(m1box, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `Unitary2qBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["m2 = np.asarray([[0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0]])\n","m2box = Unitary2qBox(m2)\n","boxycirc.add_unitary2qbox(m2box, 1, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Add an `ExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["A = np.asarray(\n"," [[1, 2, 3, 4 + 1j], [2, 0, 1j, -1], [3, -1j, 2, 1j], [4 - 1j, -1, -1j, 1]]\n",")\n","ebox = ExpBox(A, 0.5)\n","boxycirc.add_expbox(ebox, 0, 1)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `PauliExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pbox = PauliExpBox([Pauli.X, Pauli.Z, Pauli.X], 0.75)\n","boxycirc.add_gate(pbox, [0, 1, 2])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(boxycirc)"]},{"cell_type":"markdown","metadata":{},"source":["Try clicking on boxes in the diagram above to get information about the underlying subroutine."]},{"cell_type":"markdown","metadata":{},"source":["The `get_circuit()` method is available for all box types, and returns a `Circuit` object. For example if we look inside the `ExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(ebox.get_circuit())"]},{"cell_type":"markdown","metadata":{},"source":["## Circuit composition"]},{"cell_type":"markdown","metadata":{},"source":["For more discussion of circuit composition see the corresponding section of the [manual](https://tket.quantinuum.com/user-manual/manual_circuit.html#composing-circuits)."]},{"cell_type":"markdown","metadata":{},"source":["Circuits can be composed either serially, whereby wires are joined together, or in parallel, using the `append()` command.
\n","
\n","For a simple illustration of serial composition, let's create two circuits with compatible set of wires, and append the second to the first:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(2)\n","c.CX(0, 1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = Circuit(2)\n","c1.CZ(1, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["In the above example, there was a one-to-one match between the unit IDs in the two circuits, and they were matched up accordingly. The same applied with named unit IDs:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["x, y = Qubit(\"x\"), Qubit(\"y\")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","c.add_qubit(x)\n","c.add_qubit(y)\n","c.CX(x, y)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = Circuit()\n","c1.add_qubit(x)\n","c1.add_qubit(y)\n","c1.CZ(y, x)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["If either circuit contains wires not matching any wires in the other, those are added to the other circuit before composition:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["z = Qubit(\"z\")\n","c1.add_qubit(z)\n","c1.CY(y, z)\n","c.append(c1)\n","print(c.qubits)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["If the sets of unit IDs for the two circuits are disjoint, then the composition is entirely parallel.
\n","
\n","What if we want to serially compose two circuits having different sets of `Qubit`? In that case, we can use the `rename_units()` method on one or other of them to bring them into line. This method takes a dictionary mapping current unit IDs to new one:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c2 = Circuit()\n","c2.add_q_register(\"w\", 3)\n","w = [Qubit(\"w\", i) for i in range(3)]\n","c2.H(w[0]).CX(w[0], w[1]).CRz(0.25, w[1], w[2])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.rename_units({x: w[0], y: w[1], z: w[2]})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c2)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Symbolic parameters"]},{"cell_type":"markdown","metadata":{},"source":["Many of the gates supported by `pytket` are parametrized by one or more phase parameters, which represent rotations in multiples of $\\pi$. For example, $\\mathrm{Rz}(\\frac{1}{2})$ represents a quarter turn, i.e. a rotation of $\\pi/2$, about the Z axis. If we know the values of these parameters we can add the gates directly to our circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(1)\n","c.Rz(0.5, 0)"]},{"cell_type":"markdown","metadata":{},"source":["However, we may wish to construct and manipulate circuits containing such parametrized gates without specifying the values. This allows us to do calculations in a general setting, only later substituting values for the parameters.
\n","
\n","Thus `pytket` allows us to specify any of the parameters as symbols. All manipulations (such as combination and cancellation of gates) are performed on the symbolic representation:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from sympy import Symbol"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = Symbol(\"a\")\n","c.Rz(a, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["When we apply any transformation to this circuit, the symbolic parameter is preserved in the result:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.transform import Transform"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.RemoveRedundancies().apply(c)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["To substitute values for symbols, we use the `symbol_substitution()` method, supplying a dictionary from symbols to values:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.symbol_substitution({a: 0.75})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can also substitute symbols for other symbols:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b = Symbol(\"b\")\n","c = Circuit(1)\n","c.Rz(a + b, 0)\n","c.symbol_substitution({b: 2 * a})\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Custom gates"]},{"cell_type":"markdown","metadata":{},"source":["We can define custom parametrized gates in `pytket` by first setting up a circuit containing symbolic parameters and then converting this to a parametrized operation type:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import CustomGateDef"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = Symbol(\"a\")\n","b = Symbol(\"b\")\n","setup = Circuit(3)\n","setup.CX(0, 1)\n","setup.Rz(a + 0.5, 2)\n","setup.CRz(b, 0, 2)\n","my_gate = CustomGateDef.define(\"g\", setup, [a, b])\n","c = Circuit(4)\n","c.add_custom_gate(my_gate, [0.2, 1.3], [0, 3, 1])\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Custom gates can also receive symbolic parameters:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["x = Symbol(\"x\")\n","c.add_custom_gate(my_gate, [x, 1.0], [0, 1, 2])\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Decomposing boxes and custom gates"]},{"cell_type":"markdown","metadata":{},"source":["Having defined a circuit containing custom gates, we may now want to decompose it into elementary gates. The `DecomposeBoxes()` transform allows us to do this:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.DecomposeBoxes().apply(c)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["The same transform works on circuits composed of arbitrary boxes. Let's try it on a copy of the circuit we built up earlier out of various box types."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = boxycirc.copy()\n","Transform.DecomposeBoxes().apply(c)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the unitaries have been decomposed into elementary gates."]},{"cell_type":"markdown","metadata":{},"source":["## Classical controls"]},{"cell_type":"markdown","metadata":{},"source":["Most of the examples above involve only pure quantum gates. However, `pytket` can also represent gates whose operation is conditional on one or more classical inputs.
\n","
\n","For example, suppose we want to run the complex circuit `c` we've just constructed, then measure qubits 0 and 1, and finally apply an $\\mathrm{Rz}(\\frac{1}{2})$ rotation to qubit 2 if and only if the measurements were 0 and 1 respectively.
\n","
\n","First, we'll add two classical wires to the circuit to store the measurement results:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Bit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.add_c_register(\"m\", 2)\n","m = [Bit(\"m\", i) for i in range(2)]"]},{"cell_type":"markdown","metadata":{},"source":["Classically conditioned operations depend on all their inputs being 1. Since we want to condition on `m[0]` being 0, we must first apply an X gate to its qubit, and then measure:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["q = [Qubit(\"q\", i) for i in range(3)]\n","c.X(q[0])\n","c.Measure(q[0], m[0])\n","c.Measure(q[1], m[1])"]},{"cell_type":"markdown","metadata":{},"source":["Finally we add the classically conditioned Rz operation, using the `add_gate()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import OpType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.add_gate(OpType.Rz, [0.5], [q[2]], condition_bits=[m[0], m[1]], condition_value=3)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that many of the transforms and compilation passes will not accept circuits that contain classical controls."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/constraints.txt b/docs/examples/constraints.txt deleted file mode 100644 index 59013594..00000000 --- a/docs/examples/constraints.txt +++ /dev/null @@ -1,2 +0,0 @@ -cirq>=0.11.0 -pydantic>=2.5.0 \ No newline at end of file diff --git a/docs/examples/qasm/c.qasm b/docs/examples/qasm/c.qasm new file mode 100644 index 00000000..285ccaef --- /dev/null +++ b/docs/examples/qasm/c.qasm @@ -0,0 +1,10 @@ +OPENQASM 2.0; +include "qelib1.inc"; + +qreg q[3]; +creg c[1]; +h q[0]; +cx q[0],q[1]; +cx q[1],q[2]; +rz(0.25*pi) q[2]; +measure q[2] -> c[0]; diff --git a/docs/index.rst b/docs/index.rst index 3e711d6b..a8597742 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -136,19 +136,11 @@ for more. manual/manual_zx.rst .. toctree:: - :caption: Examples + :glob: + :caption: Example Notebooks :maxdepth: 2 - examples/ansatz_sequence_example.ipynb - examples/circuit_analysis_example.ipynb - examples/circuit_generation_example.ipynb - examples/compilation_example.ipynb - examples/conditional_gate_example.ipynb - examples/contextual_optimization.ipynb - examples/creating_backends.ipynb - examples/measurement_reduction_example.ipynb - examples/mapping_example.ipynb - examples/symbolics_example.ipynb - examples/phase_estimation.ipynb - examples/pytket-qujax_qaoa.ipynb - examples/ucc_vqe.ipynb + examples/circuit_construction/* + examples/backends/* + examples/circuit_compilation/* + examples/algorithms_and_protocols/* From bc85f1ed097b96639a018b8b134a74ca5691ce45 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 25 Jul 2024 16:12:03 +0100 Subject: [PATCH 51/76] fail build on warnings --- build-docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-docs.sh b/build-docs.sh index 33f7ee33..99023de9 100755 --- a/build-docs.sh +++ b/build-docs.sh @@ -3,5 +3,5 @@ cd docs/ rm -rf build/ -sphinx-build -b html . build +sphinx-build -b html . build -W From 37c912f02464f7648da004303f322cb10f0c99d3 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 25 Jul 2024 16:12:19 +0100 Subject: [PATCH 52/76] update navbar items for user guide --- docs/_static/nav-config.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/_static/nav-config.js b/docs/_static/nav-config.js index 00e998ed..598544f9 100644 --- a/docs/_static/nav-config.js +++ b/docs/_static/nav-config.js @@ -6,19 +6,14 @@ const navConfig = { "href": "../api-docs", "pathMatch": "somewhere", }, - { - "title": "Examples", - "href": "../examples", - "pathMatch": "somewhere", - }, { "title": "Blog", "href": "../blog/", "pathMatch": "somewhere", }, { - "title": "User Manual", - "href": "../user-manual", + "title": "User Guide", + "href": "../user-guide", "pathMatch": "somewhere", }, ], From d3d2d944865be90e79353b37f16185c6acb212e2 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:03:37 +0100 Subject: [PATCH 53/76] remove unused extensions --- docs/conf.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e1fdafee..0e3b86dc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,8 +10,6 @@ html_title = "pytket user guide" extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.autosummary", "sphinx.ext.intersphinx", "sphinx.ext.mathjax", "jupyter_sphinx", From 716c6533b0bc4be13461c0d6e4b9c22b6950e758 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:04:41 +0100 Subject: [PATCH 54/76] delete old workflow for testing notebooks --- .github/workflows/check-examples.yml | 75 -------------------------- docs/examples/check-examples | 57 -------------------- docs/examples/ci-tested-notebooks.txt | 11 ---- docs/examples/maintained-notebooks.txt | 22 -------- 4 files changed, 165 deletions(-) delete mode 100644 .github/workflows/check-examples.yml delete mode 100755 docs/examples/check-examples delete mode 100644 docs/examples/ci-tested-notebooks.txt delete mode 100644 docs/examples/maintained-notebooks.txt diff --git a/.github/workflows/check-examples.yml b/.github/workflows/check-examples.yml deleted file mode 100644 index d21b8056..00000000 --- a/.github/workflows/check-examples.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: check examples - -on: - pull_request: - branches: - - main - schedule: - # 04:00 every Saturday morning - - cron: '0 4 * * 6' - -jobs: - - changes: - runs-on: ubuntu-22.04 - outputs: - examples: ${{ steps.filter.outputs.examples }} - steps: - - uses: actions/checkout@v4 - - uses: dorny/paths-filter@v3 - id: filter - with: - base: ${{ github.ref }} - filters: | - examples: - - 'examples/**' - - 'example_requirements.txt' - - '.github/workflows/check-examples.yml' - - check: - name: check examples - needs: changes - if: github.event_name == 'schedule' || needs.changes.outputs.examples == 'true' - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: '0' - - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/* - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: 3.11 - - name: install python requirements for notebooks - run: | - cd examples - python -m pip install --upgrade pip - python -m pip install pre-commit==3.0.0 - python -m pip install wheel - python -m pip install -r example_requirements.txt - python -m pip install pytket-qsharp==0.40.0 - # python -m pip install pytket-pyquil==0.31.0 - python -m pip install -c constraints.txt jupyter plotly - python -m pip install docker p2j - - name: install dotnet SDK - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '6.0.x' - - name: install iqsharp - run: | - dotnet tool install -g Microsoft.Quantum.IQSharp - dotnet iqsharp install --user - - name: pull docker images - run: | - docker pull rigetti/quilc - docker pull rigetti/qvm - - name: install pre-commit hooks and check format with black - run: | - pre-commit install - pre-commit run --all-files black - - name: test example notebooks - run: | - cd examples - ./check-examples - env: - PYTKET_QA_QISKIT_TOKEN: ${{ secrets.PYTKET_QA_QISKIT_TOKEN }} diff --git a/docs/examples/check-examples b/docs/examples/check-examples deleted file mode 100755 index 67f1862b..00000000 --- a/docs/examples/check-examples +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -set -e - -# TODO Find a way to test the others. There are two issues: -# - IBMQ credential loading for IBMQEmulatorBackend: -# - expectation_value -# - spam -# - backends -# - qiskit-integration -# - Some take an impractical amount of time to run: -# - entanglement_swapping -# - qiskit_integration -# - ucc_vqe -# for name in `cat maintained-notebooks.txt` -for name in `cat ci-tested-notebooks.txt` -do - echo "Checking: ${name} ..." - # Check that notebook is generated from script: - p2j -o -t ${name}-gen.ipynb python/${name}.py - cmp ${name}.ipynb ${name}-gen.ipynb - rm ${name}-gen.ipynb - # Run script: - if [[ "$name" == "backends_example" || \ - "$name" == "expectation_value_example" || \ - "$name" == "qiskit_integration" || \ - "$name" == "spam_example" ]] ; then - echo "Attempting to load IBMQ credentials ..." - set +e - ./load-ibmq-account - RESULT=$? - set -e - if [[ $RESULT -eq 0 ]] ; then - echo "... IBMQ credentials loaded." - python python/${name}.py - else - echo "... Failed to load IBMQ credentials, skipping notebook." - fi - elif [[ "$name" == "Forest_portability_example" ]] ; then - echo "Attempting to start Rigetti VMs ..." - set +e - ./start-rigetti-vms - RESULT=$? - set -e - if [[ $RESULT -eq 0 ]] ; then - echo "... Rigetti VMs started." - python python/${name}.py - echo "Stopping VMs ..." - ./stop-vms || true - echo "VMs stopped." - else - echo "Failed to start Rigetti VMs, skipping notebook." - fi - else - python python/${name}.py - fi -done diff --git a/docs/examples/ci-tested-notebooks.txt b/docs/examples/ci-tested-notebooks.txt deleted file mode 100644 index 35cbf098..00000000 --- a/docs/examples/ci-tested-notebooks.txt +++ /dev/null @@ -1,11 +0,0 @@ -ansatz_sequence_example -circuit_analysis_example -circuit_generation_example -comparing_simulators -compilation_example -conditional_gate_example -contextual_optimization -creating_backends -measurement_reduction_example -mapping_example -symbolics_example diff --git a/docs/examples/maintained-notebooks.txt b/docs/examples/maintained-notebooks.txt deleted file mode 100644 index 7033ba8f..00000000 --- a/docs/examples/maintained-notebooks.txt +++ /dev/null @@ -1,22 +0,0 @@ -ansatz_sequence_example -backends_example -circuit_analysis_example -circuit_generation_example -comparing_simulators -compilation_example -conditional_gate_example -contextual_optimization -creating_backends -entanglement_swapping -expectation_value_example -Forest_portability_example -measurement_reduction_example -qiskit_integration -mapping_example -spam_example -symbolics_example -pytket-qujax_qaoa -pytket-qujax_heisenberg_vqe -pytket-qujax-classification -ucc_vqe -phase_estimation From cf6d7050f4c8a77ab0808b7dd090e8c8bf5b845e Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:31:25 +0100 Subject: [PATCH 55/76] add download option to notebooks --- .../algorithms_and_protocols/ansatz_sequence_example.ipynb | 2 +- .../algorithms_and_protocols/entanglement_swapping.ipynb | 2 +- .../algorithms_and_protocols/expectation_value_example.ipynb | 2 +- .../measurement_reduction_example.ipynb | 2 +- docs/examples/algorithms_and_protocols/phase_estimation.ipynb | 2 +- .../algorithms_and_protocols/pytket-qujax-classification.ipynb | 2 +- .../algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb | 2 +- docs/examples/algorithms_and_protocols/pytket-qujax_qaoa.ipynb | 2 +- docs/examples/algorithms_and_protocols/spam_example.ipynb | 2 +- docs/examples/algorithms_and_protocols/ucc_vqe.ipynb | 2 +- docs/examples/backends/Forest_portability_example.ipynb | 2 +- docs/examples/backends/backends_example.ipynb | 2 +- docs/examples/backends/comparing_simulators.ipynb | 2 +- docs/examples/backends/creating_backends.ipynb | 2 +- docs/examples/backends/qiskit_integration.ipynb | 2 +- docs/examples/circuit_compilation/compilation_example.ipynb | 2 +- ...textual_optimization.ipynb => contextual_optimisation.ipynb} | 0 docs/examples/circuit_compilation/mapping_example.ipynb | 2 +- docs/examples/circuit_compilation/symbolics_example.ipynb | 2 +- .../circuit_construction/circuit_analysis_example.ipynb | 2 +- .../circuit_construction/circuit_generation_example.ipynb | 2 +- .../circuit_construction/conditional_gate_example.ipynb | 2 +- 22 files changed, 21 insertions(+), 21 deletions(-) rename docs/examples/circuit_compilation/{contextual_optimization.ipynb => contextual_optimisation.ipynb} (100%) diff --git a/docs/examples/algorithms_and_protocols/ansatz_sequence_example.ipynb b/docs/examples/algorithms_and_protocols/ansatz_sequence_example.ipynb index cd250ada..3b6e4496 100644 --- a/docs/examples/algorithms_and_protocols/ansatz_sequence_example.ipynb +++ b/docs/examples/algorithms_and_protocols/ansatz_sequence_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Ansatz sequencing"]}, {"cell_type": "markdown", "metadata": {}, "source": ["When performing variational algorithms like VQE, one common approach to generating circuit ans\u00e4tze is to take an operator $U$ representing excitations and use this to act on a reference state $\\lvert \\phi_0 \\rangle$. One such ansatz is the Unitary Coupled Cluster ansatz. Each excitation, indexed by $j$, within $U$ is given a real coefficient $a_j$ and a parameter $t_j$, such that $U = e^{i \\sum_j \\sum_k a_j t_j P_{jk}}$, where $P_{jk} \\in \\{I, X, Y, Z \\}^{\\otimes n}$. The exact form is dependent on the chosen qubit encoding. This excitation gives us a variational state $\\lvert \\psi (t) \\rangle = U(t) \\lvert \\phi_0 \\rangle$. The operator $U$ must be Trotterised, to give a product of Pauli exponentials, and converted into native quantum gates to create the ansatz circuit.
\n", "
\n", "This notebook will describe how to use an advanced feature of `pytket` to enable automated circuit synthesis for $U$ and reduce circuit depth dramatically.
\n", "
\n", "We must create a `pytket` `QubitPauliOperator`, which represents such an operator $U$, and contains a dictionary from Pauli string $P_{jk}$ to symbolic expression. Here, we make a mock operator ourselves, which resembles the UCCSD excitation operator for the $\\mathrm{H}_2$ molecule using the Jordan-Wigner qubit encoding.
\n", "
\n", "First, we create a series of `QubitPauliString` objects, which represent each $P_{jk}$."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.pauli import Pauli, QubitPauliString\n", "from pytket.circuit import Qubit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["q = [Qubit(i) for i in range(4)]\n", "qps0 = QubitPauliString([q[0], q[1], q[2]], [Pauli.Y, Pauli.Z, Pauli.X])\n", "qps1 = QubitPauliString([q[0], q[1], q[2]], [Pauli.X, Pauli.Z, Pauli.Y])\n", "qps2 = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.X, Pauli.Y])\n", "qps3 = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.Y, Pauli.X])\n", "qps4 = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.X, Pauli.X])\n", "qps5 = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.X, Pauli.X])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Now, create some symbolic expressions for the $a_j t_j$ terms."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import fresh_symbol"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["symbol1 = fresh_symbol(\"s0\")\n", "expr1 = 1.2 * symbol1\n", "symbol2 = fresh_symbol(\"s1\")\n", "expr2 = -0.3 * symbol2"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can now create our `QubitPauliOperator`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.utils import QubitPauliOperator"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["dict1 = dict((string, expr1) for string in (qps0, qps1))\n", "dict2 = dict((string, expr2) for string in (qps2, qps3, qps4, qps5))\n", "operator = QubitPauliOperator({**dict1, **dict2})\n", "print(operator)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Now we can let `pytket` sequence the terms in this operator for us, using a selection of strategies. First, we will create a `Circuit` to generate an example reference state, and then use the `gen_term_sequence_circuit` method to append the Pauli exponentials."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import Circuit\n", "from pytket.utils import gen_term_sequence_circuit\n", "from pytket.partition import PauliPartitionStrat, GraphColourMethod"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["reference_circ = Circuit(4).X(1).X(3)\n", "ansatz_circuit = gen_term_sequence_circuit(\n", " operator, reference_circ, PauliPartitionStrat.CommutingSets, GraphColourMethod.Lazy\n", ")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This method works by generating a graph of Pauli exponentials and performing graph colouring. Here we have chosen to partition the terms so that exponentials which commute are gathered together, and we have done so using a lazy, greedy graph colouring method.
\n", "
\n", "Alternatively, we could have used the `PauliPartitionStrat.NonConflictingSets`, which puts Pauli exponentials together so that they only require single-qubit gates to be converted into the form $e^{i \\alpha Z \\otimes Z \\otimes ... \\otimes Z}$. This strategy is primarily useful for measurement reduction, a different problem.
\n", "
\n", "We could also have used the `GraphColourMethod.LargestFirst`, which still uses a greedy method, but builds the full graph and iterates through the vertices in descending order of arity. We recommend playing around with the options, but we typically find that the combination of `CommutingSets` and `Lazy` allows the best optimisation.
\n", "
\n", "In general, not all of our exponentials will commute, so the semantics of our circuit depend on the order of our sequencing. As a result, it is important for us to be able to inspect the order we have produced. `pytket` provides functionality to enable this. Each set of commuting exponentials is put into a `CircBox`, which lets us inspect the partitoning."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import OpType"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["for command in ansatz_circuit:\n", " if command.op.type == OpType.CircBox:\n", " print(\"New CircBox:\")\n", " for pauli_exp in command.op.get_circuit():\n", " print(\n", " \" {} {} {}\".format(\n", " pauli_exp, pauli_exp.op.get_paulis(), pauli_exp.op.get_phase()\n", " )\n", " )\n", " else:\n", " print(\"Native gate: {}\".format(command))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can convert this circuit into basic gates using a `pytket` `Transform`. This acts in place on the circuit to do rewriting, for gate translation and optimisation. We will start off with a naive decomposition."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.transform import Transform"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["naive_circuit = ansatz_circuit.copy()\n", "Transform.DecomposeBoxes().apply(naive_circuit)\n", "print(naive_circuit.get_commands())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This is a jumble of one- and two-qubit gates. We can get some relevant circuit metrics out:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(\"Naive CX Depth: {}\".format(naive_circuit.depth_by_type(OpType.CX)))\n", "print(\"Naive CX Count: {}\".format(naive_circuit.n_gates_of_type(OpType.CX)))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["These metrics can be improved upon significantly by smart compilation. A `Transform` exists precisely for this purpose:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.transform import PauliSynthStrat, CXConfigType"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["smart_circuit = ansatz_circuit.copy()\n", "Transform.UCCSynthesis(PauliSynthStrat.Sets, CXConfigType.Tree).apply(smart_circuit)\n", "print(\"Smart CX Depth: {}\".format(smart_circuit.depth_by_type(OpType.CX)))\n", "print(\"Smart CX Count: {}\".format(smart_circuit.n_gates_of_type(OpType.CX)))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This `Transform` takes in a `Circuit` with the structure specified above: some arbitrary gates for the reference state, along with several `CircBox` gates containing `PauliExpBox` gates.
\n", "
\n", "We have chosen `PauliSynthStrat.Sets` and `CXConfigType.Tree`. The `PauliSynthStrat` dictates the method for decomposing multiple adjacent Pauli exponentials into basic gates, while the `CXConfigType` dictates the structure of adjacent CX gates.
\n", "
\n", "If we choose a different combination of strategies, we can produce a different output circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["last_circuit = ansatz_circuit.copy()\n", "Transform.UCCSynthesis(PauliSynthStrat.Individual, CXConfigType.Snake).apply(\n", " last_circuit\n", ")\n", "print(last_circuit.get_commands())"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(\"Last CX Depth: {}\".format(last_circuit.depth_by_type(OpType.CX)))\n", "print(\"Last CX Count: {}\".format(last_circuit.n_gates_of_type(OpType.CX)))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Other than some single-qubit Cliffords we acquired via synthesis, you can check that this gives us the same circuit structure as our `Transform.DecomposeBoxes` method! It is a suboptimal synthesis method.
\n", "
\n", "As with the `gen_term_sequence` method, we recommend playing around with the arguments and seeing what circuits come out. Typically we find that `PauliSynthStrat.Sets` and `CXConfigType.Tree` work the best, although routing can affect this somewhat."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Ansatz sequencing\n","\n","**Download this notebook - {nb-download}`ansatz_sequence_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["When performing variational algorithms like VQE, one common approach to generating circuit ansätze is to take an operator $U$ representing excitations and use this to act on a reference state $\\lvert \\phi_0 \\rangle$. One such ansatz is the Unitary Coupled Cluster ansatz. Each excitation, indexed by $j$, within $U$ is given a real coefficient $a_j$ and a parameter $t_j$, such that $U = e^{i \\sum_j \\sum_k a_j t_j P_{jk}}$, where $P_{jk} \\in \\{I, X, Y, Z \\}^{\\otimes n}$. The exact form is dependent on the chosen qubit encoding. This excitation gives us a variational state $\\lvert \\psi (t) \\rangle = U(t) \\lvert \\phi_0 \\rangle$. The operator $U$ must be Trotterised, to give a product of Pauli exponentials, and converted into native quantum gates to create the ansatz circuit.
\n","
\n","This notebook will describe how to use an advanced feature of `pytket` to enable automated circuit synthesis for $U$ and reduce circuit depth dramatically.
\n","
\n","We must create a `pytket` `QubitPauliOperator`, which represents such an operator $U$, and contains a dictionary from Pauli string $P_{jk}$ to symbolic expression. Here, we make a mock operator ourselves, which resembles the UCCSD excitation operator for the $\\mathrm{H}_2$ molecule using the Jordan-Wigner qubit encoding.
\n","
\n","First, we create a series of `QubitPauliString` objects, which represent each $P_{jk}$."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.pauli import Pauli, QubitPauliString\n","from pytket.circuit import Qubit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["q = [Qubit(i) for i in range(4)]\n","qps0 = QubitPauliString([q[0], q[1], q[2]], [Pauli.Y, Pauli.Z, Pauli.X])\n","qps1 = QubitPauliString([q[0], q[1], q[2]], [Pauli.X, Pauli.Z, Pauli.Y])\n","qps2 = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.X, Pauli.Y])\n","qps3 = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.Y, Pauli.X])\n","qps4 = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.X, Pauli.X])\n","qps5 = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.X, Pauli.X])"]},{"cell_type":"markdown","metadata":{},"source":["Now, create some symbolic expressions for the $a_j t_j$ terms."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import fresh_symbol"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol1 = fresh_symbol(\"s0\")\n","expr1 = 1.2 * symbol1\n","symbol2 = fresh_symbol(\"s1\")\n","expr2 = -0.3 * symbol2"]},{"cell_type":"markdown","metadata":{},"source":["We can now create our `QubitPauliOperator`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.utils import QubitPauliOperator"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["dict1 = dict((string, expr1) for string in (qps0, qps1))\n","dict2 = dict((string, expr2) for string in (qps2, qps3, qps4, qps5))\n","operator = QubitPauliOperator({**dict1, **dict2})\n","print(operator)"]},{"cell_type":"markdown","metadata":{},"source":["Now we can let `pytket` sequence the terms in this operator for us, using a selection of strategies. First, we will create a `Circuit` to generate an example reference state, and then use the `gen_term_sequence_circuit` method to append the Pauli exponentials."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit\n","from pytket.utils import gen_term_sequence_circuit\n","from pytket.partition import PauliPartitionStrat, GraphColourMethod"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["reference_circ = Circuit(4).X(1).X(3)\n","ansatz_circuit = gen_term_sequence_circuit(\n"," operator, reference_circ, PauliPartitionStrat.CommutingSets, GraphColourMethod.Lazy\n",")"]},{"cell_type":"markdown","metadata":{},"source":["This method works by generating a graph of Pauli exponentials and performing graph colouring. Here we have chosen to partition the terms so that exponentials which commute are gathered together, and we have done so using a lazy, greedy graph colouring method.
\n","
\n","Alternatively, we could have used the `PauliPartitionStrat.NonConflictingSets`, which puts Pauli exponentials together so that they only require single-qubit gates to be converted into the form $e^{i \\alpha Z \\otimes Z \\otimes ... \\otimes Z}$. This strategy is primarily useful for measurement reduction, a different problem.
\n","
\n","We could also have used the `GraphColourMethod.LargestFirst`, which still uses a greedy method, but builds the full graph and iterates through the vertices in descending order of arity. We recommend playing around with the options, but we typically find that the combination of `CommutingSets` and `Lazy` allows the best optimisation.
\n","
\n","In general, not all of our exponentials will commute, so the semantics of our circuit depend on the order of our sequencing. As a result, it is important for us to be able to inspect the order we have produced. `pytket` provides functionality to enable this. Each set of commuting exponentials is put into a `CircBox`, which lets us inspect the partitoning."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import OpType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for command in ansatz_circuit:\n"," if command.op.type == OpType.CircBox:\n"," print(\"New CircBox:\")\n"," for pauli_exp in command.op.get_circuit():\n"," print(\n"," \" {} {} {}\".format(\n"," pauli_exp, pauli_exp.op.get_paulis(), pauli_exp.op.get_phase()\n"," )\n"," )\n"," else:\n"," print(\"Native gate: {}\".format(command))"]},{"cell_type":"markdown","metadata":{},"source":["We can convert this circuit into basic gates using a `pytket` `Transform`. This acts in place on the circuit to do rewriting, for gate translation and optimisation. We will start off with a naive decomposition."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.transform import Transform"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["naive_circuit = ansatz_circuit.copy()\n","Transform.DecomposeBoxes().apply(naive_circuit)\n","print(naive_circuit.get_commands())"]},{"cell_type":"markdown","metadata":{},"source":["This is a jumble of one- and two-qubit gates. We can get some relevant circuit metrics out:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"Naive CX Depth: {}\".format(naive_circuit.depth_by_type(OpType.CX)))\n","print(\"Naive CX Count: {}\".format(naive_circuit.n_gates_of_type(OpType.CX)))"]},{"cell_type":"markdown","metadata":{},"source":["These metrics can be improved upon significantly by smart compilation. A `Transform` exists precisely for this purpose:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.transform import PauliSynthStrat, CXConfigType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["smart_circuit = ansatz_circuit.copy()\n","Transform.UCCSynthesis(PauliSynthStrat.Sets, CXConfigType.Tree).apply(smart_circuit)\n","print(\"Smart CX Depth: {}\".format(smart_circuit.depth_by_type(OpType.CX)))\n","print(\"Smart CX Count: {}\".format(smart_circuit.n_gates_of_type(OpType.CX)))"]},{"cell_type":"markdown","metadata":{},"source":["This `Transform` takes in a `Circuit` with the structure specified above: some arbitrary gates for the reference state, along with several `CircBox` gates containing `PauliExpBox` gates.
\n","
\n","We have chosen `PauliSynthStrat.Sets` and `CXConfigType.Tree`. The `PauliSynthStrat` dictates the method for decomposing multiple adjacent Pauli exponentials into basic gates, while the `CXConfigType` dictates the structure of adjacent CX gates.
\n","
\n","If we choose a different combination of strategies, we can produce a different output circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["last_circuit = ansatz_circuit.copy()\n","Transform.UCCSynthesis(PauliSynthStrat.Individual, CXConfigType.Snake).apply(\n"," last_circuit\n",")\n","print(last_circuit.get_commands())"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"Last CX Depth: {}\".format(last_circuit.depth_by_type(OpType.CX)))\n","print(\"Last CX Count: {}\".format(last_circuit.n_gates_of_type(OpType.CX)))"]},{"cell_type":"markdown","metadata":{},"source":["Other than some single-qubit Cliffords we acquired via synthesis, you can check that this gives us the same circuit structure as our `Transform.DecomposeBoxes` method! It is a suboptimal synthesis method.
\n","
\n","As with the `gen_term_sequence` method, we recommend playing around with the arguments and seeing what circuits come out. Typically we find that `PauliSynthStrat.Sets` and `CXConfigType.Tree` work the best, although routing can affect this somewhat."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/algorithms_and_protocols/entanglement_swapping.ipynb b/docs/examples/algorithms_and_protocols/entanglement_swapping.ipynb index 7d8950dd..090ecab7 100644 --- a/docs/examples/algorithms_and_protocols/entanglement_swapping.ipynb +++ b/docs/examples/algorithms_and_protocols/entanglement_swapping.ipynb @@ -1 +1 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Iterated entanglement swapping"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:\n","- designing circuits with mid-circuit measurement and conditional gates;\n","- utilising noise models in supported simulators."]},{"cell_type":"markdown","metadata":{},"source":["This example assumes the reader is familiar with the Qubit Teleportation and Entanglement Swapping protocols, and basic models of noise in quantum devices.\n","\n","To run this example, you will need `pytket`, `pytket-qiskit`, and `plotly` (installed via `pip`). To view the graphs, you will need an intallation of `plotly-orca`.\n","\n","Current quantum hardware fits into the NISQ (Noisy, Intermediate-Scale Quantum) regime. This noise cannot realistically be combatted using conventional error correcting codes, because of the lack of available qubits, noise levels exceeding the code thresholds, and very few devices available that can perform measurements and corrections mid-circuit. Analysis of how quantum algorithms perform under noisy conditions is a very active research area, as is finding ways to cope with it. Here, we will look at how well we can perform the Entanglement Swapping protocol with different noise levels.\n","\n","The Entanglement Swapping protocol requires two parties to share Bell pairs with a third party, who applies the Qubit Teleportation protocol to generate a Bell pair between the two parties. The Qubit Teleportation step requires us to be able to measure some qubits and make subsequent corrections to the remaining qubits. There are only a handful of simulators and devices that currently support this, with others restricted to only measuring the qubits at the end of the circuit.\n","\n","The most popular circuit model with conditional gates at the moment is that provided by the OpenQASM language. This permits a very restricted model of classical logic, where we can apply a gate conditionally on the exact value of a classical register. There is no facility in the current spec for Boolean logic or classical operations to apply any function to the value prior to the equality check. For example, Qubit Teleportation can be performed by the following QASM:\n","`OPENQASM 2.0;`\n","`include \"qelib1.inc\";`\n","`qreg a[2];`\n","`qreg b[1];`\n","`creg c[2];`\n","`// Bell state between Alice and Bob`\n","`h a[1];`\n","`cx a[1],b[0];`\n","`// Bell measurement of Alice's qubits`\n","`cx a[0],a[1];`\n","`h a[0];`\n","`measure a[0] -> c[0];`\n","`measure a[1] -> c[1];`\n","`// Correction of Bob's qubit`\n","`if(c==1) z b[0];`\n","`if(c==3) z b[0];`\n","`if(c==2) x b[0];`\n","`if(c==3) x b[0];`\n","\n","This corresponds to the following `pytket` code:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qtel = Circuit()\n","alice = qtel.add_q_register(\"a\", 2)\n","bob = qtel.add_q_register(\"b\", 1)\n","data = qtel.add_c_register(\"d\", 2)"]},{"cell_type":"markdown","metadata":{},"source":["Bell state between Alice and Bob:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qtel.H(alice[1])\n","qtel.CX(alice[1], bob[0])"]},{"cell_type":"markdown","metadata":{},"source":["Bell measurement of Alice's qubits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qtel.CX(alice[0], alice[1])\n","qtel.H(alice[0])\n","qtel.Measure(alice[0], data[0])\n","qtel.Measure(alice[1], data[1])"]},{"cell_type":"markdown","metadata":{},"source":["Correction of Bob's qubit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qtel.X(bob[0], condition_bits=[data[0], data[1]], condition_value=2)\n","qtel.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3)\n","qtel.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=1)\n","qtel.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3)"]},{"cell_type":"markdown","metadata":{},"source":["So to demonstrate the Entanglement Swapping protocol, we just need to run this on one side of a Bell pair."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["es = Circuit()\n","ava = es.add_q_register(\"a\", 1)\n","bella = es.add_q_register(\"b\", 2)\n","charlie = es.add_q_register(\"c\", 1)\n","data = es.add_c_register(\"d\", 2)"]},{"cell_type":"markdown","metadata":{},"source":["Bell state between Ava and Bella:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["es.H(ava[0])\n","es.CX(ava[0], bella[0])"]},{"cell_type":"markdown","metadata":{},"source":["Teleport `bella[0]` to `charlie[0]`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["tel_to_c = qtel.copy()\n","tel_to_c.rename_units({alice[0]: bella[0], alice[1]: bella[1], bob[0]: charlie[0]})\n","es.append(tel_to_c)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(es.get_commands())"]},{"cell_type":"markdown","metadata":{},"source":["Let's start by running a noiseless simulation of this to verify that what we get looks like a Bell pair."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"markdown","metadata":{},"source":["Connect to a simulator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()"]},{"cell_type":"markdown","metadata":{},"source":["Make a ZZ measurement of the Bell pair:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["bell_test = es.copy()\n","bell_test.Measure(ava[0], data[0])\n","bell_test.Measure(charlie[0], data[1])"]},{"cell_type":"markdown","metadata":{},"source":["Run the experiment:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["bell_test = backend.get_compiled_circuit(bell_test)\n","from pytket.circuit.display import render_circuit_jupyter"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(bell_test)\n","handle = backend.process_circuit(bell_test, n_shots=2000)\n","counts = backend.get_result(handle).get_counts()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["This is good, we have got roughly 50/50 measurement results of 00 and 11 under the ZZ operator. But there are many other states beyond the Bell state that also generate this distribution, so to gain more confidence in our claim about the state we should make more measurements that also characterise it, i.e. perform state tomography.\n","\n","Here, we will demonstrate a naive approach to tomography that makes 3^n measurement circuits for an n-qubit state. More elaborate methods also exist."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils import append_pauli_measurement, probs_from_counts\n","from itertools import product\n","from scipy.linalg import lstsq, eigh\n","import numpy as np"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def gen_tomography_circuits(state, qubits, bits):\n"," # Yields {X, Y, Z}^n measurements in lexicographical order\n"," # Only measures qubits, storing the result in bits\n"," # (since we don't care about the ancilla qubits)\n"," assert len(qubits) == len(bits)\n"," for paulis in product([Pauli.X, Pauli.Y, Pauli.Z], repeat=len(qubits)):\n"," circ = state.copy()\n"," for qb, b, p in zip(qubits, bits, paulis):\n"," if p == Pauli.X:\n"," circ.H(qb)\n"," elif p == Pauli.Y:\n"," circ.V(qb)\n"," circ.Measure(qb, b)\n"," yield circ"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def run_tomography_circuits(state, qubits, bits, backend):\n"," circs = list(gen_tomography_circuits(state, qubits, bits))\n"," # Compile and run each circuit\n"," circs = backend.get_compiled_circuits(circs)\n"," handles = backend.process_circuits(circs, n_shots=2000)\n"," # Get the observed measurement probabilities\n"," probs_list = []\n"," for result in backend.get_results(handles):\n"," counts = result.get_counts()\n"," probs = probs_from_counts(counts)\n"," probs_list.append(probs)\n"," return probs_list"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def fit_tomography_outcomes(probs_list, n_qbs):\n"," # Define the density matrices for the basis states\n"," basis = dict()\n"," basis[(Pauli.X, 0)] = np.asarray([[0.5, 0.5], [0.5, 0.5]])\n"," basis[(Pauli.X, 1)] = np.asarray([[0.5, -0.5], [-0.5, 0.5]])\n"," basis[(Pauli.Y, 0)] = np.asarray([[0.5, -0.5j], [0.5j, 0.5]])\n"," basis[(Pauli.Y, 1)] = np.asarray([[0.5, 0.5j], [-0.5j, 0.5]])\n"," basis[(Pauli.Z, 0)] = np.asarray([[1, 0], [0, 0]])\n"," basis[(Pauli.Z, 1)] = np.asarray([[0, 0], [0, 1]])\n"," dim = 2**n_qbs\n"," # Define vector all_probs as a concatenation of probability vectors for each measurement (2**n x 3**n, 1)\n"," # Define matrix all_ops mapping a (vectorised) density matrix to a vector of probabilities for each measurement\n"," # (2**n x 3**n, 2**n x 2**n)\n"," all_probs = []\n"," all_ops = []\n"," for paulis, probs in zip(\n"," product([Pauli.X, Pauli.Y, Pauli.Z], repeat=n_qbs), probs_list\n"," ):\n"," prob_vec = []\n"," meas_ops = []\n"," for outcome in product([0, 1], repeat=n_qbs):\n"," prob_vec.append(probs.get(outcome, 0))\n"," op = np.eye(1, dtype=complex)\n"," for p, o in zip(paulis, outcome):\n"," op = np.kron(op, basis[(p, o)])\n"," meas_ops.append(op.reshape(1, dim * dim).conj())\n"," all_probs.append(np.vstack(prob_vec))\n"," all_ops.append(np.vstack(meas_ops))\n"," # Solve for density matrix by minimising || all_ops * dm - all_probs ||\n"," dm, _, _, _ = lstsq(np.vstack(all_ops), np.vstack(all_probs))\n"," dm = dm.reshape(dim, dim)\n"," # Make density matrix positive semi-definite\n"," v, w = eigh(dm)\n"," for i in range(dim):\n"," if v[i] < 0:\n"," for j in range(i + 1, dim):\n"," v[j] += v[i] / (dim - (i + 1))\n"," v[i] = 0\n"," dm = np.zeros([dim, dim], dtype=complex)\n"," for j in range(dim):\n"," dm += v[j] * np.outer(w[:, j], np.conj(w[:, j]))\n"," # Normalise trace of density matrix\n"," dm /= np.trace(dm)\n"," return dm"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["probs_list = run_tomography_circuits(\n"," es, [ava[0], charlie[0]], [data[0], data[1]], backend\n",")\n","dm = fit_tomography_outcomes(probs_list, 2)\n","print(dm.round(3))"]},{"cell_type":"markdown","metadata":{},"source":["This is very close to the true density matrix for a pure Bell state. We can attribute the error here to the sampling error since we only take 2000 samples of each measurement circuit.\n","\n","To quantify exactly how similar it is to the correct density matrix, we can calculate the fidelity."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from scipy.linalg import sqrtm"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def fidelity(dm0, dm1):\n"," # Calculate the fidelity between two density matrices\n"," sq0 = sqrtm(dm0)\n"," sq1 = sqrtm(dm1)\n"," return np.linalg.norm(sq0.dot(sq1)) ** 2"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["bell_state = np.asarray(\n"," [\n"," [0.5, 0, 0, 0.5],\n"," [0, 0, 0, 0],\n"," [0, 0, 0, 0],\n"," [0.5, 0, 0, 0.5],\n"," ]\n",")\n","print(fidelity(dm, bell_state))"]},{"cell_type":"markdown","metadata":{},"source":["This high fidelity is unsurprising since we have a completely noiseless simulation. So the next step is to add some noise to the simulation and observe how the overall fidelity is affected. The `AerBackend` wraps around the Qiskit Aer simulator and can pass on any `qiskit.providers.aer.noise.NoiseModel` to the simulator. Let's start by adding some uniform depolarising noise to each CX gate and some uniform measurement error."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from qiskit_aer.noise import NoiseModel, depolarizing_error, ReadoutError"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def make_noise_model(dep_err_rate, ro_err_rate, qubits):\n"," # Define a noise model that applies uniformly to the given qubits\n"," model = NoiseModel()\n"," dep_err = depolarizing_error(dep_err_rate, 2)\n"," ro_err = ReadoutError(\n"," [[1 - ro_err_rate, ro_err_rate], [ro_err_rate, 1 - ro_err_rate]]\n"," )\n"," # Add depolarising error to CX gates between any qubits (implying full connectivity)\n"," for i, j in product(qubits, repeat=2):\n"," if i != j:\n"," model.add_quantum_error(dep_err, [\"cx\"], [i, j])\n"," # Add readout error for each qubit\n"," for i in qubits:\n"," model.add_readout_error(ro_err, qubits=[i])\n"," return model"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["test_model = make_noise_model(0.03, 0.05, range(4))\n","backend = AerBackend(noise_model=test_model)\n","probs_list = run_tomography_circuits(\n"," es, [ava[0], charlie[0]], [data[0], data[1]], backend\n",")\n","dm = fit_tomography_outcomes(probs_list, 2)\n","print(dm.round(3))\n","print(fidelity(dm, bell_state))"]},{"cell_type":"markdown","metadata":{},"source":["Despite the very small circuit and the relatively small error rates, the fidelity of the final state has reduced considerably.\n","\n","As far as circuits go, the entanglement swapping protocol is little more than a toy example and is nothing close to the scale of circuits for most interesting quantum computational problems. However, it is possible to iterate the protocol many times to build up a larger computation, allowing us to see the impact of the noise at different scales."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import OpType\n","from plotly.graph_objects import Scatter, Figure"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def iterated_entanglement_swap(n_iter):\n"," # Iterate the entanglement swapping protocol n_iter times\n"," it_es = Circuit()\n"," ava = it_es.add_q_register(\"a\", 1)\n"," bella = it_es.add_q_register(\"b\", 2)\n"," charlie = it_es.add_q_register(\"c\", 1)\n"," data = it_es.add_c_register(\"d\", 2)\n","\n"," # Start with an initial Bell state\n"," it_es.H(ava[0])\n"," it_es.CX(ava[0], bella[0])\n"," for i in range(n_iter):\n"," if i % 2 == 0:\n"," # Teleport bella[0] to charlie[0] to give a Bell pair between ava[0] and charlier[0]\n"," tel_to_c = qtel.copy()\n"," tel_to_c.rename_units(\n"," {alice[0]: bella[0], alice[1]: bella[1], bob[0]: charlie[0]}\n"," )\n"," it_es.append(tel_to_c)\n"," it_es.add_gate(OpType.Reset, [bella[0]])\n"," it_es.add_gate(OpType.Reset, [bella[1]])\n"," else:\n"," # Teleport charlie[0] to bella[0] to give a Bell pair between ava[0] and bella[0]\n"," tel_to_b = qtel.copy()\n"," tel_to_b.rename_units(\n"," {alice[0]: charlie[0], alice[1]: bella[1], bob[0]: bella[0]}\n"," )\n"," it_es.append(tel_to_b)\n"," it_es.add_gate(OpType.Reset, [bella[1]])\n"," it_es.add_gate(OpType.Reset, [charlie[0]])\n"," # Return the circuit and the qubits expected to share a Bell pair\n"," if n_iter % 2 == 0:\n"," return it_es, [ava[0], bella[0]]\n"," else:\n"," return it_es, [ava[0], charlie[0]]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def iterated_noisy_experiment(dep_err_rate, ro_err_rate, max_iter):\n"," # Set up the noisy simulator with the given error rates\n"," test_model = make_noise_model(dep_err_rate, ro_err_rate, range(4))\n"," backend = AerBackend(noise_model=test_model)\n"," # Estimate the fidelity after n iterations, from 0 to max_iter (inclusive)\n"," fid_list = []\n"," for i in range(max_iter + 1):\n"," it_es, qubits = iterated_entanglement_swap(i)\n"," probs_list = run_tomography_circuits(it_es, qubits, [data[0], data[1]], backend)\n"," dm = fit_tomography_outcomes(probs_list, 2)\n"," fid = fidelity(dm, bell_state)\n"," fid_list.append(fid)\n"," return fid_list"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["fig = Figure()\n","fig.update_layout(\n"," title=\"Iterated Entanglement Swapping under Noise (dep_err = 0.03)\",\n"," xaxis_title=\"Iterations\",\n"," xaxis=dict(range=[0, 10]),\n"," yaxis_title=\"Fidelity\",\n",")\n","iter_range = np.arange(11)\n","for i in range(7):\n"," fids = iterated_noisy_experiment(0.03, 0.025 * i, 10)\n"," plot_data = Scatter(\n"," x=iter_range, y=fids, name=\"ro_err=\" + str(np.round(0.025 * i, 3))\n"," )\n"," fig.add_trace(plot_data)\n","try:\n"," fig.show(renderer=\"svg\")\n","except ValueError as e:\n"," print(e) # requires plotly-orca"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["fig = Figure()\n","fig.update_layout(\n"," title=\"Iterated Entanglement Swapping under Noise (ro_err = 0.05)\",\n"," xaxis_title=\"Iterations\",\n"," xaxis=dict(range=[0, 10]),\n"," yaxis_title=\"Fidelity\",\n",")\n","iter_range = np.arange(11)\n","for i in range(9):\n"," fids = iterated_noisy_experiment(0.01 * i, 0.05, 10)\n"," plot_data = Scatter(\n"," x=iter_range, y=fids, name=\"dep_err=\" + str(np.round(0.01 * i, 3))\n"," )\n"," fig.add_trace(plot_data)\n","try:\n"," fig.show(renderer=\"svg\")\n","except ValueError as e:\n"," print(e) # requires plotly-orca"]},{"cell_type":"markdown","metadata":{},"source":["These graphs are not very surprising, but are still important for seeing that the current error rates of typical NISQ devices become crippling for fidelities very quickly after repeated mid-circuit measurements and corrections (even with this overly-simplified model with uniform noise and no crosstalk or higher error modes). This provides good motivation for the adoption of error mitigation techniques, and for the development of new techniques that are robust to errors in mid-circuit measurements."]},{"cell_type":"markdown","metadata":{},"source":["Exercises:\n","- Vary the fixed noise levels to compare how impactful the depolarising and measurement errors are.\n","- Add extra noise characteristics to the noise model to obtain something that more resembles a real device. Possible options include adding error during the reset operations, extending the errors to be non-local, or constructing the noise model from a device's calibration data.\n","- Change the circuit from iterated entanglement swapping to iterated applications of a correction circuit from a simple error-correcting code. Do you expect this to be more sensitive to depolarising errors from unitary gates or measurement errors?"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Iterated entanglement swapping\n","\n","**Download this notebook - {nb-download}`entanglement_swapping.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:\n","- designing circuits with mid-circuit measurement and conditional gates;\n","- utilising noise models in supported simulators."]},{"cell_type":"markdown","metadata":{},"source":["This example assumes the reader is familiar with the Qubit Teleportation and Entanglement Swapping protocols, and basic models of noise in quantum devices.\n","\n","To run this example, you will need `pytket`, `pytket-qiskit`, and `plotly` (installed via `pip`). To view the graphs, you will need an intallation of `plotly-orca`.\n","\n","Current quantum hardware fits into the NISQ (Noisy, Intermediate-Scale Quantum) regime. This noise cannot realistically be combatted using conventional error correcting codes, because of the lack of available qubits, noise levels exceeding the code thresholds, and very few devices available that can perform measurements and corrections mid-circuit. Analysis of how quantum algorithms perform under noisy conditions is a very active research area, as is finding ways to cope with it. Here, we will look at how well we can perform the Entanglement Swapping protocol with different noise levels.\n","\n","The Entanglement Swapping protocol requires two parties to share Bell pairs with a third party, who applies the Qubit Teleportation protocol to generate a Bell pair between the two parties. The Qubit Teleportation step requires us to be able to measure some qubits and make subsequent corrections to the remaining qubits. There are only a handful of simulators and devices that currently support this, with others restricted to only measuring the qubits at the end of the circuit.\n","\n","The most popular circuit model with conditional gates at the moment is that provided by the OpenQASM language. This permits a very restricted model of classical logic, where we can apply a gate conditionally on the exact value of a classical register. There is no facility in the current spec for Boolean logic or classical operations to apply any function to the value prior to the equality check. For example, Qubit Teleportation can be performed by the following QASM:\n","`OPENQASM 2.0;`\n","`include \"qelib1.inc\";`\n","`qreg a[2];`\n","`qreg b[1];`\n","`creg c[2];`\n","`// Bell state between Alice and Bob`\n","`h a[1];`\n","`cx a[1],b[0];`\n","`// Bell measurement of Alice's qubits`\n","`cx a[0],a[1];`\n","`h a[0];`\n","`measure a[0] -> c[0];`\n","`measure a[1] -> c[1];`\n","`// Correction of Bob's qubit`\n","`if(c==1) z b[0];`\n","`if(c==3) z b[0];`\n","`if(c==2) x b[0];`\n","`if(c==3) x b[0];`\n","\n","This corresponds to the following `pytket` code:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qtel = Circuit()\n","alice = qtel.add_q_register(\"a\", 2)\n","bob = qtel.add_q_register(\"b\", 1)\n","data = qtel.add_c_register(\"d\", 2)"]},{"cell_type":"markdown","metadata":{},"source":["Bell state between Alice and Bob:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qtel.H(alice[1])\n","qtel.CX(alice[1], bob[0])"]},{"cell_type":"markdown","metadata":{},"source":["Bell measurement of Alice's qubits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qtel.CX(alice[0], alice[1])\n","qtel.H(alice[0])\n","qtel.Measure(alice[0], data[0])\n","qtel.Measure(alice[1], data[1])"]},{"cell_type":"markdown","metadata":{},"source":["Correction of Bob's qubit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qtel.X(bob[0], condition_bits=[data[0], data[1]], condition_value=2)\n","qtel.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3)\n","qtel.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=1)\n","qtel.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3)"]},{"cell_type":"markdown","metadata":{},"source":["So to demonstrate the Entanglement Swapping protocol, we just need to run this on one side of a Bell pair."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["es = Circuit()\n","ava = es.add_q_register(\"a\", 1)\n","bella = es.add_q_register(\"b\", 2)\n","charlie = es.add_q_register(\"c\", 1)\n","data = es.add_c_register(\"d\", 2)"]},{"cell_type":"markdown","metadata":{},"source":["Bell state between Ava and Bella:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["es.H(ava[0])\n","es.CX(ava[0], bella[0])"]},{"cell_type":"markdown","metadata":{},"source":["Teleport `bella[0]` to `charlie[0]`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["tel_to_c = qtel.copy()\n","tel_to_c.rename_units({alice[0]: bella[0], alice[1]: bella[1], bob[0]: charlie[0]})\n","es.append(tel_to_c)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(es.get_commands())"]},{"cell_type":"markdown","metadata":{},"source":["Let's start by running a noiseless simulation of this to verify that what we get looks like a Bell pair."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"markdown","metadata":{},"source":["Connect to a simulator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()"]},{"cell_type":"markdown","metadata":{},"source":["Make a ZZ measurement of the Bell pair:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["bell_test = es.copy()\n","bell_test.Measure(ava[0], data[0])\n","bell_test.Measure(charlie[0], data[1])"]},{"cell_type":"markdown","metadata":{},"source":["Run the experiment:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["bell_test = backend.get_compiled_circuit(bell_test)\n","from pytket.circuit.display import render_circuit_jupyter"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(bell_test)\n","handle = backend.process_circuit(bell_test, n_shots=2000)\n","counts = backend.get_result(handle).get_counts()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["This is good, we have got roughly 50/50 measurement results of 00 and 11 under the ZZ operator. But there are many other states beyond the Bell state that also generate this distribution, so to gain more confidence in our claim about the state we should make more measurements that also characterise it, i.e. perform state tomography.\n","\n","Here, we will demonstrate a naive approach to tomography that makes 3^n measurement circuits for an n-qubit state. More elaborate methods also exist."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils import append_pauli_measurement, probs_from_counts\n","from itertools import product\n","from scipy.linalg import lstsq, eigh\n","import numpy as np"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def gen_tomography_circuits(state, qubits, bits):\n"," # Yields {X, Y, Z}^n measurements in lexicographical order\n"," # Only measures qubits, storing the result in bits\n"," # (since we don't care about the ancilla qubits)\n"," assert len(qubits) == len(bits)\n"," for paulis in product([Pauli.X, Pauli.Y, Pauli.Z], repeat=len(qubits)):\n"," circ = state.copy()\n"," for qb, b, p in zip(qubits, bits, paulis):\n"," if p == Pauli.X:\n"," circ.H(qb)\n"," elif p == Pauli.Y:\n"," circ.V(qb)\n"," circ.Measure(qb, b)\n"," yield circ"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def run_tomography_circuits(state, qubits, bits, backend):\n"," circs = list(gen_tomography_circuits(state, qubits, bits))\n"," # Compile and run each circuit\n"," circs = backend.get_compiled_circuits(circs)\n"," handles = backend.process_circuits(circs, n_shots=2000)\n"," # Get the observed measurement probabilities\n"," probs_list = []\n"," for result in backend.get_results(handles):\n"," counts = result.get_counts()\n"," probs = probs_from_counts(counts)\n"," probs_list.append(probs)\n"," return probs_list"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def fit_tomography_outcomes(probs_list, n_qbs):\n"," # Define the density matrices for the basis states\n"," basis = dict()\n"," basis[(Pauli.X, 0)] = np.asarray([[0.5, 0.5], [0.5, 0.5]])\n"," basis[(Pauli.X, 1)] = np.asarray([[0.5, -0.5], [-0.5, 0.5]])\n"," basis[(Pauli.Y, 0)] = np.asarray([[0.5, -0.5j], [0.5j, 0.5]])\n"," basis[(Pauli.Y, 1)] = np.asarray([[0.5, 0.5j], [-0.5j, 0.5]])\n"," basis[(Pauli.Z, 0)] = np.asarray([[1, 0], [0, 0]])\n"," basis[(Pauli.Z, 1)] = np.asarray([[0, 0], [0, 1]])\n"," dim = 2**n_qbs\n"," # Define vector all_probs as a concatenation of probability vectors for each measurement (2**n x 3**n, 1)\n"," # Define matrix all_ops mapping a (vectorised) density matrix to a vector of probabilities for each measurement\n"," # (2**n x 3**n, 2**n x 2**n)\n"," all_probs = []\n"," all_ops = []\n"," for paulis, probs in zip(\n"," product([Pauli.X, Pauli.Y, Pauli.Z], repeat=n_qbs), probs_list\n"," ):\n"," prob_vec = []\n"," meas_ops = []\n"," for outcome in product([0, 1], repeat=n_qbs):\n"," prob_vec.append(probs.get(outcome, 0))\n"," op = np.eye(1, dtype=complex)\n"," for p, o in zip(paulis, outcome):\n"," op = np.kron(op, basis[(p, o)])\n"," meas_ops.append(op.reshape(1, dim * dim).conj())\n"," all_probs.append(np.vstack(prob_vec))\n"," all_ops.append(np.vstack(meas_ops))\n"," # Solve for density matrix by minimising || all_ops * dm - all_probs ||\n"," dm, _, _, _ = lstsq(np.vstack(all_ops), np.vstack(all_probs))\n"," dm = dm.reshape(dim, dim)\n"," # Make density matrix positive semi-definite\n"," v, w = eigh(dm)\n"," for i in range(dim):\n"," if v[i] < 0:\n"," for j in range(i + 1, dim):\n"," v[j] += v[i] / (dim - (i + 1))\n"," v[i] = 0\n"," dm = np.zeros([dim, dim], dtype=complex)\n"," for j in range(dim):\n"," dm += v[j] * np.outer(w[:, j], np.conj(w[:, j]))\n"," # Normalise trace of density matrix\n"," dm /= np.trace(dm)\n"," return dm"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["probs_list = run_tomography_circuits(\n"," es, [ava[0], charlie[0]], [data[0], data[1]], backend\n",")\n","dm = fit_tomography_outcomes(probs_list, 2)\n","print(dm.round(3))"]},{"cell_type":"markdown","metadata":{},"source":["This is very close to the true density matrix for a pure Bell state. We can attribute the error here to the sampling error since we only take 2000 samples of each measurement circuit.\n","\n","To quantify exactly how similar it is to the correct density matrix, we can calculate the fidelity."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from scipy.linalg import sqrtm"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def fidelity(dm0, dm1):\n"," # Calculate the fidelity between two density matrices\n"," sq0 = sqrtm(dm0)\n"," sq1 = sqrtm(dm1)\n"," return np.linalg.norm(sq0.dot(sq1)) ** 2"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["bell_state = np.asarray(\n"," [\n"," [0.5, 0, 0, 0.5],\n"," [0, 0, 0, 0],\n"," [0, 0, 0, 0],\n"," [0.5, 0, 0, 0.5],\n"," ]\n",")\n","print(fidelity(dm, bell_state))"]},{"cell_type":"markdown","metadata":{},"source":["This high fidelity is unsurprising since we have a completely noiseless simulation. So the next step is to add some noise to the simulation and observe how the overall fidelity is affected. The `AerBackend` wraps around the Qiskit Aer simulator and can pass on any `qiskit.providers.aer.noise.NoiseModel` to the simulator. Let's start by adding some uniform depolarising noise to each CX gate and some uniform measurement error."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from qiskit_aer.noise import NoiseModel, depolarizing_error, ReadoutError"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def make_noise_model(dep_err_rate, ro_err_rate, qubits):\n"," # Define a noise model that applies uniformly to the given qubits\n"," model = NoiseModel()\n"," dep_err = depolarizing_error(dep_err_rate, 2)\n"," ro_err = ReadoutError(\n"," [[1 - ro_err_rate, ro_err_rate], [ro_err_rate, 1 - ro_err_rate]]\n"," )\n"," # Add depolarising error to CX gates between any qubits (implying full connectivity)\n"," for i, j in product(qubits, repeat=2):\n"," if i != j:\n"," model.add_quantum_error(dep_err, [\"cx\"], [i, j])\n"," # Add readout error for each qubit\n"," for i in qubits:\n"," model.add_readout_error(ro_err, qubits=[i])\n"," return model"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["test_model = make_noise_model(0.03, 0.05, range(4))\n","backend = AerBackend(noise_model=test_model)\n","probs_list = run_tomography_circuits(\n"," es, [ava[0], charlie[0]], [data[0], data[1]], backend\n",")\n","dm = fit_tomography_outcomes(probs_list, 2)\n","print(dm.round(3))\n","print(fidelity(dm, bell_state))"]},{"cell_type":"markdown","metadata":{},"source":["Despite the very small circuit and the relatively small error rates, the fidelity of the final state has reduced considerably.\n","\n","As far as circuits go, the entanglement swapping protocol is little more than a toy example and is nothing close to the scale of circuits for most interesting quantum computational problems. However, it is possible to iterate the protocol many times to build up a larger computation, allowing us to see the impact of the noise at different scales."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import OpType\n","from plotly.graph_objects import Scatter, Figure"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def iterated_entanglement_swap(n_iter):\n"," # Iterate the entanglement swapping protocol n_iter times\n"," it_es = Circuit()\n"," ava = it_es.add_q_register(\"a\", 1)\n"," bella = it_es.add_q_register(\"b\", 2)\n"," charlie = it_es.add_q_register(\"c\", 1)\n"," data = it_es.add_c_register(\"d\", 2)\n","\n"," # Start with an initial Bell state\n"," it_es.H(ava[0])\n"," it_es.CX(ava[0], bella[0])\n"," for i in range(n_iter):\n"," if i % 2 == 0:\n"," # Teleport bella[0] to charlie[0] to give a Bell pair between ava[0] and charlier[0]\n"," tel_to_c = qtel.copy()\n"," tel_to_c.rename_units(\n"," {alice[0]: bella[0], alice[1]: bella[1], bob[0]: charlie[0]}\n"," )\n"," it_es.append(tel_to_c)\n"," it_es.add_gate(OpType.Reset, [bella[0]])\n"," it_es.add_gate(OpType.Reset, [bella[1]])\n"," else:\n"," # Teleport charlie[0] to bella[0] to give a Bell pair between ava[0] and bella[0]\n"," tel_to_b = qtel.copy()\n"," tel_to_b.rename_units(\n"," {alice[0]: charlie[0], alice[1]: bella[1], bob[0]: bella[0]}\n"," )\n"," it_es.append(tel_to_b)\n"," it_es.add_gate(OpType.Reset, [bella[1]])\n"," it_es.add_gate(OpType.Reset, [charlie[0]])\n"," # Return the circuit and the qubits expected to share a Bell pair\n"," if n_iter % 2 == 0:\n"," return it_es, [ava[0], bella[0]]\n"," else:\n"," return it_es, [ava[0], charlie[0]]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def iterated_noisy_experiment(dep_err_rate, ro_err_rate, max_iter):\n"," # Set up the noisy simulator with the given error rates\n"," test_model = make_noise_model(dep_err_rate, ro_err_rate, range(4))\n"," backend = AerBackend(noise_model=test_model)\n"," # Estimate the fidelity after n iterations, from 0 to max_iter (inclusive)\n"," fid_list = []\n"," for i in range(max_iter + 1):\n"," it_es, qubits = iterated_entanglement_swap(i)\n"," probs_list = run_tomography_circuits(it_es, qubits, [data[0], data[1]], backend)\n"," dm = fit_tomography_outcomes(probs_list, 2)\n"," fid = fidelity(dm, bell_state)\n"," fid_list.append(fid)\n"," return fid_list"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["fig = Figure()\n","fig.update_layout(\n"," title=\"Iterated Entanglement Swapping under Noise (dep_err = 0.03)\",\n"," xaxis_title=\"Iterations\",\n"," xaxis=dict(range=[0, 10]),\n"," yaxis_title=\"Fidelity\",\n",")\n","iter_range = np.arange(11)\n","for i in range(7):\n"," fids = iterated_noisy_experiment(0.03, 0.025 * i, 10)\n"," plot_data = Scatter(\n"," x=iter_range, y=fids, name=\"ro_err=\" + str(np.round(0.025 * i, 3))\n"," )\n"," fig.add_trace(plot_data)\n","try:\n"," fig.show(renderer=\"svg\")\n","except ValueError as e:\n"," print(e) # requires plotly-orca"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["fig = Figure()\n","fig.update_layout(\n"," title=\"Iterated Entanglement Swapping under Noise (ro_err = 0.05)\",\n"," xaxis_title=\"Iterations\",\n"," xaxis=dict(range=[0, 10]),\n"," yaxis_title=\"Fidelity\",\n",")\n","iter_range = np.arange(11)\n","for i in range(9):\n"," fids = iterated_noisy_experiment(0.01 * i, 0.05, 10)\n"," plot_data = Scatter(\n"," x=iter_range, y=fids, name=\"dep_err=\" + str(np.round(0.01 * i, 3))\n"," )\n"," fig.add_trace(plot_data)\n","try:\n"," fig.show(renderer=\"svg\")\n","except ValueError as e:\n"," print(e) # requires plotly-orca"]},{"cell_type":"markdown","metadata":{},"source":["These graphs are not very surprising, but are still important for seeing that the current error rates of typical NISQ devices become crippling for fidelities very quickly after repeated mid-circuit measurements and corrections (even with this overly-simplified model with uniform noise and no crosstalk or higher error modes). This provides good motivation for the adoption of error mitigation techniques, and for the development of new techniques that are robust to errors in mid-circuit measurements."]},{"cell_type":"markdown","metadata":{},"source":["Exercises:\n","- Vary the fixed noise levels to compare how impactful the depolarising and measurement errors are.\n","- Add extra noise characteristics to the noise model to obtain something that more resembles a real device. Possible options include adding error during the reset operations, extending the errors to be non-local, or constructing the noise model from a device's calibration data.\n","- Change the circuit from iterated entanglement swapping to iterated applications of a correction circuit from a simple error-correcting code. Do you expect this to be more sensitive to depolarising errors from unitary gates or measurement errors?"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/algorithms_and_protocols/expectation_value_example.ipynb b/docs/examples/algorithms_and_protocols/expectation_value_example.ipynb index ba4992dc..b0d18772 100644 --- a/docs/examples/algorithms_and_protocols/expectation_value_example.ipynb +++ b/docs/examples/algorithms_and_protocols/expectation_value_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Expectation values"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Given a circuit generating a quantum state $\\lvert \\psi \\rangle$, it is very common to have an operator $H$ and ask for the expectation value $\\langle \\psi \\vert H \\vert \\psi \\rangle$. A notable example is in quantum computational chemistry, where $\\lvert \\psi \\rangle$ encodes the wavefunction for the electronic state of a small molecule, and the energy of the molecule can be derived from the expectation value with respect to the molecule's Hamiltonian operator $H$.
\n", "
\n", "This example uses this chemistry scenario to demonstrate the overall procedure for using `pytket` to perform advanced high-level procedures. We build on top of topics covered by several other example notebooks, including circuit generation, optimisation, and using different backends.
\n", "
\n", "There is limited built-in functionality in `pytket` for obtaining expectation values from circuits. This is designed to encourage users to consider their needs for parallelising the processing of circuits, manipulating results (e.g. filtering, adjusting counts to mitigate errors, and other forms of data processing), or more advanced schemes for grouping the terms of the operator into measurement circuits. For this example, suppose that we want to focus on reducing the queueing time for IBM device backends, and filter our shots to eliminate some detected errors.
\n", "
\n", "This notebook makes use of the Qiskit and ProjectQ backend modules `pytket_qiskit` and `pytket_projectq`, as well as the electronic structure module `openfermion`, all three of which should first be installed via `pip`.
\n", "
\n", "We will start by generating an ansatz and Hamiltonian for the chemical of interest. Here, we are just using a simple model of $\\mathrm{H}_2$ with four qubits representing the occupation of four spin orbitals."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit, Qubit, Bit\n", "from sympy import symbols"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Generate ansatz and Hamiltonian:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ansatz = Circuit()\n", "qubits = ansatz.add_q_register(\"q\", 4)\n", "args = symbols(\"a0 a1 a2 a3 a4 a5 a6 a7\")\n", "for i in range(4):\n", " ansatz.Ry(args[i], qubits[i])\n", "for i in range(3):\n", " ansatz.CX(qubits[i], qubits[i + 1])\n", "for i in range(4):\n", " ansatz.Ry(args[4 + i], qubits[i])\n", "ansatz.measure_all()"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["for command in ansatz:\n", " print(command)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In reality, you would use an expectation value calculation as the objective function for a classical optimisation routine to determine the parameter values for the ground state. For the purposes of this notebook, we will use some predetermined values for the ansatz, already optimised for $\\mathrm{H}_2$."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["arg_values = [\n", " 7.17996183e-02,\n", " 2.95442468e-08,\n", " 1.00000015e00,\n", " 1.00000086e00,\n", " 9.99999826e-01,\n", " 1.00000002e00,\n", " 9.99999954e-01,\n", " 1.13489747e-06,\n", "]"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ansatz.symbol_substitution(dict(zip(args, arg_values)))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can use for example the openfermion library to express an Hamiltonian as a sum of tensors of paulis."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["import openfermion as of"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["hamiltonian = (\n", " -0.0970662681676282 * of.QubitOperator(\"\")\n", " + -0.045302615503799284 * of.QubitOperator(\"X0 X1 Y2 Y3\")\n", " + 0.045302615503799284 * of.QubitOperator(\"X0 Y1 Y2 X3\")\n", " + 0.045302615503799284 * of.QubitOperator(\"Y0 X1 X2 Y3\")\n", " + -0.045302615503799284 * of.QubitOperator(\"Y0 Y1 X2 X3\")\n", " + 0.17141282644776884 * of.QubitOperator(\"Z0\")\n", " + 0.16868898170361213 * of.QubitOperator(\"Z0 Z1\")\n", " + 0.12062523483390425 * of.QubitOperator(\"Z0 Z2\")\n", " + 0.16592785033770352 * of.QubitOperator(\"Z0 Z3\")\n", " + 0.17141282644776884 * of.QubitOperator(\"Z1\")\n", " + 0.16592785033770352 * of.QubitOperator(\"Z1 Z2\")\n", " + 0.12062523483390425 * of.QubitOperator(\"Z1 Z3\")\n", " + -0.22343153690813597 * of.QubitOperator(\"Z2\")\n", " + 0.17441287612261608 * of.QubitOperator(\"Z2 Z3\")\n", " + -0.22343153690813597 * of.QubitOperator(\"Z3\")\n", ")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This can be converted into pytket's QubitPauliOperator type.
\n", "
\n", "The OpenFermion `QubitOperator` class represents the operator by its decomposition into a linear combination of Pauli operators (tensor products of the $I$, $X$, $Y$, and $Z$ matrices).
\n", "
\n", "A `QubitPauliString` is a sparse representation of a Pauli operator with support over some subset of qubits."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.pauli import Pauli, QubitPauliString\n", "from pytket.utils.operators import QubitPauliOperator"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pauli_sym = {\"I\": Pauli.I, \"X\": Pauli.X, \"Y\": Pauli.Y, \"Z\": Pauli.Z}"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def qps_from_openfermion(paulis):\n", " \"\"\"Convert OpenFermion tensor of Paulis to pytket QubitPauliString.\"\"\"\n", " qlist = []\n", " plist = []\n", " for q, p in paulis:\n", " qlist.append(Qubit(q))\n", " plist.append(pauli_sym[p])\n", " return QubitPauliString(qlist, plist)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def qpo_from_openfermion(openf_op):\n", " \"\"\"Convert OpenFermion QubitOperator to pytket QubitPauliOperator.\"\"\"\n", " tk_op = dict()\n", " for term, coeff in openf_op.terms.items():\n", " string = qps_from_openfermion(term)\n", " tk_op[string] = coeff\n", " return QubitPauliOperator(tk_op)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["hamiltonian_op = qpo_from_openfermion(hamiltonian)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can simulate this exactly using a statevector simulator like ProjectQ. This has a built-in method for fast calculations of expectation values that works well for small examples like this."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.projectq import ProjectQBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["backend = ProjectQBackend()\n", "ideal_energy = backend.get_operator_expectation_value(ansatz, hamiltonian_op)\n", "print(ideal_energy)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Ideally the state generated by this ansatz will only span the computational basis states with exactly two of the four qubits in state $\\lvert 1 \\rangle$. This is because these basis states correspond to two electrons being present in the molecule.
\n", "
\n", "This ansatz is a hardware-efficient model that is designed to explore a large portion of the Hilbert space with relatively few entangling gates. Unfortunately, with this much freedom, it will regularly generate states that have no physical interpretation such as states spanning multiple basis states corresponding to different numbers of electrons in the system (which we assume is fixed and conserved).
\n", "
\n", "We can mitigate this by using a syndrome qubit that calculates the parity of the other qubits. Post-selecting this syndrome with $\\langle 0 \\rvert$ will project the remaining state onto the subspace of basis states with even parity, increasing the likelihood the observed state will be a physically admissible state.
\n", "
\n", "Even if the ansatz parameters are tuned to give a physical state, real devices have noise and imperfect gates, so in practice we may also measure bad states with a small probability. If this syndrome qubit is measured as 1, it means an error has definitely occurred, so we should discard the shot."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["syn = Qubit(\"synq\", 0)\n", "syn_res = Bit(\"synres\", 0)\n", "ansatz.add_qubit(syn)\n", "ansatz.add_bit(syn_res)\n", "for qb in qubits:\n", " ansatz.CX(qb, syn)\n", "ansatz.Measure(syn, syn_res)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Using this, we can define a filter function which removes the shots which the syndrome qubit detected as erroneous. `BackendResult` objects allow retrieval of shots in any bit order, so we can retrieve the `synres` results separately and use them to filter the shots from the remaining bits. The Backends example notebook describes this in more detail."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from collections import Counter"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def filter_shots(backend_result, syn_res_bit):\n", " bits = sorted(backend_result.get_bitlist())\n", " bits.remove(syn_res_bit)\n", " syn_shots = backend_result.get_shots([syn_res])[:, 0]\n", " main_shots = backend_result.get_shots(bits)\n", " return main_shots[syn_shots == 0]"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def filter_counts(backend_result, syn_res_bit):\n", " bits = sorted(backend_result.get_bitlist())\n", " syn_index = bits.index(syn_res_bit)\n", " counts = backend_result.get_counts()\n", " filtered_counts = Counter()\n", " for readout, count in counts.items():\n", " if readout[syn_index] == 0:\n", " filtered_readout = tuple(v for i, v in enumerate(readout) if i != syn_index)\n", " filtered_counts[filtered_readout] += count\n", " return filtered_counts"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Depending on which backend we will be using, we will need to compile each circuit we run to conform to the gate set and connectivity constraints. We can define a compilation pass for each backend that optimises the circuit and maps it onto the backend's gate set and connectivity constraints. We don't expect this to change our circuit too much as it is already near-optimal."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import OptimisePhaseGadgets, SequencePass"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def compiler_pass(backend):\n", " return SequencePass([OptimisePhaseGadgets(), backend.default_compilation_pass()])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Given the full statevector, the expectation value can be calculated simply by matrix multiplication. However, with a real quantum system, we cannot observe the full statevector directly. Fortunately, the Pauli decomposition of the operator gives us a sequence of measurements we should apply to obtain the relevant information to reconstruct the expectation value.
\n", "
\n", "The utility method `append_pauli_measurement` takes a single term of a `QubitPauliOperator` (a `QubitPauliString`) and appends measurements in the corresponding bases to obtain the expectation value for that particular Pauli operator. We will want to make a new `Circuit` object for each of the measurements we wish to observe.
\n", ""]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.predicates import CompilationUnit\n", "from pytket.utils import append_pauli_measurement"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def gen_pauli_measurement_circuits(state_circuit, compiler_pass, operator):\n", " # compile main circuit once\n", " state_cu = CompilationUnit(state_circuit)\n", " compiler_pass.apply(state_cu)\n", " compiled_state = state_cu.circuit\n", " final_map = state_cu.final_map\n", " # make a measurement circuit for each pauli\n", " pauli_circuits = []\n", " coeffs = []\n", " energy = 0\n", " for p, c in operator.terms.items():\n", " if p == ():\n", " # constant term\n", " energy += c\n", " else:\n", " # make measurement circuits and compile them\n", " pauli_circ = Circuit(state_circuit.n_qubits - 1) # ignore syndrome qubit\n", " append_pauli_measurement(qps_from_openfermion(p), pauli_circ)\n", " pauli_cu = CompilationUnit(pauli_circ)\n", " compiler_pass.apply(pauli_cu)\n", " pauli_circ = pauli_cu.circuit\n", " init_map = pauli_cu.initial_map\n", " # map measurements onto the placed qubits from the state\n", " rename_map = {\n", " i: final_map[o] for o, i in init_map.items() if o in final_map\n", " }\n", " pauli_circ.rename_units(rename_map)\n", " state_and_measure = compiled_state.copy()\n", " state_and_measure.append(pauli_circ)\n", " pauli_circuits.append(state_and_measure)\n", " coeffs.append(c)\n", " return pauli_circuits, coeffs, energy"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can now start composing these together to get our generalisable expectation value function. Passing all of our circuits to `process_circuits` allows them to be submitted to IBM Quantum devices at the same time, giving substantial savings in overall queueing time. Since the backend will cache any results from `Backend.process_circuits`, we will remove the results when we are done with them to prevent memory bloating when this method is called many times."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.utils import expectation_from_shots, expectation_from_counts"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def expectation_value(state_circuit, operator, backend, n_shots):\n", " if backend.supports_expectation:\n", " circuit = state_circuit.copy()\n", " compiled_circuit = backend.get_compiled_circuit(circuit)\n", " return backend.get_operator_expectation_value(\n", " compiled_circuit, qpo_from_openfermion(operator)\n", " )\n", " elif backend.supports_shots:\n", " syn_res_index = state_circuit.bit_readout[syn_res]\n", " pauli_circuits, coeffs, energy = gen_pauli_measurement_circuits(\n", " state_circuit, compiler_pass(backend), operator\n", " )\n", " handles = backend.process_circuits(pauli_circuits, n_shots=n_shots)\n", " for handle, coeff in zip(handles, coeffs):\n", " res = backend.get_result(handle)\n", " filtered = filter_shots(res, syn_res)\n", " energy += coeff * expectation_from_shots(filtered)\n", " backend.pop_result(handle)\n", " return energy\n", " elif backend.supports_counts:\n", " syn_res_index = state_circuit.bit_readout[syn_res]\n", " pauli_circuits, coeffs, energy = gen_pauli_measurement_circuits(\n", " state_circuit, compiler_pass(backend), operator\n", " )\n", " handles = backend.process_circuits(pauli_circuits, n_shots=n_shots)\n", " for handle, coeff in zip(handles, coeffs):\n", " res = backend.get_result(handle)\n", " filtered = filter_counts(res, syn_res)\n", " energy += coeff * expectation_from_counts(filtered)\n", " backend.pop_result(handle)\n", " return energy\n", " else:\n", " raise NotImplementedError(\"Implementation for state to be written\")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["...and then run it for our ansatz. `AerBackend` supports faster expectation value from snapshopts (using the `AerBackend.get_operator_expectation_value` method), but this only works when all the qubits in the circuit are default register qubits that go up from 0. So we will need to rename `synq`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import IBMQEmulatorBackend, AerBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ansatz.rename_units({Qubit(\"synq\", 0): Qubit(\"q\", 4)})"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(expectation_value(ansatz, hamiltonian, AerBackend(), 8000))\n", "# Try replacing IBMQEmulatorBackend with IBMQBackend to submit the circuits to a real IBM Quantum device.\n", "print(expectation_value(ansatz, hamiltonian, IBMQEmulatorBackend(\"ibmq_manila\"), 8000))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["For basic practice with using pytket backends and their results, try editing the code here to:
\n", "* Extend `expectation_value` to work with statevector backends (e.g. `AerStateBackend`)
\n", "* Remove the row filtering from `filter_shots` and see the effect on the expectation value on a noisy simulation/device
\n", "* Adapt `filter_shots` to be able to filter a counts dictionary and adapt `expectation_value` to calulate the result using the counts summary from the backend (`pytket.utils.expectation_from_counts` will be useful here)"]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Expectation values\n","\n","**Download this notebook - {nb-download}`expectation_value_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["Given a circuit generating a quantum state $\\lvert \\psi \\rangle$, it is very common to have an operator $H$ and ask for the expectation value $\\langle \\psi \\vert H \\vert \\psi \\rangle$. A notable example is in quantum computational chemistry, where $\\lvert \\psi \\rangle$ encodes the wavefunction for the electronic state of a small molecule, and the energy of the molecule can be derived from the expectation value with respect to the molecule's Hamiltonian operator $H$.
\n","
\n","This example uses this chemistry scenario to demonstrate the overall procedure for using `pytket` to perform advanced high-level procedures. We build on top of topics covered by several other example notebooks, including circuit generation, optimisation, and using different backends.
\n","
\n","There is limited built-in functionality in `pytket` for obtaining expectation values from circuits. This is designed to encourage users to consider their needs for parallelising the processing of circuits, manipulating results (e.g. filtering, adjusting counts to mitigate errors, and other forms of data processing), or more advanced schemes for grouping the terms of the operator into measurement circuits. For this example, suppose that we want to focus on reducing the queueing time for IBM device backends, and filter our shots to eliminate some detected errors.
\n","
\n","This notebook makes use of the Qiskit and ProjectQ backend modules `pytket_qiskit` and `pytket_projectq`, as well as the electronic structure module `openfermion`, all three of which should first be installed via `pip`.
\n","
\n","We will start by generating an ansatz and Hamiltonian for the chemical of interest. Here, we are just using a simple model of $\\mathrm{H}_2$ with four qubits representing the occupation of four spin orbitals."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit, Qubit, Bit\n","from sympy import symbols"]},{"cell_type":"markdown","metadata":{},"source":["Generate ansatz and Hamiltonian:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ansatz = Circuit()\n","qubits = ansatz.add_q_register(\"q\", 4)\n","args = symbols(\"a0 a1 a2 a3 a4 a5 a6 a7\")\n","for i in range(4):\n"," ansatz.Ry(args[i], qubits[i])\n","for i in range(3):\n"," ansatz.CX(qubits[i], qubits[i + 1])\n","for i in range(4):\n"," ansatz.Ry(args[4 + i], qubits[i])\n","ansatz.measure_all()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for command in ansatz:\n"," print(command)"]},{"cell_type":"markdown","metadata":{},"source":["In reality, you would use an expectation value calculation as the objective function for a classical optimisation routine to determine the parameter values for the ground state. For the purposes of this notebook, we will use some predetermined values for the ansatz, already optimised for $\\mathrm{H}_2$."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["arg_values = [\n"," 7.17996183e-02,\n"," 2.95442468e-08,\n"," 1.00000015e00,\n"," 1.00000086e00,\n"," 9.99999826e-01,\n"," 1.00000002e00,\n"," 9.99999954e-01,\n"," 1.13489747e-06,\n","]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ansatz.symbol_substitution(dict(zip(args, arg_values)))"]},{"cell_type":"markdown","metadata":{},"source":["We can use for example the openfermion library to express an Hamiltonian as a sum of tensors of paulis."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import openfermion as of"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["hamiltonian = (\n"," -0.0970662681676282 * of.QubitOperator(\"\")\n"," + -0.045302615503799284 * of.QubitOperator(\"X0 X1 Y2 Y3\")\n"," + 0.045302615503799284 * of.QubitOperator(\"X0 Y1 Y2 X3\")\n"," + 0.045302615503799284 * of.QubitOperator(\"Y0 X1 X2 Y3\")\n"," + -0.045302615503799284 * of.QubitOperator(\"Y0 Y1 X2 X3\")\n"," + 0.17141282644776884 * of.QubitOperator(\"Z0\")\n"," + 0.16868898170361213 * of.QubitOperator(\"Z0 Z1\")\n"," + 0.12062523483390425 * of.QubitOperator(\"Z0 Z2\")\n"," + 0.16592785033770352 * of.QubitOperator(\"Z0 Z3\")\n"," + 0.17141282644776884 * of.QubitOperator(\"Z1\")\n"," + 0.16592785033770352 * of.QubitOperator(\"Z1 Z2\")\n"," + 0.12062523483390425 * of.QubitOperator(\"Z1 Z3\")\n"," + -0.22343153690813597 * of.QubitOperator(\"Z2\")\n"," + 0.17441287612261608 * of.QubitOperator(\"Z2 Z3\")\n"," + -0.22343153690813597 * of.QubitOperator(\"Z3\")\n",")"]},{"cell_type":"markdown","metadata":{},"source":["This can be converted into pytket's QubitPauliOperator type.
\n","
\n","The OpenFermion `QubitOperator` class represents the operator by its decomposition into a linear combination of Pauli operators (tensor products of the $I$, $X$, $Y$, and $Z$ matrices).
\n","
\n","A `QubitPauliString` is a sparse representation of a Pauli operator with support over some subset of qubits."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pauli_sym = {\"I\": Pauli.I, \"X\": Pauli.X, \"Y\": Pauli.Y, \"Z\": Pauli.Z}"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qps_from_openfermion(paulis):\n"," \"\"\"Convert OpenFermion tensor of Paulis to pytket QubitPauliString.\"\"\"\n"," qlist = []\n"," plist = []\n"," for q, p in paulis:\n"," qlist.append(Qubit(q))\n"," plist.append(pauli_sym[p])\n"," return QubitPauliString(qlist, plist)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qpo_from_openfermion(openf_op):\n"," \"\"\"Convert OpenFermion QubitOperator to pytket QubitPauliOperator.\"\"\"\n"," tk_op = dict()\n"," for term, coeff in openf_op.terms.items():\n"," string = qps_from_openfermion(term)\n"," tk_op[string] = coeff\n"," return QubitPauliOperator(tk_op)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["hamiltonian_op = qpo_from_openfermion(hamiltonian)"]},{"cell_type":"markdown","metadata":{},"source":["We can simulate this exactly using a statevector simulator like ProjectQ. This has a built-in method for fast calculations of expectation values that works well for small examples like this."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.projectq import ProjectQBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = ProjectQBackend()\n","ideal_energy = backend.get_operator_expectation_value(ansatz, hamiltonian_op)\n","print(ideal_energy)"]},{"cell_type":"markdown","metadata":{},"source":["Ideally the state generated by this ansatz will only span the computational basis states with exactly two of the four qubits in state $\\lvert 1 \\rangle$. This is because these basis states correspond to two electrons being present in the molecule.
\n","
\n","This ansatz is a hardware-efficient model that is designed to explore a large portion of the Hilbert space with relatively few entangling gates. Unfortunately, with this much freedom, it will regularly generate states that have no physical interpretation such as states spanning multiple basis states corresponding to different numbers of electrons in the system (which we assume is fixed and conserved).
\n","
\n","We can mitigate this by using a syndrome qubit that calculates the parity of the other qubits. Post-selecting this syndrome with $\\langle 0 \\rvert$ will project the remaining state onto the subspace of basis states with even parity, increasing the likelihood the observed state will be a physically admissible state.
\n","
\n","Even if the ansatz parameters are tuned to give a physical state, real devices have noise and imperfect gates, so in practice we may also measure bad states with a small probability. If this syndrome qubit is measured as 1, it means an error has definitely occurred, so we should discard the shot."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["syn = Qubit(\"synq\", 0)\n","syn_res = Bit(\"synres\", 0)\n","ansatz.add_qubit(syn)\n","ansatz.add_bit(syn_res)\n","for qb in qubits:\n"," ansatz.CX(qb, syn)\n","ansatz.Measure(syn, syn_res)"]},{"cell_type":"markdown","metadata":{},"source":["Using this, we can define a filter function which removes the shots which the syndrome qubit detected as erroneous. `BackendResult` objects allow retrieval of shots in any bit order, so we can retrieve the `synres` results separately and use them to filter the shots from the remaining bits. The Backends example notebook describes this in more detail."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from collections import Counter"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def filter_shots(backend_result, syn_res_bit):\n"," bits = sorted(backend_result.get_bitlist())\n"," bits.remove(syn_res_bit)\n"," syn_shots = backend_result.get_shots([syn_res])[:, 0]\n"," main_shots = backend_result.get_shots(bits)\n"," return main_shots[syn_shots == 0]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def filter_counts(backend_result, syn_res_bit):\n"," bits = sorted(backend_result.get_bitlist())\n"," syn_index = bits.index(syn_res_bit)\n"," counts = backend_result.get_counts()\n"," filtered_counts = Counter()\n"," for readout, count in counts.items():\n"," if readout[syn_index] == 0:\n"," filtered_readout = tuple(v for i, v in enumerate(readout) if i != syn_index)\n"," filtered_counts[filtered_readout] += count\n"," return filtered_counts"]},{"cell_type":"markdown","metadata":{},"source":["Depending on which backend we will be using, we will need to compile each circuit we run to conform to the gate set and connectivity constraints. We can define a compilation pass for each backend that optimises the circuit and maps it onto the backend's gate set and connectivity constraints. We don't expect this to change our circuit too much as it is already near-optimal."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import OptimisePhaseGadgets, SequencePass"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def compiler_pass(backend):\n"," return SequencePass([OptimisePhaseGadgets(), backend.default_compilation_pass()])"]},{"cell_type":"markdown","metadata":{},"source":["Given the full statevector, the expectation value can be calculated simply by matrix multiplication. However, with a real quantum system, we cannot observe the full statevector directly. Fortunately, the Pauli decomposition of the operator gives us a sequence of measurements we should apply to obtain the relevant information to reconstruct the expectation value.
\n","
\n","The utility method `append_pauli_measurement` takes a single term of a `QubitPauliOperator` (a `QubitPauliString`) and appends measurements in the corresponding bases to obtain the expectation value for that particular Pauli operator. We will want to make a new `Circuit` object for each of the measurements we wish to observe.
\n"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.predicates import CompilationUnit\n","from pytket.utils import append_pauli_measurement"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def gen_pauli_measurement_circuits(state_circuit, compiler_pass, operator):\n"," # compile main circuit once\n"," state_cu = CompilationUnit(state_circuit)\n"," compiler_pass.apply(state_cu)\n"," compiled_state = state_cu.circuit\n"," final_map = state_cu.final_map\n"," # make a measurement circuit for each pauli\n"," pauli_circuits = []\n"," coeffs = []\n"," energy = 0\n"," for p, c in operator.terms.items():\n"," if p == ():\n"," # constant term\n"," energy += c\n"," else:\n"," # make measurement circuits and compile them\n"," pauli_circ = Circuit(state_circuit.n_qubits - 1) # ignore syndrome qubit\n"," append_pauli_measurement(qps_from_openfermion(p), pauli_circ)\n"," pauli_cu = CompilationUnit(pauli_circ)\n"," compiler_pass.apply(pauli_cu)\n"," pauli_circ = pauli_cu.circuit\n"," init_map = pauli_cu.initial_map\n"," # map measurements onto the placed qubits from the state\n"," rename_map = {\n"," i: final_map[o] for o, i in init_map.items() if o in final_map\n"," }\n"," pauli_circ.rename_units(rename_map)\n"," state_and_measure = compiled_state.copy()\n"," state_and_measure.append(pauli_circ)\n"," pauli_circuits.append(state_and_measure)\n"," coeffs.append(c)\n"," return pauli_circuits, coeffs, energy"]},{"cell_type":"markdown","metadata":{},"source":["We can now start composing these together to get our generalisable expectation value function. Passing all of our circuits to `process_circuits` allows them to be submitted to IBM Quantum devices at the same time, giving substantial savings in overall queueing time. Since the backend will cache any results from `Backend.process_circuits`, we will remove the results when we are done with them to prevent memory bloating when this method is called many times."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.utils import expectation_from_shots, expectation_from_counts"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def expectation_value(state_circuit, operator, backend, n_shots):\n"," if backend.supports_expectation:\n"," circuit = state_circuit.copy()\n"," compiled_circuit = backend.get_compiled_circuit(circuit)\n"," return backend.get_operator_expectation_value(\n"," compiled_circuit, qpo_from_openfermion(operator)\n"," )\n"," elif backend.supports_shots:\n"," syn_res_index = state_circuit.bit_readout[syn_res]\n"," pauli_circuits, coeffs, energy = gen_pauli_measurement_circuits(\n"," state_circuit, compiler_pass(backend), operator\n"," )\n"," handles = backend.process_circuits(pauli_circuits, n_shots=n_shots)\n"," for handle, coeff in zip(handles, coeffs):\n"," res = backend.get_result(handle)\n"," filtered = filter_shots(res, syn_res)\n"," energy += coeff * expectation_from_shots(filtered)\n"," backend.pop_result(handle)\n"," return energy\n"," elif backend.supports_counts:\n"," syn_res_index = state_circuit.bit_readout[syn_res]\n"," pauli_circuits, coeffs, energy = gen_pauli_measurement_circuits(\n"," state_circuit, compiler_pass(backend), operator\n"," )\n"," handles = backend.process_circuits(pauli_circuits, n_shots=n_shots)\n"," for handle, coeff in zip(handles, coeffs):\n"," res = backend.get_result(handle)\n"," filtered = filter_counts(res, syn_res)\n"," energy += coeff * expectation_from_counts(filtered)\n"," backend.pop_result(handle)\n"," return energy\n"," else:\n"," raise NotImplementedError(\"Implementation for state to be written\")"]},{"cell_type":"markdown","metadata":{},"source":["...and then run it for our ansatz. `AerBackend` supports faster expectation value from snapshopts (using the `AerBackend.get_operator_expectation_value` method), but this only works when all the qubits in the circuit are default register qubits that go up from 0. So we will need to rename `synq`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import IBMQEmulatorBackend, AerBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ansatz.rename_units({Qubit(\"synq\", 0): Qubit(\"q\", 4)})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(expectation_value(ansatz, hamiltonian, AerBackend(), 8000))\n","# Try replacing IBMQEmulatorBackend with IBMQBackend to submit the circuits to a real IBM Quantum device.\n","print(expectation_value(ansatz, hamiltonian, IBMQEmulatorBackend(\"ibmq_manila\"), 8000))"]},{"cell_type":"markdown","metadata":{},"source":["For basic practice with using pytket backends and their results, try editing the code here to:
\n","* Extend `expectation_value` to work with statevector backends (e.g. `AerStateBackend`)
\n","* Remove the row filtering from `filter_shots` and see the effect on the expectation value on a noisy simulation/device
\n","* Adapt `filter_shots` to be able to filter a counts dictionary and adapt `expectation_value` to calulate the result using the counts summary from the backend (`pytket.utils.expectation_from_counts` will be useful here)"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/algorithms_and_protocols/measurement_reduction_example.ipynb b/docs/examples/algorithms_and_protocols/measurement_reduction_example.ipynb index 5b6ea777..54046d0f 100644 --- a/docs/examples/algorithms_and_protocols/measurement_reduction_example.ipynb +++ b/docs/examples/algorithms_and_protocols/measurement_reduction_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Advanced expectation values and measurement reduction"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This notebook is an advanced follow-up to the \"expectation_value_example\" notebook, focussing on reducing the number of circuits required for measurement.
\n", "
\n", "When calculating the expectation value $\\langle \\psi \\vert H \\vert \\psi \\rangle$ of some operator $H$ on a quantum computer, we prepare $\\vert \\psi \\rangle$ using a circuit, and the operator $H$ is first decomposed into a sum of smaller, tractable operators of the form $\\alpha P$, where $P \\in \\mathcal{G}_n$, the multi-qubit Pauli group. Naively, one would obtain the expectation value of each of these smaller operators individually by doing shots on the quantum computer and measuring in the correct Pauli bases. Assuming the device measures only single qubits in the $Z$-basis, this basis change requires single-qubit Clifford gates, which are \"cheaper\" (less noisy and quicker) than entangling gates. The sum of these smaller operator expectation values is then used to obtain the desired $\\langle \\psi \\vert H \\vert \\psi \\rangle$.
\n", "
\n", "However, the scaling of this process can be poor, meaning that many shots are required. Instead, several of these smaller operators can be measured simultaneously, reducing the total number of measurements. For some sets of measurements, it can be done \"for free\", meaning that no extra entangling gates are required to perform simultaneous measurement. For general commuting sets of Pauli measurements, Clifford gates are required for simultaneous measurement, including entangling gates."]}, {"cell_type": "markdown", "metadata": {}, "source": ["There are several strategies for measurement reduction throughout the literature. Examples include https://arxiv.org/abs/1908.06942, https://arxiv.org/abs/1908.08067 and https://arxiv.org/abs/1907.07859."]}, {"cell_type": "markdown", "metadata": {}, "source": ["In `pytket`, we provide tools to perform measurement reduction. The most accessible way is to use the utils method, `get_operator_expectation_value`. This method wraps up some under-the-hood processes to allow users to calculate expectation values, agnostic to the backend, operator, or circuit. In this tutorial we will use the Qiskit Aer simulators via the `AerBackend`, for shots, and the `AerStateBackend`, for statevector simulation.
\n", "
\n", "We use the `QubitPauliOperator` class to represent the operator $H$."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import Circuit, Qubit\n", "from pytket.pauli import Pauli, QubitPauliString\n", "from pytket.utils import QubitPauliOperator\n", "from pytket.utils.expectations import get_operator_expectation_value\n", "from pytket.extensions.qiskit import AerBackend, AerStateBackend"]}, {"cell_type": "markdown", "metadata": {}, "source": ["First, let's get some results on a toy circuit without using any measurement reduction:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["shots_backend = AerBackend()\n", "n_shots = 10000"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(5)\n", "c.H(4)\n", "c.V(2)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = shots_backend.get_compiled_circuit(c)\n", "op = QubitPauliOperator(\n", " {\n", " QubitPauliString([Qubit(0)], [Pauli.Z]): 0.1,\n", " QubitPauliString(\n", " [Qubit(0), Qubit(1), Qubit(2), Qubit(3), Qubit(4)],\n", " [Pauli.Y, Pauli.Z, Pauli.X, Pauli.X, Pauli.Y],\n", " ): 0.4,\n", " QubitPauliString([Qubit(0), Qubit(1)], [Pauli.X, Pauli.X]): 0.2,\n", " }\n", ")"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["shots_result = get_operator_expectation_value(c, op, shots_backend, n_shots)\n", "print(shots_result)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The result should be around 0.1, although as the shot simulator is stochastic this will be inexact. Let's test to check what the exact result should be using the statevector simulator:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["state_backend = AerStateBackend()\n", "state_result = get_operator_expectation_value(c, op, state_backend)\n", "print(state_result)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Now we can introduce measurement reduction. First we need to choose a strategy:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.partition import PauliPartitionStrat"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This first one only performs measurements on simultaneous Pauli operators when there is no cost incurred to do so."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["strat = PauliPartitionStrat.NonConflictingSets\n", "shots_result = get_operator_expectation_value(c, op, shots_backend, n_shots, strat)\n", "print(shots_result)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The other strategy we use groups together arbitrary Pauli operators, with the condition that all Pauli operators within a group commute. For an input circuit with $n$ qubits, our method requires the addition of up to $\\frac{n(n-1)}{2}$ $CX$ gates to \"diagonalise\" the Pauli operators, although in practice we find that our techniques tend to give far lower gate overhead than this bound. We describe the procedure in an upcoming paper."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["strat = PauliPartitionStrat.CommutingSets\n", "shots_result = get_operator_expectation_value(c, op, shots_backend, n_shots, strat)\n", "print(shots_result)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Obviously, the `AerBackend` can be swapped out for the backend of a real machine."]}, {"cell_type": "markdown", "metadata": {}, "source": ["We will now demonstrate how to manually use the methods that are being called by `get_operator_expectation_value`. These methods are primarily intended for internal use, but we show them here for advanced users who may wish to have more information about the number of CX gates being added to each circuit, the number of circuits being run and other diagnostics."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import OpType\n", "from pytket.partition import measurement_reduction"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["id_string = QubitPauliString()\n", "qpt_list = [p for p in op._dict.keys() if (p != id_string)]\n", "setup_1 = measurement_reduction(qpt_list, PauliPartitionStrat.NonConflictingSets)\n", "print(\"Circuits required for measurement: {}\".format(len(setup_1.measurement_circs)))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This produced a `MeasurementSetup` object using the `NonConflictingSets` strategy of measurement reduction. This object holds a set of circuits which perform different basis changes, and the measurements associated with these circuits.
\n", "
\n", "There are 3 circuits held within the `MeasurementSetup` object, meaning that our original `QubitOperator` has been reduced from the 5 originally required measurements to 3."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["for circ in setup_1.measurement_circs:\n", " print(\"CX gates for measurement: {}\".format(circ.n_gates_of_type(OpType.CX)))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["No CX gates have been added for any of the required measurements. Now, we will change to the `CommutingSets` strategy."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["setup_2 = measurement_reduction(qpt_list, PauliPartitionStrat.CommutingSets)\n", "print(\"Circuits required for measurement: {}\".format(len(setup_2.measurement_circs)))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["There are only 2 circuits required when expanding the scope of allowed simultaneous measurements. However, this comes at a cost:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["for circ in setup_2.measurement_circs:\n", " print(\"CX gates for measurement: {}\".format(circ.n_gates_of_type(OpType.CX)))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["A CX gate has been introduced to one of the measurement circuits, to convert to the correct Pauli basis set. On current devices which are extremely constrained in the number of entangling gates, the reduction in number of shots may not be worth the gate overhead."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Advanced expectation values and measurement reduction\n","\n","**Download this notebook - {nb-download}`measurement_reduction_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["This notebook is an advanced follow-up to the \"expectation_value_example\" notebook, focussing on reducing the number of circuits required for measurement.
\n","
\n","When calculating the expectation value $\\langle \\psi \\vert H \\vert \\psi \\rangle$ of some operator $H$ on a quantum computer, we prepare $\\vert \\psi \\rangle$ using a circuit, and the operator $H$ is first decomposed into a sum of smaller, tractable operators of the form $\\alpha P$, where $P \\in \\mathcal{G}_n$, the multi-qubit Pauli group. Naively, one would obtain the expectation value of each of these smaller operators individually by doing shots on the quantum computer and measuring in the correct Pauli bases. Assuming the device measures only single qubits in the $Z$-basis, this basis change requires single-qubit Clifford gates, which are \"cheaper\" (less noisy and quicker) than entangling gates. The sum of these smaller operator expectation values is then used to obtain the desired $\\langle \\psi \\vert H \\vert \\psi \\rangle$.
\n","
\n","However, the scaling of this process can be poor, meaning that many shots are required. Instead, several of these smaller operators can be measured simultaneously, reducing the total number of measurements. For some sets of measurements, it can be done \"for free\", meaning that no extra entangling gates are required to perform simultaneous measurement. For general commuting sets of Pauli measurements, Clifford gates are required for simultaneous measurement, including entangling gates."]},{"cell_type":"markdown","metadata":{},"source":["There are several strategies for measurement reduction throughout the literature. Examples include https://arxiv.org/abs/1908.06942, https://arxiv.org/abs/1908.08067 and https://arxiv.org/abs/1907.07859."]},{"cell_type":"markdown","metadata":{},"source":["In `pytket`, we provide tools to perform measurement reduction. The most accessible way is to use the utils method, `get_operator_expectation_value`. This method wraps up some under-the-hood processes to allow users to calculate expectation values, agnostic to the backend, operator, or circuit. In this tutorial we will use the Qiskit Aer simulators via the `AerBackend`, for shots, and the `AerStateBackend`, for statevector simulation.
\n","
\n","We use the `QubitPauliOperator` class to represent the operator $H$."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit, Qubit\n","from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils import QubitPauliOperator\n","from pytket.utils.expectations import get_operator_expectation_value\n","from pytket.extensions.qiskit import AerBackend, AerStateBackend"]},{"cell_type":"markdown","metadata":{},"source":["First, let's get some results on a toy circuit without using any measurement reduction:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["shots_backend = AerBackend()\n","n_shots = 10000"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(5)\n","c.H(4)\n","c.V(2)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = shots_backend.get_compiled_circuit(c)\n","op = QubitPauliOperator(\n"," {\n"," QubitPauliString([Qubit(0)], [Pauli.Z]): 0.1,\n"," QubitPauliString(\n"," [Qubit(0), Qubit(1), Qubit(2), Qubit(3), Qubit(4)],\n"," [Pauli.Y, Pauli.Z, Pauli.X, Pauli.X, Pauli.Y],\n"," ): 0.4,\n"," QubitPauliString([Qubit(0), Qubit(1)], [Pauli.X, Pauli.X]): 0.2,\n"," }\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["shots_result = get_operator_expectation_value(c, op, shots_backend, n_shots)\n","print(shots_result)"]},{"cell_type":"markdown","metadata":{},"source":["The result should be around 0.1, although as the shot simulator is stochastic this will be inexact. Let's test to check what the exact result should be using the statevector simulator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["state_backend = AerStateBackend()\n","state_result = get_operator_expectation_value(c, op, state_backend)\n","print(state_result)"]},{"cell_type":"markdown","metadata":{},"source":["Now we can introduce measurement reduction. First we need to choose a strategy:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.partition import PauliPartitionStrat"]},{"cell_type":"markdown","metadata":{},"source":["This first one only performs measurements on simultaneous Pauli operators when there is no cost incurred to do so."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["strat = PauliPartitionStrat.NonConflictingSets\n","shots_result = get_operator_expectation_value(c, op, shots_backend, n_shots, strat)\n","print(shots_result)"]},{"cell_type":"markdown","metadata":{},"source":["The other strategy we use groups together arbitrary Pauli operators, with the condition that all Pauli operators within a group commute. For an input circuit with $n$ qubits, our method requires the addition of up to $\\frac{n(n-1)}{2}$ $CX$ gates to \"diagonalise\" the Pauli operators, although in practice we find that our techniques tend to give far lower gate overhead than this bound. We describe the procedure in an upcoming paper."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["strat = PauliPartitionStrat.CommutingSets\n","shots_result = get_operator_expectation_value(c, op, shots_backend, n_shots, strat)\n","print(shots_result)"]},{"cell_type":"markdown","metadata":{},"source":["Obviously, the `AerBackend` can be swapped out for the backend of a real machine."]},{"cell_type":"markdown","metadata":{},"source":["We will now demonstrate how to manually use the methods that are being called by `get_operator_expectation_value`. These methods are primarily intended for internal use, but we show them here for advanced users who may wish to have more information about the number of CX gates being added to each circuit, the number of circuits being run and other diagnostics."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import OpType\n","from pytket.partition import measurement_reduction"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["id_string = QubitPauliString()\n","qpt_list = [p for p in op._dict.keys() if (p != id_string)]\n","setup_1 = measurement_reduction(qpt_list, PauliPartitionStrat.NonConflictingSets)\n","print(\"Circuits required for measurement: {}\".format(len(setup_1.measurement_circs)))"]},{"cell_type":"markdown","metadata":{},"source":["This produced a `MeasurementSetup` object using the `NonConflictingSets` strategy of measurement reduction. This object holds a set of circuits which perform different basis changes, and the measurements associated with these circuits.
\n","
\n","There are 3 circuits held within the `MeasurementSetup` object, meaning that our original `QubitOperator` has been reduced from the 5 originally required measurements to 3."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for circ in setup_1.measurement_circs:\n"," print(\"CX gates for measurement: {}\".format(circ.n_gates_of_type(OpType.CX)))"]},{"cell_type":"markdown","metadata":{},"source":["No CX gates have been added for any of the required measurements. Now, we will change to the `CommutingSets` strategy."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["setup_2 = measurement_reduction(qpt_list, PauliPartitionStrat.CommutingSets)\n","print(\"Circuits required for measurement: {}\".format(len(setup_2.measurement_circs)))"]},{"cell_type":"markdown","metadata":{},"source":["There are only 2 circuits required when expanding the scope of allowed simultaneous measurements. However, this comes at a cost:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for circ in setup_2.measurement_circs:\n"," print(\"CX gates for measurement: {}\".format(circ.n_gates_of_type(OpType.CX)))"]},{"cell_type":"markdown","metadata":{},"source":["A CX gate has been introduced to one of the measurement circuits, to convert to the correct Pauli basis set. On current devices which are extremely constrained in the number of entangling gates, the reduction in number of shots may not be worth the gate overhead."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/algorithms_and_protocols/phase_estimation.ipynb b/docs/examples/algorithms_and_protocols/phase_estimation.ipynb index 780c97ff..2e014592 100644 --- a/docs/examples/algorithms_and_protocols/phase_estimation.ipynb +++ b/docs/examples/algorithms_and_protocols/phase_estimation.ipynb @@ -1 +1 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Quantum Phase Estimation\n","\n","When constructing circuits for quantum algorithms it is useful to think of higher level operations than just individual quantum gates. In `pytket` we can construct circuits using box structures which abstract away the complexity of the underlying circuit. This notebook is intended to complement the [boxes section](https://tket.quantinuum.com/user-manual/manual_circuit.html#boxes) of the user manual which introduces the different box types.\n","\n","To demonstrate boxes in `pytket` we will consider the Quantum Phase Estimation algorithm (QPE). This is an important subroutine in several quantum algorithms including Shor's algorithm and fault-tolerant approaches to quantum chemistry.\n","\n","## Overview of Phase Estimation\n","\n","The Quantum Phase Estimation algorithm can be used to estimate the eigenvalues of some unitary operator $U$ to some desired precision.\n","\n","The eigenvalues of $U$ lie on the unit circle, giving us the following eigenvalue equation\n","\n","$$\n","\\begin{equation}\n","U |\\psi \\rangle = e^{2 \\pi i \\theta} |\\psi\\rangle\\,, \\quad 0 \\leq \\theta \\leq 1\n","\\end{equation}\n","$$\n","\n","Here $|\\psi \\rangle$ is an eigenstate of the operator $U$. In phase estimation we estimate the eigenvalue $e^{2 \\pi i \\theta}$ by approximating $\\theta$.\n","\n","\n","The circuit for Quantum phase estimation is itself composed of several subroutines which we can realise as boxes.\n","\n","![](images/phase_est.png \"Quantum Phase Estimation Circuit\")"]},{"cell_type":"markdown","metadata":{},"source":["QPE is generally split up into three stages\n","\n","1. Firstly we prepare an initial state in one register. In parallel we prepare a uniform superposition state using Hadamard gates on some ancilla (measurement) qubits. The number of ancilla qubits determines how precisely we can estimate the phase $\\theta$.\n","\n","2. Secondly we apply successive controlled $U$ gates. This has the effect of \"kicking back\" phases onto the ancilla qubits according to the eigenvalue equation above.\n","\n","3. Finally we apply the inverse Quantum Fourier Transform (QFT). This essentially plays the role of destructive interference, suppressing amplitudes from \"undesirable states\" and hopefully allowing us to measure a single outcome (or a small number of outcomes) with high probability.\n","\n","\n","There is some subtlety around the first point. The initial state used can be an exact eigenstate of $U$ however this may be difficult to prepare if we don't know the eigenvalues of $U$ in advance. Alternatively we could use an initial state that is a linear combination of eigenstates, as the phase estimation will project into the eigenspace of $U$."]},{"cell_type":"markdown","metadata":{},"source":["We also assume that we can implement $U$ with a quantum circuit. In chemistry applications $U$ could be of the form $U=e^{-iHt}$ where $H$ is the Hamiltonian of some system of interest. In the textbook algorithm, the number of controlled unitaries we apply scales exponentially with the number of measurement qubits. This allows more precision at the expense of a larger quantum circuit."]},{"cell_type":"markdown","metadata":{},"source":["## The Quantum Fourier Transform"]},{"cell_type":"markdown","metadata":{},"source":["Before considering the other parts of the QPE algorithm, lets focus on the Quantum Fourier Transform (QFT) subroutine.\n","\n","Mathematically, the QFT has the following action.\n","\n","$$\n","\\begin{equation}\n","QFT : |j\\rangle\\ \\longmapsto \\frac{1}{\\sqrt{N}} \\sum_{k=0}^{N - 1} e^{2 \\pi ijk/N}|k\\rangle, \\quad N= 2^n\n","\\end{equation}\n","$$\n","\n","This is essentially the Discrete Fourier transform except the input is a quantum state $|j\\rangle$.\n","\n","We can build the circuit for the $n$ qubit QFT using $n$ Hadamard gates $\\lfloor{\\frac{n}{2}}\\rfloor$ swap gates and $\\frac{n(n-1)}{2}$ controlled unitary rotations $\\text{CU1}$.\n","\n","$$\n"," \\begin{equation}\n","U1(\\phi) =\n"," \\begin{pmatrix}\n"," 1 & 0 \\\\\n"," 0 & e^{i \\pi \\phi}\n"," \\end{pmatrix}\\, , \\quad\n"," CU1(\\phi) =\n"," \\begin{pmatrix}\n"," 1 & 0 & 0 & 0 \\\\\n"," 0 & 1 & 0 & 0 \\\\\n"," 0 & 0 & 1 & 0 \\\\\n"," 0 & 0 & 0 & e^{i \\pi \\phi}\n"," \\end{pmatrix}\n"," \\end{equation}\n","$$\n","\n","The circuit for the Quantum Fourier transform on three qubits is the following\n","\n","![](images/qft.png \"QFT Circuit\")\n","\n","We can build this circuit in `pytket` by adding gate operations manually:"]},{"cell_type":"markdown","metadata":{},"source":["lets build the QFT for three qubits"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit\n","from pytket.circuit.display import render_circuit_jupyter"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qft3_circ = Circuit(3)\n","qft3_circ.H(0)\n","qft3_circ.CU1(0.5, 1, 0)\n","qft3_circ.CU1(0.25, 2, 0)\n","qft3_circ.H(1)\n","qft3_circ.CU1(0.5, 2, 1)\n","qft3_circ.H(2)\n","qft3_circ.SWAP(0, 2)\n","render_circuit_jupyter(qft3_circ)"]},{"cell_type":"markdown","metadata":{},"source":["We can generalise the quantum Fourier transform to $n$ qubits by iterating over the qubits as follows"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def build_qft_circuit(n_qubits: int) -> Circuit:\n"," circ = Circuit(n_qubits, name=\"QFT\")\n"," for i in range(n_qubits):\n"," circ.H(i)\n"," for j in range(i + 1, n_qubits):\n"," circ.CU1(1 / 2 ** (j - i), j, i)\n"," for k in range(0, n_qubits // 2):\n"," circ.SWAP(k, n_qubits - k - 1)\n"," return circ"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qft4_circ: Circuit = build_qft_circuit(4)\n","render_circuit_jupyter(qft4_circ)"]},{"cell_type":"markdown","metadata":{},"source":["Now that we have the generalised circuit we can wrap it up in a `CircBox` which can then be added to another circuit as a subroutine."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import CircBox"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qft4_box: CircBox = CircBox(qft4_circ)\n","qft_circ = Circuit(4).add_gate(qft4_box, [0, 1, 2, 3])\n","render_circuit_jupyter(qft_circ)"]},{"cell_type":"markdown","metadata":{},"source":["Note how the `CircBox` inherits the name `QFT` from the underlying circuit."]},{"cell_type":"markdown","metadata":{},"source":["Recall that in our phase estimation algorithm we need to use the inverse QFT.\n","\n","$$\n","\\begin{equation}\n","\\text{QFT}^† : \\frac{1}{\\sqrt{N}} \\sum_{k=0}^{N - 1} e^{2 \\pi ijk/N}|k\\rangle \\longmapsto |j\\rangle\\,, \\quad N= 2^n\n","\\end{equation}\n","$$\n","\n","\n","Now that we have the QFT circuit we can obtain the inverse by using `CircBox.dagger`. We can also verify that this is correct by inspecting the circuit inside with `CircBox.get_circuit()`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["inv_qft4_box = qft4_box.dagger\n","# Explicitly set the name of the `CircBox` to \"QFT†\"\n","inv_qft4_box.circuit_name = \"QFT†\"\n","qft_inv_circ = Circuit(4)\n","qft_inv_circ.add_gate(inv_qft4_box, [0, 1, 2, 3])\n","render_circuit_jupyter(qft_inv_circ)"]},{"cell_type":"markdown","metadata":{},"source":["## Building the Phase Estimation Circuit"]},{"cell_type":"markdown","metadata":{},"source":["We can now define a function to build our entire QPE circuit. We can make this function take a state preparation circuit and a unitary circuit as input as well. The function also has the number of measurement qubits as input which will determine the precision of our phase estimate."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import QControlBox"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def build_phase_estimation_circuit(\n"," n_measurement_qubits: int, state_prep_circuit: Circuit, unitary_circuit: Circuit\n",") -> Circuit:\n"," # Define a Circuit with a measurement and prep register\n"," qpe_circ: Circuit = Circuit()\n"," n_state_prep_qubits = state_prep_circuit.n_qubits\n"," measurement_register = qpe_circ.add_q_register(\"m\", n_measurement_qubits)\n"," state_prep_register = qpe_circ.add_q_register(\"p\", n_state_prep_qubits)\n"," qpe_circ.add_circuit(state_prep_circuit, list(state_prep_register))\n","\n"," # Create a controlled unitary with a single control qubit\n"," unitary_circuit.name = \"U\"\n"," controlled_u_gate = QControlBox(CircBox(unitary_circuit), 1)\n","\n"," # Add Hadamard gates to every qubit in the measurement register\n"," for m_qubit in measurement_register:\n"," qpe_circ.H(m_qubit)\n","\n"," # Add all (2**n_measurement_qubits - 1) of the controlled unitaries sequentially\n"," for m_qubit in range(n_measurement_qubits):\n"," control_index = n_measurement_qubits - m_qubit - 1\n"," control_qubit = [measurement_register[control_index]]\n"," for _ in range(2**m_qubit):\n"," qpe_circ.add_gate(\n"," controlled_u_gate, control_qubit + list(state_prep_register)\n"," )\n","\n"," # Finally, append the inverse qft and measure the qubits\n"," qft_box = CircBox(build_qft_circuit(n_measurement_qubits))\n"," inverse_qft_box = qft_box.dagger\n"," inverse_qft_box.circuit_name = \"QFT†\"\n"," qpe_circ.add_gate(inverse_qft_box, list(measurement_register))\n"," qpe_circ.measure_register(measurement_register, \"c\")\n"," return qpe_circ"]},{"cell_type":"markdown","metadata":{},"source":["## Phase Estimation with a Trivial Eigenstate\n","\n","Lets test our circuit construction by preparing a trivial $|1\\rangle$ eigenstate of the $\\text{U1}$ gate. We can then see if our phase estimation circuit returns the expected eigenvalue."]},{"cell_type":"markdown","metadata":{},"source":["$$\n","\\begin{equation}\n","U1(\\phi)|1\\rangle = e^{i \\pi \\phi}|1\\rangle = e^{2 \\pi i \\theta} |1\\rangle \\implies \\theta = \\frac{\\phi}{2}\n","\\end{equation}\n","$$\n","\n","So we expect that our ideal phase $\\theta$ will be half the input angle $\\phi$ to our $U1$ gate."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["prep_circuit = Circuit(1).X(0) # prepare the |1> eigenstate of U1"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["input_angle = 0.73 # angle as number of half turns"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["unitary_circuit = Circuit(1).U1(input_angle, 0) # Base unitary for controlled U ops"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qpe_circ_trivial = build_phase_estimation_circuit(\n"," 4, state_prep_circuit=prep_circuit, unitary_circuit=unitary_circuit\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(qpe_circ_trivial)"]},{"cell_type":"markdown","metadata":{},"source":["Lets use the noiseless `AerBackend` simulator to run our phase estimation circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["compiled_circ = backend.get_compiled_circuit(qpe_circ_trivial)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_shots = 1000\n","result = backend.run_circuit(compiled_circ, n_shots)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(result.get_counts())"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.backends.backendresult import BackendResult\n","import matplotlib.pyplot as plt"]},{"cell_type":"markdown","metadata":{},"source":["plotting function for QPE Notebook"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def plot_qpe_results(\n"," sim_result: BackendResult,\n"," n_strings: int = 4,\n"," dark_mode: bool = False,\n"," y_limit: int = 1000,\n",") -> None:\n"," \"\"\"\n"," Plots results in a barchart given a BackendResult. the number of stings displayed\n"," can be specified with the n_strings argument.\n"," \"\"\"\n"," counts_dict = sim_result.get_counts()\n"," sorted_shots = counts_dict.most_common()\n"," n_most_common_strings = sorted_shots[:n_strings]\n"," x_axis_values = [str(entry[0]) for entry in n_most_common_strings] # basis states\n"," y_axis_values = [entry[1] for entry in n_most_common_strings] # counts\n"," if dark_mode:\n"," plt.style.use(\"dark_background\")\n"," fig = plt.figure()\n"," ax = fig.add_axes((0, 0, 0.75, 0.5))\n"," color_list = [\"orange\"] * (len(x_axis_values))\n"," ax.bar(\n"," x=x_axis_values,\n"," height=y_axis_values,\n"," color=color_list,\n"," )\n"," ax.set_title(label=\"Results\")\n"," plt.ylim([0, y_limit])\n"," plt.xlabel(\"Basis State\")\n"," plt.ylabel(\"Number of Shots\")\n"," plt.show()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plot_qpe_results(result, y_limit=int(1.2 * n_shots))"]},{"cell_type":"markdown","metadata":{},"source":["As expected we see one outcome with high probability. Lets now extract our approximation of $\\theta$ from our output bitstrings.\n","\n","suppose the $j$ is an integer representation of our most commonly measured bitstring."]},{"cell_type":"markdown","metadata":{},"source":["$$\n","\\begin{equation}\n","\\theta_{estimate} = \\frac{j}{N}\n","\\end{equation}\n","$$"]},{"cell_type":"markdown","metadata":{},"source":["Here $N = 2 ^m$ where $m$ is the number of measurement qubits."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.backends.backendresult import BackendResult"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def single_phase_from_backendresult(result: BackendResult) -> float:\n"," # Extract most common measurement outcome\n"," basis_state = result.get_counts().most_common()[0][0]\n"," bitstring = \"\".join([str(bit) for bit in basis_state])\n"," integer_j = int(bitstring, 2)\n","\n"," # Calculate theta estimate\n"," return integer_j / (2 ** len(bitstring))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["theta = single_phase_from_backendresult(result)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(theta)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(input_angle / 2)"]},{"cell_type":"markdown","metadata":{},"source":["Our output is close to half our input angle $\\phi$ as expected. Lets calculate our error $E$ to three decimal places."]},{"cell_type":"markdown","metadata":{},"source":["$$\n","\\begin{equation}\n","E = |\\phi - 2 \\, \\theta_{estimate}|\n","\\end{equation}\n","$$"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["error = round(abs(input_angle - (2 * theta)), 3)\n","print(error)"]},{"cell_type":"markdown","metadata":{},"source":["## Phase Estimation with Time Evolution"]},{"cell_type":"markdown","metadata":{},"source":["In the phase estimation algorithm we repeatedly perform controlled unitary operations. In the textbook variant of QPE presented here, the number of controlled unitaries will be $2^m - 1$ where $m$ is the number of measurement qubits."]},{"cell_type":"markdown","metadata":{},"source":["In the example above we've shown a trivial instance of QPE where we know the exact phase in advance. For more realistic applications of QPE we will have some non-trivial state preparation required.\n","\n","For chemistry or condensed matter physics $U$ typically be the time evolution operator $U(t) = e^{- i H t}$ where $H$ is the problem Hamiltonian.\n","Suppose that we had the following decomposition for $H$ in terms of Pauli strings $P_j$ and complex coefficients $\\alpha_j$.\n","\n","$$\n","\\begin{equation}\n","H = \\sum_j \\alpha_j P_j\\,, \\quad \\, P_j \\in \\{I, \\,X, \\,Y, \\,Z\\}^{\\otimes n}\n","\\end{equation}\n","$$\n","\n","Here the term Pauli strings refers to tensor products of Pauli operators. These strings form an orthonormal basis for $2^n \\times 2^n$ matrices."]},{"cell_type":"markdown","metadata":{},"source":["If we have a Hamiltonian in the form above, we can then implement $U(t)$ as a sequence of Pauli gadget circuits. We can do this with the [PauliExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.PauliExpBox) construct in pytket. For more on `PauliExpBox` see the [user manual](https://tket.quantinuum.com/user-manual/manual_circuit.html#pauli-exponential-boxes)."]},{"cell_type":"markdown","metadata":{},"source":["Once we have a circuit to implement our time evolution operator $U(t)$, we can construct the controlled $U(t)$ operations using [QControlBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.QControlBox). If our base unitary is a sequence of `PauliExpBox`(es) then there is some structure we can exploit to simplify our circuit. See this [blog post](https://tket.quantinuum.com/blog/posts/controlled_gates/) on [ConjugationBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.ConjugationBox) for more."]},{"cell_type":"markdown","metadata":{},"source":["As an exercise, try to use phase estimation to calculate the ground state of diatomic hydrogen $H_2$."]},{"cell_type":"markdown","metadata":{},"source":["## Suggestions for further reading\n","\n","* Quantinuum paper on Bayesian phase estimation -> https://arxiv.org/pdf/2306.16608.pdf\n","* Blog post on `ConjugationBox` (efficient circuits for controlled gates) -> https://tket.quantinuum.com/blog/posts/controlled_gates/"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Quantum Phase Estimation\n","\n","**Download this notebook - {nb-download}`phase_estimation.ipynb`**\n","\n","When constructing circuits for quantum algorithms it is useful to think of higher level operations than just individual quantum gates. In `pytket` we can construct circuits using box structures which abstract away the complexity of the underlying circuit. This notebook is intended to complement the [boxes section](https://tket.quantinuum.com/user-manual/manual_circuit.html#boxes) of the user manual which introduces the different box types.\n","\n","To demonstrate boxes in `pytket` we will consider the Quantum Phase Estimation algorithm (QPE). This is an important subroutine in several quantum algorithms including Shor's algorithm and fault-tolerant approaches to quantum chemistry.\n","\n","## Overview of Phase Estimation\n","\n","The Quantum Phase Estimation algorithm can be used to estimate the eigenvalues of some unitary operator $U$ to some desired precision.\n","\n","The eigenvalues of $U$ lie on the unit circle, giving us the following eigenvalue equation\n","\n","$$\n","\\begin{equation}\n","U |\\psi \\rangle = e^{2 \\pi i \\theta} |\\psi\\rangle\\,, \\quad 0 \\leq \\theta \\leq 1\n","\\end{equation}\n","$$\n","\n","Here $|\\psi \\rangle$ is an eigenstate of the operator $U$. In phase estimation we estimate the eigenvalue $e^{2 \\pi i \\theta}$ by approximating $\\theta$.\n","\n","\n","The circuit for Quantum phase estimation is itself composed of several subroutines which we can realise as boxes.\n","\n","![](images/phase_est.png \"Quantum Phase Estimation Circuit\")"]},{"cell_type":"markdown","metadata":{},"source":["QPE is generally split up into three stages\n","\n","1. Firstly we prepare an initial state in one register. In parallel we prepare a uniform superposition state using Hadamard gates on some ancilla (measurement) qubits. The number of ancilla qubits determines how precisely we can estimate the phase $\\theta$.\n","\n","2. Secondly we apply successive controlled $U$ gates. This has the effect of \"kicking back\" phases onto the ancilla qubits according to the eigenvalue equation above.\n","\n","3. Finally we apply the inverse Quantum Fourier Transform (QFT). This essentially plays the role of destructive interference, suppressing amplitudes from \"undesirable states\" and hopefully allowing us to measure a single outcome (or a small number of outcomes) with high probability.\n","\n","\n","There is some subtlety around the first point. The initial state used can be an exact eigenstate of $U$ however this may be difficult to prepare if we don't know the eigenvalues of $U$ in advance. Alternatively we could use an initial state that is a linear combination of eigenstates, as the phase estimation will project into the eigenspace of $U$."]},{"cell_type":"markdown","metadata":{},"source":["We also assume that we can implement $U$ with a quantum circuit. In chemistry applications $U$ could be of the form $U=e^{-iHt}$ where $H$ is the Hamiltonian of some system of interest. In the textbook algorithm, the number of controlled unitaries we apply scales exponentially with the number of measurement qubits. This allows more precision at the expense of a larger quantum circuit."]},{"cell_type":"markdown","metadata":{},"source":["## The Quantum Fourier Transform"]},{"cell_type":"markdown","metadata":{},"source":["Before considering the other parts of the QPE algorithm, lets focus on the Quantum Fourier Transform (QFT) subroutine.\n","\n","Mathematically, the QFT has the following action.\n","\n","$$\n","\\begin{equation}\n","QFT : |j\\rangle\\ \\longmapsto \\frac{1}{\\sqrt{N}} \\sum_{k=0}^{N - 1} e^{2 \\pi ijk/N}|k\\rangle, \\quad N= 2^n\n","\\end{equation}\n","$$\n","\n","This is essentially the Discrete Fourier transform except the input is a quantum state $|j\\rangle$.\n","\n","We can build the circuit for the $n$ qubit QFT using $n$ Hadamard gates $\\lfloor{\\frac{n}{2}}\\rfloor$ swap gates and $\\frac{n(n-1)}{2}$ controlled unitary rotations $\\text{CU1}$.\n","\n","$$\n"," \\begin{equation}\n","U1(\\phi) =\n"," \\begin{pmatrix}\n"," 1 & 0 \\\\\n"," 0 & e^{i \\pi \\phi}\n"," \\end{pmatrix}\\, , \\quad\n"," CU1(\\phi) =\n"," \\begin{pmatrix}\n"," 1 & 0 & 0 & 0 \\\\\n"," 0 & 1 & 0 & 0 \\\\\n"," 0 & 0 & 1 & 0 \\\\\n"," 0 & 0 & 0 & e^{i \\pi \\phi}\n"," \\end{pmatrix}\n"," \\end{equation}\n","$$\n","\n","The circuit for the Quantum Fourier transform on three qubits is the following\n","\n","![](images/qft.png \"QFT Circuit\")\n","\n","We can build this circuit in `pytket` by adding gate operations manually:"]},{"cell_type":"markdown","metadata":{},"source":["lets build the QFT for three qubits"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit\n","from pytket.circuit.display import render_circuit_jupyter"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qft3_circ = Circuit(3)\n","qft3_circ.H(0)\n","qft3_circ.CU1(0.5, 1, 0)\n","qft3_circ.CU1(0.25, 2, 0)\n","qft3_circ.H(1)\n","qft3_circ.CU1(0.5, 2, 1)\n","qft3_circ.H(2)\n","qft3_circ.SWAP(0, 2)\n","render_circuit_jupyter(qft3_circ)"]},{"cell_type":"markdown","metadata":{},"source":["We can generalise the quantum Fourier transform to $n$ qubits by iterating over the qubits as follows"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def build_qft_circuit(n_qubits: int) -> Circuit:\n"," circ = Circuit(n_qubits, name=\"QFT\")\n"," for i in range(n_qubits):\n"," circ.H(i)\n"," for j in range(i + 1, n_qubits):\n"," circ.CU1(1 / 2 ** (j - i), j, i)\n"," for k in range(0, n_qubits // 2):\n"," circ.SWAP(k, n_qubits - k - 1)\n"," return circ"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qft4_circ: Circuit = build_qft_circuit(4)\n","render_circuit_jupyter(qft4_circ)"]},{"cell_type":"markdown","metadata":{},"source":["Now that we have the generalised circuit we can wrap it up in a `CircBox` which can then be added to another circuit as a subroutine."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import CircBox"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qft4_box: CircBox = CircBox(qft4_circ)\n","qft_circ = Circuit(4).add_gate(qft4_box, [0, 1, 2, 3])\n","render_circuit_jupyter(qft_circ)"]},{"cell_type":"markdown","metadata":{},"source":["Note how the `CircBox` inherits the name `QFT` from the underlying circuit."]},{"cell_type":"markdown","metadata":{},"source":["Recall that in our phase estimation algorithm we need to use the inverse QFT.\n","\n","$$\n","\\begin{equation}\n","\\text{QFT}^† : \\frac{1}{\\sqrt{N}} \\sum_{k=0}^{N - 1} e^{2 \\pi ijk/N}|k\\rangle \\longmapsto |j\\rangle\\,, \\quad N= 2^n\n","\\end{equation}\n","$$\n","\n","\n","Now that we have the QFT circuit we can obtain the inverse by using `CircBox.dagger`. We can also verify that this is correct by inspecting the circuit inside with `CircBox.get_circuit()`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["inv_qft4_box = qft4_box.dagger\n","# Explicitly set the name of the `CircBox` to \"QFT†\"\n","inv_qft4_box.circuit_name = \"QFT†\"\n","qft_inv_circ = Circuit(4)\n","qft_inv_circ.add_gate(inv_qft4_box, [0, 1, 2, 3])\n","render_circuit_jupyter(qft_inv_circ)"]},{"cell_type":"markdown","metadata":{},"source":["## Building the Phase Estimation Circuit"]},{"cell_type":"markdown","metadata":{},"source":["We can now define a function to build our entire QPE circuit. We can make this function take a state preparation circuit and a unitary circuit as input as well. The function also has the number of measurement qubits as input which will determine the precision of our phase estimate."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import QControlBox"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def build_phase_estimation_circuit(\n"," n_measurement_qubits: int, state_prep_circuit: Circuit, unitary_circuit: Circuit\n",") -> Circuit:\n"," # Define a Circuit with a measurement and prep register\n"," qpe_circ: Circuit = Circuit()\n"," n_state_prep_qubits = state_prep_circuit.n_qubits\n"," measurement_register = qpe_circ.add_q_register(\"m\", n_measurement_qubits)\n"," state_prep_register = qpe_circ.add_q_register(\"p\", n_state_prep_qubits)\n"," qpe_circ.add_circuit(state_prep_circuit, list(state_prep_register))\n","\n"," # Create a controlled unitary with a single control qubit\n"," unitary_circuit.name = \"U\"\n"," controlled_u_gate = QControlBox(CircBox(unitary_circuit), 1)\n","\n"," # Add Hadamard gates to every qubit in the measurement register\n"," for m_qubit in measurement_register:\n"," qpe_circ.H(m_qubit)\n","\n"," # Add all (2**n_measurement_qubits - 1) of the controlled unitaries sequentially\n"," for m_qubit in range(n_measurement_qubits):\n"," control_index = n_measurement_qubits - m_qubit - 1\n"," control_qubit = [measurement_register[control_index]]\n"," for _ in range(2**m_qubit):\n"," qpe_circ.add_gate(\n"," controlled_u_gate, control_qubit + list(state_prep_register)\n"," )\n","\n"," # Finally, append the inverse qft and measure the qubits\n"," qft_box = CircBox(build_qft_circuit(n_measurement_qubits))\n"," inverse_qft_box = qft_box.dagger\n"," inverse_qft_box.circuit_name = \"QFT†\"\n"," qpe_circ.add_gate(inverse_qft_box, list(measurement_register))\n"," qpe_circ.measure_register(measurement_register, \"c\")\n"," return qpe_circ"]},{"cell_type":"markdown","metadata":{},"source":["## Phase Estimation with a Trivial Eigenstate\n","\n","Lets test our circuit construction by preparing a trivial $|1\\rangle$ eigenstate of the $\\text{U1}$ gate. We can then see if our phase estimation circuit returns the expected eigenvalue."]},{"cell_type":"markdown","metadata":{},"source":["$$\n","\\begin{equation}\n","U1(\\phi)|1\\rangle = e^{i \\pi \\phi}|1\\rangle = e^{2 \\pi i \\theta} |1\\rangle \\implies \\theta = \\frac{\\phi}{2}\n","\\end{equation}\n","$$\n","\n","So we expect that our ideal phase $\\theta$ will be half the input angle $\\phi$ to our $U1$ gate."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["prep_circuit = Circuit(1).X(0) # prepare the |1> eigenstate of U1"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["input_angle = 0.73 # angle as number of half turns"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["unitary_circuit = Circuit(1).U1(input_angle, 0) # Base unitary for controlled U ops"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qpe_circ_trivial = build_phase_estimation_circuit(\n"," 4, state_prep_circuit=prep_circuit, unitary_circuit=unitary_circuit\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(qpe_circ_trivial)"]},{"cell_type":"markdown","metadata":{},"source":["Lets use the noiseless `AerBackend` simulator to run our phase estimation circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["compiled_circ = backend.get_compiled_circuit(qpe_circ_trivial)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_shots = 1000\n","result = backend.run_circuit(compiled_circ, n_shots)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(result.get_counts())"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.backends.backendresult import BackendResult\n","import matplotlib.pyplot as plt"]},{"cell_type":"markdown","metadata":{},"source":["plotting function for QPE Notebook"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def plot_qpe_results(\n"," sim_result: BackendResult,\n"," n_strings: int = 4,\n"," dark_mode: bool = False,\n"," y_limit: int = 1000,\n",") -> None:\n"," \"\"\"\n"," Plots results in a barchart given a BackendResult. the number of stings displayed\n"," can be specified with the n_strings argument.\n"," \"\"\"\n"," counts_dict = sim_result.get_counts()\n"," sorted_shots = counts_dict.most_common()\n"," n_most_common_strings = sorted_shots[:n_strings]\n"," x_axis_values = [str(entry[0]) for entry in n_most_common_strings] # basis states\n"," y_axis_values = [entry[1] for entry in n_most_common_strings] # counts\n"," if dark_mode:\n"," plt.style.use(\"dark_background\")\n"," fig = plt.figure()\n"," ax = fig.add_axes((0, 0, 0.75, 0.5))\n"," color_list = [\"orange\"] * (len(x_axis_values))\n"," ax.bar(\n"," x=x_axis_values,\n"," height=y_axis_values,\n"," color=color_list,\n"," )\n"," ax.set_title(label=\"Results\")\n"," plt.ylim([0, y_limit])\n"," plt.xlabel(\"Basis State\")\n"," plt.ylabel(\"Number of Shots\")\n"," plt.show()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plot_qpe_results(result, y_limit=int(1.2 * n_shots))"]},{"cell_type":"markdown","metadata":{},"source":["As expected we see one outcome with high probability. Lets now extract our approximation of $\\theta$ from our output bitstrings.\n","\n","suppose the $j$ is an integer representation of our most commonly measured bitstring."]},{"cell_type":"markdown","metadata":{},"source":["$$\n","\\begin{equation}\n","\\theta_{estimate} = \\frac{j}{N}\n","\\end{equation}\n","$$"]},{"cell_type":"markdown","metadata":{},"source":["Here $N = 2 ^m$ where $m$ is the number of measurement qubits."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.backends.backendresult import BackendResult"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def single_phase_from_backendresult(result: BackendResult) -> float:\n"," # Extract most common measurement outcome\n"," basis_state = result.get_counts().most_common()[0][0]\n"," bitstring = \"\".join([str(bit) for bit in basis_state])\n"," integer_j = int(bitstring, 2)\n","\n"," # Calculate theta estimate\n"," return integer_j / (2 ** len(bitstring))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["theta = single_phase_from_backendresult(result)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(theta)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(input_angle / 2)"]},{"cell_type":"markdown","metadata":{},"source":["Our output is close to half our input angle $\\phi$ as expected. Lets calculate our error $E$ to three decimal places."]},{"cell_type":"markdown","metadata":{},"source":["$$\n","\\begin{equation}\n","E = |\\phi - 2 \\, \\theta_{estimate}|\n","\\end{equation}\n","$$"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["error = round(abs(input_angle - (2 * theta)), 3)\n","print(error)"]},{"cell_type":"markdown","metadata":{},"source":["## Phase Estimation with Time Evolution"]},{"cell_type":"markdown","metadata":{},"source":["In the phase estimation algorithm we repeatedly perform controlled unitary operations. In the textbook variant of QPE presented here, the number of controlled unitaries will be $2^m - 1$ where $m$ is the number of measurement qubits."]},{"cell_type":"markdown","metadata":{},"source":["In the example above we've shown a trivial instance of QPE where we know the exact phase in advance. For more realistic applications of QPE we will have some non-trivial state preparation required.\n","\n","For chemistry or condensed matter physics $U$ typically be the time evolution operator $U(t) = e^{- i H t}$ where $H$ is the problem Hamiltonian.\n","Suppose that we had the following decomposition for $H$ in terms of Pauli strings $P_j$ and complex coefficients $\\alpha_j$.\n","\n","$$\n","\\begin{equation}\n","H = \\sum_j \\alpha_j P_j\\,, \\quad \\, P_j \\in \\{I, \\,X, \\,Y, \\,Z\\}^{\\otimes n}\n","\\end{equation}\n","$$\n","\n","Here the term Pauli strings refers to tensor products of Pauli operators. These strings form an orthonormal basis for $2^n \\times 2^n$ matrices."]},{"cell_type":"markdown","metadata":{},"source":["If we have a Hamiltonian in the form above, we can then implement $U(t)$ as a sequence of Pauli gadget circuits. We can do this with the [PauliExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.PauliExpBox) construct in pytket. For more on `PauliExpBox` see the [user manual](https://tket.quantinuum.com/user-manual/manual_circuit.html#pauli-exponential-boxes)."]},{"cell_type":"markdown","metadata":{},"source":["Once we have a circuit to implement our time evolution operator $U(t)$, we can construct the controlled $U(t)$ operations using [QControlBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.QControlBox). If our base unitary is a sequence of `PauliExpBox`(es) then there is some structure we can exploit to simplify our circuit. See this [blog post](https://tket.quantinuum.com/blog/posts/controlled_gates/) on [ConjugationBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.ConjugationBox) for more."]},{"cell_type":"markdown","metadata":{},"source":["As an exercise, try to use phase estimation to calculate the ground state of diatomic hydrogen $H_2$."]},{"cell_type":"markdown","metadata":{},"source":["## Suggestions for further reading\n","\n","* Quantinuum paper on Bayesian phase estimation -> https://arxiv.org/pdf/2306.16608.pdf\n","* Blog post on `ConjugationBox` (efficient circuits for controlled gates) -> https://tket.quantinuum.com/blog/posts/controlled_gates/"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/algorithms_and_protocols/pytket-qujax-classification.ipynb b/docs/examples/algorithms_and_protocols/pytket-qujax-classification.ipynb index 6a14eb4d..3d0e6836 100644 --- a/docs/examples/algorithms_and_protocols/pytket-qujax-classification.ipynb +++ b/docs/examples/algorithms_and_protocols/pytket-qujax-classification.ipynb @@ -1 +1 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Binary classification using `pytket-qujax`"]},{"cell_type":"markdown","metadata":{},"source":["See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from jax import numpy as jnp, random, vmap, value_and_grad, jit\n","from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter\n","from pytket.extensions.qujax.qujax_convert import tk_to_qujax\n","import matplotlib.pyplot as plt"]},{"cell_type":"markdown","metadata":{},"source":["# Define the classification task\n","We'll try and learn a _donut_ binary classification function (i.e. a bivariate coordinate is labelled 1 if it is inside the donut and 0 if it is outside)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["inner_rad = 0.25\n","outer_rad = 0.75"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def classification_function(x, y):\n"," r = jnp.sqrt(x**2 + y**2)\n"," return jnp.where((r > inner_rad) * (r < outer_rad), 1, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["linsp = jnp.linspace(-1, 1, 1000)\n","Z = vmap(lambda x: vmap(lambda y: classification_function(x, y))(linsp))(linsp)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.contourf(linsp, linsp, Z, cmap=\"Purples\")"]},{"cell_type":"markdown","metadata":{},"source":["Now let's generate some data for our quantum circuit to learn from"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_data = 1000\n","x = random.uniform(random.PRNGKey(0), shape=(n_data, 2), minval=-1, maxval=1)\n","y = classification_function(x[:, 0], x[:, 1])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.scatter(x[:, 0], x[:, 1], alpha=jnp.where(y, 1, 0.2), s=10)"]},{"cell_type":"markdown","metadata":{},"source":["# Quantum circuit time\n","We'll use a variant of data re-uploading [Pérez-Salinas et al](https://doi.org/10.22331/q-2020-02-06-226) to encode the input data, alongside some variational parameters within a quantum circuit classifier"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_qubits = 3\n","depth = 5"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(n_qubits)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for layer in range(depth):\n"," for qi in range(n_qubits):\n"," c.Rz(0.0, qi)\n"," c.Ry(0.0, qi)\n"," c.Rz(0.0, qi)\n"," if layer < (depth - 1):\n"," for qi in range(layer, layer + n_qubits - 1, 2):\n"," c.CZ(qi % n_qubits, (qi + 1) % n_qubits)\n"," c.add_barrier(range(n_qubits))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can use `pytket-qujax` to generate our angles-to-statetensor function."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["angles_to_st = tk_to_qujax(c)"]},{"cell_type":"markdown","metadata":{},"source":["We'll parameterise each angle as\n","\n","$$\n","\\begin{equation}\n","\\theta_k = b_k + w_k \\, x_k\n","\\end{equation}\n","$$\n","\n","where $b_k, w_k$ are variational parameters to be learnt and $x_k = x_0$ if $k$ even, $x_k = x_1$ if $k$ odd for a single bivariate input point $(x_0, x_1)$."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_angles = 3 * n_qubits * depth\n","n_params = 2 * n_angles"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def param_and_x_to_angles(param, x_single):\n"," biases = param[:n_angles]\n"," weights = param[n_angles:]\n"," weights_times_data = jnp.where(\n"," jnp.arange(n_angles) % 2 == 0, weights * x_single[0], weights * x_single[1]\n"," )\n"," angles = biases + weights_times_data\n"," return angles"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_and_x_to_st = lambda param, x_single: angles_to_st(\n"," param_and_x_to_angles(param, x_single)\n",")"]},{"cell_type":"markdown","metadata":{},"source":["We'll measure the first qubit only (if its 1 we label _donut_, if its 0 we label _not donut_)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def param_and_x_to_probability(param, x_single):\n"," st = param_and_x_to_st(param, x_single)\n"," all_probs = jnp.square(jnp.abs(st))\n"," first_qubit_probs = jnp.sum(all_probs, axis=range(1, n_qubits))\n"," return first_qubit_probs[1]"]},{"cell_type":"markdown","metadata":{},"source":["For binary classification, the likelihood for our full data set $(x_{1:N}, y_{1:N})$ is\n","\n","$$\n","\\begin{equation}\n","p(y_{1:N} \\mid b, w, x_{1:N}) = \\prod_{i=1}^N p(y_i \\mid b, w, x_i) = \\prod_{i=1}^N (1 - q_{(b,w)}(x_i))^{I[y_i = 0]}q_{(b,w)}(x_i)^{I[y_i = 1]},\n","\\end{equation}\n","$$\n","\n","where $q_{(b, w)}(x)$ is the probability the quantum circuit classifies input $x$ as donut given variational parameter vectors $(b, w)$. This gives log-likelihood\n","\n","$$\n","\\begin{equation}\n"," \\log p(y_{1:N} \\mid b, w, x_{1:N}) = \\sum_{i=1}^N I[y_i = 0] \\log(1 - q_{(b,w)}(x_i)) + I[y_i = 1] \\log q_{(b,w)}(x_i),\n","\\end{equation}\n","$$\n","\n","which we would like to maximise.\n","\n","Unfortunately, the log-likelihood **cannot** be approximated unbiasedly using shots, that is we can approximate $q_{(b,w)}(x_i)$ unbiasedly but not $\\log(q_{(b,w)}(x_i))$.\n","Note that in qujax simulations we can use the statetensor to calculate this exactly, but it is still good to keep in mind loss functions that can also be used with shots from a quantum device.\n","\n","Instead we can minimise an expected distance between shots and data\n","\n","$$\n","\\begin{equation}\n","C(b, w, x, y) = E_{p(y' \\mid q_{(b, w)}(x))}[\\ell(y', y)] = (1 - q_{(b, w)}(x)) \\ell(0, y) + q_{(b, w)}(x)\\ell(1, y),\n","\\end{equation}\n","$$\n","\n","where $y'$ is a shot, $y$ is a data label and $\\ell$ is some distance between bitstrings - here we simply set $\\ell(0, 0) = \\ell(1, 1) = 0$ and $\\ell(0, 1) = \\ell(1, 0) = 1$ (which coincides with the Hamming distance for this binary example).\n","\n"," The full batch cost function is\n","\n","$$\n","\\begin{equation}\n"," C(b, w) = \\frac1N \\sum_{i=1}^N C(b,\\, w,\\, x_i,\\, y_i).\n","\\end{equation}\n","$$"]},{"cell_type":"markdown","metadata":{},"source":["Note that to calculate the cost function we need to evaluate the statetensor for every input point $x_i$. If the dataset becomes too large, we can easily minibatch."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def param_to_cost(param):\n"," donut_probs = vmap(param_and_x_to_probability, in_axes=(None, 0))(param, x)\n"," costs = jnp.where(y, 1 - donut_probs, donut_probs)\n"," return costs.mean()"]},{"cell_type":"markdown","metadata":{},"source":["# Ready to descend some gradients?\n","We'll just use vanilla gradient descent here"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_cost_and_grad = jit(value_and_grad(param_to_cost))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_iter = 1000\n","stepsize = 1e-1\n","param = random.uniform(random.PRNGKey(1), shape=(n_params,), minval=0, maxval=2)\n","costs = jnp.zeros(n_iter)\n","for i in range(n_iter):\n"," cost, grad = param_to_cost_and_grad(param)\n"," costs = costs.at[i].set(cost)\n"," param = param - stepsize * grad\n"," print(i, \"Cost: \", cost, end=\"\\r\")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.plot(costs)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]},{"cell_type":"markdown","metadata":{},"source":["# Visualise trained classifier"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["linsp = jnp.linspace(-1, 1, 100)\n","Z = vmap(\n"," lambda a: vmap(lambda b: param_and_x_to_probability(param, jnp.array([a, b])))(\n"," linsp\n"," )\n",")(linsp)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.contourf(linsp, linsp, Z, cmap=\"Purples\", alpha=0.8)\n","circle_linsp = jnp.linspace(0, 2 * jnp.pi, 100)\n","plt.plot(inner_rad * jnp.cos(circle_linsp), inner_rad * jnp.sin(circle_linsp), c=\"red\")\n","plt.plot(outer_rad * jnp.cos(circle_linsp), outer_rad * jnp.sin(circle_linsp), c=\"red\")"]},{"cell_type":"markdown","metadata":{},"source":["Looks good, it has clearly grasped the donut shape. Sincerest apologies if you are now hungry! 🍩"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Binary classification using `pytket-qujax`\n","\n","**Download this notebook - {nb-download}`pytket-qujax-classification.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html)."]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[],"source":["from jax import numpy as jnp, random, vmap, value_and_grad, jit\n","from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter\n","from pytket.extensions.qujax.qujax_convert import tk_to_qujax\n","import matplotlib.pyplot as plt"]},{"cell_type":"markdown","metadata":{},"source":["# Define the classification task\n","We'll try and learn a _donut_ binary classification function (i.e. a bivariate coordinate is labelled 1 if it is inside the donut and 0 if it is outside)"]},{"cell_type":"code","execution_count":2,"metadata":{},"outputs":[],"source":["inner_rad = 0.25\n","outer_rad = 0.75"]},{"cell_type":"code","execution_count":3,"metadata":{},"outputs":[],"source":["def classification_function(x, y):\n"," r = jnp.sqrt(x**2 + y**2)\n"," return jnp.where((r > inner_rad) * (r < outer_rad), 1, 0)"]},{"cell_type":"code","execution_count":4,"metadata":{},"outputs":[],"source":["linsp = jnp.linspace(-1, 1, 1000)\n","Z = vmap(lambda x: vmap(lambda y: classification_function(x, y))(linsp))(linsp)"]},{"cell_type":"code","execution_count":5,"metadata":{},"outputs":[{"data":{"text/plain":[""]},"execution_count":5,"metadata":{},"output_type":"execute_result"},{"data":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAAkcAAAGiCAYAAADtImJbAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/TGe4hAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA9EklEQVR4nO3de3hU1aH+8TcJkBAhCUiSIRjlZrlUIHJJCscWKXlIlLaiHEkoFsixoFagEESJB6FcbFA4SlEqarnYFuVixUurKCI8ntqUYCBFETiSolwnAWIykGiAZP/+8JdxhlyYJLPn+v08zzw6e9bes1Y2s+edtdbeO8QwDEMAAACQJIV6uwIAAAC+hHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADgwNRw9OGHH+qnP/2pEhISFBISotdff/2q6+zatUsDBw5UeHi4evbsqfXr19cps2rVKnXt2lURERFKSUlRfn6++ysPAACCkqnhqKKiQgMGDNCqVatcKn/06FGNHj1aI0aMUGFhoWbOnKlf/vKXevfdd+1lNm3apOzsbC1YsEB79+7VgAEDlJaWppKSErOaAQAAgkiIp248GxISoq1bt2rMmDENlnnkkUf0t7/9TZ9++ql9WWZmpsrKyrRt2zZJUkpKioYMGaJnn31WklRTU6PExERNnz5dc+fONbUNAAAg8LXydgUc5eXlKTU11WlZWlqaZs6cKUm6ePGiCgoKlJOTY389NDRUqampysvLa3C7VVVVqqqqsj+vqalRaWmprr32WoWEhLi3EQAAwBSGYej8+fNKSEhQaKh5g18+FY6sVqvi4+OdlsXHx8tms+nrr7/WV199perq6nrLHDp0qMHt5ubmauHChabUGQAAeNbx48d13XXXmbZ9nwpHZsnJyVF2drb9eXl5ua6//nr936EitW/f3os1AwAArjp//ry+17uH6d/dPhWOLBaLiouLnZYVFxcrKipKbdu2VVhYmMLCwuotY7FYGtxueHi4wsPD6yxv3769oqKi3FN5AADgEWZPifGp6xwNHTpUO3bscFq2fft2DR06VJLUpk0bDRo0yKlMTU2NduzYYS8DAADQEqaGowsXLqiwsFCFhYWSvj1Vv7CwUMeOHZP07XDXxIkT7eXvv/9+/fvf/9bDDz+sQ4cO6fe/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wNuaWr8vvvhCISEh9T62bNliL1ff6xs3bvREk1zSnP1y66231mnT/fff71Tm2LFjGj16tCIjIxUXF6c5c+bo8uXLZjalSZra7tLSUk2fPl29evVS27Ztdf3112vGjBkqLy93KueL+3vVqlXq2rWrIiIilJKSovz8/EbLb9myRb1791ZERIT69eunt99+2+l1Vz7vvqAp7X7xxRf1wx/+UB06dFCHDh2Umppap/zkyZPr7Nv09HSzm9FkTWn3+vXr67QpIiLCqUwg7u/6jmEhISEaPXq0vYyv7+8PP/xQP/3pT5WQkKCQkBC9/vrrV11n165dGjhwoMLDw9WzZ0+tX7++TpmmHi/qZQSg+fPnG0899ZSRnZ1tREdHu7TO0qVLjejoaOP11183/vWvfxk/+9nPjG7duhlff/21vUx6eroxYMAA45///Kfxv//7v0bPnj2N8ePHm9SKpmtq/S5fvmycPn3a6bFw4UKjXbt2xvnz5+3lJBnr1q1zKuf4d/G25uyX4cOHG1OmTHFqU3l5uf31y5cvGzfddJORmppq7Nu3z3j77beNTp06GTk5OWY3x2VNbfcnn3xi3HXXXcabb75pHDlyxNixY4dx4403GmPHjnUq52v7e+PGjUabNm2MtWvXGgcOHDCmTJlixMTEGMXFxfWW/+ijj4ywsDDjySefND777DNj3rx5RuvWrY1PPvnEXsaVz7u3NbXdP//5z41Vq1YZ+/btMw4ePGhMnjzZiI6ONk6cOGEvM2nSJCM9Pd1p35aWlnqqSS5parvXrVtnREVFObXJarU6lQnE/X3u3DmnNn/66adGWFiYsW7dOnsZX9/fb7/9tvHf//3fxmuvvWZIMrZu3dpo+X//+99GZGSkkZ2dbXz22WfGM888Y4SFhRnbtm2zl2nq37EhARmOaq1bt86lcFRTU2NYLBZj2bJl9mVlZWVGeHi48corrxiGYRifffaZIcnYs2ePvcw777xjhISEGCdPnnR73ZvKXfVLSkoy/uu//stpmSv/aL2lue0ePny48etf/7rB199++20jNDTU6SD73HPPGVFRUUZVVZVb6t4S7trfmzdvNtq0aWNcunTJvszX9ndycrLx4IMP2p9XV1cbCQkJRm5ubr3lx40bZ4wePdppWUpKinHfffcZhuHa590XNLXdV7p8+bLRvn1746WXXrIvmzRpknHH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","text/plain":["
"]},"metadata":{},"output_type":"display_data"}],"source":["plt.contourf(linsp, linsp, Z, cmap=\"Purples\")"]},{"cell_type":"markdown","metadata":{},"source":["Now let's generate some data for our quantum circuit to learn from"]},{"cell_type":"code","execution_count":6,"metadata":{},"outputs":[],"source":["n_data = 1000\n","x = random.uniform(random.PRNGKey(0), shape=(n_data, 2), minval=-1, maxval=1)\n","y = classification_function(x[:, 0], x[:, 1])"]},{"cell_type":"code","execution_count":7,"metadata":{},"outputs":[{"data":{"text/plain":[""]},"execution_count":7,"metadata":{},"output_type":"execute_result"},{"data":{"image/png":"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mzZBVVXs378fzzzzzIL7f+c738GuXbugqir27NmDJ554ouH1Zu7J1WYpbfz617+O3//930cmk0Emk8GBAwdm7X/XXXfNula33npru5sxL0tp32OPPTbr2FVVbdhnvV/DufoUjuNw22231fdZS9fwf/7nf/BHf/RHGBwcBMdx+MEPfrDoe5566im86lWvgqIo2LZtGx577LFZ+yz13l4yhNE0999/P/nSl75EPvShD5FUKtXUex544AGSSqXID37wA/K73/2O/PEf/zHZvHkzsW27vs+tt95Krr32WvLrX/+a/O///i/Ztm0bedvb3tamVszPUo8jCAIyPj7e8O9Tn/oUicfjxDTN+n4AyKOPPtqw36XtX02Wc65vuukmcvfddzccf6lUqr8eBAHZvXs3OXDgADl06BB54oknSE9PD7nvvvva3Zw5WWobn3/+efLmN7+Z/OhHPyInTpwgP//5z8n27dvJW97ylob9rsR1fPzxx4ksy+SRRx4hL774Irn77rtJOp0muVxuzv1/9atfEUEQyBe+8AVy5MgR8vGPf5xIkkSef/75+j7N3JOryVLb+Pa3v5189atfJYcOHSJHjx4ld911F0mlUuTChQv1fe68805y6623NlyrfD6/Wk1qYKnte/TRR0kymWw49omJiYZ91vs1nJmZaWjfCy+8QARBII8++mh9n7V0DZ944gnyD//wD+R73/seAUC+//3vL7j/qVOniK7r5EMf+hA5cuQI+cpXvkIEQSBPPvlkfZ+lnrPlwAKcZfDoo482FeBEUUT6+/vJF7/4xfq2YrFIFEUh//Ef/0EIIeTIkSMEAPnNb35T3+fHP/4x4TiOjI6OtvzY56NVx3HdddeRP//zP2/Y1swNsRost4033XQT+Zu/+Zt5X3/iiScIz/MNnfC//uu/kmQySVzXbcmxN0urruO3v/1tIssy8X2/vu1KXMfrr7+evO9976v/HYYhGRwcJJ///Ofn3P/P/uzPyG233dawbf/+/eQv/uIvCCHN3ZOrzVLbeDlBEJBEIkG+8Y1v1Lfdeeed5Pbbb2/1oS6LpbZvsf61E6/hP/3TP5FEIkHK5XJ921q6hpfSTD/w0Y9+lFxzzTUN29761reSW265pf73Ss9ZM7AlqjZy+vRpTExM4MCBA/VtqVQK+/fvx8GDBwEABw8eRDqdxmte85r6PgcOHADP83j66adX7VhbcRzPPvssDh8+jPe85z2zXnvf+96Hnp4eXH/99XjkkUeaKnXfalbSxm9+85vo6enB7t27cd9998GyrIbP3bNnD/r6+urbbrnlFhiGgRdffLH1DVmAVv2eSqUSkskkRLGxXN1qXkfP8/Dss8823D88z+PAgQP1++dyDh482LA/QK9Fbf9m7snVZDltvBzLsuD7Prq6uhq2P/XUU8hms9i5cyfe+973YmZmpqXH3gzLbV+5XMbGjRsxMjKC22+/veE+6sRr+PDDD+OOO+5ALBZr2L4WruFyWOw+bMU5a4ZXZLHN1WJiYgIAGh58tb9rr01MTCCbzTa8Looiurq66vusBq04jocffhhXXXUVbrzxxobtn/70p/EHf/AH0HUdP/3pT/FXf/VXKJfL+MAHPtCy42+G5bbx7W9/OzZu3IjBwUE899xz+NjHPoZjx47he9/7Xv1z57rGtddWk1Zcx+npaXzmM5/BPffc07B9ta/j9PQ0wjCc89y+9NJLc75nvmtx6f1W2zbfPqvJctp4OR/72McwODjY8LC49dZb8eY3vxmbN2/GyZMn8fd///d44xvfiIMHD0IQhJa2YSGW076dO3fikUcewd69e1EqlfDQQw/hxhtvxIsvvojh4eGOu4bPPPMMXnjhBTz88MMN29fKNVwO892HhmHAtm0UCoUV/+6b4RUf4Nx777148MEHF9zn6NGj2LVr1yodUWtptn0rxbZtfOtb38InPvGJWa9dum3fvn2oVCr44he/2LIHY7vbeOmDfs+ePRgYGMDNN9+MkydPYuvWrcv+3KWwWtfRMAzcdtttuPrqq/GP//iPDa+1+zoyls4DDzyAxx9/HE899VSDEPeOO+6o/3/Pnj3Yu3cvtm7diqeeego333zzlTjUprnhhhtwww031P++8cYbcdVVV+FrX/saPvOZz1zBI2sPDz/8MPbs2YPrr7++Yft6voZrhVd8gPPhD38Yd91114L7bNmyZVmf3d/fDwDI5XIYGBiob8/lcrjuuuvq+0xOTja8LwgC5PP5+vtXQrPtW+lxfPe734VlWXjXu9616L779+/HZz7zGbiu25I6JavVxhr79+8HAJw4cQJbt25Ff3//LPV/LpcDgJZcQ2B12miaJm699VYkEgl8//vfhyRJC+7f6ut4OT09PRAEoX4ua+RyuXnb0t/fv+D+zdyTq8ly2ljjoYcewgMPPICf/exn2Lt374L7btmyBT09PThx4sSqPhxX0r4akiRh3759OHHiBIDOuoaVSgWPP/44Pv3pTy/6PVfqGi6H+e7DZDIJTdMgCMKKfxdN0TI1zyuIpYqMH3roofq2Uqk0p8j4t7/9bX2fn/zkJ1dMZLzc47jppptmZd3Mx2c/+1mSyWSWfazLpVXn+pe//CUBQH73u98RQi6KjC9V/3/ta18jyWSSOI7TugY0wXLbWCqVyO/93u+Rm266iVQqlaa+azWu4/XXX0/e//731/8Ow5AMDQ0tKDL+wz/8w4ZtN9xwwyyR8UL35Gqz1DYSQsiDDz5IkskkOXjwYFPfcf78ecJxHPnhD3+44uNdKstp36UEQUB27txJ/vZv/5YQ0jnXkBD6LFEUhUxPTy/6HVfyGl4KmhQZ7969u2Hb2972tlki45X8Lpo61pZ90iuAs2fPkkOHDtVToQ8dOkQOHTrUkBK9c+dO8r3vfa/+9wMPPEDS6TT54Q9/SJ577jly++23z5kmvm/fPvL000+TX/7yl2T79u1XLE18oeO4cOEC2blzJ3n66acb3nf8+HHCcRz58Y9/POszf/SjH5Gvf/3r5PnnnyfHjx8n//Iv/0J0XSf3339/29szF0tt44kTJ8inP/1p8tvf/pacPn2a/PCHPyRbtmwhr3vd6+rvqaWJv+ENbyCHDx8mTz75JOnt7b2iaeJLaWOpVCL79+8ne/bsISdOnGhISw2CgBBy5a7j448/ThRFIY899hg5cuQIueeee0g6na5nrL3zne8k9957b33/X/3qV0QURfLQQw+Ro0ePkk9+8pNzpokvdk+uJktt4wMPPEBkWSbf/e53G65VrR8yTZP83d/9HTl48CA5ffo0+dnPfkZe9apXke3bt696wL2c9n3qU58iP/nJT8jJkyfJs88+S+644w6iqip58cUX6/us92tY47WvfS1561vfOmv7WruGpmnWn3cAyJe+9CVy6NAhcvbsWUIIIffeey955zvfWd+/lib+kY98hBw9epR89atfnTNNfKFz1gpYgLME7rzzTgJg1r9f/OIX9X1Q9QqpEUUR+cQnPkH6+vqIoijk5ptvJseOHWv43JmZGfK2t72NxONxkkwmybvf/e6GoGm1WOw4Tp8+Pau9hBBy3333kZGRERKG4azP/PGPf0yuu+46Eo/HSSwWI9deey35t3/7tzn3XQ2W2sZz586R173udaSrq4soikK2bdtGPvKRjzT44BBCyJkzZ8gb3/hGomka6enpIR/+8IcbUqxXk6W28Re/+MWcv2sA5PTp04SQK3sdv/KVr5ANGzYQWZbJ9ddfT37961/XX7vpppvInXfe2bD/t7/9bbJjxw4iyzK55ppryH/+5382vN7MPbnaLKWNGzdunPNaffKTnySEEGJZFnnDG95Aent7iSRJZOPGjeTuu+9u6YNjqSylfR/84Afr+/b19ZE3velN5P/+7/8aPm+9X0NCCHnppZcIAPLTn/501mettWs4Xx9Ra9Odd95Jbrrpplnvue6664gsy2TLli0Nz8UaC52zVsARcgXydRkMBoPBYDDaCPPBYTAYDAaD0XGwAIfBYDAYDEbHwQIcBoPBYDAYHQcLcBgMBoPBYHQcLMBhMBgMBoPRcbAAh8FgMBgMRsfBAhwGg8FgMBgdBwtwGAwGg8FgdBwswGEwGAwGg9FxsACHwWAwGAxGx8ECHAaDwWAwGB0HC3AYDAaDwWB0HP8f4XHWfbcCd6UAAAAASUVORK5CYII=","text/plain":["
"]},"metadata":{},"output_type":"display_data"}],"source":["plt.scatter(x[:, 0], x[:, 1], alpha=jnp.where(y, 1, 0.2), s=10)"]},{"cell_type":"markdown","metadata":{},"source":["# Quantum circuit time\n","We'll use a variant of data re-uploading [Pérez-Salinas et al](https://doi.org/10.22331/q-2020-02-06-226) to encode the input data, alongside some variational parameters within a quantum circuit classifier"]},{"cell_type":"code","execution_count":8,"metadata":{},"outputs":[],"source":["n_qubits = 3\n","depth = 5"]},{"cell_type":"code","execution_count":9,"metadata":{},"outputs":[],"source":["c = Circuit(n_qubits)"]},{"cell_type":"code","execution_count":10,"metadata":{},"outputs":[],"source":["for layer in range(depth):\n"," for qi in range(n_qubits):\n"," c.Rz(0.0, qi)\n"," c.Ry(0.0, qi)\n"," c.Rz(0.0, qi)\n"," if layer < (depth - 1):\n"," for qi in range(layer, layer + n_qubits - 1, 2):\n"," c.CZ(qi % n_qubits, (qi + 1) % n_qubits)\n"," c.add_barrier(range(n_qubits))"]},{"cell_type":"code","execution_count":11,"metadata":{},"outputs":[{"data":{"text/html":["\n","\n","\n","\n","\n","\n","
\n"," \n","
\n","\n"],"text/plain":[""]},"metadata":{},"output_type":"display_data"}],"source":["render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can use `pytket-qujax` to generate our angles-to-statetensor function."]},{"cell_type":"code","execution_count":12,"metadata":{},"outputs":[],"source":["angles_to_st = tk_to_qujax(c)"]},{"cell_type":"markdown","metadata":{},"source":["We'll parameterise each angle as\n","\n","$$\n","\\begin{equation}\n","\\theta_k = b_k + w_k \\, x_k\n","\\end{equation}\n","$$\n","\n","where $b_k, w_k$ are variational parameters to be learnt and $x_k = x_0$ if $k$ even, $x_k = x_1$ if $k$ odd for a single bivariate input point $(x_0, x_1)$."]},{"cell_type":"code","execution_count":13,"metadata":{},"outputs":[],"source":["n_angles = 3 * n_qubits * depth\n","n_params = 2 * n_angles"]},{"cell_type":"code","execution_count":14,"metadata":{},"outputs":[],"source":["def param_and_x_to_angles(param, x_single):\n"," biases = param[:n_angles]\n"," weights = param[n_angles:]\n"," weights_times_data = jnp.where(\n"," jnp.arange(n_angles) % 2 == 0, weights * x_single[0], weights * x_single[1]\n"," )\n"," angles = biases + weights_times_data\n"," return angles"]},{"cell_type":"code","execution_count":15,"metadata":{},"outputs":[],"source":["param_and_x_to_st = lambda param, x_single: angles_to_st(\n"," param_and_x_to_angles(param, x_single)\n",")"]},{"cell_type":"markdown","metadata":{},"source":["We'll measure the first qubit only (if its 1 we label _donut_, if its 0 we label _not donut_)"]},{"cell_type":"code","execution_count":16,"metadata":{},"outputs":[],"source":["def param_and_x_to_probability(param, x_single):\n"," st = param_and_x_to_st(param, x_single)\n"," all_probs = jnp.square(jnp.abs(st))\n"," first_qubit_probs = jnp.sum(all_probs, axis=range(1, n_qubits))\n"," return first_qubit_probs[1]"]},{"cell_type":"markdown","metadata":{},"source":["For binary classification, the likelihood for our full data set $(x_{1:N}, y_{1:N})$ is\n","\n","$$\n","\\begin{equation}\n","p(y_{1:N} \\mid b, w, x_{1:N}) = \\prod_{i=1}^N p(y_i \\mid b, w, x_i) = \\prod_{i=1}^N (1 - q_{(b,w)}(x_i))^{I[y_i = 0]}q_{(b,w)}(x_i)^{I[y_i = 1]},\n","\\end{equation}\n","$$\n","\n","where $q_{(b, w)}(x)$ is the probability the quantum circuit classifies input $x$ as donut given variational parameter vectors $(b, w)$. This gives log-likelihood\n","\n","$$\n","\\begin{equation}\n"," \\log p(y_{1:N} \\mid b, w, x_{1:N}) = \\sum_{i=1}^N I[y_i = 0] \\log(1 - q_{(b,w)}(x_i)) + I[y_i = 1] \\log q_{(b,w)}(x_i),\n","\\end{equation}\n","$$\n","\n","which we would like to maximise.\n","\n","Unfortunately, the log-likelihood **cannot** be approximated unbiasedly using shots, that is we can approximate $q_{(b,w)}(x_i)$ unbiasedly but not $\\log(q_{(b,w)}(x_i))$.\n","Note that in qujax simulations we can use the statetensor to calculate this exactly, but it is still good to keep in mind loss functions that can also be used with shots from a quantum device.\n","\n","Instead we can minimise an expected distance between shots and data\n","\n","$$\n","\\begin{equation}\n","C(b, w, x, y) = E_{p(y' \\mid q_{(b, w)}(x))}[\\ell(y', y)] = (1 - q_{(b, w)}(x)) \\ell(0, y) + q_{(b, w)}(x)\\ell(1, y),\n","\\end{equation}\n","$$\n","\n","where $y'$ is a shot, $y$ is a data label and $\\ell$ is some distance between bitstrings - here we simply set $\\ell(0, 0) = \\ell(1, 1) = 0$ and $\\ell(0, 1) = \\ell(1, 0) = 1$ (which coincides with the Hamming distance for this binary example).\n","\n"," The full batch cost function is\n","\n","$$\n","\\begin{equation}\n"," C(b, w) = \\frac1N \\sum_{i=1}^N C(b,\\, w,\\, x_i,\\, y_i).\n","\\end{equation}\n","$$"]},{"cell_type":"markdown","metadata":{},"source":["Note that to calculate the cost function we need to evaluate the statetensor for every input point $x_i$. If the dataset becomes too large, we can easily minibatch."]},{"cell_type":"code","execution_count":17,"metadata":{},"outputs":[],"source":["def param_to_cost(param):\n"," donut_probs = vmap(param_and_x_to_probability, in_axes=(None, 0))(param, x)\n"," costs = jnp.where(y, 1 - donut_probs, donut_probs)\n"," return costs.mean()"]},{"cell_type":"markdown","metadata":{},"source":["# Ready to descend some gradients?\n","We'll just use vanilla gradient descent here"]},{"cell_type":"code","execution_count":18,"metadata":{},"outputs":[],"source":["param_to_cost_and_grad = jit(value_and_grad(param_to_cost))"]},{"cell_type":"code","execution_count":19,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["999 Cost: 0.25950647\r"]}],"source":["n_iter = 1000\n","stepsize = 1e-1\n","param = random.uniform(random.PRNGKey(1), shape=(n_params,), minval=0, maxval=2)\n","costs = jnp.zeros(n_iter)\n","for i in range(n_iter):\n"," cost, grad = param_to_cost_and_grad(param)\n"," costs = costs.at[i].set(cost)\n"," param = param - stepsize * grad\n"," print(i, \"Cost: \", cost, end=\"\\r\")"]},{"cell_type":"code","execution_count":20,"metadata":{},"outputs":[{"data":{"text/plain":["Text(0, 0.5, 'Cost')"]},"execution_count":20,"metadata":{},"output_type":"execute_result"},{"data":{"image/png":"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z6a/3iKYOBwOPfLII4xC+QF97R/0s3/Qz/5DX/uH2f3MyA8AAAgqjPwAAICgQvgBAABBhfADAACCCuEHAAAEFcKPH82bN0/p6ekKCwtTZmamVq1aZXZJ7Upubq5GjBih6OhoJSYmavz48dq6datPm+rqat1xxx3q1KmToqKidO211x73CpX8/HxdfvnlioiIUGJion7/+9+rvr7en5fSrsyZM0cWi0UzZszwbqOfW8e+fft0ww03qFOnTgoPD9fAgQO1Zs0a737DMDRz5kylpKQoPDxcWVlZ2rZtm885Dh8+rEmTJikmJkaxsbG66aabVF5e7u9LCVhut1sPP/ywunfvrvDwcPXs2VOPPfaYz7uf6Ofm+cc//qErr7xSqampslgsev/99332t1a/btq0SRdccIHCwsKUlpamJ598suXFG/CLRYsWGXa73XjppZeMr7/+2pg+fboRGxtrFBYWml1au5GdnW389a9/NbZs2WJs2LDBuOyyy4yuXbsa5eXl3ja33nqrkZaWZuTl5Rlr1qwxzj33XGP06NHe/fX19caAAQOMrKwsY/369cZHH31kJCQkGA888IAZlxTwVq1aZaSnpxuDBg0y7r77bu92+rnlDh8+bHTr1s248cYbjZUrVxo//PCD8cknnxjbt2/3tpkzZ47hdDqN999/39i4caPxi1/8wujevbtRVVXlbTN27Fhj8ODBxldffWV8+eWXRq9evYzrr7/ejEsKSLNnzzY6depkfPjhh8bOnTuNt99+24iKijKefvppbxv6uXk++ugj46GHHjLeffddQ5Lx3nvv+exvjX4tLS01kpKSjEmTJhlbtmwx3nzzTSM8PNz485//3KLaCT9+MnLkSOOOO+7w/ux2u43U1FQjNzfXxKrat4MHDxqSjGXLlhmGYRglJSVGaGio8fbbb3vbfPvtt4YkY8WKFYZhNPzHarVajYKCAm+bF154wYiJiTFqamr8ewEBrqyszOjdu7fx6aefGj/72c+84Yd+bh333Xefcf755590v8fjMZKTk40//elP3m0lJSWGw+Ew3nzzTcMwDOObb74xJBmrV6/2tvn4448Ni8Vi7Nu3r+2Kb0cuv/xy49e//rXPtmuuucaYNGmSYRj0c2v5cfhprX59/vnnjbi4OJ9/N+677z6jT58+LaqXaS8/qK2t1dq1a5WVleXdZrValZWVpRUrVphYWftWWloqSYqPj5ckrV27VnV1dT793LdvX3Xt2tXbzytWrNDAgQO9r1CRpOzsbLlcLn399dd+rD7w3XHHHbr88st9+lOin1vL//zP/ygjI0P/9m//psTERA0dOlQLFizw7t+5c6cKCgp8+tnpdCozM9Onn2NjY31e8pyVlSWr1aqVK1f672IC2OjRo5WXl6fvv/9ekrRx40YtX75c48aNk0Q/t5XW6tcVK1bowgsvlN1u97bJzs7W1q1bdeTIkWbXZ+q7vYJFUVGR3G63zx8CSUpKStJ3331nUlXtm8fj0YwZM3TeeedpwIABkqSCggLZ7XbFxsb6tE1KSvK+BLegoOCE/zs07UODRYsWad26dVq9evVx++jn1vHDDz/ohRdeUE5Ojh588EGtXr1av/nNb2S32zV16lRvP52oH4/t58TERJ/9ISEhio+Pp58b3X///XK5XOrbt69sNpvcbrdmz56tSZMmSRL93EZaq18LCgrUvXv3487RtC8uLq5Z9RF+0C7dcccd2rJli5YvX252KR3Onj17dPfdd+vTTz9VWFiY2eV0WB6PRxkZGfrjH/8oSRo6dKi2bNmi+fPna+rUqSZX13G89dZbev311/XGG2/onHPO0YYNGzRjxgylpqbSz0GMaS8/SEhIkM1mO+5umMLCQiUnJ5tUVft155136sMPP9QXX3yhs846y7s9OTlZtbW1Kikp8Wl/bD8nJyef8H+Hpn1omNY6ePCghg0bppCQEIWEhGjZsmV65plnFBISoqSkJPq5FaSkpKh///4+2/r166f8/HxJR/vpVP9uJCcn6+DBgz776+vrdfjwYfq50e9//3vdf//9mjhxogYOHKjJkyfrt7/9rXJzcyXRz22ltfq1rf4tIfz4gd1u1/Dhw5WXl+fd5vF4lJeXp1GjRplYWftiGIbuvPNOvffee/r888+PGwodPny4QkNDffp569atys/P9/bzqFGjtHnzZp//4D799FPFxMQc94coWI0ZM0abN2/Whg0bvJ+MjAxNmjTJ+z393HLnnXfecY9q+P7779WtWzdJUvfu3ZWcnOzTzy6XSytXrvTp55KSEq1du9bb5vPPP5fH41FmZqYfriLwVVZWymr1/VNns9nk8Xgk0c9tpbX6ddSoUfrHP/6huro6b5tPP/1Uffr0afaUlyRudfeXRYsWGQ6Hw3j5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","text/plain":["
"]},"metadata":{},"output_type":"display_data"}],"source":["plt.plot(costs)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]},{"cell_type":"markdown","metadata":{},"source":["# Visualise trained classifier"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["linsp = jnp.linspace(-1, 1, 100)\n","Z = vmap(\n"," lambda a: vmap(lambda b: param_and_x_to_probability(param, jnp.array([a, b])))(\n"," linsp\n"," )\n",")(linsp)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.contourf(linsp, linsp, Z, cmap=\"Purples\", alpha=0.8)\n","circle_linsp = jnp.linspace(0, 2 * jnp.pi, 100)\n","plt.plot(inner_rad * jnp.cos(circle_linsp), inner_rad * jnp.sin(circle_linsp), c=\"red\")\n","plt.plot(outer_rad * jnp.cos(circle_linsp), outer_rad * jnp.sin(circle_linsp), c=\"red\")"]},{"cell_type":"markdown","metadata":{},"source":["Looks good, it has clearly grasped the donut shape. Sincerest apologies if you are now hungry! 🍩"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.1"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb b/docs/examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb index 98c890aa..97720494 100644 --- a/docs/examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb +++ b/docs/examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb @@ -1 +1 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# VQE example with `pytket-qujax`"]},{"cell_type":"markdown","metadata":{},"source":["See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from jax import numpy as jnp, random, value_and_grad, jit\n","from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter\n","import matplotlib.pyplot as plt"]},{"cell_type":"markdown","metadata":{},"source":["## Let's start with a TKET circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import qujax\n","from pytket.extensions.qujax.qujax_convert import tk_to_qujax"]},{"cell_type":"markdown","metadata":{},"source":["We place barriers to stop tket automatically rearranging gates and we also store the number of circuit parameters as we'll need this later."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def get_circuit(n_qubits, depth):\n"," n_params = 2 * n_qubits * (depth + 1)\n"," param = jnp.zeros((n_params,))\n"," circuit = Circuit(n_qubits)\n"," k = 0\n"," for i in range(n_qubits):\n"," circuit.H(i)\n"," for i in range(n_qubits):\n"," circuit.Rx(param[k], i)\n"," k += 1\n"," for i in range(n_qubits):\n"," circuit.Ry(param[k], i)\n"," k += 1\n"," for _ in range(depth):\n"," for i in range(0, n_qubits - 1):\n"," circuit.CZ(i, i + 1)\n"," circuit.add_barrier(range(0, n_qubits))\n"," for i in range(n_qubits):\n"," circuit.Rx(param[k], i)\n"," k += 1\n"," for i in range(n_qubits):\n"," circuit.Ry(param[k], i)\n"," k += 1\n"," return circuit, n_params"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_qubits = 4\n","depth = 2\n","circuit, n_params = get_circuit(n_qubits, depth)\n","render_circuit_jupyter(circuit)"]},{"cell_type":"markdown","metadata":{},"source":["## Now let's invoke qujax\n","The `pytket.extensions.qujax.tk_to_qujax` function will generate a parameters -> statetensor function for us."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_st = tk_to_qujax(circuit)"]},{"cell_type":"markdown","metadata":{},"source":["Let's try it out on some random parameters values. Be aware that's JAX's random number generator requires a `jax.random.PRNGkey` every time it's called - more info on that [here](https://jax.readthedocs.io/en/latest/jax.random.html).\n","Be aware that we still have convention where parameters are specified as multiples of $\\pi$ - that is in [0,2]."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["params = random.uniform(random.PRNGKey(0), shape=(n_params,), minval=0.0, maxval=2.0)\n","statetensor = param_to_st(params)\n","print(statetensor)\n","print(statetensor.shape)"]},{"cell_type":"markdown","metadata":{},"source":["Note that this function also has an optional second argument where an initiating `statetensor_in` can be provided. If it is not provided it will default to the all 0s state (as we use here)."]},{"cell_type":"markdown","metadata":{},"source":["We can obtain statevector by simply calling `.flatten()`"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["statevector = statetensor.flatten()\n","statevector.shape"]},{"cell_type":"markdown","metadata":{},"source":["And sampling probabilities by squaring the absolute value of the statevector"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["sample_probs = jnp.square(jnp.abs(statevector))\n","plt.bar(jnp.arange(statevector.size), sample_probs)"]},{"cell_type":"markdown","metadata":{},"source":["## Cost function"]},{"cell_type":"markdown","metadata":{},"source":["Now we have our `param_to_st` function we are free to define a cost function that acts on bitstrings (e.g. maxcut) or integers by directly wrapping a function around `param_to_st`. However, cost functions defined via quantum Hamiltonians are a bit more involved.\n","Fortunately, we can encode an Hamiltonian in JAX via the `qujax.get_statetensor_to_expectation_func` function which generates a statetensor -> expected value function for us.\n","It takes three arguments as input\n","- `gate_seq_seq`: A list of string (or array) lists encoding the gates in each term of the Hamiltonian. I.e. `[['X','X'], ['Y','Y'], ['Z','Z']]` corresponds to $H = aX_iX_j + bY_kY_l + cZ_mZ_n$ with qubit indices $i,j,k,l,m,n$ specified in the second argument and coefficients $a,b,c$ specified in the third argument\n","- `qubit_inds_seq`: A list of integer lists encoding which qubit indices to apply the aforementioned gates. I.e. `[[0, 1],[0,1],[0,1]]`. Must have the same structure as `gate_seq_seq` above.\n","- `coefficients`: A list of floats encoding any coefficients in the Hamiltonian. I.e. `[2.3, 0.8, 1.2]` corresponds to $a=2.3,b=0.8,c=1.2$ above. Must have the same length as the two above arguments."]},{"cell_type":"markdown","metadata":{},"source":["More specifically let's consider the problem of finding the ground state of the quantum Heisenberg Hamiltonian"]},{"cell_type":"markdown","metadata":{},"source":["$$\n","\\begin{equation}\n","H = \\sum_{i=1}^{n_\\text{qubits}-1} X_i X_{i+1} + Y_i Y_{i+1} + Z_i Z_{i+1}.\n","\\end{equation}\n","$$\n","\n","As described, we define the Hamiltonian via its gate strings, qubit indices and coefficients."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["hamiltonian_gates = [[\"X\", \"X\"], [\"Y\", \"Y\"], [\"Z\", \"Z\"]] * (n_qubits - 1)\n","hamiltonian_qubit_inds = [\n"," [int(i), int(i) + 1] for i in jnp.repeat(jnp.arange(n_qubits), 3)\n","]\n","coefficients = [1.0] * len(hamiltonian_qubit_inds)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"Gates:\\t\", hamiltonian_gates)\n","print(\"Qubits:\\t\", hamiltonian_qubit_inds)\n","print(\"Coefficients:\\t\", coefficients)"]},{"cell_type":"markdown","metadata":{},"source":["Now let's get the Hamiltonian as a pure JAX function"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["st_to_expectation = qujax.get_statetensor_to_expectation_func(\n"," hamiltonian_gates, hamiltonian_qubit_inds, coefficients\n",")"]},{"cell_type":"markdown","metadata":{},"source":["Let's check it works on the statetensor we've already generated."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["expected_val = st_to_expectation(statetensor)\n","expected_val"]},{"cell_type":"markdown","metadata":{},"source":["Now let's wrap the `param_to_st` and `st_to_expectation` together to give us an all in one `param_to_expectation` cost function."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_expectation = lambda param: st_to_expectation(param_to_st(param))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_expectation(params)"]},{"cell_type":"markdown","metadata":{},"source":["Sanity check that a different, randomly generated set of parameters gives us a new expected value."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["new_params = random.uniform(\n"," random.PRNGKey(1), shape=(n_params,), minval=0.0, maxval=2.0\n",")\n","param_to_expectation(new_params)"]},{"cell_type":"markdown","metadata":{},"source":["## Exact gradients within a VQE algorithm\n","The `param_to_expectation` function we created is a pure JAX function and outputs a scalar. This means we can pass it to `jax.grad` (or even better `jax.value_and_grad`)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_and_grad = value_and_grad(param_to_expectation)"]},{"cell_type":"markdown","metadata":{},"source":["The `cost_and_grad` function returns a tuple with the exact cost value and exact gradient evaluated at the parameters."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_and_grad(params)"]},{"cell_type":"markdown","metadata":{},"source":["## Now we have all the tools we need to design our VQE!\n","We'll just use vanilla gradient descent with a constant stepsize"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def vqe(init_param, n_steps, stepsize):\n"," params = jnp.zeros((n_steps, n_params))\n"," params = params.at[0].set(init_param)\n"," cost_vals = jnp.zeros(n_steps)\n"," cost_vals = cost_vals.at[0].set(param_to_expectation(init_param))\n"," for step in range(1, n_steps):\n"," cost_val, cost_grad = cost_and_grad(params[step - 1])\n"," cost_vals = cost_vals.at[step].set(cost_val)\n"," new_param = params[step - 1] - stepsize * cost_grad\n"," params = params.at[step].set(new_param)\n"," print(\"Iteration:\", step, \"\\tCost:\", cost_val, end=\"\\r\")\n"," print(\"\\n\")\n"," return params, cost_vals"]},{"cell_type":"markdown","metadata":{},"source":["Ok enough talking, let's run (and whilst we're at it we'll time it too)"]},{"cell_type":"markdown","metadata":{},"source":["%time"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["vqe_params, vqe_cost_vals = vqe(params, n_steps=250, stepsize=0.01)"]},{"cell_type":"markdown","metadata":{},"source":["Let's plot the results..."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.plot(vqe_cost_vals)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]},{"cell_type":"markdown","metadata":{},"source":["Pretty good!"]},{"cell_type":"markdown","metadata":{},"source":["## `jax.jit` speedup\n","One last thing... We can significantly speed up the VQE above via the `jax.jit`. In our current implementation, the expensive `cost_and_grad` function is compiled to [XLA](https://www.tensorflow.org/xla) and then executed at each call. By invoking `jax.jit` we ensure that the function is compiled only once (on the first call) and then simply executed at each future call - this is much faster!"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_and_grad = jit(cost_and_grad)"]},{"cell_type":"markdown","metadata":{},"source":["We'll demonstrate this using the second set of initial parameters we randomly generated (to be sure of no caching)."]},{"cell_type":"markdown","metadata":{},"source":["%time"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["new_vqe_params, new_vqe_cost_vals = vqe(new_params, n_steps=250, stepsize=0.01)"]},{"cell_type":"markdown","metadata":{},"source":["That's some speedup!\n","But let's also plot the training to be sure it converged correctly"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.plot(new_vqe_cost_vals)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# VQE example with `pytket-qujax`\n","\n","**Download this notebook - {nb-download}`pytket-qujax_heisenberg_vqe.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from jax import numpy as jnp, random, value_and_grad, jit\n","from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter\n","import matplotlib.pyplot as plt"]},{"cell_type":"markdown","metadata":{},"source":["## Let's start with a TKET circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import qujax\n","from pytket.extensions.qujax.qujax_convert import tk_to_qujax"]},{"cell_type":"markdown","metadata":{},"source":["We place barriers to stop tket automatically rearranging gates and we also store the number of circuit parameters as we'll need this later."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def get_circuit(n_qubits, depth):\n"," n_params = 2 * n_qubits * (depth + 1)\n"," param = jnp.zeros((n_params,))\n"," circuit = Circuit(n_qubits)\n"," k = 0\n"," for i in range(n_qubits):\n"," circuit.H(i)\n"," for i in range(n_qubits):\n"," circuit.Rx(param[k], i)\n"," k += 1\n"," for i in range(n_qubits):\n"," circuit.Ry(param[k], i)\n"," k += 1\n"," for _ in range(depth):\n"," for i in range(0, n_qubits - 1):\n"," circuit.CZ(i, i + 1)\n"," circuit.add_barrier(range(0, n_qubits))\n"," for i in range(n_qubits):\n"," circuit.Rx(param[k], i)\n"," k += 1\n"," for i in range(n_qubits):\n"," circuit.Ry(param[k], i)\n"," k += 1\n"," return circuit, n_params"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_qubits = 4\n","depth = 2\n","circuit, n_params = get_circuit(n_qubits, depth)\n","render_circuit_jupyter(circuit)"]},{"cell_type":"markdown","metadata":{},"source":["## Now let's invoke qujax\n","The `pytket.extensions.qujax.tk_to_qujax` function will generate a parameters -> statetensor function for us."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_st = tk_to_qujax(circuit)"]},{"cell_type":"markdown","metadata":{},"source":["Let's try it out on some random parameters values. Be aware that's JAX's random number generator requires a `jax.random.PRNGkey` every time it's called - more info on that [here](https://jax.readthedocs.io/en/latest/jax.random.html).\n","Be aware that we still have convention where parameters are specified as multiples of $\\pi$ - that is in [0,2]."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["params = random.uniform(random.PRNGKey(0), shape=(n_params,), minval=0.0, maxval=2.0)\n","statetensor = param_to_st(params)\n","print(statetensor)\n","print(statetensor.shape)"]},{"cell_type":"markdown","metadata":{},"source":["Note that this function also has an optional second argument where an initiating `statetensor_in` can be provided. If it is not provided it will default to the all 0s state (as we use here)."]},{"cell_type":"markdown","metadata":{},"source":["We can obtain statevector by simply calling `.flatten()`"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["statevector = statetensor.flatten()\n","statevector.shape"]},{"cell_type":"markdown","metadata":{},"source":["And sampling probabilities by squaring the absolute value of the statevector"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["sample_probs = jnp.square(jnp.abs(statevector))\n","plt.bar(jnp.arange(statevector.size), sample_probs)"]},{"cell_type":"markdown","metadata":{},"source":["## Cost function"]},{"cell_type":"markdown","metadata":{},"source":["Now we have our `param_to_st` function we are free to define a cost function that acts on bitstrings (e.g. maxcut) or integers by directly wrapping a function around `param_to_st`. However, cost functions defined via quantum Hamiltonians are a bit more involved.\n","Fortunately, we can encode an Hamiltonian in JAX via the `qujax.get_statetensor_to_expectation_func` function which generates a statetensor -> expected value function for us.\n","It takes three arguments as input\n","- `gate_seq_seq`: A list of string (or array) lists encoding the gates in each term of the Hamiltonian. I.e. `[['X','X'], ['Y','Y'], ['Z','Z']]` corresponds to $H = aX_iX_j + bY_kY_l + cZ_mZ_n$ with qubit indices $i,j,k,l,m,n$ specified in the second argument and coefficients $a,b,c$ specified in the third argument\n","- `qubit_inds_seq`: A list of integer lists encoding which qubit indices to apply the aforementioned gates. I.e. `[[0, 1],[0,1],[0,1]]`. Must have the same structure as `gate_seq_seq` above.\n","- `coefficients`: A list of floats encoding any coefficients in the Hamiltonian. I.e. `[2.3, 0.8, 1.2]` corresponds to $a=2.3,b=0.8,c=1.2$ above. Must have the same length as the two above arguments."]},{"cell_type":"markdown","metadata":{},"source":["More specifically let's consider the problem of finding the ground state of the quantum Heisenberg Hamiltonian"]},{"cell_type":"markdown","metadata":{},"source":["$$\n","\\begin{equation}\n","H = \\sum_{i=1}^{n_\\text{qubits}-1} X_i X_{i+1} + Y_i Y_{i+1} + Z_i Z_{i+1}.\n","\\end{equation}\n","$$\n","\n","As described, we define the Hamiltonian via its gate strings, qubit indices and coefficients."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["hamiltonian_gates = [[\"X\", \"X\"], [\"Y\", \"Y\"], [\"Z\", \"Z\"]] * (n_qubits - 1)\n","hamiltonian_qubit_inds = [\n"," [int(i), int(i) + 1] for i in jnp.repeat(jnp.arange(n_qubits), 3)\n","]\n","coefficients = [1.0] * len(hamiltonian_qubit_inds)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"Gates:\\t\", hamiltonian_gates)\n","print(\"Qubits:\\t\", hamiltonian_qubit_inds)\n","print(\"Coefficients:\\t\", coefficients)"]},{"cell_type":"markdown","metadata":{},"source":["Now let's get the Hamiltonian as a pure JAX function"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["st_to_expectation = qujax.get_statetensor_to_expectation_func(\n"," hamiltonian_gates, hamiltonian_qubit_inds, coefficients\n",")"]},{"cell_type":"markdown","metadata":{},"source":["Let's check it works on the statetensor we've already generated."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["expected_val = st_to_expectation(statetensor)\n","expected_val"]},{"cell_type":"markdown","metadata":{},"source":["Now let's wrap the `param_to_st` and `st_to_expectation` together to give us an all in one `param_to_expectation` cost function."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_expectation = lambda param: st_to_expectation(param_to_st(param))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_expectation(params)"]},{"cell_type":"markdown","metadata":{},"source":["Sanity check that a different, randomly generated set of parameters gives us a new expected value."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["new_params = random.uniform(\n"," random.PRNGKey(1), shape=(n_params,), minval=0.0, maxval=2.0\n",")\n","param_to_expectation(new_params)"]},{"cell_type":"markdown","metadata":{},"source":["## Exact gradients within a VQE algorithm\n","The `param_to_expectation` function we created is a pure JAX function and outputs a scalar. This means we can pass it to `jax.grad` (or even better `jax.value_and_grad`)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_and_grad = value_and_grad(param_to_expectation)"]},{"cell_type":"markdown","metadata":{},"source":["The `cost_and_grad` function returns a tuple with the exact cost value and exact gradient evaluated at the parameters."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_and_grad(params)"]},{"cell_type":"markdown","metadata":{},"source":["## Now we have all the tools we need to design our VQE!\n","We'll just use vanilla gradient descent with a constant stepsize"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def vqe(init_param, n_steps, stepsize):\n"," params = jnp.zeros((n_steps, n_params))\n"," params = params.at[0].set(init_param)\n"," cost_vals = jnp.zeros(n_steps)\n"," cost_vals = cost_vals.at[0].set(param_to_expectation(init_param))\n"," for step in range(1, n_steps):\n"," cost_val, cost_grad = cost_and_grad(params[step - 1])\n"," cost_vals = cost_vals.at[step].set(cost_val)\n"," new_param = params[step - 1] - stepsize * cost_grad\n"," params = params.at[step].set(new_param)\n"," print(\"Iteration:\", step, \"\\tCost:\", cost_val, end=\"\\r\")\n"," print(\"\\n\")\n"," return params, cost_vals"]},{"cell_type":"markdown","metadata":{},"source":["Ok enough talking, let's run (and whilst we're at it we'll time it too)"]},{"cell_type":"markdown","metadata":{},"source":["%time"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["vqe_params, vqe_cost_vals = vqe(params, n_steps=250, stepsize=0.01)"]},{"cell_type":"markdown","metadata":{},"source":["Let's plot the results..."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.plot(vqe_cost_vals)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]},{"cell_type":"markdown","metadata":{},"source":["Pretty good!"]},{"cell_type":"markdown","metadata":{},"source":["## `jax.jit` speedup\n","One last thing... We can significantly speed up the VQE above via the `jax.jit`. In our current implementation, the expensive `cost_and_grad` function is compiled to [XLA](https://www.tensorflow.org/xla) and then executed at each call. By invoking `jax.jit` we ensure that the function is compiled only once (on the first call) and then simply executed at each future call - this is much faster!"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_and_grad = jit(cost_and_grad)"]},{"cell_type":"markdown","metadata":{},"source":["We'll demonstrate this using the second set of initial parameters we randomly generated (to be sure of no caching)."]},{"cell_type":"markdown","metadata":{},"source":["%time"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["new_vqe_params, new_vqe_cost_vals = vqe(new_params, n_steps=250, stepsize=0.01)"]},{"cell_type":"markdown","metadata":{},"source":["That's some speedup!\n","But let's also plot the training to be sure it converged correctly"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.plot(new_vqe_cost_vals)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/algorithms_and_protocols/pytket-qujax_qaoa.ipynb b/docs/examples/algorithms_and_protocols/pytket-qujax_qaoa.ipynb index 1b8167c9..3bd9541a 100644 --- a/docs/examples/algorithms_and_protocols/pytket-qujax_qaoa.ipynb +++ b/docs/examples/algorithms_and_protocols/pytket-qujax_qaoa.ipynb @@ -1 +1 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Symbolic circuits with `pytket-qujax`\n","In this notebook we will show how to manipulate symbolic circuits with the `pytket-qujax` extension. In particular, we will consider a QAOA and an Ising Hamiltonian."]},{"cell_type":"markdown","metadata":{},"source":["See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter\n","from jax import numpy as jnp, random, value_and_grad, jit\n","from sympy import Symbol\n","import matplotlib.pyplot as plt"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import qujax\n","from pytket.extensions.qujax import tk_to_qujax"]},{"cell_type":"markdown","metadata":{},"source":["## QAOA\n","The Quantum Approximate Optimization Algorithm (QAOA), first introduced by [Farhi et al.](https://arxiv.org/pdf/1411.4028.pdf), is a quantum variational algorithm used to solve optimization problems. It consists of a unitary $U(\\beta, \\gamma)$ formed by alternate repetitions of $U(\\beta)=e^{-i\\beta H_B}$ and $U(\\gamma)=e^{-i\\gamma H_P}$, where $H_B$ is the mixing Hamiltonian and $H_P$ the problem Hamiltonian. The goal is to find the optimal parameters that minimize $H_P$.\n","Given a depth $d$, the expression of the final unitary is $U(\\beta, \\gamma) = U(\\beta_d)U(\\gamma_d)\\cdots U(\\beta_1)U(\\gamma_1)$. Notice that for each repetition the parameters are different.\n","\n","## Problem Hamiltonian\n","QAOA uses a problem dependent ansatz. Therefore, we first need to know the problem that we want to solve. In this case we will consider an Ising Hamiltonian with only $Z$ interactions. Given a set of pairs (or qubit indices) $E$, the problem Hamiltonian will be:\n","\n","$$\n","\\begin{equation}\n","H_P = \\sum_{(i, j) \\in E}\\alpha_{ij}Z_iZ_j,\n","\\end{equation}\n","$$\n","\n","where $\\alpha_{ij}$ are the coefficients.\n","Let's build our problem Hamiltonian with random coefficients and a set of pairs for a given number of qubits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_qubits = 4\n","hamiltonian_qubit_inds = [(0, 1), (1, 2), (0, 2), (1, 3)]\n","hamiltonian_gates = [[\"Z\", \"Z\"]] * (len(hamiltonian_qubit_inds))"]},{"cell_type":"markdown","metadata":{},"source":["Notice that in order to use the random package from jax we first need to define a seeded key"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["seed = 13\n","key = random.PRNGKey(seed)\n","coefficients = random.uniform(key, shape=(len(hamiltonian_qubit_inds),))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"Gates:\\t\", hamiltonian_gates)\n","print(\"Qubits:\\t\", hamiltonian_qubit_inds)\n","print(\"Coefficients:\\t\", coefficients)"]},{"cell_type":"markdown","metadata":{},"source":["## Variational Circuit\n","Before constructing the circuit, we still need to select the mixing Hamiltonian. In our case, we will be using $X$ gates in each qubit, so $H_B = \\sum_{i=1}^{n}X_i$, where $n$ is the number of qubits. Notice that the unitary $U(\\beta)$, given this mixing Hamiltonian, is an $X$ rotation in each qubit with angle $\\beta$.\n","As for the unitary corresponding to the problem Hamiltonian, $U(\\gamma)$, it has the following form:\n","\n","$$\n","\\begin{equation}\n","U(\\gamma)=\\prod_{(i, j) \\in E}e^{-i\\gamma\\alpha_{ij}Z_i Z_j}\n","\\end{equation}\n","$$\n","\n","The operation $e^{-i\\gamma\\alpha_{ij}Z_iZ_j}$ can be performed using two CNOT gates with qubit $i$ as control and qubit $j$ as target and a $Z$ rotation in qubit $j$ in between them, with angle $\\gamma\\alpha_{ij}$.\n","Finally, the initial state used, in general, with the QAOA is an equal superposition of all the basis states. This can be achieved adding a first layer of Hadamard gates in each qubit at the beginning of the circuit."]},{"cell_type":"markdown","metadata":{},"source":["With all the building blocks, let's construct the symbolic circuit using tket. Notice that in order to define the parameters, we use the ```Symbol``` object from the `sympy` package. More info can be found in this [documentation](https://tket.quantinuum.com/user-manual/manual_circuit.html#symbolic-circuits). In order to later convert the circuit to qujax, we need to return the list of symbolic parameters as well."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qaoa_circuit(n_qubits, depth):\n"," circuit = Circuit(n_qubits)\n"," p_keys = []\n","\n"," # Initial State\n"," for i in range(n_qubits):\n"," circuit.H(i)\n"," for d in range(depth):\n"," # Hamiltonian unitary\n"," gamma_d = Symbol(f\"γ_{d}\")\n"," for index in range(len(hamiltonian_qubit_inds)):\n"," pair = hamiltonian_qubit_inds[index]\n"," coef = coefficients[index]\n"," circuit.CX(pair[0], pair[1])\n"," circuit.Rz(gamma_d * coef, pair[1])\n"," circuit.CX(pair[0], pair[1])\n"," circuit.add_barrier(range(0, n_qubits))\n"," p_keys.append(gamma_d)\n","\n"," # Mixing unitary\n"," beta_d = Symbol(f\"β_{d}\")\n"," for i in range(n_qubits):\n"," circuit.Rx(beta_d, i)\n"," p_keys.append(beta_d)\n"," return circuit, p_keys"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["depth = 3\n","circuit, keys = qaoa_circuit(n_qubits, depth)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["keys"]},{"cell_type":"markdown","metadata":{},"source":["Let's check the circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circuit)"]},{"cell_type":"markdown","metadata":{},"source":["## Now for `qujax`\n","The `pytket.extensions.qujax.tk_to_qujax` function will generate a parameters -> statetensor function for us. However, in order to convert a symbolic circuit we first need to define the `symbol_map`. This object maps each symbol key to their corresponding index. In our case, since the object `keys` contains the symbols in the correct order, we can simply construct the dictionary as follows:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol_map = {keys[i]: i for i in range(len(keys))}"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol_map"]},{"cell_type":"markdown","metadata":{},"source":["Then, we invoke the `tk_to_qujax` with both the circuit and the symbolic map."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_st = tk_to_qujax(circuit, symbol_map=symbol_map)"]},{"cell_type":"markdown","metadata":{},"source":["And we also construct the expectation map using the problem Hamiltonian via qujax:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["st_to_expectation = qujax.get_statetensor_to_expectation_func(\n"," hamiltonian_gates, hamiltonian_qubit_inds, coefficients\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_expectation = lambda param: st_to_expectation(param_to_st(param))"]},{"cell_type":"markdown","metadata":{},"source":["## Training process\n","We construct a function that, given a parameter vector, returns the value of the cost function and the gradient.\n","We also `jit` to avoid recompilation, this means that the expensive `cost_and_grad` function is compiled once into a very fast XLA (C++) function which is then executed at each iteration. Alternatively, we could get the same speedup by replacing our `for` loop with `jax.lax.scan`. You can read more about JIT compilation in the [JAX documentation](https://jax.readthedocs.io/en/latest/jax-101/02-jitting.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_and_grad = jit(value_and_grad(param_to_expectation))"]},{"cell_type":"markdown","metadata":{},"source":["For the training process we'll use vanilla gradient descent with a constant stepsize:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["seed = 123\n","key = random.PRNGKey(seed)\n","init_param = random.uniform(key, shape=(len(symbol_map),))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_steps = 150\n","stepsize = 0.01"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param = init_param"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_vals = jnp.zeros(n_steps)\n","cost_vals = cost_vals.at[0].set(param_to_expectation(init_param))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for step in range(1, n_steps):\n"," cost_val, cost_grad = cost_and_grad(param)\n"," cost_vals = cost_vals.at[step].set(cost_val)\n"," param = param - stepsize * cost_grad\n"," print(\"Iteration:\", step, \"\\tCost:\", cost_val, end=\"\\r\")"]},{"cell_type":"markdown","metadata":{},"source":["Let's visualise the gradient descent"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.plot(cost_vals)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Symbolic circuits with `pytket-qujax`\n","\n","**Download this notebook - {nb-download}`pytket-qujax_qaoa.ipynb`**\n","\n","In this notebook we will show how to manipulate symbolic circuits with the `pytket-qujax` extension. In particular, we will consider a QAOA and an Ising Hamiltonian."]},{"cell_type":"markdown","metadata":{},"source":["See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter\n","from jax import numpy as jnp, random, value_and_grad, jit\n","from sympy import Symbol\n","import matplotlib.pyplot as plt"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import qujax\n","from pytket.extensions.qujax import tk_to_qujax"]},{"cell_type":"markdown","metadata":{},"source":["## QAOA\n","The Quantum Approximate Optimization Algorithm (QAOA), first introduced by [Farhi et al.](https://arxiv.org/pdf/1411.4028.pdf), is a quantum variational algorithm used to solve optimization problems. It consists of a unitary $U(\\beta, \\gamma)$ formed by alternate repetitions of $U(\\beta)=e^{-i\\beta H_B}$ and $U(\\gamma)=e^{-i\\gamma H_P}$, where $H_B$ is the mixing Hamiltonian and $H_P$ the problem Hamiltonian. The goal is to find the optimal parameters that minimize $H_P$.\n","Given a depth $d$, the expression of the final unitary is $U(\\beta, \\gamma) = U(\\beta_d)U(\\gamma_d)\\cdots U(\\beta_1)U(\\gamma_1)$. Notice that for each repetition the parameters are different.\n","\n","## Problem Hamiltonian\n","QAOA uses a problem dependent ansatz. Therefore, we first need to know the problem that we want to solve. In this case we will consider an Ising Hamiltonian with only $Z$ interactions. Given a set of pairs (or qubit indices) $E$, the problem Hamiltonian will be:\n","\n","$$\n","\\begin{equation}\n","H_P = \\sum_{(i, j) \\in E}\\alpha_{ij}Z_iZ_j,\n","\\end{equation}\n","$$\n","\n","where $\\alpha_{ij}$ are the coefficients.\n","Let's build our problem Hamiltonian with random coefficients and a set of pairs for a given number of qubits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_qubits = 4\n","hamiltonian_qubit_inds = [(0, 1), (1, 2), (0, 2), (1, 3)]\n","hamiltonian_gates = [[\"Z\", \"Z\"]] * (len(hamiltonian_qubit_inds))"]},{"cell_type":"markdown","metadata":{},"source":["Notice that in order to use the random package from jax we first need to define a seeded key"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["seed = 13\n","key = random.PRNGKey(seed)\n","coefficients = random.uniform(key, shape=(len(hamiltonian_qubit_inds),))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"Gates:\\t\", hamiltonian_gates)\n","print(\"Qubits:\\t\", hamiltonian_qubit_inds)\n","print(\"Coefficients:\\t\", coefficients)"]},{"cell_type":"markdown","metadata":{},"source":["## Variational Circuit\n","Before constructing the circuit, we still need to select the mixing Hamiltonian. In our case, we will be using $X$ gates in each qubit, so $H_B = \\sum_{i=1}^{n}X_i$, where $n$ is the number of qubits. Notice that the unitary $U(\\beta)$, given this mixing Hamiltonian, is an $X$ rotation in each qubit with angle $\\beta$.\n","As for the unitary corresponding to the problem Hamiltonian, $U(\\gamma)$, it has the following form:\n","\n","$$\n","\\begin{equation}\n","U(\\gamma)=\\prod_{(i, j) \\in E}e^{-i\\gamma\\alpha_{ij}Z_i Z_j}\n","\\end{equation}\n","$$\n","\n","The operation $e^{-i\\gamma\\alpha_{ij}Z_iZ_j}$ can be performed using two CNOT gates with qubit $i$ as control and qubit $j$ as target and a $Z$ rotation in qubit $j$ in between them, with angle $\\gamma\\alpha_{ij}$.\n","Finally, the initial state used, in general, with the QAOA is an equal superposition of all the basis states. This can be achieved adding a first layer of Hadamard gates in each qubit at the beginning of the circuit."]},{"cell_type":"markdown","metadata":{},"source":["With all the building blocks, let's construct the symbolic circuit using tket. Notice that in order to define the parameters, we use the ```Symbol``` object from the `sympy` package. More info can be found in this [documentation](https://tket.quantinuum.com/user-manual/manual_circuit.html#symbolic-circuits). In order to later convert the circuit to qujax, we need to return the list of symbolic parameters as well."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qaoa_circuit(n_qubits, depth):\n"," circuit = Circuit(n_qubits)\n"," p_keys = []\n","\n"," # Initial State\n"," for i in range(n_qubits):\n"," circuit.H(i)\n"," for d in range(depth):\n"," # Hamiltonian unitary\n"," gamma_d = Symbol(f\"γ_{d}\")\n"," for index in range(len(hamiltonian_qubit_inds)):\n"," pair = hamiltonian_qubit_inds[index]\n"," coef = coefficients[index]\n"," circuit.CX(pair[0], pair[1])\n"," circuit.Rz(gamma_d * coef, pair[1])\n"," circuit.CX(pair[0], pair[1])\n"," circuit.add_barrier(range(0, n_qubits))\n"," p_keys.append(gamma_d)\n","\n"," # Mixing unitary\n"," beta_d = Symbol(f\"β_{d}\")\n"," for i in range(n_qubits):\n"," circuit.Rx(beta_d, i)\n"," p_keys.append(beta_d)\n"," return circuit, p_keys"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["depth = 3\n","circuit, keys = qaoa_circuit(n_qubits, depth)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["keys"]},{"cell_type":"markdown","metadata":{},"source":["Let's check the circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circuit)"]},{"cell_type":"markdown","metadata":{},"source":["## Now for `qujax`\n","The `pytket.extensions.qujax.tk_to_qujax` function will generate a parameters -> statetensor function for us. However, in order to convert a symbolic circuit we first need to define the `symbol_map`. This object maps each symbol key to their corresponding index. In our case, since the object `keys` contains the symbols in the correct order, we can simply construct the dictionary as follows:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol_map = {keys[i]: i for i in range(len(keys))}"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol_map"]},{"cell_type":"markdown","metadata":{},"source":["Then, we invoke the `tk_to_qujax` with both the circuit and the symbolic map."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_st = tk_to_qujax(circuit, symbol_map=symbol_map)"]},{"cell_type":"markdown","metadata":{},"source":["And we also construct the expectation map using the problem Hamiltonian via qujax:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["st_to_expectation = qujax.get_statetensor_to_expectation_func(\n"," hamiltonian_gates, hamiltonian_qubit_inds, coefficients\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param_to_expectation = lambda param: st_to_expectation(param_to_st(param))"]},{"cell_type":"markdown","metadata":{},"source":["## Training process\n","We construct a function that, given a parameter vector, returns the value of the cost function and the gradient.\n","We also `jit` to avoid recompilation, this means that the expensive `cost_and_grad` function is compiled once into a very fast XLA (C++) function which is then executed at each iteration. Alternatively, we could get the same speedup by replacing our `for` loop with `jax.lax.scan`. You can read more about JIT compilation in the [JAX documentation](https://jax.readthedocs.io/en/latest/jax-101/02-jitting.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_and_grad = jit(value_and_grad(param_to_expectation))"]},{"cell_type":"markdown","metadata":{},"source":["For the training process we'll use vanilla gradient descent with a constant stepsize:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["seed = 123\n","key = random.PRNGKey(seed)\n","init_param = random.uniform(key, shape=(len(symbol_map),))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_steps = 150\n","stepsize = 0.01"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["param = init_param"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cost_vals = jnp.zeros(n_steps)\n","cost_vals = cost_vals.at[0].set(param_to_expectation(init_param))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for step in range(1, n_steps):\n"," cost_val, cost_grad = cost_and_grad(param)\n"," cost_vals = cost_vals.at[step].set(cost_val)\n"," param = param - stepsize * cost_grad\n"," print(\"Iteration:\", step, \"\\tCost:\", cost_val, end=\"\\r\")"]},{"cell_type":"markdown","metadata":{},"source":["Let's visualise the gradient descent"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.plot(cost_vals)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/algorithms_and_protocols/spam_example.ipynb b/docs/examples/algorithms_and_protocols/spam_example.ipynb index bcdce3b9..c0eec02e 100644 --- a/docs/examples/algorithms_and_protocols/spam_example.ipynb +++ b/docs/examples/algorithms_and_protocols/spam_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Calibration and correction of state preparation and measurement (SPAM)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Quantum Computers available in the NISQ-era are limited by significant sources of device noise which cause errors in computation. One such noise source is errors in the preparation and measurement of quantum states, more commonly know as SPAM.
\n", "
\n", "If device SPAM error can be characterised, then device results can be modified to mitigate the error. Characterisation proceeds by determining overlap between different prepared basis states when measured, and mitigation modifies the distribution over output states of the corrected circuit. No modification of the quantum circuit being corrected is required. The ``` pytket``` ```SpamCorrecter``` class supports characterisation and mitigation of device SPAM error.
\n", "
\n", "In this tutorial we will show how the ```SpamCorrecter``` class can be used to modify real results and improve device performance when running experiments.
\n", "
\n", "This tutorial will require installation of ```pytket```, ```pytket_qiskit``` and ```qiskit```, all available on pip.
\n", "
\n", "First, import the ```SpamCorrecter``` class."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.utils.spam import SpamCorrecter"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The SpamCorrecter class has methods for generating State Preparation and Measurement (SPAM) calibration experiments for pytket backends and correcting counts generated from those same backends.
\n", "
\n", "Let's first mitigate error from a noisy simulation, using a noise model straight from the 5-qubit IBMQ manila device. This will require a preloaded IBMQ account."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from qiskit import IBMQ"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["IBMQ.load_account()"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import process_characterisation"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ibmq_manila_backend = IBMQ.providers()[0].get_backend(\"ibmq_manila\")\n", "pytket_manila_characterisation = process_characterisation(ibmq_manila_backend)\n", "pytket_manila_architecture = pytket_manila_characterisation[\"Architecture\"]"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["import networkx as nx\n", "import matplotlib.pyplot as plt"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["manila_graph = nx.Graph(pytket_manila_architecture.coupling)\n", "nx.draw(manila_graph, labels={node: node for node in manila_graph.nodes()})"]}, {"cell_type": "markdown", "metadata": {}, "source": ["SPAM correction requires subsets of qubits which are assumed to only have SPAM errors correlated with each other, and no other qubits.
\n", "
\n", "Correlated errors are usually dependent on the connectivity layout of devices, as shown above.
\n", "
\n", "As manila is a small 5-qubit device with few connections, let's assume that all qubits have correlated SPAM errors. The number of calibration circuits produced is exponential in the maximum number of correlated circuits, so finding good subsets of correlated qubits is important for characterising larger devices with smaller experimental overhead.
\n", "
\n", "We can produce an ```IBMQEmulatorBackend``` to run this. This uses a noise model from ```ibmq_manila``` produced using qiskit-aer. We can then execute all calibration circuits through the backend."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import IBMQEmulatorBackend, AerBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["n_shots = 8192\n", "pytket_noisy_sim_backend = IBMQEmulatorBackend(\"ibmq_manila\")\n", "manila_node_subsets = pytket_noisy_sim_backend.backend_info.architecture.nodes\n", "manila_spam = SpamCorrecter([manila_node_subsets], pytket_noisy_sim_backend)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The SpamCorrecter uses these subsets of qubits to produce calibration circuits."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["calibration_circuits = manila_spam.calibration_circuits()\n", "print(\"Number of calibration circuits: \", len(calibration_circuits))"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["sim_handles = pytket_noisy_sim_backend.process_circuits(calibration_circuits, n_shots)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Count results from the simulator are then used to calculate the matrices used for SPAM correction for ```ibmq_manila```."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["sim_count_results = pytket_noisy_sim_backend.get_results(sim_handles)\n", "manila_spam.calculate_matrices(sim_count_results)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ghz_circuit = (\n", " Circuit(len(pytket_noisy_sim_backend.backend_info.architecture.nodes))\n", " .H(0)\n", " .CX(0, 1)\n", " .CX(1, 2)\n", " .measure_all()\n", ")\n", "ghz_circuit = pytket_noisy_sim_backend.get_compiled_circuit(ghz_circuit)\n", "ghz_noisy_handle = pytket_noisy_sim_backend.process_circuit(ghz_circuit, n_shots)\n", "ghz_noisy_result = pytket_noisy_sim_backend.get_result(ghz_noisy_handle)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We also run a noiseless simulation so we can compare performance."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pytket_noiseless_sim_backend = AerBackend()\n", "ghz_noiseless_handle = pytket_noiseless_sim_backend.process_circuit(\n", " ghz_circuit, n_shots\n", ")\n", "ghz_noiseless_result = pytket_noiseless_sim_backend.get_result(ghz_noiseless_handle)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Noisy simulator counts are corrected using the ```SpamCorrecter``` objects ```correct_counts``` method.
\n", "
\n", "To correctly amend counts, the ```correct_counts``` method requires a ``ParallelMeasures`` type object, a list of ``Dict[Qubit, Bit]`` where each dictionary denotes a set of Qubit measured in parallel and the Bit their measured values are assigned to.
\n", "
\n", "The ``SpamCorrecter`` class has a helper method ``get_parallel_measure`` for retrieving this object for a Circuit."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ghz_parallel_measure = manila_spam.get_parallel_measure(ghz_circuit)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ghz_spam_corrected_result = manila_spam.correct_counts(\n", " ghz_noisy_result, ghz_parallel_measure\n", ")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Import and define the Jensen-Shannon divergence, which we will use for comparing performance. The Jensen-Shannon divergence is a symmetric and finite measure of similarity between two probability distributions. A smaller divergence implies more similarity between two probability distributions."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from scipy.stats import entropy\n", "import numpy as np\n", "import itertools"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def binseq(k):\n", " return [\"\".join(x) for x in itertools.product(\"01\", repeat=k)]"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def probs_from_counts(result):\n", " counts = result.get_counts()\n", " counts_dict = dict()\n", " for x in counts:\n", " counts_dict[\"\".join(str(e) for e in x)] = counts[x]\n", " converted = []\n", " binary_strings = binseq(len(list(counts.keys())[0]))\n", " for b in binary_strings:\n", " converted.append(counts_dict.get(b, 0))\n", " return converted / np.sum(converted)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def JSD(P, Q):\n", " _P = P / np.linalg.norm(P, ord=1)\n", " _Q = Q / np.linalg.norm(Q, ord=1)\n", " _M = 0.5 * (_P + _Q)\n", " return 0.5 * (entropy(_P, _M) + entropy(_Q, _M))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Convert our counts results to a probability distribution over the basis states for comparison."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ghz_noiseless_probabilities = probs_from_counts(ghz_noiseless_result)\n", "ghz_noisy_probabilities = probs_from_counts(ghz_noisy_result)\n", "ghz_spam_corrected_probabilities = probs_from_counts(ghz_spam_corrected_result)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(\n", " \"Jensen-Shannon Divergence between noiseless simulation probability distribution and noisy simulation probability distribution: \",\n", " JSD(ghz_noiseless_probabilities, ghz_noisy_probabilities),\n", ")\n", "print(\n", " \"Jensen-Shannon Divergence between noiseless simulation probability distribution and spam corrected noisy simulation probability distribution: \",\n", " JSD(ghz_noiseless_probabilities, ghz_spam_corrected_probabilities),\n", ")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In our noisy simulated case, spam corrected results produced a distribution closer to the expected distribution.
\n", "
\n", "There are two methods available for correcting counts: the default ```bayesian```, and ```invert```. Further information on each method is available at our [documentation](https://cqcl.github.io/tket/pytket/api/utils.html#module-pytket.utils.spam).
\n", "
\n", "Let's look at how the ```invert``` method performs."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ghz_invert_corrected_result = manila_spam.correct_counts(\n", " ghz_noisy_result, ghz_parallel_measure, method=\"invert\"\n", ")\n", "ghz_invert_probabilities = probs_from_counts(ghz_invert_corrected_result)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(\n", " \"Jensen-Shannon Divergence between noiseless simulation probability distribution and Bayesian-corrected noisy simulation probability distribution: \",\n", " JSD(ghz_noiseless_probabilities, ghz_spam_corrected_probabilities),\n", ")\n", "print(\n", " \"Jensen-Shannon Divergence between noiseless simulation probability distribution and invert-corrected noisy simulation probability distribution: \",\n", " JSD(ghz_noiseless_probabilities, ghz_invert_probabilities),\n", ")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["To see how SPAM correction performs on results from a real IBMQ quantum device, try replacing `IBMQEmulatorBackend` with `IBMQBackend`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import IBMQBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ibm_backend = IBMQBackend(\"ibmq_manila\")"]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Calibration and correction of state preparation and measurement (SPAM)\n","\n","**Download this notebook - {nb-download}`spam_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["Quantum Computers available in the NISQ-era are limited by significant sources of device noise which cause errors in computation. One such noise source is errors in the preparation and measurement of quantum states, more commonly know as SPAM.
\n","
\n","If device SPAM error can be characterised, then device results can be modified to mitigate the error. Characterisation proceeds by determining overlap between different prepared basis states when measured, and mitigation modifies the distribution over output states of the corrected circuit. No modification of the quantum circuit being corrected is required. The ``` pytket``` ```SpamCorrecter``` class supports characterisation and mitigation of device SPAM error.
\n","
\n","In this tutorial we will show how the ```SpamCorrecter``` class can be used to modify real results and improve device performance when running experiments.
\n","
\n","This tutorial will require installation of ```pytket```, ```pytket_qiskit``` and ```qiskit```, all available on pip.
\n","
\n","First, import the ```SpamCorrecter``` class."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.utils.spam import SpamCorrecter"]},{"cell_type":"markdown","metadata":{},"source":["The SpamCorrecter class has methods for generating State Preparation and Measurement (SPAM) calibration experiments for pytket backends and correcting counts generated from those same backends.
\n","
\n","Let's first mitigate error from a noisy simulation, using a noise model straight from the 5-qubit IBMQ manila device. This will require a preloaded IBMQ account."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from qiskit import IBMQ"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["IBMQ.load_account()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import process_characterisation"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ibmq_manila_backend = IBMQ.providers()[0].get_backend(\"ibmq_manila\")\n","pytket_manila_characterisation = process_characterisation(ibmq_manila_backend)\n","pytket_manila_architecture = pytket_manila_characterisation[\"Architecture\"]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import networkx as nx\n","import matplotlib.pyplot as plt"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["manila_graph = nx.Graph(pytket_manila_architecture.coupling)\n","nx.draw(manila_graph, labels={node: node for node in manila_graph.nodes()})"]},{"cell_type":"markdown","metadata":{},"source":["SPAM correction requires subsets of qubits which are assumed to only have SPAM errors correlated with each other, and no other qubits.
\n","
\n","Correlated errors are usually dependent on the connectivity layout of devices, as shown above.
\n","
\n","As manila is a small 5-qubit device with few connections, let's assume that all qubits have correlated SPAM errors. The number of calibration circuits produced is exponential in the maximum number of correlated circuits, so finding good subsets of correlated qubits is important for characterising larger devices with smaller experimental overhead.
\n","
\n","We can produce an ```IBMQEmulatorBackend``` to run this. This uses a noise model from ```ibmq_manila``` produced using qiskit-aer. We can then execute all calibration circuits through the backend."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import IBMQEmulatorBackend, AerBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_shots = 8192\n","pytket_noisy_sim_backend = IBMQEmulatorBackend(\"ibmq_manila\")\n","manila_node_subsets = pytket_noisy_sim_backend.backend_info.architecture.nodes\n","manila_spam = SpamCorrecter([manila_node_subsets], pytket_noisy_sim_backend)"]},{"cell_type":"markdown","metadata":{},"source":["The SpamCorrecter uses these subsets of qubits to produce calibration circuits."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["calibration_circuits = manila_spam.calibration_circuits()\n","print(\"Number of calibration circuits: \", len(calibration_circuits))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["sim_handles = pytket_noisy_sim_backend.process_circuits(calibration_circuits, n_shots)"]},{"cell_type":"markdown","metadata":{},"source":["Count results from the simulator are then used to calculate the matrices used for SPAM correction for ```ibmq_manila```."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["sim_count_results = pytket_noisy_sim_backend.get_results(sim_handles)\n","manila_spam.calculate_matrices(sim_count_results)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ghz_circuit = (\n"," Circuit(len(pytket_noisy_sim_backend.backend_info.architecture.nodes))\n"," .H(0)\n"," .CX(0, 1)\n"," .CX(1, 2)\n"," .measure_all()\n",")\n","ghz_circuit = pytket_noisy_sim_backend.get_compiled_circuit(ghz_circuit)\n","ghz_noisy_handle = pytket_noisy_sim_backend.process_circuit(ghz_circuit, n_shots)\n","ghz_noisy_result = pytket_noisy_sim_backend.get_result(ghz_noisy_handle)"]},{"cell_type":"markdown","metadata":{},"source":["We also run a noiseless simulation so we can compare performance."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pytket_noiseless_sim_backend = AerBackend()\n","ghz_noiseless_handle = pytket_noiseless_sim_backend.process_circuit(\n"," ghz_circuit, n_shots\n",")\n","ghz_noiseless_result = pytket_noiseless_sim_backend.get_result(ghz_noiseless_handle)"]},{"cell_type":"markdown","metadata":{},"source":["Noisy simulator counts are corrected using the ```SpamCorrecter``` objects ```correct_counts``` method.
\n","
\n","To correctly amend counts, the ```correct_counts``` method requires a ``ParallelMeasures`` type object, a list of ``Dict[Qubit, Bit]`` where each dictionary denotes a set of Qubit measured in parallel and the Bit their measured values are assigned to.
\n","
\n","The ``SpamCorrecter`` class has a helper method ``get_parallel_measure`` for retrieving this object for a Circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ghz_parallel_measure = manila_spam.get_parallel_measure(ghz_circuit)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ghz_spam_corrected_result = manila_spam.correct_counts(\n"," ghz_noisy_result, ghz_parallel_measure\n",")"]},{"cell_type":"markdown","metadata":{},"source":["Import and define the Jensen-Shannon divergence, which we will use for comparing performance. The Jensen-Shannon divergence is a symmetric and finite measure of similarity between two probability distributions. A smaller divergence implies more similarity between two probability distributions."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from scipy.stats import entropy\n","import numpy as np\n","import itertools"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def binseq(k):\n"," return [\"\".join(x) for x in itertools.product(\"01\", repeat=k)]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def probs_from_counts(result):\n"," counts = result.get_counts()\n"," counts_dict = dict()\n"," for x in counts:\n"," counts_dict[\"\".join(str(e) for e in x)] = counts[x]\n"," converted = []\n"," binary_strings = binseq(len(list(counts.keys())[0]))\n"," for b in binary_strings:\n"," converted.append(counts_dict.get(b, 0))\n"," return converted / np.sum(converted)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def JSD(P, Q):\n"," _P = P / np.linalg.norm(P, ord=1)\n"," _Q = Q / np.linalg.norm(Q, ord=1)\n"," _M = 0.5 * (_P + _Q)\n"," return 0.5 * (entropy(_P, _M) + entropy(_Q, _M))"]},{"cell_type":"markdown","metadata":{},"source":["Convert our counts results to a probability distribution over the basis states for comparison."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ghz_noiseless_probabilities = probs_from_counts(ghz_noiseless_result)\n","ghz_noisy_probabilities = probs_from_counts(ghz_noisy_result)\n","ghz_spam_corrected_probabilities = probs_from_counts(ghz_spam_corrected_result)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\n"," \"Jensen-Shannon Divergence between noiseless simulation probability distribution and noisy simulation probability distribution: \",\n"," JSD(ghz_noiseless_probabilities, ghz_noisy_probabilities),\n",")\n","print(\n"," \"Jensen-Shannon Divergence between noiseless simulation probability distribution and spam corrected noisy simulation probability distribution: \",\n"," JSD(ghz_noiseless_probabilities, ghz_spam_corrected_probabilities),\n",")"]},{"cell_type":"markdown","metadata":{},"source":["In our noisy simulated case, spam corrected results produced a distribution closer to the expected distribution.
\n","
\n","There are two methods available for correcting counts: the default ```bayesian```, and ```invert```. Further information on each method is available at our [documentation](https://cqcl.github.io/tket/pytket/api/utils.html#module-pytket.utils.spam).
\n","
\n","Let's look at how the ```invert``` method performs."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ghz_invert_corrected_result = manila_spam.correct_counts(\n"," ghz_noisy_result, ghz_parallel_measure, method=\"invert\"\n",")\n","ghz_invert_probabilities = probs_from_counts(ghz_invert_corrected_result)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\n"," \"Jensen-Shannon Divergence between noiseless simulation probability distribution and Bayesian-corrected noisy simulation probability distribution: \",\n"," JSD(ghz_noiseless_probabilities, ghz_spam_corrected_probabilities),\n",")\n","print(\n"," \"Jensen-Shannon Divergence between noiseless simulation probability distribution and invert-corrected noisy simulation probability distribution: \",\n"," JSD(ghz_noiseless_probabilities, ghz_invert_probabilities),\n",")"]},{"cell_type":"markdown","metadata":{},"source":["To see how SPAM correction performs on results from a real IBMQ quantum device, try replacing `IBMQEmulatorBackend` with `IBMQBackend`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import IBMQBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ibm_backend = IBMQBackend(\"ibmq_manila\")"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/algorithms_and_protocols/ucc_vqe.ipynb b/docs/examples/algorithms_and_protocols/ucc_vqe.ipynb index 20f67839..c01acf5a 100644 --- a/docs/examples/algorithms_and_protocols/ucc_vqe.ipynb +++ b/docs/examples/algorithms_and_protocols/ucc_vqe.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# VQE with UCC ansatz"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In this tutorial, we will focus on:
\n", "- building parameterised ans\u00e4tze for variational algorithms;
\n", "- compilation tools for UCC-style ans\u00e4tze."]}, {"cell_type": "markdown", "metadata": {}, "source": ["This example assumes the reader is familiar with the Variational Quantum Eigensolver and its application to electronic structure problems through the Unitary Coupled Cluster approach.
\n", "
\n", "To run this example, you will need `pytket` and `pytket-qiskit`, as well as `openfermion`, `scipy`, and `sympy`.
\n", "
\n", "We will start with a basic implementation and then gradually modify it to make it faster, more general, and less noisy. The final solution is given in full at the bottom of the notebook.
\n", "
\n", "Suppose we have some electronic configuration problem, expressed via a physical Hamiltonian. (The Hamiltonian and excitations in this example were obtained using `qiskit-aqua` version 0.5.2 and `pyscf` for H2, bond length 0.75A, sto3g basis, Jordan-Wigner encoding, with no qubit reduction or orbital freezing.). We express it succinctly using the openfermion library:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["import openfermion as of"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["hamiltonian = (\n", " -0.8153001706270075 * of.QubitOperator(\"\")\n", " + 0.16988452027940318 * of.QubitOperator(\"Z0\")\n", " + -0.21886306781219608 * of.QubitOperator(\"Z1\")\n", " + 0.16988452027940323 * of.QubitOperator(\"Z2\")\n", " + -0.2188630678121961 * of.QubitOperator(\"Z3\")\n", " + 0.12005143072546047 * of.QubitOperator(\"Z0 Z1\")\n", " + 0.16821198673715723 * of.QubitOperator(\"Z0 Z2\")\n", " + 0.16549431486978672 * of.QubitOperator(\"Z0 Z3\")\n", " + 0.16549431486978672 * of.QubitOperator(\"Z1 Z2\")\n", " + 0.1739537877649417 * of.QubitOperator(\"Z1 Z3\")\n", " + 0.12005143072546047 * of.QubitOperator(\"Z2 Z3\")\n", " + 0.04544288414432624 * of.QubitOperator(\"X0 X1 X2 X3\")\n", " + 0.04544288414432624 * of.QubitOperator(\"X0 X1 Y2 Y3\")\n", " + 0.04544288414432624 * of.QubitOperator(\"Y0 Y1 X2 X3\")\n", " + 0.04544288414432624 * of.QubitOperator(\"Y0 Y1 Y2 Y3\")\n", ")\n", "nuclear_repulsion_energy = 0.70556961456"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We would like to define our ansatz for arbitrary parameter values. For simplicity, let's start with a Hardware Efficient Ansatz."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Hardware efficient ansatz:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def hea(params):\n", " ansatz = Circuit(4)\n", " for i in range(4):\n", " ansatz.Ry(params[i], i)\n", " for i in range(3):\n", " ansatz.CX(i, i + 1)\n", " for i in range(4):\n", " ansatz.Ry(params[4 + i], i)\n", " return ansatz"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can use this to build the objective function for our optimisation."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import AerBackend\n", "from pytket.utils.expectations import expectation_from_counts"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["backend = AerBackend()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Naive objective function:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def objective(params):\n", " energy = 0\n", " for term, coeff in hamiltonian.terms.items():\n", " if not term:\n", " energy += coeff\n", " continue\n", " circ = hea(params)\n", " circ.add_c_register(\"c\", len(term))\n", " for i, (q, pauli) in enumerate(term):\n", " if pauli == \"X\":\n", " circ.H(q)\n", " elif pauli == \"Y\":\n", " circ.V(q)\n", " circ.Measure(q, i)\n", " compiled_circ = backend.get_compiled_circuit(circ)\n", " counts = backend.run_circuit(compiled_circ, n_shots=4000).get_counts()\n", " energy += coeff * expectation_from_counts(counts)\n", " return energy + nuclear_repulsion_energy"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This objective function is then run through a classical optimiser to find the set of parameter values that minimise the energy of the system. For the sake of example, we will just run this with a single parameter value."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["arg_values = [\n", " -7.31158201e-02,\n", " -1.64514836e-04,\n", " 1.12585591e-03,\n", " -2.58367544e-03,\n", " 1.00006068e00,\n", " -1.19551357e-03,\n", " 9.99963988e-01,\n", " 2.53283285e-03,\n", "]"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["energy = objective(arg_values)\n", "print(energy)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The HEA is designed to cram as many orthogonal degrees of freedom into a small circuit as possible to be able to explore a large region of the Hilbert space whilst the circuits themselves can be run with minimal noise. These ans\u00e4tze give virtually-optimal circuits by design, but suffer from an excessive number of variational parameters making convergence slow, barren plateaus where the classical optimiser fails to make progress, and spanning a space where most states lack a physical interpretation. These drawbacks can necessitate adding penalties and may mean that the ansatz cannot actually express the true ground state.
\n", "
\n", "The UCC ansatz, on the other hand, is derived from the electronic configuration. It sacrifices efficiency of the circuit for the guarantee of physical states and the variational parameters all having some meaningful effect, which helps the classical optimisation to converge.
\n", "
\n", "This starts by defining the terms of our single and double excitations. These would usually be generated using the orbital configurations, so we will just use a hard-coded example here for the purposes of demonstration."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import Qubit\n", "from pytket.pauli import Pauli, QubitPauliString"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["q = [Qubit(i) for i in range(4)]\n", "xyii = QubitPauliString([q[0], q[1]], [Pauli.X, Pauli.Y])\n", "yxii = QubitPauliString([q[0], q[1]], [Pauli.Y, Pauli.X])\n", "iixy = QubitPauliString([q[2], q[3]], [Pauli.X, Pauli.Y])\n", "iiyx = QubitPauliString([q[2], q[3]], [Pauli.Y, Pauli.X])\n", "xxxy = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.X, Pauli.Y])\n", "xxyx = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.Y, Pauli.X])\n", "xyxx = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.X, Pauli.X])\n", "yxxx = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.X, Pauli.X])\n", "yyyx = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.Y, Pauli.X])\n", "yyxy = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.X, Pauli.Y])\n", "yxyy = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.Y, Pauli.Y])\n", "xyyy = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.Y, Pauli.Y])"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["singles_a = {xyii: 1.0, yxii: -1.0}\n", "singles_b = {iixy: 1.0, iiyx: -1.0}\n", "doubles = {\n", " xxxy: 0.25,\n", " xxyx: -0.25,\n", " xyxx: 0.25,\n", " yxxx: -0.25,\n", " yyyx: -0.25,\n", " yyxy: 0.25,\n", " yxyy: -0.25,\n", " xyyy: 0.25,\n", "}"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Building the ansatz circuit itself is often done naively by defining the map from each term down to basic gates and then applying it to each term."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def add_operator_term(circuit: Circuit, term: QubitPauliString, angle: float):\n", " qubits = []\n", " for q, p in term.map.items():\n", " if p != Pauli.I:\n", " qubits.append(q)\n", " if p == Pauli.X:\n", " circuit.H(q)\n", " elif p == Pauli.Y:\n", " circuit.V(q)\n", " for i in range(len(qubits) - 1):\n", " circuit.CX(i, i + 1)\n", " circuit.Rz(angle, len(qubits) - 1)\n", " for i in reversed(range(len(qubits) - 1)):\n", " circuit.CX(i, i + 1)\n", " for q, p in term.map.items():\n", " if p == Pauli.X:\n", " circuit.H(q)\n", " elif p == Pauli.Y:\n", " circuit.Vdg(q)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Unitary Coupled Cluster Singles & Doubles ansatz:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def ucc(params):\n", " ansatz = Circuit(4)\n", " # Set initial reference state\n", " ansatz.X(1).X(3)\n", " # Evolve by excitations\n", " for term, coeff in singles_a.items():\n", " add_operator_term(ansatz, term, coeff * params[0])\n", " for term, coeff in singles_b.items():\n", " add_operator_term(ansatz, term, coeff * params[1])\n", " for term, coeff in doubles.items():\n", " add_operator_term(ansatz, term, coeff * params[2])\n", " return ansatz"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This is already quite verbose, but `pytket` has a neat shorthand construction for these operator terms using the `PauliExpBox` construction. We can then decompose these into basic gates using the `DecomposeBoxes` compiler pass."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import PauliExpBox\n", "from pytket.passes import DecomposeBoxes"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def add_excitation(circ, term_dict, param):\n", " for term, coeff in term_dict.items():\n", " qubits, paulis = zip(*term.map.items())\n", " pbox = PauliExpBox(paulis, coeff * param)\n", " circ.add_gate(pbox, qubits)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["UCC ansatz with syntactic shortcuts:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def ucc(params):\n", " ansatz = Circuit(4)\n", " ansatz.X(1).X(3)\n", " add_excitation(ansatz, singles_a, params[0])\n", " add_excitation(ansatz, singles_b, params[1])\n", " add_excitation(ansatz, doubles, params[2])\n", " DecomposeBoxes().apply(ansatz)\n", " return ansatz"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The objective function can also be simplified using a utility method for constructing the measurement circuits and processing for expectation value calculations. For that, we convert the Hamiltonian to a pytket QubitPauliOperator:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.utils.operators import QubitPauliOperator"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pauli_sym = {\"I\": Pauli.I, \"X\": Pauli.X, \"Y\": Pauli.Y, \"Z\": Pauli.Z}"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def qps_from_openfermion(paulis):\n", " \"\"\"Convert OpenFermion tensor of Paulis to pytket QubitPauliString.\"\"\"\n", " qlist = []\n", " plist = []\n", " for q, p in paulis:\n", " qlist.append(Qubit(q))\n", " plist.append(pauli_sym[p])\n", " return QubitPauliString(qlist, plist)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def qpo_from_openfermion(openf_op):\n", " \"\"\"Convert OpenFermion QubitOperator to pytket QubitPauliOperator.\"\"\"\n", " tk_op = dict()\n", " for term, coeff in openf_op.terms.items():\n", " string = qps_from_openfermion(term)\n", " tk_op[string] = coeff\n", " return QubitPauliOperator(tk_op)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["hamiltonian_op = qpo_from_openfermion(hamiltonian)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Simplified objective function using utilities:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.utils.expectations import get_operator_expectation_value"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def objective(params):\n", " circ = ucc(params)\n", " return (\n", " get_operator_expectation_value(circ, hamiltonian_op, backend, n_shots=4000)\n", " + nuclear_repulsion_energy\n", " )"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["arg_values = [-3.79002933e-05, 2.42964799e-05, 4.63447157e-01]"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["energy = objective(arg_values)\n", "print(energy)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This is now the simplest form that this operation can take, but it isn't necessarily the most effective. When we decompose the ansatz circuit into basic gates, it is still very expensive. We can employ some of the circuit simplification passes available in `pytket` to reduce its size and improve fidelity in practice.
\n", "
\n", "A good example is to decompose each `PauliExpBox` into basic gates and then apply `FullPeepholeOptimise`, which defines a compilation strategy utilising all of the simplifications in `pytket` that act locally on small regions of a circuit. We can examine the effectiveness by looking at the number of two-qubit gates before and after simplification, which tends to be a good indicator of fidelity for near-term systems where these gates are often slow and inaccurate."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import OpType\n", "from pytket.passes import FullPeepholeOptimise"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["test_circuit = ucc(arg_values)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(\"CX count before\", test_circuit.n_gates_of_type(OpType.CX))\n", "print(\"CX depth before\", test_circuit.depth_by_type(OpType.CX))"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["FullPeepholeOptimise().apply(test_circuit)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(\"CX count after FPO\", test_circuit.n_gates_of_type(OpType.CX))\n", "print(\"CX depth after FPO\", test_circuit.depth_by_type(OpType.CX))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["These simplification techniques are very general and are almost always beneficial to apply to a circuit if you want to eliminate local redundancies. But UCC ans\u00e4tze have extra structure that we can exploit further. They are defined entirely out of exponentiated tensors of Pauli matrices, giving the regular structure described by the `PauliExpBox`es. Under many circumstances, it is more efficient to not synthesise these constructions individually, but simultaneously in groups. The `PauliSimp` pass finds the description of a given circuit as a sequence of `PauliExpBox`es and resynthesises them (by default, in groups of commuting terms). This can cause great change in the overall structure and shape of the circuit, enabling the identification and elimination of non-local redundancy."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import PauliSimp"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["test_circuit = ucc(arg_values)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(\"CX count before\", test_circuit.n_gates_of_type(OpType.CX))\n", "print(\"CX depth before\", test_circuit.depth_by_type(OpType.CX))"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["PauliSimp().apply(test_circuit)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(\"CX count after PS\", test_circuit.n_gates_of_type(OpType.CX))\n", "print(\"CX depth after PS\", test_circuit.depth_by_type(OpType.CX))"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["FullPeepholeOptimise().apply(test_circuit)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(\"CX count after PS+FPO\", test_circuit.n_gates_of_type(OpType.CX))\n", "print(\"CX depth after PS+FPO\", test_circuit.depth_by_type(OpType.CX))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["To include this into our routines, we can just add the simplification passes to the objective function. The `get_operator_expectation_value` utility handles compiling to meet the requirements of the backend, so we don't have to worry about that here."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Objective function with circuit simplification:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def objective(params):\n", " circ = ucc(params)\n", " PauliSimp().apply(circ)\n", " FullPeepholeOptimise().apply(circ)\n", " return (\n", " get_operator_expectation_value(circ, hamiltonian_op, backend, n_shots=4000)\n", " + nuclear_repulsion_energy\n", " )"]}, {"cell_type": "markdown", "metadata": {}, "source": ["These circuit simplification techniques have tried to preserve the exact unitary of the circuit, but there are ways to change the unitary whilst preserving the correctness of the algorithm as a whole.
\n", "
\n", "For example, the excitation terms are generated by trotterisation of the excitation operator, and the order of the terms does not change the unitary in the limit of many trotter steps, so in this sense we are free to sequence the terms how we like and it is sensible to do this in a way that enables efficient synthesis of the circuit. Prioritising collecting terms into commuting sets is a very beneficial heuristic for this and can be performed using the `gen_term_sequence_circuit` method to group the terms together into collections of `PauliExpBox`es and the `GuidedPauliSimp` pass to utilise these sets for synthesis."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import GuidedPauliSimp\n", "from pytket.utils import gen_term_sequence_circuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def ucc(params):\n", " singles_params = {qps: params[0] * coeff for qps, coeff in singles.items()}\n", " doubles_params = {qps: params[1] * coeff for qps, coeff in doubles.items()}\n", " excitation_op = QubitPauliOperator({**singles_params, **doubles_params})\n", " reference_circ = Circuit(4).X(1).X(3)\n", " ansatz = gen_term_sequence_circuit(excitation_op, reference_circ)\n", " GuidedPauliSimp().apply(ansatz)\n", " FullPeepholeOptimise().apply(ansatz)\n", " return ansatz"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Adding these simplification routines doesn't come for free. Compiling and simplifying the circuit to achieve the best results possible can be a difficult task, which can take some time for the classical computer to perform.
\n", "
\n", "During a VQE run, we will call this objective function many times and run many measurement circuits within each, but the circuits that are run on the quantum computer are almost identical, having the same gate structure but with different gate parameters and measurements. We have already exploited this within the body of the objective function by simplifying the ansatz circuit before we call `get_operator_expectation_value`, so it is only done once per objective calculation rather than once per measurement circuit.
\n", "
\n", "We can go even further by simplifying it once outside of the objective function, and then instantiating the simplified ansatz with the parameter values needed. For this, we will construct the UCC ansatz circuit using symbolic (parametric) gates."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from sympy import symbols"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Symbolic UCC ansatz generation:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["syms = symbols(\"p0 p1 p2\")\n", "singles_a_syms = {qps: syms[0] * coeff for qps, coeff in singles_a.items()}\n", "singles_b_syms = {qps: syms[1] * coeff for qps, coeff in singles_b.items()}\n", "doubles_syms = {qps: syms[2] * coeff for qps, coeff in doubles.items()}\n", "excitation_op = QubitPauliOperator({**singles_a_syms, **singles_b_syms, **doubles_syms})\n", "ucc_ref = Circuit(4).X(1).X(3)\n", "ucc = gen_term_sequence_circuit(excitation_op, ucc_ref)\n", "GuidedPauliSimp().apply(ucc)\n", "FullPeepholeOptimise().apply(ucc)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Objective function using the symbolic ansatz:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def objective(params):\n", " circ = ucc.copy()\n", " sym_map = dict(zip(syms, params))\n", " circ.symbol_substitution(sym_map)\n", " return (\n", " get_operator_expectation_value(circ, hamiltonian_op, backend, n_shots=4000)\n", " + nuclear_repulsion_energy\n", " )"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We have now got some very good use of `pytket` for simplifying each individual circuit used in our experiment and for minimising the amount of time spent compiling, but there is still more we can do in terms of reducing the amount of work the quantum computer has to do. Currently, each (non-trivial) term in our measurement hamiltonian is measured by a different circuit within each expectation value calculation. Measurement reduction techniques exist for identifying when these observables commute and hence can be simultaneously measured, reducing the number of circuits required for the full expectation value calculation.
\n", "
\n", "This is built in to the `get_operator_expectation_value` method and can be applied by specifying a way to partition the measuremrnt terms. `PauliPartitionStrat.CommutingSets` can greatly reduce the number of measurement circuits by combining any number of terms that mutually commute. However, this involves potentially adding an arbitrary Clifford circuit to change the basis of the measurements which can be costly on NISQ devices, so `PauliPartitionStrat.NonConflictingSets` trades off some of the reduction in circuit number to guarantee that only single-qubit gates are introduced."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.partition import PauliPartitionStrat"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Objective function using measurement reduction:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def objective(params):\n", " circ = ucc.copy()\n", " sym_map = dict(zip(syms, params))\n", " circ.symbol_substitution(sym_map)\n", " return (\n", " get_operator_expectation_value(\n", " circ,\n", " operator,\n", " backend,\n", " n_shots=4000,\n", " partition_strat=PauliPartitionStrat.CommutingSets,\n", " )\n", " + nuclear_repulsion_energy\n", " )"]}, {"cell_type": "markdown", "metadata": {}, "source": ["At this point, we have completely transformed how our VQE objective function works, improving its resilience to noise, cutting the number of circuits run, and maintaining fast runtimes. In doing this, we have explored a number of the features `pytket` offers that are beneficial to VQE and the UCC method:
\n", "- high-level syntactic constructs for evolution operators;
\n", "- utility methods for easy expectation value calculations;
\n", "- both generic and domain-specific circuit simplification methods;
\n", "- symbolic circuit compilation;
\n", "- measurement reduction for expectation value calculations."]}, {"cell_type": "markdown", "metadata": {}, "source": ["For the sake of completeness, the following gives the full code for the final solution, including passing the objective function to a classical optimiser to find the ground state:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["import openfermion as of\n", "from scipy.optimize import minimize\n", "from sympy import symbols"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import AerBackend\n", "from pytket.circuit import Circuit, Qubit\n", "from pytket.partition import PauliPartitionStrat\n", "from pytket.passes import GuidedPauliSimp, FullPeepholeOptimise\n", "from pytket.pauli import Pauli, QubitPauliString\n", "from pytket.utils import get_operator_expectation_value, gen_term_sequence_circuit\n", "from pytket.utils.operators import QubitPauliOperator"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Obtain electronic Hamiltonian:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["hamiltonian = (\n", " -0.8153001706270075 * of.QubitOperator(\"\")\n", " + 0.16988452027940318 * of.QubitOperator(\"Z0\")\n", " + -0.21886306781219608 * of.QubitOperator(\"Z1\")\n", " + 0.16988452027940323 * of.QubitOperator(\"Z2\")\n", " + -0.2188630678121961 * of.QubitOperator(\"Z3\")\n", " + 0.12005143072546047 * of.QubitOperator(\"Z0 Z1\")\n", " + 0.16821198673715723 * of.QubitOperator(\"Z0 Z2\")\n", " + 0.16549431486978672 * of.QubitOperator(\"Z0 Z3\")\n", " + 0.16549431486978672 * of.QubitOperator(\"Z1 Z2\")\n", " + 0.1739537877649417 * of.QubitOperator(\"Z1 Z3\")\n", " + 0.12005143072546047 * of.QubitOperator(\"Z2 Z3\")\n", " + 0.04544288414432624 * of.QubitOperator(\"X0 X1 X2 X3\")\n", " + 0.04544288414432624 * of.QubitOperator(\"X0 X1 Y2 Y3\")\n", " + 0.04544288414432624 * of.QubitOperator(\"Y0 Y1 X2 X3\")\n", " + 0.04544288414432624 * of.QubitOperator(\"Y0 Y1 Y2 Y3\")\n", ")\n", "nuclear_repulsion_energy = 0.70556961456"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pauli_sym = {\"I\": Pauli.I, \"X\": Pauli.X, \"Y\": Pauli.Y, \"Z\": Pauli.Z}"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def qps_from_openfermion(paulis):\n", " \"\"\"Convert OpenFermion tensor of Paulis to pytket QubitPauliString.\"\"\"\n", " qlist = []\n", " plist = []\n", " for q, p in paulis:\n", " qlist.append(Qubit(q))\n", " plist.append(pauli_sym[p])\n", " return QubitPauliString(qlist, plist)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def qpo_from_openfermion(openf_op):\n", " \"\"\"Convert OpenFermion QubitOperator to pytket QubitPauliOperator.\"\"\"\n", " tk_op = dict()\n", " for term, coeff in openf_op.terms.items():\n", " string = qps_from_openfermion(term)\n", " tk_op[string] = coeff\n", " return QubitPauliOperator(tk_op)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["hamiltonian_op = qpo_from_openfermion(hamiltonian)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Obtain terms for single and double excitations:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["q = [Qubit(i) for i in range(4)]\n", "xyii = QubitPauliString([q[0], q[1]], [Pauli.X, Pauli.Y])\n", "yxii = QubitPauliString([q[0], q[1]], [Pauli.Y, Pauli.X])\n", "iixy = QubitPauliString([q[2], q[3]], [Pauli.X, Pauli.Y])\n", "iiyx = QubitPauliString([q[2], q[3]], [Pauli.Y, Pauli.X])\n", "xxxy = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.X, Pauli.Y])\n", "xxyx = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.Y, Pauli.X])\n", "xyxx = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.X, Pauli.X])\n", "yxxx = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.X, Pauli.X])\n", "yyyx = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.Y, Pauli.X])\n", "yyxy = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.X, Pauli.Y])\n", "yxyy = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.Y, Pauli.Y])\n", "xyyy = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.Y, Pauli.Y])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Symbolic UCC ansatz generation:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["syms = symbols(\"p0 p1 p2\")\n", "singles_syms = {xyii: syms[0], yxii: -syms[0], iixy: syms[1], iiyx: -syms[1]}\n", "doubles_syms = {\n", " xxxy: 0.25 * syms[2],\n", " xxyx: -0.25 * syms[2],\n", " xyxx: 0.25 * syms[2],\n", " yxxx: -0.25 * syms[2],\n", " yyyx: -0.25 * syms[2],\n", " yyxy: 0.25 * syms[2],\n", " yxyy: -0.25 * syms[2],\n", " xyyy: 0.25 * syms[2],\n", "}\n", "excitation_op = QubitPauliOperator({**singles_syms, **doubles_syms})\n", "ucc_ref = Circuit(4).X(0).X(2)\n", "ucc = gen_term_sequence_circuit(excitation_op, ucc_ref)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Circuit simplification:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["GuidedPauliSimp().apply(ucc)\n", "FullPeepholeOptimise().apply(ucc)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Connect to a simulator/device:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["backend = AerBackend()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Objective function:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def objective(params):\n", " circ = ucc.copy()\n", " sym_map = dict(zip(syms, params))\n", " circ.symbol_substitution(sym_map)\n", " return (\n", " get_operator_expectation_value(\n", " circ,\n", " hamiltonian_op,\n", " backend,\n", " n_shots=4000,\n", " partition_strat=PauliPartitionStrat.CommutingSets,\n", " )\n", " + nuclear_repulsion_energy\n", " ).real"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Optimise against the objective function:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["initial_params = [1e-4, 1e-4, 4e-1]\n", "# #result = minimize(objective, initial_params, method=\"Nelder-Mead\")\n", "# #print(\"Final parameter values\", result.x)\n", "# #print(\"Final energy value\", result.fun)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Exercises:
\n", "- Replace the `get_operator_expectation_value` call with its implementation and use this to pull the analysis for measurement reduction outside of the objective function, so our circuits can be fully determined and compiled once. This means that the `symbol_substitution` method will need to be applied to each measurement circuit instead of just the state preparation circuit.
\n", "- Use the `SpamCorrecter` class to add some mitigation of the measurement errors. Start by running the characterisation circuits first, before your main VQE loop, then apply the mitigation to each of the circuits run within the objective function.
\n", "- Change the `backend` by passing in a `Qiskit` `NoiseModel` to simulate a noisy device. Compare the accuracy of the objective function both with and without the circuit simplification. Try running a classical optimiser over the objective function and compare the convergence rates with different noise models. If you have access to a QPU, try changing the `backend` to connect to that and compare the results to the simulator."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# VQE with UCC ansatz\n","\n","**Download this notebook - {nb-download}`ucc_vqe.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:
\n","- building parameterised ansätze for variational algorithms;
\n","- compilation tools for UCC-style ansätze."]},{"cell_type":"markdown","metadata":{},"source":["This example assumes the reader is familiar with the Variational Quantum Eigensolver and its application to electronic structure problems through the Unitary Coupled Cluster approach.
\n","
\n","To run this example, you will need `pytket` and `pytket-qiskit`, as well as `openfermion`, `scipy`, and `sympy`.
\n","
\n","We will start with a basic implementation and then gradually modify it to make it faster, more general, and less noisy. The final solution is given in full at the bottom of the notebook.
\n","
\n","Suppose we have some electronic configuration problem, expressed via a physical Hamiltonian. (The Hamiltonian and excitations in this example were obtained using `qiskit-aqua` version 0.5.2 and `pyscf` for H2, bond length 0.75A, sto3g basis, Jordan-Wigner encoding, with no qubit reduction or orbital freezing.). We express it succinctly using the openfermion library:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import openfermion as of"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["hamiltonian = (\n"," -0.8153001706270075 * of.QubitOperator(\"\")\n"," + 0.16988452027940318 * of.QubitOperator(\"Z0\")\n"," + -0.21886306781219608 * of.QubitOperator(\"Z1\")\n"," + 0.16988452027940323 * of.QubitOperator(\"Z2\")\n"," + -0.2188630678121961 * of.QubitOperator(\"Z3\")\n"," + 0.12005143072546047 * of.QubitOperator(\"Z0 Z1\")\n"," + 0.16821198673715723 * of.QubitOperator(\"Z0 Z2\")\n"," + 0.16549431486978672 * of.QubitOperator(\"Z0 Z3\")\n"," + 0.16549431486978672 * of.QubitOperator(\"Z1 Z2\")\n"," + 0.1739537877649417 * of.QubitOperator(\"Z1 Z3\")\n"," + 0.12005143072546047 * of.QubitOperator(\"Z2 Z3\")\n"," + 0.04544288414432624 * of.QubitOperator(\"X0 X1 X2 X3\")\n"," + 0.04544288414432624 * of.QubitOperator(\"X0 X1 Y2 Y3\")\n"," + 0.04544288414432624 * of.QubitOperator(\"Y0 Y1 X2 X3\")\n"," + 0.04544288414432624 * of.QubitOperator(\"Y0 Y1 Y2 Y3\")\n",")\n","nuclear_repulsion_energy = 0.70556961456"]},{"cell_type":"markdown","metadata":{},"source":["We would like to define our ansatz for arbitrary parameter values. For simplicity, let's start with a Hardware Efficient Ansatz."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit"]},{"cell_type":"markdown","metadata":{},"source":["Hardware efficient ansatz:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def hea(params):\n"," ansatz = Circuit(4)\n"," for i in range(4):\n"," ansatz.Ry(params[i], i)\n"," for i in range(3):\n"," ansatz.CX(i, i + 1)\n"," for i in range(4):\n"," ansatz.Ry(params[4 + i], i)\n"," return ansatz"]},{"cell_type":"markdown","metadata":{},"source":["We can use this to build the objective function for our optimisation."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import AerBackend\n","from pytket.utils.expectations import expectation_from_counts"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()"]},{"cell_type":"markdown","metadata":{},"source":["Naive objective function:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def objective(params):\n"," energy = 0\n"," for term, coeff in hamiltonian.terms.items():\n"," if not term:\n"," energy += coeff\n"," continue\n"," circ = hea(params)\n"," circ.add_c_register(\"c\", len(term))\n"," for i, (q, pauli) in enumerate(term):\n"," if pauli == \"X\":\n"," circ.H(q)\n"," elif pauli == \"Y\":\n"," circ.V(q)\n"," circ.Measure(q, i)\n"," compiled_circ = backend.get_compiled_circuit(circ)\n"," counts = backend.run_circuit(compiled_circ, n_shots=4000).get_counts()\n"," energy += coeff * expectation_from_counts(counts)\n"," return energy + nuclear_repulsion_energy"]},{"cell_type":"markdown","metadata":{},"source":["This objective function is then run through a classical optimiser to find the set of parameter values that minimise the energy of the system. For the sake of example, we will just run this with a single parameter value."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["arg_values = [\n"," -7.31158201e-02,\n"," -1.64514836e-04,\n"," 1.12585591e-03,\n"," -2.58367544e-03,\n"," 1.00006068e00,\n"," -1.19551357e-03,\n"," 9.99963988e-01,\n"," 2.53283285e-03,\n","]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["energy = objective(arg_values)\n","print(energy)"]},{"cell_type":"markdown","metadata":{},"source":["The HEA is designed to cram as many orthogonal degrees of freedom into a small circuit as possible to be able to explore a large region of the Hilbert space whilst the circuits themselves can be run with minimal noise. These ansätze give virtually-optimal circuits by design, but suffer from an excessive number of variational parameters making convergence slow, barren plateaus where the classical optimiser fails to make progress, and spanning a space where most states lack a physical interpretation. These drawbacks can necessitate adding penalties and may mean that the ansatz cannot actually express the true ground state.
\n","
\n","The UCC ansatz, on the other hand, is derived from the electronic configuration. It sacrifices efficiency of the circuit for the guarantee of physical states and the variational parameters all having some meaningful effect, which helps the classical optimisation to converge.
\n","
\n","This starts by defining the terms of our single and double excitations. These would usually be generated using the orbital configurations, so we will just use a hard-coded example here for the purposes of demonstration."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Qubit\n","from pytket.pauli import Pauli, QubitPauliString"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["q = [Qubit(i) for i in range(4)]\n","xyii = QubitPauliString([q[0], q[1]], [Pauli.X, Pauli.Y])\n","yxii = QubitPauliString([q[0], q[1]], [Pauli.Y, Pauli.X])\n","iixy = QubitPauliString([q[2], q[3]], [Pauli.X, Pauli.Y])\n","iiyx = QubitPauliString([q[2], q[3]], [Pauli.Y, Pauli.X])\n","xxxy = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.X, Pauli.Y])\n","xxyx = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.Y, Pauli.X])\n","xyxx = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.X, Pauli.X])\n","yxxx = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.X, Pauli.X])\n","yyyx = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.Y, Pauli.X])\n","yyxy = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.X, Pauli.Y])\n","yxyy = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.Y, Pauli.Y])\n","xyyy = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.Y, Pauli.Y])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["singles_a = {xyii: 1.0, yxii: -1.0}\n","singles_b = {iixy: 1.0, iiyx: -1.0}\n","doubles = {\n"," xxxy: 0.25,\n"," xxyx: -0.25,\n"," xyxx: 0.25,\n"," yxxx: -0.25,\n"," yyyx: -0.25,\n"," yyxy: 0.25,\n"," yxyy: -0.25,\n"," xyyy: 0.25,\n","}"]},{"cell_type":"markdown","metadata":{},"source":["Building the ansatz circuit itself is often done naively by defining the map from each term down to basic gates and then applying it to each term."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def add_operator_term(circuit: Circuit, term: QubitPauliString, angle: float):\n"," qubits = []\n"," for q, p in term.map.items():\n"," if p != Pauli.I:\n"," qubits.append(q)\n"," if p == Pauli.X:\n"," circuit.H(q)\n"," elif p == Pauli.Y:\n"," circuit.V(q)\n"," for i in range(len(qubits) - 1):\n"," circuit.CX(i, i + 1)\n"," circuit.Rz(angle, len(qubits) - 1)\n"," for i in reversed(range(len(qubits) - 1)):\n"," circuit.CX(i, i + 1)\n"," for q, p in term.map.items():\n"," if p == Pauli.X:\n"," circuit.H(q)\n"," elif p == Pauli.Y:\n"," circuit.Vdg(q)"]},{"cell_type":"markdown","metadata":{},"source":["Unitary Coupled Cluster Singles & Doubles ansatz:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def ucc(params):\n"," ansatz = Circuit(4)\n"," # Set initial reference state\n"," ansatz.X(1).X(3)\n"," # Evolve by excitations\n"," for term, coeff in singles_a.items():\n"," add_operator_term(ansatz, term, coeff * params[0])\n"," for term, coeff in singles_b.items():\n"," add_operator_term(ansatz, term, coeff * params[1])\n"," for term, coeff in doubles.items():\n"," add_operator_term(ansatz, term, coeff * params[2])\n"," return ansatz"]},{"cell_type":"markdown","metadata":{},"source":["This is already quite verbose, but `pytket` has a neat shorthand construction for these operator terms using the `PauliExpBox` construction. We can then decompose these into basic gates using the `DecomposeBoxes` compiler pass."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import PauliExpBox\n","from pytket.passes import DecomposeBoxes"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def add_excitation(circ, term_dict, param):\n"," for term, coeff in term_dict.items():\n"," qubits, paulis = zip(*term.map.items())\n"," pbox = PauliExpBox(paulis, coeff * param)\n"," circ.add_gate(pbox, qubits)"]},{"cell_type":"markdown","metadata":{},"source":["UCC ansatz with syntactic shortcuts:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def ucc(params):\n"," ansatz = Circuit(4)\n"," ansatz.X(1).X(3)\n"," add_excitation(ansatz, singles_a, params[0])\n"," add_excitation(ansatz, singles_b, params[1])\n"," add_excitation(ansatz, doubles, params[2])\n"," DecomposeBoxes().apply(ansatz)\n"," return ansatz"]},{"cell_type":"markdown","metadata":{},"source":["The objective function can also be simplified using a utility method for constructing the measurement circuits and processing for expectation value calculations. For that, we convert the Hamiltonian to a pytket QubitPauliOperator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pauli_sym = {\"I\": Pauli.I, \"X\": Pauli.X, \"Y\": Pauli.Y, \"Z\": Pauli.Z}"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qps_from_openfermion(paulis):\n"," \"\"\"Convert OpenFermion tensor of Paulis to pytket QubitPauliString.\"\"\"\n"," qlist = []\n"," plist = []\n"," for q, p in paulis:\n"," qlist.append(Qubit(q))\n"," plist.append(pauli_sym[p])\n"," return QubitPauliString(qlist, plist)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qpo_from_openfermion(openf_op):\n"," \"\"\"Convert OpenFermion QubitOperator to pytket QubitPauliOperator.\"\"\"\n"," tk_op = dict()\n"," for term, coeff in openf_op.terms.items():\n"," string = qps_from_openfermion(term)\n"," tk_op[string] = coeff\n"," return QubitPauliOperator(tk_op)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["hamiltonian_op = qpo_from_openfermion(hamiltonian)"]},{"cell_type":"markdown","metadata":{},"source":["Simplified objective function using utilities:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.utils.expectations import get_operator_expectation_value"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def objective(params):\n"," circ = ucc(params)\n"," return (\n"," get_operator_expectation_value(circ, hamiltonian_op, backend, n_shots=4000)\n"," + nuclear_repulsion_energy\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["arg_values = [-3.79002933e-05, 2.42964799e-05, 4.63447157e-01]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["energy = objective(arg_values)\n","print(energy)"]},{"cell_type":"markdown","metadata":{},"source":["This is now the simplest form that this operation can take, but it isn't necessarily the most effective. When we decompose the ansatz circuit into basic gates, it is still very expensive. We can employ some of the circuit simplification passes available in `pytket` to reduce its size and improve fidelity in practice.
\n","
\n","A good example is to decompose each `PauliExpBox` into basic gates and then apply `FullPeepholeOptimise`, which defines a compilation strategy utilising all of the simplifications in `pytket` that act locally on small regions of a circuit. We can examine the effectiveness by looking at the number of two-qubit gates before and after simplification, which tends to be a good indicator of fidelity for near-term systems where these gates are often slow and inaccurate."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import OpType\n","from pytket.passes import FullPeepholeOptimise"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["test_circuit = ucc(arg_values)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"CX count before\", test_circuit.n_gates_of_type(OpType.CX))\n","print(\"CX depth before\", test_circuit.depth_by_type(OpType.CX))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["FullPeepholeOptimise().apply(test_circuit)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"CX count after FPO\", test_circuit.n_gates_of_type(OpType.CX))\n","print(\"CX depth after FPO\", test_circuit.depth_by_type(OpType.CX))"]},{"cell_type":"markdown","metadata":{},"source":["These simplification techniques are very general and are almost always beneficial to apply to a circuit if you want to eliminate local redundancies. But UCC ansätze have extra structure that we can exploit further. They are defined entirely out of exponentiated tensors of Pauli matrices, giving the regular structure described by the `PauliExpBox`es. Under many circumstances, it is more efficient to not synthesise these constructions individually, but simultaneously in groups. The `PauliSimp` pass finds the description of a given circuit as a sequence of `PauliExpBox`es and resynthesises them (by default, in groups of commuting terms). This can cause great change in the overall structure and shape of the circuit, enabling the identification and elimination of non-local redundancy."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import PauliSimp"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["test_circuit = ucc(arg_values)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"CX count before\", test_circuit.n_gates_of_type(OpType.CX))\n","print(\"CX depth before\", test_circuit.depth_by_type(OpType.CX))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["PauliSimp().apply(test_circuit)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"CX count after PS\", test_circuit.n_gates_of_type(OpType.CX))\n","print(\"CX depth after PS\", test_circuit.depth_by_type(OpType.CX))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["FullPeepholeOptimise().apply(test_circuit)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(\"CX count after PS+FPO\", test_circuit.n_gates_of_type(OpType.CX))\n","print(\"CX depth after PS+FPO\", test_circuit.depth_by_type(OpType.CX))"]},{"cell_type":"markdown","metadata":{},"source":["To include this into our routines, we can just add the simplification passes to the objective function. The `get_operator_expectation_value` utility handles compiling to meet the requirements of the backend, so we don't have to worry about that here."]},{"cell_type":"markdown","metadata":{},"source":["Objective function with circuit simplification:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def objective(params):\n"," circ = ucc(params)\n"," PauliSimp().apply(circ)\n"," FullPeepholeOptimise().apply(circ)\n"," return (\n"," get_operator_expectation_value(circ, hamiltonian_op, backend, n_shots=4000)\n"," + nuclear_repulsion_energy\n"," )"]},{"cell_type":"markdown","metadata":{},"source":["These circuit simplification techniques have tried to preserve the exact unitary of the circuit, but there are ways to change the unitary whilst preserving the correctness of the algorithm as a whole.
\n","
\n","For example, the excitation terms are generated by trotterisation of the excitation operator, and the order of the terms does not change the unitary in the limit of many trotter steps, so in this sense we are free to sequence the terms how we like and it is sensible to do this in a way that enables efficient synthesis of the circuit. Prioritising collecting terms into commuting sets is a very beneficial heuristic for this and can be performed using the `gen_term_sequence_circuit` method to group the terms together into collections of `PauliExpBox`es and the `GuidedPauliSimp` pass to utilise these sets for synthesis."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import GuidedPauliSimp\n","from pytket.utils import gen_term_sequence_circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def ucc(params):\n"," singles_params = {qps: params[0] * coeff for qps, coeff in singles.items()}\n"," doubles_params = {qps: params[1] * coeff for qps, coeff in doubles.items()}\n"," excitation_op = QubitPauliOperator({**singles_params, **doubles_params})\n"," reference_circ = Circuit(4).X(1).X(3)\n"," ansatz = gen_term_sequence_circuit(excitation_op, reference_circ)\n"," GuidedPauliSimp().apply(ansatz)\n"," FullPeepholeOptimise().apply(ansatz)\n"," return ansatz"]},{"cell_type":"markdown","metadata":{},"source":["Adding these simplification routines doesn't come for free. Compiling and simplifying the circuit to achieve the best results possible can be a difficult task, which can take some time for the classical computer to perform.
\n","
\n","During a VQE run, we will call this objective function many times and run many measurement circuits within each, but the circuits that are run on the quantum computer are almost identical, having the same gate structure but with different gate parameters and measurements. We have already exploited this within the body of the objective function by simplifying the ansatz circuit before we call `get_operator_expectation_value`, so it is only done once per objective calculation rather than once per measurement circuit.
\n","
\n","We can go even further by simplifying it once outside of the objective function, and then instantiating the simplified ansatz with the parameter values needed. For this, we will construct the UCC ansatz circuit using symbolic (parametric) gates."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from sympy import symbols"]},{"cell_type":"markdown","metadata":{},"source":["Symbolic UCC ansatz generation:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["syms = symbols(\"p0 p1 p2\")\n","singles_a_syms = {qps: syms[0] * coeff for qps, coeff in singles_a.items()}\n","singles_b_syms = {qps: syms[1] * coeff for qps, coeff in singles_b.items()}\n","doubles_syms = {qps: syms[2] * coeff for qps, coeff in doubles.items()}\n","excitation_op = QubitPauliOperator({**singles_a_syms, **singles_b_syms, **doubles_syms})\n","ucc_ref = Circuit(4).X(1).X(3)\n","ucc = gen_term_sequence_circuit(excitation_op, ucc_ref)\n","GuidedPauliSimp().apply(ucc)\n","FullPeepholeOptimise().apply(ucc)"]},{"cell_type":"markdown","metadata":{},"source":["Objective function using the symbolic ansatz:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def objective(params):\n"," circ = ucc.copy()\n"," sym_map = dict(zip(syms, params))\n"," circ.symbol_substitution(sym_map)\n"," return (\n"," get_operator_expectation_value(circ, hamiltonian_op, backend, n_shots=4000)\n"," + nuclear_repulsion_energy\n"," )"]},{"cell_type":"markdown","metadata":{},"source":["We have now got some very good use of `pytket` for simplifying each individual circuit used in our experiment and for minimising the amount of time spent compiling, but there is still more we can do in terms of reducing the amount of work the quantum computer has to do. Currently, each (non-trivial) term in our measurement hamiltonian is measured by a different circuit within each expectation value calculation. Measurement reduction techniques exist for identifying when these observables commute and hence can be simultaneously measured, reducing the number of circuits required for the full expectation value calculation.
\n","
\n","This is built in to the `get_operator_expectation_value` method and can be applied by specifying a way to partition the measuremrnt terms. `PauliPartitionStrat.CommutingSets` can greatly reduce the number of measurement circuits by combining any number of terms that mutually commute. However, this involves potentially adding an arbitrary Clifford circuit to change the basis of the measurements which can be costly on NISQ devices, so `PauliPartitionStrat.NonConflictingSets` trades off some of the reduction in circuit number to guarantee that only single-qubit gates are introduced."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.partition import PauliPartitionStrat"]},{"cell_type":"markdown","metadata":{},"source":["Objective function using measurement reduction:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def objective(params):\n"," circ = ucc.copy()\n"," sym_map = dict(zip(syms, params))\n"," circ.symbol_substitution(sym_map)\n"," return (\n"," get_operator_expectation_value(\n"," circ,\n"," operator,\n"," backend,\n"," n_shots=4000,\n"," partition_strat=PauliPartitionStrat.CommutingSets,\n"," )\n"," + nuclear_repulsion_energy\n"," )"]},{"cell_type":"markdown","metadata":{},"source":["At this point, we have completely transformed how our VQE objective function works, improving its resilience to noise, cutting the number of circuits run, and maintaining fast runtimes. In doing this, we have explored a number of the features `pytket` offers that are beneficial to VQE and the UCC method:
\n","- high-level syntactic constructs for evolution operators;
\n","- utility methods for easy expectation value calculations;
\n","- both generic and domain-specific circuit simplification methods;
\n","- symbolic circuit compilation;
\n","- measurement reduction for expectation value calculations."]},{"cell_type":"markdown","metadata":{},"source":["For the sake of completeness, the following gives the full code for the final solution, including passing the objective function to a classical optimiser to find the ground state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import openfermion as of\n","from scipy.optimize import minimize\n","from sympy import symbols"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import AerBackend\n","from pytket.circuit import Circuit, Qubit\n","from pytket.partition import PauliPartitionStrat\n","from pytket.passes import GuidedPauliSimp, FullPeepholeOptimise\n","from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils import get_operator_expectation_value, gen_term_sequence_circuit\n","from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"markdown","metadata":{},"source":["Obtain electronic Hamiltonian:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["hamiltonian = (\n"," -0.8153001706270075 * of.QubitOperator(\"\")\n"," + 0.16988452027940318 * of.QubitOperator(\"Z0\")\n"," + -0.21886306781219608 * of.QubitOperator(\"Z1\")\n"," + 0.16988452027940323 * of.QubitOperator(\"Z2\")\n"," + -0.2188630678121961 * of.QubitOperator(\"Z3\")\n"," + 0.12005143072546047 * of.QubitOperator(\"Z0 Z1\")\n"," + 0.16821198673715723 * of.QubitOperator(\"Z0 Z2\")\n"," + 0.16549431486978672 * of.QubitOperator(\"Z0 Z3\")\n"," + 0.16549431486978672 * of.QubitOperator(\"Z1 Z2\")\n"," + 0.1739537877649417 * of.QubitOperator(\"Z1 Z3\")\n"," + 0.12005143072546047 * of.QubitOperator(\"Z2 Z3\")\n"," + 0.04544288414432624 * of.QubitOperator(\"X0 X1 X2 X3\")\n"," + 0.04544288414432624 * of.QubitOperator(\"X0 X1 Y2 Y3\")\n"," + 0.04544288414432624 * of.QubitOperator(\"Y0 Y1 X2 X3\")\n"," + 0.04544288414432624 * of.QubitOperator(\"Y0 Y1 Y2 Y3\")\n",")\n","nuclear_repulsion_energy = 0.70556961456"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pauli_sym = {\"I\": Pauli.I, \"X\": Pauli.X, \"Y\": Pauli.Y, \"Z\": Pauli.Z}"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qps_from_openfermion(paulis):\n"," \"\"\"Convert OpenFermion tensor of Paulis to pytket QubitPauliString.\"\"\"\n"," qlist = []\n"," plist = []\n"," for q, p in paulis:\n"," qlist.append(Qubit(q))\n"," plist.append(pauli_sym[p])\n"," return QubitPauliString(qlist, plist)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qpo_from_openfermion(openf_op):\n"," \"\"\"Convert OpenFermion QubitOperator to pytket QubitPauliOperator.\"\"\"\n"," tk_op = dict()\n"," for term, coeff in openf_op.terms.items():\n"," string = qps_from_openfermion(term)\n"," tk_op[string] = coeff\n"," return QubitPauliOperator(tk_op)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["hamiltonian_op = qpo_from_openfermion(hamiltonian)"]},{"cell_type":"markdown","metadata":{},"source":["Obtain terms for single and double excitations:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["q = [Qubit(i) for i in range(4)]\n","xyii = QubitPauliString([q[0], q[1]], [Pauli.X, Pauli.Y])\n","yxii = QubitPauliString([q[0], q[1]], [Pauli.Y, Pauli.X])\n","iixy = QubitPauliString([q[2], q[3]], [Pauli.X, Pauli.Y])\n","iiyx = QubitPauliString([q[2], q[3]], [Pauli.Y, Pauli.X])\n","xxxy = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.X, Pauli.Y])\n","xxyx = QubitPauliString(q, [Pauli.X, Pauli.X, Pauli.Y, Pauli.X])\n","xyxx = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.X, Pauli.X])\n","yxxx = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.X, Pauli.X])\n","yyyx = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.Y, Pauli.X])\n","yyxy = QubitPauliString(q, [Pauli.Y, Pauli.Y, Pauli.X, Pauli.Y])\n","yxyy = QubitPauliString(q, [Pauli.Y, Pauli.X, Pauli.Y, Pauli.Y])\n","xyyy = QubitPauliString(q, [Pauli.X, Pauli.Y, Pauli.Y, Pauli.Y])"]},{"cell_type":"markdown","metadata":{},"source":["Symbolic UCC ansatz generation:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["syms = symbols(\"p0 p1 p2\")\n","singles_syms = {xyii: syms[0], yxii: -syms[0], iixy: syms[1], iiyx: -syms[1]}\n","doubles_syms = {\n"," xxxy: 0.25 * syms[2],\n"," xxyx: -0.25 * syms[2],\n"," xyxx: 0.25 * syms[2],\n"," yxxx: -0.25 * syms[2],\n"," yyyx: -0.25 * syms[2],\n"," yyxy: 0.25 * syms[2],\n"," yxyy: -0.25 * syms[2],\n"," xyyy: 0.25 * syms[2],\n","}\n","excitation_op = QubitPauliOperator({**singles_syms, **doubles_syms})\n","ucc_ref = Circuit(4).X(0).X(2)\n","ucc = gen_term_sequence_circuit(excitation_op, ucc_ref)"]},{"cell_type":"markdown","metadata":{},"source":["Circuit simplification:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["GuidedPauliSimp().apply(ucc)\n","FullPeepholeOptimise().apply(ucc)"]},{"cell_type":"markdown","metadata":{},"source":["Connect to a simulator/device:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()"]},{"cell_type":"markdown","metadata":{},"source":["Objective function:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def objective(params):\n"," circ = ucc.copy()\n"," sym_map = dict(zip(syms, params))\n"," circ.symbol_substitution(sym_map)\n"," return (\n"," get_operator_expectation_value(\n"," circ,\n"," hamiltonian_op,\n"," backend,\n"," n_shots=4000,\n"," partition_strat=PauliPartitionStrat.CommutingSets,\n"," )\n"," + nuclear_repulsion_energy\n"," ).real"]},{"cell_type":"markdown","metadata":{},"source":["Optimise against the objective function:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["initial_params = [1e-4, 1e-4, 4e-1]\n","# #result = minimize(objective, initial_params, method=\"Nelder-Mead\")\n","# #print(\"Final parameter values\", result.x)\n","# #print(\"Final energy value\", result.fun)"]},{"cell_type":"markdown","metadata":{},"source":["Exercises:
\n","- Replace the `get_operator_expectation_value` call with its implementation and use this to pull the analysis for measurement reduction outside of the objective function, so our circuits can be fully determined and compiled once. This means that the `symbol_substitution` method will need to be applied to each measurement circuit instead of just the state preparation circuit.
\n","- Use the `SpamCorrecter` class to add some mitigation of the measurement errors. Start by running the characterisation circuits first, before your main VQE loop, then apply the mitigation to each of the circuits run within the objective function.
\n","- Change the `backend` by passing in a `Qiskit` `NoiseModel` to simulate a noisy device. Compare the accuracy of the objective function both with and without the circuit simplification. Try running a classical optimiser over the objective function and compare the convergence rates with different noise models. If you have access to a QPU, try changing the `backend` to connect to that and compare the results to the simulator."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/backends/Forest_portability_example.ipynb b/docs/examples/backends/Forest_portability_example.ipynb index fea497a0..12cd9848 100644 --- a/docs/examples/backends/Forest_portability_example.ipynb +++ b/docs/examples/backends/Forest_portability_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Code portability and intro to forest"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The quantum hardware landscape is incredibly competitive and rapidly changing. Many full-stack quantum software platforms lock users into them in order to use the associated devices and simulators. This notebook demonstrates how `pytket` can free up your existing high-level code to be used on devices from other providers. We will take a state-preparation and evolution circuit generated using `qiskit`, and enable it to be run on several Rigetti backends.
\n", "
\n", "To use a real hardware device, this notebook should be run from a Rigetti QMI instance. Look [here](https://www.rigetti.com/qcs/docs/intro-to-qcs) for information on how to set this up. Otherwise, make sure you have QuilC and QVM running in server mode. You will need to have `pytket`, `pytket_pyquil`, and `pytket_qiskit` installed, which are all available from PyPI."]}, {"cell_type": "markdown", "metadata": {}, "source": ["We will start by using `qiskit` to build a random initial state over some qubits. (We remove the initial \"reset\" gates from the circuit since these are not recognized by the Forest backends, which assume an all-zero initial state.)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from qiskit import QuantumCircuit\n", "from qiskit.quantum_info.states.random import random_statevector"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["n_qubits = 3\n", "state = random_statevector((1 << n_qubits, 1)).data\n", "state_prep_circ = QuantumCircuit(n_qubits)\n", "state_prep_circ.initialize(state)\n", "state_prep_circ = state_prep_circ.decompose().decompose()\n", "state_prep_circ.data = [\n", " datum for datum in state_prep_circ.data if datum[0].name != \"reset\"\n", "]"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(state_prep_circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can now evolve this state under an operator for a given duration."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from qiskit.opflow import PauliTrotterEvolution\n", "from qiskit.opflow.primitive_ops import PauliSumOp\n", "from qiskit.quantum_info import Pauli"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["duration = 1.2\n", "op = PauliSumOp.from_list([(\"XXI\", 0.3), (\"YYI\", 0.5), (\"ZZZ\", -0.4)])\n", "evolved_op = (duration * op).exp_i()\n", "evolution_circ = PauliTrotterEvolution(reps=1).convert(evolved_op).to_circuit()\n", "print(evolution_circ)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["for op in evolution_circ:\n", " state_prep_circ.append(op)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Now that we have a circuit, `pytket` can take this and start operating on it directly. For example, we can apply some basic compilation passes to simplify it."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import qiskit_to_tk"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["tk_circ = qiskit_to_tk(state_prep_circ)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import (\n", " SequencePass,\n", " CliffordSimp,\n", " DecomposeBoxes,\n", " KAKDecomposition,\n", " SynthesiseTket,\n", ")"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["DecomposeBoxes().apply(tk_circ)\n", "optimise = SequencePass([KAKDecomposition(), CliffordSimp(False), SynthesiseTket()])\n", "optimise.apply(tk_circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Display the optimised circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit.display import render_circuit_jupyter"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(tk_circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The Backends in `pytket` abstract away the differences between different devices and simulators as much as possible, allowing painless switching between them. The `pytket_pyquil` package provides two Backends: `ForestBackend` encapsulates both running on physical devices via Rigetti QCS and simulating those devices on the QVM, and `ForestStateBackend` acts as a wrapper to the pyQuil Wavefunction Simulator.
\n", "
\n", "Both of these still have a few restrictions on the circuits that can be run. Each only supports a subset of the gate types available in `pytket`, and a real device or associated simulation will have restricted qubit connectivity. The Backend objects will contain a default compilation pass that will statisfy these constraints as much as possible, with minimal or no optimisation.
\n", "
\n", "The `ForestStateBackend` will allow us to view the full statevector (wavefunction) expected from a perfect execution of the circuit."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.pyquil import ForestStateBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["state_backend = ForestStateBackend()\n", "tk_circ = state_backend.get_compiled_circuit(tk_circ)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["handle = state_backend.process_circuit(tk_circ)\n", "state = state_backend.get_result(handle).get_state()\n", "print(state)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["For users who are familiar with the Forest SDK, the association of qubits to indices of bitstrings (and consequently the ordering of statevectors) used by default in `pytket` Backends differs from that described in the [Forest docs](http://docs.rigetti.com/en/stable/wavefunction_simulator.html#multi-qubit-basis-enumeration). You can recover the ordering used by the Forest systems with `BackendResult.get_state(tk_circ, basis:pytket.BasisOrder.dlo)` (see our docs on the `BasisOrder` enum for more details)."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Connecting to real devices works very similarly. Instead of obtaining the full statevector, we are only able to measure the quantum state and sample from the resulting distribution. Beyond that, the process is pretty much the same.
\n", "
\n", "The following shows how to run the circuit on the \"9q-square\" lattice. The `as_qvm` switch on the `get_qc` method will switch between connecting to the real Aspen device and the QVM, allowing you to test your code with a simulator before you reserve your slot with the device."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["tk_circ.measure_all()"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pyquil import get_qc\n", "from pytket.extensions.pyquil import ForestBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["aspen_qc = get_qc(\"9q-square\", as_qvm=True)\n", "aspen_backend = ForestBackend(aspen_qc)\n", "tk_circ = aspen_backend.get_compiled_circuit(tk_circ)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["counts = aspen_backend.run_circuit(tk_circ, 2000).get_counts()\n", "print(counts)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that attempting to connect to a live quantum device (using a `QuantumComputer` constructed with `as_qvm=False`) will fail unless it is running from a QMI instance during a reservation for the named lattice."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Code portability and intro to forest"]},{"cell_type":"markdown","metadata":{},"source":["**Download this notebook - {nb-download}`Forest_portability_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["The quantum hardware landscape is incredibly competitive and rapidly changing. Many full-stack quantum software platforms lock users into them in order to use the associated devices and simulators. This notebook demonstrates how `pytket` can free up your existing high-level code to be used on devices from other providers. We will take a state-preparation and evolution circuit generated using `qiskit`, and enable it to be run on several Rigetti backends.
\n","
\n","To use a real hardware device, this notebook should be run from a Rigetti QMI instance. Look [here](https://www.rigetti.com/qcs/docs/intro-to-qcs) for information on how to set this up. Otherwise, make sure you have QuilC and QVM running in server mode. You will need to have `pytket`, `pytket_pyquil`, and `pytket_qiskit` installed, which are all available from PyPI."]},{"cell_type":"markdown","metadata":{},"source":["We will start by using `qiskit` to build a random initial state over some qubits. (We remove the initial \"reset\" gates from the circuit since these are not recognized by the Forest backends, which assume an all-zero initial state.)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from qiskit import QuantumCircuit\n","from qiskit.quantum_info.states.random import random_statevector"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n_qubits = 3\n","state = random_statevector((1 << n_qubits, 1)).data\n","state_prep_circ = QuantumCircuit(n_qubits)\n","state_prep_circ.initialize(state)\n","state_prep_circ = state_prep_circ.decompose().decompose()\n","state_prep_circ.data = [\n"," datum for datum in state_prep_circ.data if datum[0].name != \"reset\"\n","]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(state_prep_circ)"]},{"cell_type":"markdown","metadata":{},"source":["We can now evolve this state under an operator for a given duration."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from qiskit.opflow import PauliTrotterEvolution\n","from qiskit.opflow.primitive_ops import PauliSumOp\n","from qiskit.quantum_info import Pauli"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["duration = 1.2\n","op = PauliSumOp.from_list([(\"XXI\", 0.3), (\"YYI\", 0.5), (\"ZZZ\", -0.4)])\n","evolved_op = (duration * op).exp_i()\n","evolution_circ = PauliTrotterEvolution(reps=1).convert(evolved_op).to_circuit()\n","print(evolution_circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for op in evolution_circ:\n"," state_prep_circ.append(op)"]},{"cell_type":"markdown","metadata":{},"source":["Now that we have a circuit, `pytket` can take this and start operating on it directly. For example, we can apply some basic compilation passes to simplify it."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import qiskit_to_tk"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["tk_circ = qiskit_to_tk(state_prep_circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import (\n"," SequencePass,\n"," CliffordSimp,\n"," DecomposeBoxes,\n"," KAKDecomposition,\n"," SynthesiseTket,\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["DecomposeBoxes().apply(tk_circ)\n","optimise = SequencePass([KAKDecomposition(), CliffordSimp(False), SynthesiseTket()])\n","optimise.apply(tk_circ)"]},{"cell_type":"markdown","metadata":{},"source":["Display the optimised circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit.display import render_circuit_jupyter"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(tk_circ)"]},{"cell_type":"markdown","metadata":{},"source":["The Backends in `pytket` abstract away the differences between different devices and simulators as much as possible, allowing painless switching between them. The `pytket_pyquil` package provides two Backends: `ForestBackend` encapsulates both running on physical devices via Rigetti QCS and simulating those devices on the QVM, and `ForestStateBackend` acts as a wrapper to the pyQuil Wavefunction Simulator.
\n","
\n","Both of these still have a few restrictions on the circuits that can be run. Each only supports a subset of the gate types available in `pytket`, and a real device or associated simulation will have restricted qubit connectivity. The Backend objects will contain a default compilation pass that will statisfy these constraints as much as possible, with minimal or no optimisation.
\n","
\n","The `ForestStateBackend` will allow us to view the full statevector (wavefunction) expected from a perfect execution of the circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.pyquil import ForestStateBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["state_backend = ForestStateBackend()\n","tk_circ = state_backend.get_compiled_circuit(tk_circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["handle = state_backend.process_circuit(tk_circ)\n","state = state_backend.get_result(handle).get_state()\n","print(state)"]},{"cell_type":"markdown","metadata":{},"source":["For users who are familiar with the Forest SDK, the association of qubits to indices of bitstrings (and consequently the ordering of statevectors) used by default in `pytket` Backends differs from that described in the [Forest docs](http://docs.rigetti.com/en/stable/wavefunction_simulator.html#multi-qubit-basis-enumeration). You can recover the ordering used by the Forest systems with `BackendResult.get_state(tk_circ, basis:pytket.BasisOrder.dlo)` (see our docs on the `BasisOrder` enum for more details)."]},{"cell_type":"markdown","metadata":{},"source":["Connecting to real devices works very similarly. Instead of obtaining the full statevector, we are only able to measure the quantum state and sample from the resulting distribution. Beyond that, the process is pretty much the same.
\n","
\n","The following shows how to run the circuit on the \"9q-square\" lattice. The `as_qvm` switch on the `get_qc` method will switch between connecting to the real Aspen device and the QVM, allowing you to test your code with a simulator before you reserve your slot with the device."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["tk_circ.measure_all()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pyquil import get_qc\n","from pytket.extensions.pyquil import ForestBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["aspen_qc = get_qc(\"9q-square\", as_qvm=True)\n","aspen_backend = ForestBackend(aspen_qc)\n","tk_circ = aspen_backend.get_compiled_circuit(tk_circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["counts = aspen_backend.run_circuit(tk_circ, 2000).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["Note that attempting to connect to a live quantum device (using a `QuantumComputer` constructed with `as_qvm=False`) will fail unless it is running from a QMI instance during a reservation for the named lattice."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/backends/backends_example.ipynb b/docs/examples/backends/backends_example.ipynb index dad7cbc3..d7e8d64a 100644 --- a/docs/examples/backends/backends_example.ipynb +++ b/docs/examples/backends/backends_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# TKET `Backend` tutorial"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This example shows how to use `pytket` to execute quantum circuits on both simulators and real devices, and how to interpret the results. As tket is designed to be platform-agnostic, we have unified the interfaces of different providers as much as possible into the `Backend` class for maximum portability of code."]}, {"cell_type": "markdown", "metadata": {}, "source": ["For the full list of supported backends see the pytket [extensions index page](https://tket.quantinuum.com/api-docs/extensions)."]}, {"cell_type": "markdown", "metadata": {}, "source": ["In this notebook we will focus on the Aer, IBMQ and ProjectQ backends.
\n", "
\n", "To get started, we must install the core pytket package and the subpackages required to interface with the desired providers. We will also need the `QubitOperator` class from `openfermion` to construct operators for a later example. To get everything run the following in shell:
\n", "
\n", "`pip install pytket pytket-qiskit pytket-projectq openfermion`
\n", "
\n", "First, import the backends that we will be demonstrating."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import (\n", " AerStateBackend,\n", " AerBackend,\n", " AerUnitaryBackend,\n", " IBMQBackend,\n", " IBMQEmulatorBackend,\n", ")\n", "from pytket.extensions.projectq import ProjectQBackend"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We are also going to be making a circuit to run on these backends, so import the `Circuit` class."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit, Qubit"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Below we generate a circuit which will produce a Bell state, assuming the qubits are all initialised in the |0> state:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = Circuit(2)\n", "circ.H(0)\n", "circ.CX(0, 1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["As a sanity check, we will use the `AerStateBackend` to verify that `circ` does actually produce a Bell state.
\n", "
\n", "To submit a circuit for excution on a backend we can use `process_circuit` with appropriate arguments. If we have multiple circuits to excecute, we can use `process_circuits` (note the plural), which will attempt to batch up the circuits if possible. Both methods return a `ResultHandle` object per submitted `Circuit` which you can use with result retrieval methods to get the result type you want (as long as that result type is supported by the backend).
\n", "
\n", "Calling `get_state` will return a `numpy` array corresponding to the statevector.
\n", "
\n", "This style of usage is used consistently in the `pytket` backends."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["aer_state_b = AerStateBackend()\n", "state_handle = aer_state_b.process_circuit(circ)\n", "statevector = aer_state_b.get_result(state_handle).get_state()\n", "print(statevector)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["As we can see, the output state vector $\\lvert \\psi_{\\mathrm{circ}}\\rangle$ is $(\\lvert00\\rangle + \\lvert11\\rangle)/\\sqrt2$.
\n", "
\n", "This is a symmetric state. For non-symmetric states, we default to an ILO-BE format (increasing lexicographic order of (qu)bit ids, big-endian), but an alternative convention can be specified when retrieving results from backends. See the docs for the `BasisOrder` enum for more information."]}, {"cell_type": "markdown", "metadata": {}, "source": ["A lesser-used simulator available through Qiskit Aer is their unitary simulator. This will be somewhat more expensive to run, but returns the full unitary matrix for the provided circuit. This is useful in the design of small subcircuits that will be used multiple times within other larger circuits - statevector simulators will only test that they act correctly on the $\\lvert 0 \\rangle^{\\otimes n}$ state, which is not enough to guarantee the circuit's correctness.
\n", "
\n", "The `AerUnitaryBackend` provides a convenient access point for this simulator for use with `pytket` circuits. The unitary of the circuit can be retrieved from backends that support it using the `BackendResult.get_unitary` interface. In this example, we chose to use `Backend.run_circuit`, which is equivalent to calling `process_circuit` followed by `get_result`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["aer_unitary_b = AerUnitaryBackend()\n", "result = aer_unitary_b.run_circuit(circ)\n", "print(result.get_unitary())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that state vector and unitary simulations are also available in pytket directly. In general, we recommend you use these unless you require another Backend explicitly."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["statevector = circ.get_statevector()\n", "unitary = circ.get_unitary()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Now suppose we want to measure this Bell state to get some actual results out, so let's append some `Measure` gates to the circuit. The `Circuit` class has the `measure_all` utility function which appends `Measure` gates on every qubit. All of these results will be written to the default classical register ('c'). This function will automatically add the classical bits to the circuit if they are not already there."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ.measure_all()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can get some measured counts out from the `AerBackend`, which is an interface to the Qiskit Aer QASM simulator. Suppose we would like to get 10 shots out (10 repeats of the circuit and measurement). We can seed the simulator's random-number generator in order to make the results reproducible, using an optional keyword argument to `process_circuit`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["aer_b = AerBackend()\n", "handle = aer_b.process_circuit(circ, n_shots=10, seed=1)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["counts = aer_b.get_result(handle).get_counts()\n", "print(counts)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["What happens if we simulate some noise in our imagined device, using the Qiskit Aer noise model?"]}, {"cell_type": "markdown", "metadata": {}, "source": ["To investigate this, we will require an import from Qiskit. For more information about noise modelling using Qiskit Aer, see the [Qiskit device noise](https://qiskit.org/documentation/apidoc/aer_noise.html) documentation."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from qiskit_aer.noise import NoiseModel"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["my_noise_model = NoiseModel()\n", "readout_error = 0.2\n", "for q in range(2):\n", " my_noise_model.add_readout_error(\n", " [[1 - readout_error, readout_error], [readout_error, 1 - readout_error]], [q]\n", " )"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This simple noise model gives a 20% chance that, upon measurement, a qubit that would otherwise have been measured as $0$ would instead be measured as $1$, and vice versa. Let's see what our shot table looks like with this model:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["noisy_aer_b = AerBackend(my_noise_model)\n", "noisy_handle = noisy_aer_b.process_circuit(circ, n_shots=10, seed=1, valid_check=False)\n", "noisy_counts = noisy_aer_b.get_result(noisy_handle).get_counts()\n", "print(noisy_counts)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We now have some spurious $01$ and $10$ measurements, which could never happen when measuring a Bell state on a noiseless device.
\n", "
\n", "The `AerBackend` class can accept any Qiskit noise model.
\n", "
\n", "All backends expose a generic `get_result` method which takes a `ResultHandle` and returns the respective result in the form of a `BackendResult` object. This object may hold measured results in the form of shots or counts, or an exact statevector from simulation. Measured results are stored as `OutcomeArray` objects, which compresses measured bit values into 8-bit integers. We can extract the bitwise values using `to_readouts`.
\n", "
\n", "Instead of an assumed ILO or DLO convention, we can use this object to request only the `Bit` measurements we want, in the order we want. Let's try reversing the bits of the noisy results."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["backend_result = noisy_aer_b.get_result(noisy_handle)\n", "bits = circ.bits"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["outcomes = backend_result.get_counts([bits[1], bits[0]])\n", "print(outcomes)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["`BackendResult` objects can be natively serialized to and deserialized from a dictionary. This dictionary can be immediately dumped to `json` for storing results."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.backends.backendresult import BackendResult"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["result_dict = backend_result.to_dict()\n", "print(result_dict)\n", "print(BackendResult.from_dict(result_dict).get_counts())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The last simulator we will demonstrate is the `ProjectQBackend`. ProjectQ offers fast simulation of quantum circuits with built-in support for fast expectation values from operators. The `ProjectQBackend` exposes this functionality to take in OpenFermion `QubitOperator` instances. These are convertible to and from `QubitPauliOperator` instances in Pytket.
\n", "
\n", "Note: ProjectQ can also produce statevectors in the style of `AerStateBackend`, and similarly Aer backends can calculate expectation values directly, consult the relevant documentation to see more.
\n", "
\n", "Let's create an OpenFermion `QubitOperator` object and a new circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["import openfermion as of"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["hamiltonian = 0.5 * of.QubitOperator(\"X0 X2\") + 0.3 * of.QubitOperator(\"Z0\")"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ2 = Circuit(3)\n", "circ2.Y(0)\n", "circ2.H(1)\n", "circ2.Rx(0.3, 2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We convert the OpenFermion Hamiltonian into a pytket QubitPauliOperator:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.pauli import Pauli, QubitPauliString\n", "from pytket.utils.operators import QubitPauliOperator"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pauli_sym = {\"I\": Pauli.I, \"X\": Pauli.X, \"Y\": Pauli.Y, \"Z\": Pauli.Z}"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def qps_from_openfermion(paulis):\n", " \"\"\"Convert OpenFermion tensor of Paulis to pytket QubitPauliString.\"\"\"\n", " qlist = []\n", " plist = []\n", " for q, p in paulis:\n", " qlist.append(Qubit(q))\n", " plist.append(pauli_sym[p])\n", " return QubitPauliString(qlist, plist)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def qpo_from_openfermion(openf_op):\n", " \"\"\"Convert OpenFermion QubitOperator to pytket QubitPauliOperator.\"\"\"\n", " tk_op = dict()\n", " for term, coeff in openf_op.terms.items():\n", " string = qps_from_openfermion(term)\n", " tk_op[string] = coeff\n", " return QubitPauliOperator(tk_op)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["hamiltonian_op = qpo_from_openfermion(hamiltonian)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Now we can create a `ProjectQBackend` instance and feed it our circuit and `QubitOperator`:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.utils.operators import QubitPauliOperator"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["projectq_b = ProjectQBackend()\n", "expectation = projectq_b.get_operator_expectation_value(circ2, hamiltonian_op)\n", "print(expectation)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The last leg of this tour includes running a pytket circuit on an actual quantum computer. To do this, you will need an IBM quantum experience account and have your credentials stored on your computer. See https://quantum-computing.ibm.com to make an account and view available devices and their specs.
\n", "
\n", "Physical devices have much stronger constraints on the form of admissible circuits than simulators. They tend to support a minimal gate set, have restricted connectivity between qubits for two-qubit gates, and can have limited support for classical control flow or conditional gates. This is where we can invoke the tket compiler passes to transform our desired circuit into one that is suitable for the backend.
\n", "
\n", "To check our code works correctly, we can use the `IBMQEmulatorBackend` to run our code exactly as if it were going to run on a real device, but just execute on a simulator (with a basic noise model adapted from the reported device properties)."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Let's create an `IBMQEmulatorBackend` for the `ibmq_manila` device and check if our circuit is valid to be run."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ibmq_b_emu = IBMQEmulatorBackend(\"ibmq_manila\")\n", "ibmq_b_emu.valid_circuit(circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["It looks like we need to compile this circuit to be compatible with the device. To simplify this procedure, we provide minimal compilation passes designed for each backend (the `default_compilation_pass()` method) which will guarantee compatibility with the device. These may still fail if the input circuit has too many qubits or unsupported usage of conditional gates. The default passes can have their degree of optimisation by changing an integer parameter (optimisation levels 0, 1, 2), and they can be easily composed with any of tket's other optimisation passes for better performance.
\n", "
\n", "For convenience, we also wrap up this pass into the `get_compiled_circuit` method if you just want to compile a single circuit."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["compiled_circ = ibmq_b_emu.get_compiled_circuit(circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Let's create a backend for running on the actual device and check our compiled circuit is valid for this backend too."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["ibmq_b = IBMQBackend(\"ibmq_manila\")\n", "ibmq_b.valid_circuit(compiled_circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We are now good to run this circuit on the device. After submitting, we can use the handle to check on the status of the job, so that we know when results are ready to be retrieved. The `circuit_status` method works for all backends, and returns a `CircuitStatus` object. If we just run `get_result` straight away, the backend will wait for results to complete, blocking any other code from running.
\n", "
\n", "In this notebook we will use the emulated backend `ibmq_b_emu` to illustrate, but the workflow is the same as for the real backend `ibmq_b` (except that the latter will typically take much longer because of the size of the queue)."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["quantum_handle = ibmq_b_emu.process_circuit(compiled_circ, n_shots=10)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(ibmq_b_emu.circuit_status(quantum_handle))"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["quantum_counts = ibmq_b_emu.get_result(quantum_handle).get_counts()\n", "print(quantum_counts)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["These are from an actual device, so it's impossible to perfectly predict what the results will be. However, because of the problem of noise, it would be unsurprising to find a few $01$ or $10$ results in the table. The circuit is very short, so it should be fairly close to the ideal result.
\n", "
\n", "The devices available through the IBM Q Experience serve jobs one at a time from their respective queues, so a large amount of experiment time can be taken up by waiting for your jobs to reach the front of the queue. `pytket` allows circuits to be submitted to any backend in a single batch using the `process_circuits` method. For the `IBMQBackend`, this will collate the circuits into as few jobs as possible which will all be sent off into the queue for the device. The method returns a `ResultHandle` per submitted circuit, in the order of submission."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circuits = []\n", "for i in range(5):\n", " c = Circuit(2)\n", " c.Rx(0.2 * i, 0).CX(0, 1)\n", " c.measure_all()\n", " circuits.append(ibmq_b_emu.get_compiled_circuit(c))\n", "handles = ibmq_b_emu.process_circuits(circuits, n_shots=100)\n", "print(handles)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can now retrieve the results and process them. As we measured each circuit in the $Z$-basis, we can obtain the expectation value for the $ZZ$ operator immediately from these measurement results. We can calculate this using the `expectation_from_counts` utility method in `pytket`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.utils import expectation_from_counts"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["for handle in handles:\n", " counts = ibmq_b_emu.get_result(handle).get_counts()\n", " exp_val = expectation_from_counts(counts)\n", " print(exp_val)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["A `ResultHandle` can be easily stored in its string representaton and later reconstructed using the `from_str` method. For example, we could do something like this:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.backends import ResultHandle"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(2).Rx(0.5, 0).CX(0, 1).measure_all()\n", "c = ibmq_b_emu.get_compiled_circuit(c)\n", "handle = ibmq_b_emu.process_circuit(c, n_shots=10)\n", "handlestring = str(handle)\n", "print(handlestring)\n", "# ... later ...\n", "oldhandle = ResultHandle.from_str(handlestring)\n", "print(ibmq_b_emu.get_result(oldhandle).get_counts())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["For backends which support persistent handles (e.g. `IBMQBackend`, `QuantinuumBackend`, `BraketBackend` and `AQTBackend`) you can even stop your python session and use your result handles in a separate script to retrive results when they are ready, by storing the handle strings. For experiments with long queue times, this enables separate job submission and retrieval. Use `Backend.persistent_handles` to check whether a backend supports this feature.
\n", "
\n", "All backends will also cache all results obtained in the current python session, so you can use the `ResultHandle` to retrieve the results many times if you need to reuse the results. Over a long experiment, this can consume a large amount of RAM, so we recommend removing results from the cache when you are done with them. A simple way to achieve this is by calling `Backend.empty_cache` (e.g. at the end of each loop of a variational algorithm), or removing individual results with `Backend.pop_result`."]}, {"cell_type": "markdown", "metadata": {}, "source": ["The backends in `pytket` are designed to be as similar to one another as possible. The example above using physical devices can be run entirely on a simulator by swapping out the `IBMQBackend` constructor for any other backend supporting shot outputs (e.g. `AerBackend`, `ProjectQBackend`, `ForestBackend`), or passing it the name of a different device. Furthermore, using pytket it is simple to convert between handling shot tables, counts maps and statevectors.
\n", "
\n", "For more information on backends and other `pytket` features, read our [documentation](https://tket.quantinuum.com/api-docs/) or see the other pytket examples."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# TKET `Backend` tutorial\n","\n","**Download this notebook -> {nb-download}`backends_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["This example shows how to use `pytket` to execute quantum circuits on both simulators and real devices, and how to interpret the results. As tket is designed to be platform-agnostic, we have unified the interfaces of different providers as much as possible into the `Backend` class for maximum portability of code."]},{"cell_type":"markdown","metadata":{},"source":["For the full list of supported backends see the pytket [extensions index page](https://tket.quantinuum.com/api-docs/extensions)."]},{"cell_type":"markdown","metadata":{},"source":["In this notebook we will focus on the Aer, IBMQ and ProjectQ backends.
\n","
\n","To get started, we must install the core pytket package and the subpackages required to interface with the desired providers. We will also need the `QubitOperator` class from `openfermion` to construct operators for a later example. To get everything run the following in shell:
\n","
\n","`pip install pytket pytket-qiskit pytket-projectq openfermion`
\n","
\n","First, import the backends that we will be demonstrating."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import (\n"," AerStateBackend,\n"," AerBackend,\n"," AerUnitaryBackend,\n"," IBMQBackend,\n"," IBMQEmulatorBackend,\n",")\n","from pytket.extensions.projectq import ProjectQBackend"]},{"cell_type":"markdown","metadata":{},"source":["We are also going to be making a circuit to run on these backends, so import the `Circuit` class."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit, Qubit"]},{"cell_type":"markdown","metadata":{},"source":["Below we generate a circuit which will produce a Bell state, assuming the qubits are all initialised in the |0> state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(2)\n","circ.H(0)\n","circ.CX(0, 1)"]},{"cell_type":"markdown","metadata":{},"source":["As a sanity check, we will use the `AerStateBackend` to verify that `circ` does actually produce a Bell state.
\n","
\n","To submit a circuit for excution on a backend we can use `process_circuit` with appropriate arguments. If we have multiple circuits to excecute, we can use `process_circuits` (note the plural), which will attempt to batch up the circuits if possible. Both methods return a `ResultHandle` object per submitted `Circuit` which you can use with result retrieval methods to get the result type you want (as long as that result type is supported by the backend).
\n","
\n","Calling `get_state` will return a `numpy` array corresponding to the statevector.
\n","
\n","This style of usage is used consistently in the `pytket` backends."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["aer_state_b = AerStateBackend()\n","state_handle = aer_state_b.process_circuit(circ)\n","statevector = aer_state_b.get_result(state_handle).get_state()\n","print(statevector)"]},{"cell_type":"markdown","metadata":{},"source":["As we can see, the output state vector $\\lvert \\psi_{\\mathrm{circ}}\\rangle$ is $(\\lvert00\\rangle + \\lvert11\\rangle)/\\sqrt2$.
\n","
\n","This is a symmetric state. For non-symmetric states, we default to an ILO-BE format (increasing lexicographic order of (qu)bit ids, big-endian), but an alternative convention can be specified when retrieving results from backends. See the docs for the `BasisOrder` enum for more information."]},{"cell_type":"markdown","metadata":{},"source":["A lesser-used simulator available through Qiskit Aer is their unitary simulator. This will be somewhat more expensive to run, but returns the full unitary matrix for the provided circuit. This is useful in the design of small subcircuits that will be used multiple times within other larger circuits - statevector simulators will only test that they act correctly on the $\\lvert 0 \\rangle^{\\otimes n}$ state, which is not enough to guarantee the circuit's correctness.
\n","
\n","The `AerUnitaryBackend` provides a convenient access point for this simulator for use with `pytket` circuits. The unitary of the circuit can be retrieved from backends that support it using the `BackendResult.get_unitary` interface. In this example, we chose to use `Backend.run_circuit`, which is equivalent to calling `process_circuit` followed by `get_result`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["aer_unitary_b = AerUnitaryBackend()\n","result = aer_unitary_b.run_circuit(circ)\n","print(result.get_unitary())"]},{"cell_type":"markdown","metadata":{},"source":["Note that state vector and unitary simulations are also available in pytket directly. In general, we recommend you use these unless you require another Backend explicitly."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["statevector = circ.get_statevector()\n","unitary = circ.get_unitary()"]},{"cell_type":"markdown","metadata":{},"source":["Now suppose we want to measure this Bell state to get some actual results out, so let's append some `Measure` gates to the circuit. The `Circuit` class has the `measure_all` utility function which appends `Measure` gates on every qubit. All of these results will be written to the default classical register ('c'). This function will automatically add the classical bits to the circuit if they are not already there."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.measure_all()"]},{"cell_type":"markdown","metadata":{},"source":["We can get some measured counts out from the `AerBackend`, which is an interface to the Qiskit Aer QASM simulator. Suppose we would like to get 10 shots out (10 repeats of the circuit and measurement). We can seed the simulator's random-number generator in order to make the results reproducible, using an optional keyword argument to `process_circuit`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["aer_b = AerBackend()\n","handle = aer_b.process_circuit(circ, n_shots=10, seed=1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["counts = aer_b.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["What happens if we simulate some noise in our imagined device, using the Qiskit Aer noise model?"]},{"cell_type":"markdown","metadata":{},"source":["To investigate this, we will require an import from Qiskit. For more information about noise modelling using Qiskit Aer, see the [Qiskit device noise](https://qiskit.org/documentation/apidoc/aer_noise.html) documentation."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from qiskit_aer.noise import NoiseModel"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["my_noise_model = NoiseModel()\n","readout_error = 0.2\n","for q in range(2):\n"," my_noise_model.add_readout_error(\n"," [[1 - readout_error, readout_error], [readout_error, 1 - readout_error]], [q]\n"," )"]},{"cell_type":"markdown","metadata":{},"source":["This simple noise model gives a 20% chance that, upon measurement, a qubit that would otherwise have been measured as $0$ would instead be measured as $1$, and vice versa. Let's see what our shot table looks like with this model:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["noisy_aer_b = AerBackend(my_noise_model)\n","noisy_handle = noisy_aer_b.process_circuit(circ, n_shots=10, seed=1, valid_check=False)\n","noisy_counts = noisy_aer_b.get_result(noisy_handle).get_counts()\n","print(noisy_counts)"]},{"cell_type":"markdown","metadata":{},"source":["We now have some spurious $01$ and $10$ measurements, which could never happen when measuring a Bell state on a noiseless device.
\n","
\n","The `AerBackend` class can accept any Qiskit noise model.
\n","
\n","All backends expose a generic `get_result` method which takes a `ResultHandle` and returns the respective result in the form of a `BackendResult` object. This object may hold measured results in the form of shots or counts, or an exact statevector from simulation. Measured results are stored as `OutcomeArray` objects, which compresses measured bit values into 8-bit integers. We can extract the bitwise values using `to_readouts`.
\n","
\n","Instead of an assumed ILO or DLO convention, we can use this object to request only the `Bit` measurements we want, in the order we want. Let's try reversing the bits of the noisy results."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend_result = noisy_aer_b.get_result(noisy_handle)\n","bits = circ.bits"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["outcomes = backend_result.get_counts([bits[1], bits[0]])\n","print(outcomes)"]},{"cell_type":"markdown","metadata":{},"source":["`BackendResult` objects can be natively serialized to and deserialized from a dictionary. This dictionary can be immediately dumped to `json` for storing results."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.backends.backendresult import BackendResult"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["result_dict = backend_result.to_dict()\n","print(result_dict)\n","print(BackendResult.from_dict(result_dict).get_counts())"]},{"cell_type":"markdown","metadata":{},"source":["The last simulator we will demonstrate is the `ProjectQBackend`. ProjectQ offers fast simulation of quantum circuits with built-in support for fast expectation values from operators. The `ProjectQBackend` exposes this functionality to take in OpenFermion `QubitOperator` instances. These are convertible to and from `QubitPauliOperator` instances in Pytket.
\n","
\n","Note: ProjectQ can also produce statevectors in the style of `AerStateBackend`, and similarly Aer backends can calculate expectation values directly, consult the relevant documentation to see more.
\n","
\n","Let's create an OpenFermion `QubitOperator` object and a new circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import openfermion as of"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["hamiltonian = 0.5 * of.QubitOperator(\"X0 X2\") + 0.3 * of.QubitOperator(\"Z0\")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ2 = Circuit(3)\n","circ2.Y(0)\n","circ2.H(1)\n","circ2.Rx(0.3, 2)"]},{"cell_type":"markdown","metadata":{},"source":["We convert the OpenFermion Hamiltonian into a pytket QubitPauliOperator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pauli_sym = {\"I\": Pauli.I, \"X\": Pauli.X, \"Y\": Pauli.Y, \"Z\": Pauli.Z}"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qps_from_openfermion(paulis):\n"," \"\"\"Convert OpenFermion tensor of Paulis to pytket QubitPauliString.\"\"\"\n"," qlist = []\n"," plist = []\n"," for q, p in paulis:\n"," qlist.append(Qubit(q))\n"," plist.append(pauli_sym[p])\n"," return QubitPauliString(qlist, plist)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def qpo_from_openfermion(openf_op):\n"," \"\"\"Convert OpenFermion QubitOperator to pytket QubitPauliOperator.\"\"\"\n"," tk_op = dict()\n"," for term, coeff in openf_op.terms.items():\n"," string = qps_from_openfermion(term)\n"," tk_op[string] = coeff\n"," return QubitPauliOperator(tk_op)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["hamiltonian_op = qpo_from_openfermion(hamiltonian)"]},{"cell_type":"markdown","metadata":{},"source":["Now we can create a `ProjectQBackend` instance and feed it our circuit and `QubitOperator`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["projectq_b = ProjectQBackend()\n","expectation = projectq_b.get_operator_expectation_value(circ2, hamiltonian_op)\n","print(expectation)"]},{"cell_type":"markdown","metadata":{},"source":["The last leg of this tour includes running a pytket circuit on an actual quantum computer. To do this, you will need an IBM quantum experience account and have your credentials stored on your computer. See https://quantum-computing.ibm.com to make an account and view available devices and their specs.
\n","
\n","Physical devices have much stronger constraints on the form of admissible circuits than simulators. They tend to support a minimal gate set, have restricted connectivity between qubits for two-qubit gates, and can have limited support for classical control flow or conditional gates. This is where we can invoke the tket compiler passes to transform our desired circuit into one that is suitable for the backend.
\n","
\n","To check our code works correctly, we can use the `IBMQEmulatorBackend` to run our code exactly as if it were going to run on a real device, but just execute on a simulator (with a basic noise model adapted from the reported device properties)."]},{"cell_type":"markdown","metadata":{},"source":["Let's create an `IBMQEmulatorBackend` for the `ibmq_manila` device and check if our circuit is valid to be run."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ibmq_b_emu = IBMQEmulatorBackend(\"ibmq_manila\")\n","ibmq_b_emu.valid_circuit(circ)"]},{"cell_type":"markdown","metadata":{},"source":["It looks like we need to compile this circuit to be compatible with the device. To simplify this procedure, we provide minimal compilation passes designed for each backend (the `default_compilation_pass()` method) which will guarantee compatibility with the device. These may still fail if the input circuit has too many qubits or unsupported usage of conditional gates. The default passes can have their degree of optimisation by changing an integer parameter (optimisation levels 0, 1, 2), and they can be easily composed with any of tket's other optimisation passes for better performance.
\n","
\n","For convenience, we also wrap up this pass into the `get_compiled_circuit` method if you just want to compile a single circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["compiled_circ = ibmq_b_emu.get_compiled_circuit(circ)"]},{"cell_type":"markdown","metadata":{},"source":["Let's create a backend for running on the actual device and check our compiled circuit is valid for this backend too."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["ibmq_b = IBMQBackend(\"ibmq_manila\")\n","ibmq_b.valid_circuit(compiled_circ)"]},{"cell_type":"markdown","metadata":{},"source":["We are now good to run this circuit on the device. After submitting, we can use the handle to check on the status of the job, so that we know when results are ready to be retrieved. The `circuit_status` method works for all backends, and returns a `CircuitStatus` object. If we just run `get_result` straight away, the backend will wait for results to complete, blocking any other code from running.
\n","
\n","In this notebook we will use the emulated backend `ibmq_b_emu` to illustrate, but the workflow is the same as for the real backend `ibmq_b` (except that the latter will typically take much longer because of the size of the queue)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["quantum_handle = ibmq_b_emu.process_circuit(compiled_circ, n_shots=10)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(ibmq_b_emu.circuit_status(quantum_handle))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["quantum_counts = ibmq_b_emu.get_result(quantum_handle).get_counts()\n","print(quantum_counts)"]},{"cell_type":"markdown","metadata":{},"source":["These are from an actual device, so it's impossible to perfectly predict what the results will be. However, because of the problem of noise, it would be unsurprising to find a few $01$ or $10$ results in the table. The circuit is very short, so it should be fairly close to the ideal result.
\n","
\n","The devices available through the IBM Q Experience serve jobs one at a time from their respective queues, so a large amount of experiment time can be taken up by waiting for your jobs to reach the front of the queue. `pytket` allows circuits to be submitted to any backend in a single batch using the `process_circuits` method. For the `IBMQBackend`, this will collate the circuits into as few jobs as possible which will all be sent off into the queue for the device. The method returns a `ResultHandle` per submitted circuit, in the order of submission."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circuits = []\n","for i in range(5):\n"," c = Circuit(2)\n"," c.Rx(0.2 * i, 0).CX(0, 1)\n"," c.measure_all()\n"," circuits.append(ibmq_b_emu.get_compiled_circuit(c))\n","handles = ibmq_b_emu.process_circuits(circuits, n_shots=100)\n","print(handles)"]},{"cell_type":"markdown","metadata":{},"source":["We can now retrieve the results and process them. As we measured each circuit in the $Z$-basis, we can obtain the expectation value for the $ZZ$ operator immediately from these measurement results. We can calculate this using the `expectation_from_counts` utility method in `pytket`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.utils import expectation_from_counts"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["for handle in handles:\n"," counts = ibmq_b_emu.get_result(handle).get_counts()\n"," exp_val = expectation_from_counts(counts)\n"," print(exp_val)"]},{"cell_type":"markdown","metadata":{},"source":["A `ResultHandle` can be easily stored in its string representaton and later reconstructed using the `from_str` method. For example, we could do something like this:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.backends import ResultHandle"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(2).Rx(0.5, 0).CX(0, 1).measure_all()\n","c = ibmq_b_emu.get_compiled_circuit(c)\n","handle = ibmq_b_emu.process_circuit(c, n_shots=10)\n","handlestring = str(handle)\n","print(handlestring)\n","# ... later ...\n","oldhandle = ResultHandle.from_str(handlestring)\n","print(ibmq_b_emu.get_result(oldhandle).get_counts())"]},{"cell_type":"markdown","metadata":{},"source":["For backends which support persistent handles (e.g. `IBMQBackend`, `QuantinuumBackend`, `BraketBackend` and `AQTBackend`) you can even stop your python session and use your result handles in a separate script to retrive results when they are ready, by storing the handle strings. For experiments with long queue times, this enables separate job submission and retrieval. Use `Backend.persistent_handles` to check whether a backend supports this feature.
\n","
\n","All backends will also cache all results obtained in the current python session, so you can use the `ResultHandle` to retrieve the results many times if you need to reuse the results. Over a long experiment, this can consume a large amount of RAM, so we recommend removing results from the cache when you are done with them. A simple way to achieve this is by calling `Backend.empty_cache` (e.g. at the end of each loop of a variational algorithm), or removing individual results with `Backend.pop_result`."]},{"cell_type":"markdown","metadata":{},"source":["The backends in `pytket` are designed to be as similar to one another as possible. The example above using physical devices can be run entirely on a simulator by swapping out the `IBMQBackend` constructor for any other backend supporting shot outputs (e.g. `AerBackend`, `ProjectQBackend`, `ForestBackend`), or passing it the name of a different device. Furthermore, using pytket it is simple to convert between handling shot tables, counts maps and statevectors.
\n","
\n","For more information on backends and other `pytket` features, read our [documentation](https://tket.quantinuum.com/api-docs/) or see the other pytket examples."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/backends/comparing_simulators.ipynb b/docs/examples/backends/comparing_simulators.ipynb index 2ba2bda3..34aa07fe 100644 --- a/docs/examples/backends/comparing_simulators.ipynb +++ b/docs/examples/backends/comparing_simulators.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Comparison of the simulators available through TKET"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In this tutorial, we will focus on:
\n", "- exploring the wide array of simulators available through the extension modules for `pytket`;
\n", "- comparing their unique features and capabilities."]}, {"cell_type": "markdown", "metadata": {}, "source": ["This example assumes the reader is familiar with the basics of circuit construction and evaluation.
\n", "
\n", "To run every option in this example, you will need `pytket`, `pytket-qiskit`, `pytket-pyquil`, `pytket-qsharp`, `pytket-qulacs`, and `pytket-projectq`.
\n", "
\n", "With the number of simulator `Backend`s available across the `pytket` extension modules, we are often asked why to use one over another. Surely, any two simulators are equivalent if they are able to sample the circuits in the same way, right? Not quite. In this notebook we go through each of the simulators in turn and describe what sets them apart from others and how to make use of any unique features.
\n", "
\n", "But first, to demonstrate the significant overlap in functionality, we'll just give some examples of common usage for different types of backends."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Sampling simulator usage"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit\n", "from pytket.extensions.qiskit import AerBackend"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Define a circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(3, 3)\n", "c.Ry(0.7, 0)\n", "c.CX(0, 1)\n", "c.X(2)\n", "c.measure_all()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Run on the backend:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["backend = AerBackend()\n", "c = backend.get_compiled_circuit(c)\n", "handle = backend.process_circuit(c, n_shots=2000)\n", "counts = backend.get_result(handle).get_counts()\n", "print(counts)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Statevector simulator usage"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit\n", "from pytket.extensions.qiskit import AerStateBackend"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Build a quantum state:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(3)\n", "c.H(0).CX(0, 1)\n", "c.Rz(0.3, 0)\n", "c.Rz(-0.3, 1)\n", "c.Ry(0.8, 2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Examine the statevector:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["backend = AerStateBackend()\n", "c = backend.get_compiled_circuit(c)\n", "handle = backend.process_circuit(c)\n", "state = backend.get_result(handle).get_state()\n", "print(state)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Expectation value usage"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit, Qubit\n", "from pytket.extensions.qiskit import AerBackend, AerStateBackend\n", "from pytket.pauli import Pauli, QubitPauliString\n", "from pytket.utils.operators import QubitPauliOperator"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Build a quantum state:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(3)\n", "c.H(0).CX(0, 1)\n", "c.Rz(0.3, 0)\n", "c.Rz(-0.3, 1)\n", "c.Ry(0.8, 2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Define the measurement operator:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["xxi = QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X})\n", "zzz = QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z, Qubit(2): Pauli.Z})\n", "op = QubitPauliOperator({xxi: -1.8, zzz: 0.7})"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Run on the backend:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["backend = AerBackend()\n", "c = backend.get_compiled_circuit(c)\n", "exp = backend.get_operator_expectation_value(c, op)\n", "print(exp)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `AerBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["`AerBackend` wraps up the `qasm_simulator` from the Qiskit Aer package. It supports an extremely flexible set of circuits and uses many effective simulation methods making it a great all-purpose sampling simulator.
\n", "
\n", "Unique features:
\n", "- supports mid-circuit measurement and OpenQASM-style conditional gates;
\n", "- encompasses a variety of underlying simulation methods and automatically selects the best one for each circuit (including statevector, density matrix, (extended) stabilizer and matrix product state);
\n", "- can be provided with a `qiskit.providers.Aer.noise.NoiseModel` on instantiation to perform a noisy simulation."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Useful features:
\n", "- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit\n", "from pytket.extensions.qiskit import AerBackend\n", "from itertools import combinations\n", "from qiskit_aer.noise import NoiseModel, depolarizing_error"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Quantum teleportation circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit()\n", "alice = c.add_q_register(\"a\", 2)\n", "bob = c.add_q_register(\"b\", 1)\n", "data = c.add_c_register(\"d\", 2)\n", "final = c.add_c_register(\"f\", 1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Start in an interesting state:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.Rx(0.3, alice[0])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Set up a Bell state between Alice and Bob:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.H(alice[1]).CX(alice[1], bob[0])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Measure Alice's qubits in the Bell basis:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.CX(alice[0], alice[1]).H(alice[0])\n", "c.Measure(alice[0], data[0])\n", "c.Measure(alice[1], data[1])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Correct Bob's qubit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=1)\n", "c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3)\n", "c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=2)\n", "c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Measure Bob's qubit to observe the interesting state:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.Measure(bob[0], final[0])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Set up a noisy simulator:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["model = NoiseModel()\n", "dep_err = depolarizing_error(0.04, 2)\n", "for i, j in combinations(range(3), r=2):\n", " model.add_quantum_error(dep_err, [\"cx\"], [i, j])\n", " model.add_quantum_error(dep_err, [\"cx\"], [j, i])\n", "backend = AerBackend(noise_model=model)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Run circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = backend.get_compiled_circuit(c)\n", "handle = backend.process_circuit(c, n_shots=2000)\n", "result = backend.get_result(handle)\n", "counts = result.get_counts([final[0]])\n", "print(counts)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `AerStateBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["`AerStateBackend` provides access to Qiskit Aer's `statevector_simulator`. It supports a similarly large gate set and has competitive speed for statevector simulations.
\n", "
\n", "Useful features:
\n", "- no dependency on external executables, making it easy to install and run on any computer;
\n", "- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `AerUnitaryBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Finishing the set of simulators from Qiskit Aer, `AerUnitaryBackend` captures the `unitary_simulator`, allowing for the entire unitary of a pure quantum process to be calculated. This is especially useful for testing small subcircuits that will be used many times in a larger computation.
\n", "
\n", "Unique features:
\n", "- provides the full unitary matrix for a pure quantum circuit."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit\n", "from pytket.extensions.qiskit import AerUnitaryBackend\n", "from pytket.predicates import NoClassicalControlPredicate"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Define a simple quantum incrementer:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(3)\n", "c.CCX(2, 1, 0)\n", "c.CX(2, 1)\n", "c.X(2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Examine the unitary:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["backend = AerUnitaryBackend()\n", "c = backend.get_compiled_circuit(c)\n", "result = backend.run_circuit(c)\n", "unitary = result.get_unitary()\n", "print(unitary.round(1).real)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `ForestBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Whilst it can, with suitable credentials, be used to access the Rigetti QPUs, the `ForestBackend` also features a simulator mode which turns it into a noiseless sampling simulator that matches the constraints of the simulated device (e.g. the same gate set, restricted connectivity, measurement model, etc.). This is useful when playing around with custom compilation strategies to ensure that your final circuits are suitable to run on the device and for checking that your overall program works fine before you invest in reserving a QPU.
\n", "
\n", "Unique features:
\n", "- faithful recreation of the circuit constraints of Rigetti QPUs."]}, {"cell_type": "markdown", "metadata": {}, "source": ["If trying to use the `ForestBackend` locally (i.e. not on a Rigetti QMI), you will need to have `quilc` and `qvm` running as separate processes in server mode. One easy way of doing this is with `docker` (see the `quilc` and `qvm` documentation for alternative methods of running them):
\n", "`docker run --rm -it -p 5555:5555 rigetti/quilc -R`
\n", "`docker run --rm -it -p 5000:5000 rigetti/qvm -S`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `ForestStateBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The Rigetti `pyquil` package also provides the `WavefunctionSimulator`, which we present as the `ForestStateBackend`. Functionally, it is very similar to the `AerStateBackend` so can be used interchangeably. It does require that `quilc` and `qvm` are running as separate processes when not running on a Rigetti QMI.
\n", "
\n", "Useful features:
\n", "- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `QsharpSimulatorBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `QsharpSimulatorBackend` is another basic sampling simulator that is interchangeable with others, using the Microsoft QDK simulator. Note that the `pytket-qsharp` package is dependent on the `dotnet` SDK and `iqsharp` tool. Please consult the `pytket-qsharp` installation instructions for recommendations."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `QsharpToffoliSimulatorBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Toffoli circuits form a strict fragment of quantum circuits and can be efficiently simulated. The `QsharpToffoliSimulatorBackend` can only operate on these circuits, but scales much better with system size than regular simulators.
\n", "
\n", "Unique features:
\n", "- efficient simulation of Toffoli circuits."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit\n", "from pytket.extensions.qsharp import QsharpToffoliSimulatorBackend"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Define a circuit - start in a basis state:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(3)\n", "c.X(0).X(2)\n", "# Define a circuit - incrementer\n", "c.CCX(2, 1, 0)\n", "c.CX(2, 1)\n", "c.X(2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Run on the backend:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["backend = QsharpToffoliSimulatorBackend()\n", "c = backend.get_compiled_circuit(c)\n", "handle = backend.process_circuit(c, n_shots=10)\n", "counts = backend.get_result(handle).get_counts()\n", "print(counts)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `QsharpEstimatorBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `QsharpEstimatorBackend` is not strictly a simulator, as it doesn't model the state of the quantum system and try to identify the final state, but instead analyses the circuit to estimate the required resources to run it. It does not support any of the regular outcome types (e.g. shots, counts, statevector), just the summary of the estimated resources.
\n", "
\n", "Unique features:
\n", "- estimates resources to perform the circuit, without actually simulating/running it."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit"]}, {"cell_type": "markdown", "metadata": {}, "source": ["from pytket.extensions.qsharp import QsharpEstimatorBackend"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Define a circuit - start in a basis state:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(3)\n", "c.X(0).X(2)\n", "# Define a circuit - incrementer\n", "c.CCX(2, 1, 0)\n", "c.CX(2, 1)\n", "c.X(2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Run on the backend:"]}, {"cell_type": "markdown", "metadata": {}, "source": ["(disabled because of https://github.com/CQCL/pytket-qsharp/issues/37)
\n", "backend = QsharpEstimatorBackend()
\n", "c = backend.get_compiled_circuit(c)
\n", "handle = backend.process_circuit(c, n_shots=10)
\n", "resources = backend.get_resources(handle)
\n", "print(resources)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `QulacsBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `QulacsBackend` is an all-purpose simulator with both sampling and statevector modes, using the basic CPU simulator from Qulacs.
\n", "
\n", "Unique features:
\n", "- supports both sampling (shots/counts) and complete statevector outputs."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Useful features:
\n", "- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `QulacsGPUBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["If the GPU version of Qulacs is installed, the `QulacsGPUBackend` will use that to benefit from even faster speeds. It is very easy to get started with using a GPU, as it only requires a CUDA installation and the `qulacs-gpu` package from `pip`. Functionally, it is identical to the `QulacsBackend`, but potentially faster if you have GPU resources available.
\n", "
\n", "Unique features:
\n", "- GPU support for very fast simulation."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## `ProjectQBackend`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["ProjectQ is a popular quantum circuit simulator, thanks to its availability and ease of use. It provides a similar level of performance and features to `AerStateBackend`.
\n", "
\n", "Useful features:
\n", "- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Comparison of the simulators available through TKET\n","\n","**Download this notebook - {nb-download}`comparing_simulators.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:
\n","- exploring the wide array of simulators available through the extension modules for `pytket`;
\n","- comparing their unique features and capabilities."]},{"cell_type":"markdown","metadata":{},"source":["This example assumes the reader is familiar with the basics of circuit construction and evaluation.
\n","
\n","To run every option in this example, you will need `pytket`, `pytket-qiskit`, `pytket-pyquil`, `pytket-qsharp`, `pytket-qulacs`, and `pytket-projectq`.
\n","
\n","With the number of simulator `Backend`s available across the `pytket` extension modules, we are often asked why to use one over another. Surely, any two simulators are equivalent if they are able to sample the circuits in the same way, right? Not quite. In this notebook we go through each of the simulators in turn and describe what sets them apart from others and how to make use of any unique features.
\n","
\n","But first, to demonstrate the significant overlap in functionality, we'll just give some examples of common usage for different types of backends."]},{"cell_type":"markdown","metadata":{},"source":["## Sampling simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3, 3)\n","c.Ry(0.7, 0)\n","c.CX(0, 1)\n","c.X(2)\n","c.measure_all()"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","counts = backend.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## Statevector simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerStateBackend"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the statevector:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerStateBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c)\n","state = backend.get_result(handle).get_state()\n","print(state)"]},{"cell_type":"markdown","metadata":{},"source":["## Expectation value usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit, Qubit\n","from pytket.extensions.qiskit import AerBackend, AerStateBackend\n","from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Define the measurement operator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["xxi = QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X})\n","zzz = QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z, Qubit(2): Pauli.Z})\n","op = QubitPauliOperator({xxi: -1.8, zzz: 0.7})"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","exp = backend.get_operator_expectation_value(c, op)\n","print(exp)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerBackend` wraps up the `qasm_simulator` from the Qiskit Aer package. It supports an extremely flexible set of circuits and uses many effective simulation methods making it a great all-purpose sampling simulator.
\n","
\n","Unique features:
\n","- supports mid-circuit measurement and OpenQASM-style conditional gates;
\n","- encompasses a variety of underlying simulation methods and automatically selects the best one for each circuit (including statevector, density matrix, (extended) stabilizer and matrix product state);
\n","- can be provided with a `qiskit.providers.Aer.noise.NoiseModel` on instantiation to perform a noisy simulation."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend\n","from itertools import combinations\n","from qiskit_aer.noise import NoiseModel, depolarizing_error"]},{"cell_type":"markdown","metadata":{},"source":["Quantum teleportation circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","alice = c.add_q_register(\"a\", 2)\n","bob = c.add_q_register(\"b\", 1)\n","data = c.add_c_register(\"d\", 2)\n","final = c.add_c_register(\"f\", 1)"]},{"cell_type":"markdown","metadata":{},"source":["Start in an interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Rx(0.3, alice[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a Bell state between Alice and Bob:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.H(alice[1]).CX(alice[1], bob[0])"]},{"cell_type":"markdown","metadata":{},"source":["Measure Alice's qubits in the Bell basis:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.CX(alice[0], alice[1]).H(alice[0])\n","c.Measure(alice[0], data[0])\n","c.Measure(alice[1], data[1])"]},{"cell_type":"markdown","metadata":{},"source":["Correct Bob's qubit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=1)\n","c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=2)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3)"]},{"cell_type":"markdown","metadata":{},"source":["Measure Bob's qubit to observe the interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Measure(bob[0], final[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a noisy simulator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["model = NoiseModel()\n","dep_err = depolarizing_error(0.04, 2)\n","for i, j in combinations(range(3), r=2):\n"," model.add_quantum_error(dep_err, [\"cx\"], [i, j])\n"," model.add_quantum_error(dep_err, [\"cx\"], [j, i])\n","backend = AerBackend(noise_model=model)"]},{"cell_type":"markdown","metadata":{},"source":["Run circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","result = backend.get_result(handle)\n","counts = result.get_counts([final[0]])\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerStateBackend` provides access to Qiskit Aer's `statevector_simulator`. It supports a similarly large gate set and has competitive speed for statevector simulations.
\n","
\n","Useful features:
\n","- no dependency on external executables, making it easy to install and run on any computer;
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `AerUnitaryBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Finishing the set of simulators from Qiskit Aer, `AerUnitaryBackend` captures the `unitary_simulator`, allowing for the entire unitary of a pure quantum process to be calculated. This is especially useful for testing small subcircuits that will be used many times in a larger computation.
\n","
\n","Unique features:
\n","- provides the full unitary matrix for a pure quantum circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerUnitaryBackend\n","from pytket.predicates import NoClassicalControlPredicate"]},{"cell_type":"markdown","metadata":{},"source":["Define a simple quantum incrementer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the unitary:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerUnitaryBackend()\n","c = backend.get_compiled_circuit(c)\n","result = backend.run_circuit(c)\n","unitary = result.get_unitary()\n","print(unitary.round(1).real)"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Whilst it can, with suitable credentials, be used to access the Rigetti QPUs, the `ForestBackend` also features a simulator mode which turns it into a noiseless sampling simulator that matches the constraints of the simulated device (e.g. the same gate set, restricted connectivity, measurement model, etc.). This is useful when playing around with custom compilation strategies to ensure that your final circuits are suitable to run on the device and for checking that your overall program works fine before you invest in reserving a QPU.
\n","
\n","Unique features:
\n","- faithful recreation of the circuit constraints of Rigetti QPUs."]},{"cell_type":"markdown","metadata":{},"source":["If trying to use the `ForestBackend` locally (i.e. not on a Rigetti QMI), you will need to have `quilc` and `qvm` running as separate processes in server mode. One easy way of doing this is with `docker` (see the `quilc` and `qvm` documentation for alternative methods of running them):
\n","`docker run --rm -it -p 5555:5555 rigetti/quilc -R`
\n","`docker run --rm -it -p 5000:5000 rigetti/qvm -S`"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The Rigetti `pyquil` package also provides the `WavefunctionSimulator`, which we present as the `ForestStateBackend`. Functionally, it is very similar to the `AerStateBackend` so can be used interchangeably. It does require that `quilc` and `qvm` are running as separate processes when not running on a Rigetti QMI.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpSimulatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QsharpSimulatorBackend` is another basic sampling simulator that is interchangeable with others, using the Microsoft QDK simulator. Note that the `pytket-qsharp` package is dependent on the `dotnet` SDK and `iqsharp` tool. Please consult the `pytket-qsharp` installation instructions for recommendations."]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpToffoliSimulatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Toffoli circuits form a strict fragment of quantum circuits and can be efficiently simulated. The `QsharpToffoliSimulatorBackend` can only operate on these circuits, but scales much better with system size than regular simulators.
\n","
\n","Unique features:
\n","- efficient simulation of Toffoli circuits."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qsharp import QsharpToffoliSimulatorBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit - start in a basis state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.X(0).X(2)\n","# Define a circuit - incrementer\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = QsharpToffoliSimulatorBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=10)\n","counts = backend.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpEstimatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QsharpEstimatorBackend` is not strictly a simulator, as it doesn't model the state of the quantum system and try to identify the final state, but instead analyses the circuit to estimate the required resources to run it. It does not support any of the regular outcome types (e.g. shots, counts, statevector), just the summary of the estimated resources.
\n","
\n","Unique features:
\n","- estimates resources to perform the circuit, without actually simulating/running it."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit"]},{"cell_type":"markdown","metadata":{},"source":["from pytket.extensions.qsharp import QsharpEstimatorBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit - start in a basis state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.X(0).X(2)\n","# Define a circuit - incrementer\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"markdown","metadata":{},"source":["(disabled because of https://github.com/CQCL/pytket-qsharp/issues/37)
\n","backend = QsharpEstimatorBackend()
\n","c = backend.get_compiled_circuit(c)
\n","handle = backend.process_circuit(c, n_shots=10)
\n","resources = backend.get_resources(handle)
\n","print(resources)"]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QulacsBackend` is an all-purpose simulator with both sampling and statevector modes, using the basic CPU simulator from Qulacs.
\n","
\n","Unique features:
\n","- supports both sampling (shots/counts) and complete statevector outputs."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsGPUBackend`"]},{"cell_type":"markdown","metadata":{},"source":["If the GPU version of Qulacs is installed, the `QulacsGPUBackend` will use that to benefit from even faster speeds. It is very easy to get started with using a GPU, as it only requires a CUDA installation and the `qulacs-gpu` package from `pip`. Functionally, it is identical to the `QulacsBackend`, but potentially faster if you have GPU resources available.
\n","
\n","Unique features:
\n","- GPU support for very fast simulation."]},{"cell_type":"markdown","metadata":{},"source":["## `ProjectQBackend`"]},{"cell_type":"markdown","metadata":{},"source":["ProjectQ is a popular quantum circuit simulator, thanks to its availability and ease of use. It provides a similar level of performance and features to `AerStateBackend`.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/backends/creating_backends.ipynb b/docs/examples/backends/creating_backends.ipynb index 889ab5e4..f977ca2e 100644 --- a/docs/examples/backends/creating_backends.ipynb +++ b/docs/examples/backends/creating_backends.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# How to create your own `Backend` using `pytket`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In this tutorial, we will focus on:
\n", "- the components of the abstract `Backend` class;
\n", "- adaptations for statevector simulation versus measurement sampling."]}, {"cell_type": "markdown", "metadata": {}, "source": ["To run this example, you will only need the core `pytket` package.
\n", "
\n", "The `pytket` framework currently has direct integration with the largest variety of devices and simulators out of any quantum software platform, but this is certainly not a complete collection. New quantum backends are frequently being rolled out as more device manufacturers bring their machines online and advanced in simulation research give rise to many purpose-built simulators for fast execution of specific circuit fragments.
\n", "
\n", "If you have something that can take circuits (i.e. a sequence of gates) and run/simulate them, adding integration with `pytket` connects it to a great number of users and enables existing software solutions to immediately take advantage of your new backend. This reach is further extended beyond just software written with `pytket` by exploiting its integration with the rest of the quantum software ecosystem, such as via the `TketBackend` wrapper to use the new backend within Qiskit projects.
\n", "
\n", "This notebook will take a toy simulator and demonstrate how to write each component of the `Backend` class to make it work with the rest of `pytket`. We'll start by defining the internal representation of a circuit that our simulator will use. Rather than common gates, this example will use exponentiated Pauli tensors ($e^{-i \\theta P}$ for $P \\in \\{I, X, Y, Z\\}^n$) as its basic operation, which are universal for unitary circuits. To keep it simple, we will ignore measurements for now and just consider unitaries."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Qubit\n", "from pytket.pauli import QubitPauliString\n", "from typing import List"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["class MyCircuit:\n", " \"\"\"A minimal representation of a unitary circuit\"\"\"\n", " def __init__(self, qubits: List[Qubit]):\n", " \"\"\"Creates a circuit over some set of qubits\n", " :param qubits: The list of qubits in the circuit\n", " :type qubits: List[Qubit]\n", " \"\"\"\n", " self.qubits = sorted(qubits, reverse=True)\n", " self.gates = list()\n", " def add_gate(self, qps: QubitPauliString, angle: float):\n", " \"\"\"Adds a gate to the end of the circuit e^{-0.5i * qps * angle}\n", " :param qps: Pauli string to rotate around\n", " :type qps: QubitPauliString\n", " :param angle: Angle of rotation in radians\n", " :type angle: float\n", " \"\"\"\n", " self.gates.append((qps, angle))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["To simulate these, it is enough to generate the matrix of these exponentials and apply them in sequence to our initial state. Calculating these matrix exponentials is easy since we can exploit the following property: if an operator $A$ satisfies $A^2 = I$, then $e^{i\\theta A} = \\mathrm{cos}(\\theta)I + i \\mathrm{sin}(\\theta) A$. This works for any tensor of Pauli matrices. Furthermore, since each Pauli matrix is some combination of a diagonal matrix and a permutation matrix, they benefit greatly from a sparse matrix representation, which we can obtain from the `QubitPauliString`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["import numpy as np"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["class MySimulator:\n", " \"\"\"A minimal statevector simulator for unitary circuits\"\"\"\n", " def __init__(self, qubits: List[Qubit]):\n", " \"\"\"Initialise a statevector, setting all qubits to the |0\u276d state.\n", " We treat qubits[0] as the most-significant qubit\n", " :param qubits: The list of qubits in the circuit\n", " :type qubits: List[Qubit]\n", " \"\"\"\n", " self._qubits = qubits\n", " self._qstate = np.zeros((2 ** len(qubits),), dtype=complex)\n", " self._qstate[0] = 1.0\n", " def apply_Pauli_rot(self, qps: QubitPauliString, angle: float):\n", " \"\"\"Applies e^{-0.5i * qps * angle} to the state\n", " :param qps: Pauli to rotate around\n", " :type qps: QubitPauliString\n", " :param angle: Angle of rotation in radians\n", " :type angle: float\n", " \"\"\"\n", " pauli_tensor = qps.to_sparse_matrix(self._qubits)\n", " exponent = -0.5 * angle\n", " self._qstate = np.cos(exponent) * self._qstate + 1j * np.sin(\n", " exponent\n", " ) * pauli_tensor.dot(self._qstate)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def run_mycircuit(circ: MyCircuit) -> np.ndarray:\n", " \"\"\"Gives the state after applying the circuit to the all-|0\u276d state\n", " :param circ: The circuit to simulate\n", " :type circ: MyCircuit\n", " :return: The final statevector\n", " :rtype: np.ndarray\n", " \"\"\"\n", " sim = MySimulator(circ.qubits)\n", " for qps, angle in circ.gates:\n", " sim.apply_Pauli_rot(qps, angle)\n", " return sim._qstate"]}, {"cell_type": "markdown", "metadata": {}, "source": ["And that's all we need for a basic simulator! We can check that this works by trying to generate a Bell state (up to global phase)."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.pauli import Pauli"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["q = [Qubit(0), Qubit(1)]\n", "circ = MyCircuit(q)\n", "# Hadamard on Qubit(0)\n", "circ.add_gate(QubitPauliString(Qubit(0), Pauli.Z), np.pi / 2)\n", "circ.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2)\n", "circ.add_gate(QubitPauliString(Qubit(0), Pauli.Z), np.pi / 2)\n", "# CX with control Qubit(0) and target Qubit(1)\n", "circ.add_gate(QubitPauliString(Qubit(0), Pauli.Z), -np.pi / 2)\n", "circ.add_gate(QubitPauliString(Qubit(1), Pauli.X), -np.pi / 2)\n", "circ.add_gate(QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.X}), np.pi / 2)\n", "print(run_mycircuit(circ))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["A useful first step to integrating this is to define a conversion from the `pytket.Circuit` class to the `MyCircuit` class. In most cases, this will just amount to converting one gate at a time by a simple syntax map. We need not specify how to convert every possible `OpType`, since we can rely on the compilation passes in `pytket` to map the circuit into the required gate set as long as it is universal. For this example, the definitions of `OpType.Rx`, `OpType.Ry`, `OpType.Rz`, and `OpType.ZZMax` all match the form of a single Pauli exponential."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit, OpType"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def tk_to_mycircuit(tkc: Circuit) -> MyCircuit:\n", " \"\"\"Convert a pytket Circuit to a MyCircuit object.\n", " Supports Rz, Rx, Ry, and ZZMax gates.\n", " :param tkc: The Circuit to convert\n", " :type tkc: Circuit\n", " :return: An equivalent MyCircuit object\n", " :rtype: MyCircuit\n", " \"\"\"\n", " circ = MyCircuit(tkc.qubits)\n", " for command in tkc:\n", " optype = command.op.type\n", " if optype == OpType.Rx:\n", " circ.add_gate(\n", " QubitPauliString(command.args[0], Pauli.X), np.pi * command.op.params[0]\n", " )\n", " elif optype == OpType.Ry:\n", " circ.add_gate(\n", " QubitPauliString(command.args[0], Pauli.Y), np.pi * command.op.params[0]\n", " )\n", " elif optype == OpType.Rz:\n", " circ.add_gate(\n", " QubitPauliString(command.args[0], Pauli.Z), np.pi * command.op.params[0]\n", " )\n", " elif optype == OpType.ZZMax:\n", " circ.add_gate(\n", " QubitPauliString(command.args, [Pauli.Z, Pauli.Z]), np.pi * 0.5\n", " )\n", " else:\n", " raise ValueError(\"Cannot convert optype to MyCircuit: \", optype)\n", " return circ"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Now we turn to the `Backend` class. This provides a uniform API to submit `Circuit` objects for evaluation, typically returning either a statevector or a set of measurement shots. It also captures all of the information needed for compilation and asynchronous job management.
\n", "
\n", "We will make a subclass of `Backend` for our statevector simulator. The `_supports_state` flag lets the methods of the abstract `Backend` class know that this implementation supports statevector simulation. We also set `_persistent_handles` to `False` since this `Backend` will not be able to retrieve results from a previous Python session.
\n", "
\n", "Since we do not need to connect to a remote process for the simulator, the constructor doesn't need to set anything up. The base `Backend` constructor will initialise the `_cache` field for storing job data."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.backends import Backend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["class MyBackend(Backend):\n", " \"\"\"A pytket Backend wrapping around the MySimulator statevector simulator\"\"\"\n", " _supports_state = True\n", " _persistent_handles = False\n", " def __init__(self):\n", " \"\"\"Create a new instance of the MyBackend class\"\"\"\n", " super().__init__()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Most `Backend`s will only support a small fragment of the `Circuit` language, either through implementation limitations or since a specific presentation is universal. It is helpful to keep this information in the `Backend` object itself so that users can clearly see how a `Circuit` needs to look before it can be successfully run. The `Predicate` classes in `pytket` can capture many common restrictions. The idea behind the `required_predicates` list is that any `Circuit` satisfying every `Predicate` in the list can be run on the `Backend` successfully as it is.
\n", "
\n", "However, a typical high-level user will not be writing `Circuit`s that satisfies all of the `required_predicates`, preferring instead to use the model that is most natural for the algorithm they are implementing. Providing a `default_compilation_pass` gives users an easy starting point for compiling an arbitrary `Circuit` into a form that can be executed (when not blocked by paradigm constraints like `NoMidMeasurePredicate` or `NoClassicalControlPredicate` that cannot easily be solved by compilation).
\n", "
\n", "You can provide several options using the `optimisation_level` argument. We tend to use `0` for very basic compilation with no optimisation applied, `1` for the inclusion of fast optimisations (e.g. `SynthesiseIBM` is a pre-defined sequence of optimisation passes that scales well with circuit size), and `2` for heavier optimisation (e.g. `FullPeepholeOptimise` incorporates `SynthesiseTket` alongside some extra passes that may take longer for large circuits).
\n", "
\n", "When designing these compilation pass sequences for a given `Backend`, it can be a good idea to start with the passes that solve individual constraints from `required_predicates` (like `FullMappingPass` for `ConnectivityPredicate` or `RebaseX` for `GateSetPredicate`), and find an ordering such that no later pass invalidates the work of an earlier one.
\n", "
\n", "For `MyBackend`, we will need to enforce that our circuits are expressed entirely in terms of `OpType.Rx`, `OpType.Ry`, `OpType.Rz`, and `OpType.ZZMax` gates which we can solve using `RebaseCustom`. Note that we omit `OpType.Measure` since we can only run pure quantum circuits.
\n", "
\n", "The standard docstrings for these and other abstract methods can be seen in the abstract `Backend` [API reference](https://cqcl.github.io/tket/pytket/api/backends.html#pytket.backends.Backend)."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.predicates import Predicate, GateSetPredicate, NoClassicalBitsPredicate\n", "from pytket.passes import (\n", " BasePass,\n", " SequencePass,\n", " DecomposeBoxes,\n", " SynthesiseTket,\n", " FullPeepholeOptimise,\n", " RebaseCustom,\n", " SquashCustom,\n", ")"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["@property\n", "def required_predicates(self) -> List[Predicate]:\n", " \"\"\"\n", " The minimum set of predicates that a circuit must satisfy before it can\n", " be successfully run on this backend.\n", " :return: Required predicates.\n", " :rtype: List[Predicate]\n", " \"\"\"\n", " preds = [\n", " NoClassicalBitsPredicate(),\n", " GateSetPredicate(\n", " {\n", " OpType.Rx,\n", " OpType.Ry,\n", " OpType.Rz,\n", " OpType.ZZMax,\n", " }\n", " ),\n", " ]\n", " return preds"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Every `Backend` must define a rebasing method, which will normally be called from its default compilation passes (see below), but which may also be called independently. Given the target gate set, it is usually straightforward to define this using the `RebaseCustom` pass, with a couple of helpers defining rebase of an `OpType.CX` and a general `OpType.TK1` gate:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cx_circ = Circuit(2)\n", "cx_circ.Sdg(0)\n", "cx_circ.V(1)\n", "cx_circ.Sdg(1)\n", "cx_circ.Vdg(1)\n", "cx_circ.ZZMax(0, 1)\n", "cx_circ.Vdg(1)\n", "cx_circ.Sdg(1)\n", "cx_circ.add_phase(0.5)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def sq(a, b, c):\n", " circ = Circuit(1)\n", " if c != 0:\n", " circ.Rz(c, 0)\n", " if b != 0:\n", " circ.Rx(b, 0)\n", " if a != 0:\n", " circ.Rz(a, 0)\n", " return circ"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["rebase = RebaseCustom({OpType.Rx, OpType.Ry, OpType.Rz, OpType.ZZMax}, cx_circ, sq)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass:\n", " \"\"\"\n", " A suggested compilation pass that will guarantee the resulting circuit\n", " will be suitable to run on this backend with as few preconditions as\n", " possible.\n", " :param optimisation_level: The level of optimisation to perform during\n", " compilation. Level 0 just solves the device constraints without\n", " optimising. Level 1 additionally performs some light optimisations.\n", " Level 2 adds more intensive optimisations that can increase compilation\n", " time for large circuits. Defaults to 1.\n", " :type optimisation_level: int, optional\n", " :return: Compilation pass guaranteeing required predicates.\n", " :rtype: BasePass\n", " \"\"\"\n", " assert optimisation_level in range(3)\n", " squash = SquashCustom({OpType.Rz, OpType.Rx, OpType.Ry}, sq)\n", " seq = [DecomposeBoxes()] # Decompose boxes into basic gates\n", " if optimisation_level == 1:\n", " seq.append(SynthesiseTket()) # Optional fast optimisation\n", " elif optimisation_level == 2:\n", " seq.append(FullPeepholeOptimise()) # Optional heavy optimisation\n", " seq.append(rebase) # Map to target gate set\n", " if optimisation_level != 0:\n", " seq.append(squash) # Optionally simplify 1qb gate chains within this gate set\n", " return SequencePass(seq)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `backend_info` property is used for storing various properties of a backend. By default it provides all device information useful for compilation. Typically we would make it return a class attribute `self._backend_info` that we initialise on construction, but we will define it at point of request here. We use a `FullyConnected` architecture producing an `Architecture` object with couplings between 4 qubits."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.backends.backendinfo import BackendInfo\n", "from pytket.architecture import FullyConnected"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["@property\n", "def backend_info(self) -> BackendInfo:\n", " return BackendInfo(\n", " \"MyBackend\",\n", " \"MySimulator\",\n", " \"1.0\",\n", " FullyConnected(4),\n", " {\n", " OpType.Rx,\n", " OpType.Ry,\n", " OpType.Rz,\n", " OpType.ZZMax,\n", " OpType.Measure,\n", " },\n", " supports_midcircuit_measurement=False,\n", " misc={\"characterisation\": None},\n", " )"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Asynchronous job management is all managed through the `ResultHandle` associated with a particular `Circuit` that has been submitted. We can use it to inspect the status of the job to see if it has completed, or to look up the results if they are available.
\n", "
\n", "For devices, `circuit_status` should query the job to see if it is in a queue, currently being executed, completed successfully, etc. The `CircuitStatus` class is mostly driven by the `StatusEnum` values, but can also contain messages to give more detailed feedback if available. For our simulator, we are not running things asynchronously, so a `Circuit` has either not been run or it will have been completed.
\n", "
\n", "Since a device API will probably define its own data type for job handles, the `ResultHandle` definition is flexible enough to cover many possible data types so you can likely use the underlying job handle as the `ResultHandle`. The `_result_id_type` property specifies what data type a `ResultHandle` for this `Backend` should look like. Since our simulator has no underlying job handle, we can just use a UUID string."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.backends import ResultHandle, CircuitStatus, StatusEnum, CircuitNotRunError\n", "from pytket.backends.resulthandle import _ResultIdTuple"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["@property\n", "def _result_id_type(self) -> _ResultIdTuple:\n", " \"\"\"Identifier type signature for ResultHandle for this backend.\n", " :return: Type signature (tuple of hashable types)\n", " :rtype: _ResultIdTuple\n", " \"\"\"\n", " return (str,)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def circuit_status(self, handle: ResultHandle) -> CircuitStatus:\n", " \"\"\"\n", " Return a CircuitStatus reporting the status of the circuit execution\n", " corresponding to the ResultHandle\n", " \"\"\"\n", " if handle in self._cache:\n", " return CircuitStatus(StatusEnum.COMPLETED)\n", " raise CircuitNotRunError(handle)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["And finally, we have the method that actually submits a job for execution. `process_circuits` should take a collection of (compiled) `Circuit` objects, process them and return a `ResultHandle` for each `Circuit`. If execution is synchronous, then this can simply wait until it is finished, store the result in `_cache` and return. For backends that support asynchronous jobs, you will need to set up an event to format and store the result on completion.
\n", "
\n", "It is recommended to use the `valid_check` parameter to control a call to `Backend._check_all_circuits()`, which will raise an exception if any of the circuits do not satisfy everything in `required_predicates`.
\n", "
\n", "The `_cache` fields stores all of the information about current jobs that have been run. When a job has finished execution, the results are expected to be stored in `_cache[handle][\"result\"]`, though it can also be used to store other data about the job such as some information about the `Circuit` required to properly format the results. Methods like `Backend.get_result()` and `Backend.empty_cache()` expect to interact with the results of a given job in this way.
\n", "
\n", "The final output of the execution is stored in a `BackendResult` object. This captures enough information about the results to reinterpret it in numerous ways, such as requesting the statevector in a specific qubit ordering or converting a complete shot table to a summary of the counts. If we create a `BackendResult` with quantum data (e.g. a statevector or unitary), we must provide the `Qubit` ids in order from most-significant to least-significant with regards to indexing the state. Similarly, creating one with classical readouts (e.g. a shot table or counts summary), we give the `Bit` ids in the order they appear in a readout (left-to-right).
\n", "
\n", "For a statevector simulation, we should also take into account the global phase stored in the `Circuit` object and any implicit qubit permutations, since these become observable when inspecting the quantum state. We can handle the qubit permutation by changing the order in which we pass the `Qubit` ids into the `BackendResult` object."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.backends.backendresult import BackendResult\n", "from pytket.utils.results import KwargTypes\n", "from typing import Iterable, Optional\n", "from uuid import uuid4"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def process_circuits(\n", " self,\n", " circuits: Iterable[Circuit],\n", " n_shots: Optional[int] = None,\n", " valid_check: bool = True,\n", " **kwargs: KwargTypes,\n", ") -> List[ResultHandle]:\n", " \"\"\"\n", " Submit circuits to the backend for running. The results will be stored\n", " in the backend's result cache to be retrieved by the corresponding\n", " get_ method.\n", " Use keyword arguments to specify parameters to be used in submitting circuits\n", " See specific Backend derived class for available parameters, from the following\n", " list:\n", " * `seed`: RNG seed for simulators\n", " :param circuits: Circuits to process on the backend.\n", " :type circuits: Iterable[Circuit]\n", " :param n_shots: Number of shots to run per circuit. None is to be used\n", " for state/unitary simulators. Defaults to None.\n", " :type n_shots: Optional[int], optional\n", " :param valid_check: Explicitly check that all circuits satisfy all required\n", " predicates to run on the backend. Defaults to True\n", " :type valid_check: bool, optional\n", " :return: Handles to results for each input circuit, as an interable in\n", " the same order as the circuits.\n", " :rtype: List[ResultHandle]\n", " \"\"\"\n", " circuit_list = list(circuits)\n", " if valid_check:\n", " self._check_all_circuits(circuit_list)\n", " handle_list = []\n", " for circuit in circuit_list:\n", " handle = ResultHandle(str(uuid4()))\n", " mycirc = tk_to_mycircuit(circuit)\n", " state = run_mycircuit(mycirc)\n", " state *= np.exp(1j * np.pi * circuit.phase)\n", " implicit_perm = circuit.implicit_qubit_permutation()\n", " res_qubits = [implicit_perm[qb] for qb in sorted(circuit.qubits, reverse=True)]\n", " res = BackendResult(q_bits=res_qubits, state=state)\n", " self._cache[handle] = {\"result\": res}\n", " handle_list.append(handle)\n", " return handle_list"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Let's redefine our `MyBackend` class to use these methods to finish it off."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["class MyBackend(Backend):\n", " \"\"\"A pytket Backend wrapping around the MySimulator statevector simulator\"\"\"\n", " _supports_state = True\n", " _persistent_handles = False\n", " def __init__(self):\n", " \"\"\"Create a new instance of the MyBackend class\"\"\"\n", " super().__init__()\n", " required_predicates = required_predicates\n", " rebase_pass = rebase\n", " default_compilation_pass = default_compilation_pass\n", " _result_id_type = _result_id_type\n", " circuit_status = circuit_status\n", " process_circuits = process_circuits"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Our new `Backend` subclass is now complete, so let's test it out. If you are planning on maintaining a backend class, it is recommended to set up some unit tests. The following tests will cover basic operation and integration with `pytket` utilities."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import BasisOrder, Unitary1qBox\n", "from pytket.passes import CliffordSimp\n", "from pytket.utils import get_operator_expectation_value\n", "from pytket.utils.operators import QubitPauliOperator\n", "import pytest"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def test_bell() -> None:\n", " c = Circuit(2)\n", " c.H(0)\n", " c.CX(0, 1)\n", " b = MyBackend()\n", " c = b.get_compiled_circuit(c)\n", " h = b.process_circuit(c)\n", " assert np.allclose(\n", " b.get_result(h).get_state(), np.asarray([1, 0, 0, 1]) * 1 / np.sqrt(2)\n", " )"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def test_basisorder() -> None:\n", " c = Circuit(2)\n", " c.X(1)\n", " b = MyBackend()\n", " c = b.get_compiled_circuit(c)\n", " h = b.process_circuit(c)\n", " r = b.get_result(h)\n", " assert np.allclose(r.get_state(), np.asarray([0, 1, 0, 0]))\n", " assert np.allclose(r.get_state(basis=BasisOrder.dlo), np.asarray([0, 0, 1, 0]))"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def test_implicit_perm() -> None:\n", " c = Circuit(2)\n", " c.CX(0, 1)\n", " c.CX(1, 0)\n", " c.Ry(0.1, 1)\n", " c1 = c.copy()\n", " CliffordSimp().apply(c1)\n", " b = MyBackend()\n", " c = b.get_compiled_circuit(c, optimisation_level=1)\n", " c1 = b.get_compiled_circuit(c1, optimisation_level=1)\n", " assert c.implicit_qubit_permutation() != c1.implicit_qubit_permutation()\n", " h, h1 = b.process_circuits([c, c1])\n", " r, r1 = b.get_results([h, h1])\n", " for bo in [BasisOrder.ilo, BasisOrder.dlo]:\n", " s = r.get_state(basis=bo)\n", " s1 = r1.get_state(basis=bo)\n", " assert np.allclose(s, s1)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def test_compilation_pass() -> None:\n", " b = MyBackend()\n", " for opt_level in range(3):\n", " c = Circuit(2)\n", " c.CX(0, 1)\n", " u = np.asarray([[0, 1], [-1j, 0]])\n", " c.add_unitary1qbox(Unitary1qBox(u), 1)\n", " c.CX(0, 1)\n", " c.add_gate(OpType.CRz, 0.35, [1, 0])\n", " assert not (b.valid_circuit(c))\n", " c = b.get_compiled_circuit(c, optimisation_level=opt_level)\n", " assert b.valid_circuit(c)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def test_invalid_measures() -> None:\n", " c = Circuit(2)\n", " c.H(0).CX(0, 1).measure_all()\n", " b = MyBackend()\n", " c = b.get_compiled_circuit(c)\n", " assert not (b.valid_circuit(c))"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def test_expectation_value() -> None:\n", " c = Circuit(2)\n", " c.H(0)\n", " c.CX(0, 1)\n", " op = QubitPauliOperator(\n", " {\n", " QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z}): 1.0,\n", " QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X}): 0.3,\n", " QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Y}): 0.8j,\n", " QubitPauliString({Qubit(0): Pauli.Y}): -0.4j,\n", " }\n", " )\n", " b = MyBackend()\n", " c = b.get_compiled_circuit(c)\n", " assert get_operator_expectation_value(c, op, b) == pytest.approx(1.3)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Explicit calls are needed for this notebook. Normally pytest will just find these \"test_X\" methods when run from the command line:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["test_bell()\n", "test_basisorder()\n", "test_implicit_perm()\n", "test_compilation_pass()\n", "test_invalid_measures()\n", "test_expectation_value()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["To show how this compares to a sampling simulator, let's extend our simulator to handle end-of-circuit measurements."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from typing import Set"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def sample_mycircuit(\n", " circ: MyCircuit, qubits: Set[Qubit], n_shots: int, seed: Optional[int] = None\n", ") -> np.ndarray:\n", " \"\"\"Run the circuit on the all-|0\u276d state and measures a set of qubits\n", " :param circ: The circuit to simulate\n", " :type circ: MyCircuit\n", " :param qubits: The set of qubits to measure\n", " :type qubits: Set[Qubit]\n", " :param n_shots: The number of samples to take\n", " :type n_shots: int\n", " :param seed: Seed for the random number generator, defaults to no seed\n", " :type seed: Optional[int], optional\n", " :return: Table of shots; each row is a shot, columns are qubit readouts in ascending Qubit order\n", " :rtype: np.ndarray\n", " \"\"\"\n", " state = run_mycircuit(circ)\n", " cumulative_probs = (state * state.conjugate()).cumsum()\n", " if seed is not None:\n", " np.random.seed(seed)\n", " shots = np.zeros((n_shots, len(circ.qubits)))\n", " for s in range(n_shots):\n", " # Pick a random point in the distribution\n", " point = np.random.uniform(0.0, 1.0)\n", " # Find the corresponding readout\n", " index = np.searchsorted(cumulative_probs, point)\n", " # Convert index to a binary array\n", " # `bin` maps e.g. index 6 to '0b110'\n", " # So we ignore the first two symbols and add leading 0s to make it a fixed length\n", " bitstring = bin(index)[2:].zfill(len(circ.qubits))\n", " shots[s] = np.asarray([int(b) for b in bitstring])\n", " filtered = np.zeros((n_shots, len(qubits)))\n", " target = 0\n", " for col, q in enumerate(circ.qubits):\n", " if q in qubits:\n", " filtered[:, target] = shots[:, col]\n", " target += 1\n", " return filtered"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Since `MyCircuit` doesn't have a representation for measurement gates, our converter must return both the `MyCircuit` object and some way of capturing the measurements. Since we will also want to know how they map into our `Bit` ids, the simplest option is just a dictionary from `Qubit` to `Bit`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Bit\n", "from typing import Dict, Tuple"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def tk_to_mymeasures(tkc: Circuit) -> Tuple[MyCircuit, Dict[Qubit, Bit]]:\n", " \"\"\"Convert a pytket Circuit to a MyCircuit object and a measurement map.\n", " Supports Rz, Rx, Ry, and ZZMax gates, as well as end-of-circuit measurements.\n", " :param tkc: The Circuit to convert\n", " :type tkc: Circuit\n", " :return: An equivalent MyCircuit object and a map from measured Qubit to the Bit containing the result\n", " :rtype: Tuple[MyCircuit, Dict[Qubit, Bit]]\n", " \"\"\"\n", " circ = MyCircuit(tkc.qubits)\n", " measure_map = dict()\n", " measured_units = (\n", " set()\n", " ) # Track measured Qubits/used Bits to identify mid-circuit measurement\n", " for command in tkc:\n", " for u in command.args:\n", " if u in measured_units:\n", " raise ValueError(\"Circuit contains a mid-circuit measurement\")\n", " optype = command.op.type\n", " if optype == OpType.Rx:\n", " circ.add_gate(\n", " QubitPauliString(command.args[0], Pauli.X), np.pi * command.op.params[0]\n", " )\n", " elif optype == OpType.Ry:\n", " circ.add_gate(\n", " QubitPauliString(command.args[0], Pauli.Y), np.pi * command.op.params[0]\n", " )\n", " elif optype == OpType.Rz:\n", " circ.add_gate(\n", " QubitPauliString(command.args[0], Pauli.Z), np.pi * command.op.params[0]\n", " )\n", " elif optype == OpType.ZZMax:\n", " circ.add_gate(\n", " QubitPauliString(command.args, [Pauli.Z, Pauli.Z]), np.pi * 0.5\n", " )\n", " elif optype == OpType.Measure:\n", " measure_map[command.args[0]] = command.args[1]\n", " measured_units.add(command.args[0])\n", " measured_units.add(command.args[1])\n", " else:\n", " raise ValueError(\"Cannot convert optype to MyCircuit: \", optype)\n", " return circ, measure_map"]}, {"cell_type": "markdown", "metadata": {}, "source": ["To build a `Backend` subclass for this sampling simulator, we only need to change how we write `required_predicates` and `process_circuits`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.predicates import NoMidMeasurePredicate, NoClassicalControlPredicate\n", "from pytket.utils.outcomearray import OutcomeArray"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["class MySampler(Backend):\n", " \"\"\"A pytket Backend wrapping around the MySimulator simulator with readout sampling\"\"\"\n", " _supports_shots = True\n", " _supports_counts = True\n", " _persistent_handles = False\n", " def __init__(self):\n", " \"\"\"Create a new instance of the MySampler class\"\"\"\n", " super().__init__()\n", " rebase_pass = rebase\n", " default_compilation_pass = default_compilation_pass\n", " _result_id_type = _result_id_type\n", " circuit_status = circuit_status\n", " @property\n", " def required_predicates(self) -> List[Predicate]:\n", " \"\"\"\n", " The minimum set of predicates that a circuit must satisfy before it can\n", " be successfully run on this backend.\n", " :return: Required predicates.\n", " :rtype: List[Predicate]\n", " \"\"\"\n", " preds = [\n", " NoClassicalControlPredicate(),\n", " NoMidMeasurePredicate(),\n", " GateSetPredicate(\n", " {\n", " OpType.Rx,\n", " OpType.Ry,\n", " OpType.Rz,\n", " OpType.ZZMax,\n", " OpType.Measure,\n", " }\n", " ),\n", " ]\n", " return preds\n", " def process_circuits(\n", " self,\n", " circuits: Iterable[Circuit],\n", " n_shots: Optional[int] = None,\n", " valid_check: bool = True,\n", " **kwargs: KwargTypes,\n", " ) -> List[ResultHandle]:\n", " \"\"\"\n", " Submit circuits to the backend for running. The results will be stored\n", " in the backend's result cache to be retrieved by the corresponding\n", " get_ method.\n", " Use keyword arguments to specify parameters to be used in submitting circuits\n", " See specific Backend derived class for available parameters, from the following\n", " list:\n", " * `seed`: RNG seed for simulators\n", " :param circuits: Circuits to process on the backend.\n", " :type circuits: Iterable[Circuit]\n", " :param n_shots: Number of shots to run per circuit. None is to be used\n", " for state/unitary simulators. Defaults to None.\n", " :type n_shots: Optional[int], optional\n", " :param valid_check: Explicitly check that all circuits satisfy all required\n", " predicates to run on the backend. Defaults to True\n", " :type valid_check: bool, optional\n", " :return: Handles to results for each input circuit, as an interable in\n", " the same order as the circuits.\n", " :rtype: List[ResultHandle]\n", " \"\"\"\n", " circuit_list = list(circuits)\n", " if valid_check:\n", " self._check_all_circuits(circuit_list)\n", " handle_list = []\n", " for circuit in circuit_list:\n", " handle = ResultHandle(str(uuid4()))\n", " mycirc, measure_map = tk_to_mymeasures(circuit)\n", " qubit_list, bit_list = zip(*measure_map.items())\n", " qubit_shots = sample_mycircuit(\n", " mycirc, set(qubit_list), n_shots, kwargs.get(\"seed\")\n", " )\n", " # Pad shot table with 0 columns for unused bits\n", " all_shots = np.zeros((n_shots, len(circuit.bits)), dtype=int)\n", " all_shots[:, : len(qubit_list)] = qubit_shots\n", " res_bits = [measure_map[q] for q in sorted(qubit_list, reverse=True)]\n", " for b in circuit.bits:\n", " if b not in bit_list:\n", " res_bits.append(b)\n", " res = BackendResult(\n", " c_bits=res_bits, shots=OutcomeArray.from_readouts(all_shots)\n", " )\n", " self._cache[handle] = {\"result\": res}\n", " handle_list.append(handle)\n", " return handle_list"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Likewise, we run some basic tests to make sure it works."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def test_sampler_bell() -> None:\n", " c = Circuit(2, 2)\n", " c.H(0)\n", " c.CX(0, 1)\n", " c.measure_all()\n", " b = MySampler()\n", " c = b.get_compiled_circuit(c)\n", " res = b.run_circuit(c, n_shots=10, seed=3)\n", " assert res.get_shots().shape == (10, 2)\n", " assert res.get_counts() == {(0, 0): 5, (1, 1): 5}"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def test_sampler_basisorder() -> None:\n", " c = Circuit(2, 2)\n", " c.X(1)\n", " c.measure_all()\n", " b = MySampler()\n", " c = b.get_compiled_circuit(c)\n", " res = b.run_circuit(c, n_shots=10, seed=0)\n", " assert res.get_counts() == {(0, 1): 10}\n", " assert res.get_counts(basis=BasisOrder.dlo) == {(1, 0): 10}"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def test_sampler_compilation_pass() -> None:\n", " b = MySampler()\n", " for opt_level in range(3):\n", " c = Circuit(2)\n", " c.CX(0, 1)\n", " u = np.asarray([[0, 1], [-1j, 0]])\n", " c.add_unitary1qbox(Unitary1qBox(u), 1)\n", " c.CX(0, 1)\n", " c.add_gate(OpType.CRz, 0.35, [1, 0])\n", " c.measure_all()\n", " assert not (b.valid_circuit(c))\n", " c = b.get_compiled_circuit(c, optimisation_level=opt_level)\n", " assert b.valid_circuit(c)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def test_sampler_expectation_value() -> None:\n", " c = Circuit(2)\n", " c.H(0)\n", " c.CX(0, 1)\n", " op = QubitPauliOperator(\n", " {\n", " QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z}): 1.0,\n", " QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X}): 0.3,\n", " QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Y}): 0.8j,\n", " QubitPauliString({Qubit(0): Pauli.Y}): -0.4j,\n", " }\n", " )\n", " b = MySampler()\n", " c = b.get_compiled_circuit(c)\n", " expectation = get_operator_expectation_value(c, op, b, n_shots=2000, seed=0)\n", " assert (np.real(expectation), np.imag(expectation)) == pytest.approx(\n", " (1.3, 0.0), abs=0.1\n", " )"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["test_sampler_bell()\n", "test_sampler_basisorder()\n", "test_sampler_compilation_pass()\n", "test_sampler_expectation_value()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Exercises:
\n", "- Add some extra gate definitions to the simulator and expand the accepted gate set of the backends. Start with some that are easily represented as exponentiated Pauli tensors like `OpType.YYPhase`. For a challenge, try adding `OpType.CCX` efficiently (it is possible to encode it using seven Pauli rotations).
\n", "- Restrict the simulator to a limited qubit connectivity. Express this in the backends by modifying the `Architecture` property of the `BackendInfo` attribute object and adding to the `required_predicates`. Adjust the `default_compilation_pass` to solve for the connectivity.
\n", "- The file `creating_backends_exercise.py` extends the simulators above to allow for mid-circuit measurement and conditional gates using a binary decision tree. Implement an appropriate converter and `Backend` class for this simulator."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# How to create your own `Backend` using `pytket`\n","\n","**Download this notebook - {nb-download}`creating_backends.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:
\n","- the components of the abstract `Backend` class;
\n","- adaptations for statevector simulation versus measurement sampling."]},{"cell_type":"markdown","metadata":{},"source":["To run this example, you will only need the core `pytket` package.
\n","
\n","The `pytket` framework currently has direct integration with the largest variety of devices and simulators out of any quantum software platform, but this is certainly not a complete collection. New quantum backends are frequently being rolled out as more device manufacturers bring their machines online and advanced in simulation research give rise to many purpose-built simulators for fast execution of specific circuit fragments.
\n","
\n","If you have something that can take circuits (i.e. a sequence of gates) and run/simulate them, adding integration with `pytket` connects it to a great number of users and enables existing software solutions to immediately take advantage of your new backend. This reach is further extended beyond just software written with `pytket` by exploiting its integration with the rest of the quantum software ecosystem, such as via the `TketBackend` wrapper to use the new backend within Qiskit projects.
\n","
\n","This notebook will take a toy simulator and demonstrate how to write each component of the `Backend` class to make it work with the rest of `pytket`. We'll start by defining the internal representation of a circuit that our simulator will use. Rather than common gates, this example will use exponentiated Pauli tensors ($e^{-i \\theta P}$ for $P \\in \\{I, X, Y, Z\\}^n$) as its basic operation, which are universal for unitary circuits. To keep it simple, we will ignore measurements for now and just consider unitaries."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Qubit\n","from pytket.pauli import QubitPauliString\n","from typing import List"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["class MyCircuit:\n"," \"\"\"A minimal representation of a unitary circuit\"\"\"\n"," def __init__(self, qubits: List[Qubit]):\n"," \"\"\"Creates a circuit over some set of qubits\n"," :param qubits: The list of qubits in the circuit\n"," :type qubits: List[Qubit]\n"," \"\"\"\n"," self.qubits = sorted(qubits, reverse=True)\n"," self.gates = list()\n"," def add_gate(self, qps: QubitPauliString, angle: float):\n"," \"\"\"Adds a gate to the end of the circuit e^{-0.5i * qps * angle}\n"," :param qps: Pauli string to rotate around\n"," :type qps: QubitPauliString\n"," :param angle: Angle of rotation in radians\n"," :type angle: float\n"," \"\"\"\n"," self.gates.append((qps, angle))"]},{"cell_type":"markdown","metadata":{},"source":["To simulate these, it is enough to generate the matrix of these exponentials and apply them in sequence to our initial state. Calculating these matrix exponentials is easy since we can exploit the following property: if an operator $A$ satisfies $A^2 = I$, then $e^{i\\theta A} = \\mathrm{cos}(\\theta)I + i \\mathrm{sin}(\\theta) A$. This works for any tensor of Pauli matrices. Furthermore, since each Pauli matrix is some combination of a diagonal matrix and a permutation matrix, they benefit greatly from a sparse matrix representation, which we can obtain from the `QubitPauliString`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import numpy as np"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["class MySimulator:\n"," \"\"\"A minimal statevector simulator for unitary circuits\"\"\"\n"," def __init__(self, qubits: List[Qubit]):\n"," \"\"\"Initialise a statevector, setting all qubits to the |0❭ state.\n"," We treat qubits[0] as the most-significant qubit\n"," :param qubits: The list of qubits in the circuit\n"," :type qubits: List[Qubit]\n"," \"\"\"\n"," self._qubits = qubits\n"," self._qstate = np.zeros((2 ** len(qubits),), dtype=complex)\n"," self._qstate[0] = 1.0\n"," def apply_Pauli_rot(self, qps: QubitPauliString, angle: float):\n"," \"\"\"Applies e^{-0.5i * qps * angle} to the state\n"," :param qps: Pauli to rotate around\n"," :type qps: QubitPauliString\n"," :param angle: Angle of rotation in radians\n"," :type angle: float\n"," \"\"\"\n"," pauli_tensor = qps.to_sparse_matrix(self._qubits)\n"," exponent = -0.5 * angle\n"," self._qstate = np.cos(exponent) * self._qstate + 1j * np.sin(\n"," exponent\n"," ) * pauli_tensor.dot(self._qstate)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def run_mycircuit(circ: MyCircuit) -> np.ndarray:\n"," \"\"\"Gives the state after applying the circuit to the all-|0❭ state\n"," :param circ: The circuit to simulate\n"," :type circ: MyCircuit\n"," :return: The final statevector\n"," :rtype: np.ndarray\n"," \"\"\"\n"," sim = MySimulator(circ.qubits)\n"," for qps, angle in circ.gates:\n"," sim.apply_Pauli_rot(qps, angle)\n"," return sim._qstate"]},{"cell_type":"markdown","metadata":{},"source":["And that's all we need for a basic simulator! We can check that this works by trying to generate a Bell state (up to global phase)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.pauli import Pauli"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["q = [Qubit(0), Qubit(1)]\n","circ = MyCircuit(q)\n","# Hadamard on Qubit(0)\n","circ.add_gate(QubitPauliString(Qubit(0), Pauli.Z), np.pi / 2)\n","circ.add_gate(QubitPauliString(Qubit(0), Pauli.X), np.pi / 2)\n","circ.add_gate(QubitPauliString(Qubit(0), Pauli.Z), np.pi / 2)\n","# CX with control Qubit(0) and target Qubit(1)\n","circ.add_gate(QubitPauliString(Qubit(0), Pauli.Z), -np.pi / 2)\n","circ.add_gate(QubitPauliString(Qubit(1), Pauli.X), -np.pi / 2)\n","circ.add_gate(QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.X}), np.pi / 2)\n","print(run_mycircuit(circ))"]},{"cell_type":"markdown","metadata":{},"source":["A useful first step to integrating this is to define a conversion from the `pytket.Circuit` class to the `MyCircuit` class. In most cases, this will just amount to converting one gate at a time by a simple syntax map. We need not specify how to convert every possible `OpType`, since we can rely on the compilation passes in `pytket` to map the circuit into the required gate set as long as it is universal. For this example, the definitions of `OpType.Rx`, `OpType.Ry`, `OpType.Rz`, and `OpType.ZZMax` all match the form of a single Pauli exponential."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit, OpType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def tk_to_mycircuit(tkc: Circuit) -> MyCircuit:\n"," \"\"\"Convert a pytket Circuit to a MyCircuit object.\n"," Supports Rz, Rx, Ry, and ZZMax gates.\n"," :param tkc: The Circuit to convert\n"," :type tkc: Circuit\n"," :return: An equivalent MyCircuit object\n"," :rtype: MyCircuit\n"," \"\"\"\n"," circ = MyCircuit(tkc.qubits)\n"," for command in tkc:\n"," optype = command.op.type\n"," if optype == OpType.Rx:\n"," circ.add_gate(\n"," QubitPauliString(command.args[0], Pauli.X), np.pi * command.op.params[0]\n"," )\n"," elif optype == OpType.Ry:\n"," circ.add_gate(\n"," QubitPauliString(command.args[0], Pauli.Y), np.pi * command.op.params[0]\n"," )\n"," elif optype == OpType.Rz:\n"," circ.add_gate(\n"," QubitPauliString(command.args[0], Pauli.Z), np.pi * command.op.params[0]\n"," )\n"," elif optype == OpType.ZZMax:\n"," circ.add_gate(\n"," QubitPauliString(command.args, [Pauli.Z, Pauli.Z]), np.pi * 0.5\n"," )\n"," else:\n"," raise ValueError(\"Cannot convert optype to MyCircuit: \", optype)\n"," return circ"]},{"cell_type":"markdown","metadata":{},"source":["Now we turn to the `Backend` class. This provides a uniform API to submit `Circuit` objects for evaluation, typically returning either a statevector or a set of measurement shots. It also captures all of the information needed for compilation and asynchronous job management.
\n","
\n","We will make a subclass of `Backend` for our statevector simulator. The `_supports_state` flag lets the methods of the abstract `Backend` class know that this implementation supports statevector simulation. We also set `_persistent_handles` to `False` since this `Backend` will not be able to retrieve results from a previous Python session.
\n","
\n","Since we do not need to connect to a remote process for the simulator, the constructor doesn't need to set anything up. The base `Backend` constructor will initialise the `_cache` field for storing job data."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.backends import Backend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["class MyBackend(Backend):\n"," \"\"\"A pytket Backend wrapping around the MySimulator statevector simulator\"\"\"\n"," _supports_state = True\n"," _persistent_handles = False\n"," def __init__(self):\n"," \"\"\"Create a new instance of the MyBackend class\"\"\"\n"," super().__init__()"]},{"cell_type":"markdown","metadata":{},"source":["Most `Backend`s will only support a small fragment of the `Circuit` language, either through implementation limitations or since a specific presentation is universal. It is helpful to keep this information in the `Backend` object itself so that users can clearly see how a `Circuit` needs to look before it can be successfully run. The `Predicate` classes in `pytket` can capture many common restrictions. The idea behind the `required_predicates` list is that any `Circuit` satisfying every `Predicate` in the list can be run on the `Backend` successfully as it is.
\n","
\n","However, a typical high-level user will not be writing `Circuit`s that satisfies all of the `required_predicates`, preferring instead to use the model that is most natural for the algorithm they are implementing. Providing a `default_compilation_pass` gives users an easy starting point for compiling an arbitrary `Circuit` into a form that can be executed (when not blocked by paradigm constraints like `NoMidMeasurePredicate` or `NoClassicalControlPredicate` that cannot easily be solved by compilation).
\n","
\n","You can provide several options using the `optimisation_level` argument. We tend to use `0` for very basic compilation with no optimisation applied, `1` for the inclusion of fast optimisations (e.g. `SynthesiseIBM` is a pre-defined sequence of optimisation passes that scales well with circuit size), and `2` for heavier optimisation (e.g. `FullPeepholeOptimise` incorporates `SynthesiseTket` alongside some extra passes that may take longer for large circuits).
\n","
\n","When designing these compilation pass sequences for a given `Backend`, it can be a good idea to start with the passes that solve individual constraints from `required_predicates` (like `FullMappingPass` for `ConnectivityPredicate` or `RebaseX` for `GateSetPredicate`), and find an ordering such that no later pass invalidates the work of an earlier one.
\n","
\n","For `MyBackend`, we will need to enforce that our circuits are expressed entirely in terms of `OpType.Rx`, `OpType.Ry`, `OpType.Rz`, and `OpType.ZZMax` gates which we can solve using `RebaseCustom`. Note that we omit `OpType.Measure` since we can only run pure quantum circuits.
\n","
\n","The standard docstrings for these and other abstract methods can be seen in the abstract `Backend` [API reference](https://cqcl.github.io/tket/pytket/api/backends.html#pytket.backends.Backend)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.predicates import Predicate, GateSetPredicate, NoClassicalBitsPredicate\n","from pytket.passes import (\n"," BasePass,\n"," SequencePass,\n"," DecomposeBoxes,\n"," SynthesiseTket,\n"," FullPeepholeOptimise,\n"," RebaseCustom,\n"," SquashCustom,\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["@property\n","def required_predicates(self) -> List[Predicate]:\n"," \"\"\"\n"," The minimum set of predicates that a circuit must satisfy before it can\n"," be successfully run on this backend.\n"," :return: Required predicates.\n"," :rtype: List[Predicate]\n"," \"\"\"\n"," preds = [\n"," NoClassicalBitsPredicate(),\n"," GateSetPredicate(\n"," {\n"," OpType.Rx,\n"," OpType.Ry,\n"," OpType.Rz,\n"," OpType.ZZMax,\n"," }\n"," ),\n"," ]\n"," return preds"]},{"cell_type":"markdown","metadata":{},"source":["Every `Backend` must define a rebasing method, which will normally be called from its default compilation passes (see below), but which may also be called independently. Given the target gate set, it is usually straightforward to define this using the `RebaseCustom` pass, with a couple of helpers defining rebase of an `OpType.CX` and a general `OpType.TK1` gate:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cx_circ = Circuit(2)\n","cx_circ.Sdg(0)\n","cx_circ.V(1)\n","cx_circ.Sdg(1)\n","cx_circ.Vdg(1)\n","cx_circ.ZZMax(0, 1)\n","cx_circ.Vdg(1)\n","cx_circ.Sdg(1)\n","cx_circ.add_phase(0.5)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def sq(a, b, c):\n"," circ = Circuit(1)\n"," if c != 0:\n"," circ.Rz(c, 0)\n"," if b != 0:\n"," circ.Rx(b, 0)\n"," if a != 0:\n"," circ.Rz(a, 0)\n"," return circ"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["rebase = RebaseCustom({OpType.Rx, OpType.Ry, OpType.Rz, OpType.ZZMax}, cx_circ, sq)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass:\n"," \"\"\"\n"," A suggested compilation pass that will guarantee the resulting circuit\n"," will be suitable to run on this backend with as few preconditions as\n"," possible.\n"," :param optimisation_level: The level of optimisation to perform during\n"," compilation. Level 0 just solves the device constraints without\n"," optimising. Level 1 additionally performs some light optimisations.\n"," Level 2 adds more intensive optimisations that can increase compilation\n"," time for large circuits. Defaults to 1.\n"," :type optimisation_level: int, optional\n"," :return: Compilation pass guaranteeing required predicates.\n"," :rtype: BasePass\n"," \"\"\"\n"," assert optimisation_level in range(3)\n"," squash = SquashCustom({OpType.Rz, OpType.Rx, OpType.Ry}, sq)\n"," seq = [DecomposeBoxes()] # Decompose boxes into basic gates\n"," if optimisation_level == 1:\n"," seq.append(SynthesiseTket()) # Optional fast optimisation\n"," elif optimisation_level == 2:\n"," seq.append(FullPeepholeOptimise()) # Optional heavy optimisation\n"," seq.append(rebase) # Map to target gate set\n"," if optimisation_level != 0:\n"," seq.append(squash) # Optionally simplify 1qb gate chains within this gate set\n"," return SequencePass(seq)"]},{"cell_type":"markdown","metadata":{},"source":["The `backend_info` property is used for storing various properties of a backend. By default it provides all device information useful for compilation. Typically we would make it return a class attribute `self._backend_info` that we initialise on construction, but we will define it at point of request here. We use a `FullyConnected` architecture producing an `Architecture` object with couplings between 4 qubits."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.backends.backendinfo import BackendInfo\n","from pytket.architecture import FullyConnected"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["@property\n","def backend_info(self) -> BackendInfo:\n"," return BackendInfo(\n"," \"MyBackend\",\n"," \"MySimulator\",\n"," \"1.0\",\n"," FullyConnected(4),\n"," {\n"," OpType.Rx,\n"," OpType.Ry,\n"," OpType.Rz,\n"," OpType.ZZMax,\n"," OpType.Measure,\n"," },\n"," supports_midcircuit_measurement=False,\n"," misc={\"characterisation\": None},\n"," )"]},{"cell_type":"markdown","metadata":{},"source":["Asynchronous job management is all managed through the `ResultHandle` associated with a particular `Circuit` that has been submitted. We can use it to inspect the status of the job to see if it has completed, or to look up the results if they are available.
\n","
\n","For devices, `circuit_status` should query the job to see if it is in a queue, currently being executed, completed successfully, etc. The `CircuitStatus` class is mostly driven by the `StatusEnum` values, but can also contain messages to give more detailed feedback if available. For our simulator, we are not running things asynchronously, so a `Circuit` has either not been run or it will have been completed.
\n","
\n","Since a device API will probably define its own data type for job handles, the `ResultHandle` definition is flexible enough to cover many possible data types so you can likely use the underlying job handle as the `ResultHandle`. The `_result_id_type` property specifies what data type a `ResultHandle` for this `Backend` should look like. Since our simulator has no underlying job handle, we can just use a UUID string."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.backends import ResultHandle, CircuitStatus, StatusEnum, CircuitNotRunError\n","from pytket.backends.resulthandle import _ResultIdTuple"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["@property\n","def _result_id_type(self) -> _ResultIdTuple:\n"," \"\"\"Identifier type signature for ResultHandle for this backend.\n"," :return: Type signature (tuple of hashable types)\n"," :rtype: _ResultIdTuple\n"," \"\"\"\n"," return (str,)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def circuit_status(self, handle: ResultHandle) -> CircuitStatus:\n"," \"\"\"\n"," Return a CircuitStatus reporting the status of the circuit execution\n"," corresponding to the ResultHandle\n"," \"\"\"\n"," if handle in self._cache:\n"," return CircuitStatus(StatusEnum.COMPLETED)\n"," raise CircuitNotRunError(handle)"]},{"cell_type":"markdown","metadata":{},"source":["And finally, we have the method that actually submits a job for execution. `process_circuits` should take a collection of (compiled) `Circuit` objects, process them and return a `ResultHandle` for each `Circuit`. If execution is synchronous, then this can simply wait until it is finished, store the result in `_cache` and return. For backends that support asynchronous jobs, you will need to set up an event to format and store the result on completion.
\n","
\n","It is recommended to use the `valid_check` parameter to control a call to `Backend._check_all_circuits()`, which will raise an exception if any of the circuits do not satisfy everything in `required_predicates`.
\n","
\n","The `_cache` fields stores all of the information about current jobs that have been run. When a job has finished execution, the results are expected to be stored in `_cache[handle][\"result\"]`, though it can also be used to store other data about the job such as some information about the `Circuit` required to properly format the results. Methods like `Backend.get_result()` and `Backend.empty_cache()` expect to interact with the results of a given job in this way.
\n","
\n","The final output of the execution is stored in a `BackendResult` object. This captures enough information about the results to reinterpret it in numerous ways, such as requesting the statevector in a specific qubit ordering or converting a complete shot table to a summary of the counts. If we create a `BackendResult` with quantum data (e.g. a statevector or unitary), we must provide the `Qubit` ids in order from most-significant to least-significant with regards to indexing the state. Similarly, creating one with classical readouts (e.g. a shot table or counts summary), we give the `Bit` ids in the order they appear in a readout (left-to-right).
\n","
\n","For a statevector simulation, we should also take into account the global phase stored in the `Circuit` object and any implicit qubit permutations, since these become observable when inspecting the quantum state. We can handle the qubit permutation by changing the order in which we pass the `Qubit` ids into the `BackendResult` object."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.backends.backendresult import BackendResult\n","from pytket.utils.results import KwargTypes\n","from typing import Iterable, Optional\n","from uuid import uuid4"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def process_circuits(\n"," self,\n"," circuits: Iterable[Circuit],\n"," n_shots: Optional[int] = None,\n"," valid_check: bool = True,\n"," **kwargs: KwargTypes,\n",") -> List[ResultHandle]:\n"," \"\"\"\n"," Submit circuits to the backend for running. The results will be stored\n"," in the backend's result cache to be retrieved by the corresponding\n"," get_ method.\n"," Use keyword arguments to specify parameters to be used in submitting circuits\n"," See specific Backend derived class for available parameters, from the following\n"," list:\n"," * `seed`: RNG seed for simulators\n"," :param circuits: Circuits to process on the backend.\n"," :type circuits: Iterable[Circuit]\n"," :param n_shots: Number of shots to run per circuit. None is to be used\n"," for state/unitary simulators. Defaults to None.\n"," :type n_shots: Optional[int], optional\n"," :param valid_check: Explicitly check that all circuits satisfy all required\n"," predicates to run on the backend. Defaults to True\n"," :type valid_check: bool, optional\n"," :return: Handles to results for each input circuit, as an interable in\n"," the same order as the circuits.\n"," :rtype: List[ResultHandle]\n"," \"\"\"\n"," circuit_list = list(circuits)\n"," if valid_check:\n"," self._check_all_circuits(circuit_list)\n"," handle_list = []\n"," for circuit in circuit_list:\n"," handle = ResultHandle(str(uuid4()))\n"," mycirc = tk_to_mycircuit(circuit)\n"," state = run_mycircuit(mycirc)\n"," state *= np.exp(1j * np.pi * circuit.phase)\n"," implicit_perm = circuit.implicit_qubit_permutation()\n"," res_qubits = [implicit_perm[qb] for qb in sorted(circuit.qubits, reverse=True)]\n"," res = BackendResult(q_bits=res_qubits, state=state)\n"," self._cache[handle] = {\"result\": res}\n"," handle_list.append(handle)\n"," return handle_list"]},{"cell_type":"markdown","metadata":{},"source":["Let's redefine our `MyBackend` class to use these methods to finish it off."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["class MyBackend(Backend):\n"," \"\"\"A pytket Backend wrapping around the MySimulator statevector simulator\"\"\"\n"," _supports_state = True\n"," _persistent_handles = False\n"," def __init__(self):\n"," \"\"\"Create a new instance of the MyBackend class\"\"\"\n"," super().__init__()\n"," required_predicates = required_predicates\n"," rebase_pass = rebase\n"," default_compilation_pass = default_compilation_pass\n"," _result_id_type = _result_id_type\n"," circuit_status = circuit_status\n"," process_circuits = process_circuits"]},{"cell_type":"markdown","metadata":{},"source":["Our new `Backend` subclass is now complete, so let's test it out. If you are planning on maintaining a backend class, it is recommended to set up some unit tests. The following tests will cover basic operation and integration with `pytket` utilities."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import BasisOrder, Unitary1qBox\n","from pytket.passes import CliffordSimp\n","from pytket.utils import get_operator_expectation_value\n","from pytket.utils.operators import QubitPauliOperator\n","import pytest"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def test_bell() -> None:\n"," c = Circuit(2)\n"," c.H(0)\n"," c.CX(0, 1)\n"," b = MyBackend()\n"," c = b.get_compiled_circuit(c)\n"," h = b.process_circuit(c)\n"," assert np.allclose(\n"," b.get_result(h).get_state(), np.asarray([1, 0, 0, 1]) * 1 / np.sqrt(2)\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def test_basisorder() -> None:\n"," c = Circuit(2)\n"," c.X(1)\n"," b = MyBackend()\n"," c = b.get_compiled_circuit(c)\n"," h = b.process_circuit(c)\n"," r = b.get_result(h)\n"," assert np.allclose(r.get_state(), np.asarray([0, 1, 0, 0]))\n"," assert np.allclose(r.get_state(basis=BasisOrder.dlo), np.asarray([0, 0, 1, 0]))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def test_implicit_perm() -> None:\n"," c = Circuit(2)\n"," c.CX(0, 1)\n"," c.CX(1, 0)\n"," c.Ry(0.1, 1)\n"," c1 = c.copy()\n"," CliffordSimp().apply(c1)\n"," b = MyBackend()\n"," c = b.get_compiled_circuit(c, optimisation_level=1)\n"," c1 = b.get_compiled_circuit(c1, optimisation_level=1)\n"," assert c.implicit_qubit_permutation() != c1.implicit_qubit_permutation()\n"," h, h1 = b.process_circuits([c, c1])\n"," r, r1 = b.get_results([h, h1])\n"," for bo in [BasisOrder.ilo, BasisOrder.dlo]:\n"," s = r.get_state(basis=bo)\n"," s1 = r1.get_state(basis=bo)\n"," assert np.allclose(s, s1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def test_compilation_pass() -> None:\n"," b = MyBackend()\n"," for opt_level in range(3):\n"," c = Circuit(2)\n"," c.CX(0, 1)\n"," u = np.asarray([[0, 1], [-1j, 0]])\n"," c.add_unitary1qbox(Unitary1qBox(u), 1)\n"," c.CX(0, 1)\n"," c.add_gate(OpType.CRz, 0.35, [1, 0])\n"," assert not (b.valid_circuit(c))\n"," c = b.get_compiled_circuit(c, optimisation_level=opt_level)\n"," assert b.valid_circuit(c)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def test_invalid_measures() -> None:\n"," c = Circuit(2)\n"," c.H(0).CX(0, 1).measure_all()\n"," b = MyBackend()\n"," c = b.get_compiled_circuit(c)\n"," assert not (b.valid_circuit(c))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def test_expectation_value() -> None:\n"," c = Circuit(2)\n"," c.H(0)\n"," c.CX(0, 1)\n"," op = QubitPauliOperator(\n"," {\n"," QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z}): 1.0,\n"," QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X}): 0.3,\n"," QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Y}): 0.8j,\n"," QubitPauliString({Qubit(0): Pauli.Y}): -0.4j,\n"," }\n"," )\n"," b = MyBackend()\n"," c = b.get_compiled_circuit(c)\n"," assert get_operator_expectation_value(c, op, b) == pytest.approx(1.3)"]},{"cell_type":"markdown","metadata":{},"source":["Explicit calls are needed for this notebook. Normally pytest will just find these \"test_X\" methods when run from the command line:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["test_bell()\n","test_basisorder()\n","test_implicit_perm()\n","test_compilation_pass()\n","test_invalid_measures()\n","test_expectation_value()"]},{"cell_type":"markdown","metadata":{},"source":["To show how this compares to a sampling simulator, let's extend our simulator to handle end-of-circuit measurements."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from typing import Set"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def sample_mycircuit(\n"," circ: MyCircuit, qubits: Set[Qubit], n_shots: int, seed: Optional[int] = None\n",") -> np.ndarray:\n"," \"\"\"Run the circuit on the all-|0❭ state and measures a set of qubits\n"," :param circ: The circuit to simulate\n"," :type circ: MyCircuit\n"," :param qubits: The set of qubits to measure\n"," :type qubits: Set[Qubit]\n"," :param n_shots: The number of samples to take\n"," :type n_shots: int\n"," :param seed: Seed for the random number generator, defaults to no seed\n"," :type seed: Optional[int], optional\n"," :return: Table of shots; each row is a shot, columns are qubit readouts in ascending Qubit order\n"," :rtype: np.ndarray\n"," \"\"\"\n"," state = run_mycircuit(circ)\n"," cumulative_probs = (state * state.conjugate()).cumsum()\n"," if seed is not None:\n"," np.random.seed(seed)\n"," shots = np.zeros((n_shots, len(circ.qubits)))\n"," for s in range(n_shots):\n"," # Pick a random point in the distribution\n"," point = np.random.uniform(0.0, 1.0)\n"," # Find the corresponding readout\n"," index = np.searchsorted(cumulative_probs, point)\n"," # Convert index to a binary array\n"," # `bin` maps e.g. index 6 to '0b110'\n"," # So we ignore the first two symbols and add leading 0s to make it a fixed length\n"," bitstring = bin(index)[2:].zfill(len(circ.qubits))\n"," shots[s] = np.asarray([int(b) for b in bitstring])\n"," filtered = np.zeros((n_shots, len(qubits)))\n"," target = 0\n"," for col, q in enumerate(circ.qubits):\n"," if q in qubits:\n"," filtered[:, target] = shots[:, col]\n"," target += 1\n"," return filtered"]},{"cell_type":"markdown","metadata":{},"source":["Since `MyCircuit` doesn't have a representation for measurement gates, our converter must return both the `MyCircuit` object and some way of capturing the measurements. Since we will also want to know how they map into our `Bit` ids, the simplest option is just a dictionary from `Qubit` to `Bit`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Bit\n","from typing import Dict, Tuple"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def tk_to_mymeasures(tkc: Circuit) -> Tuple[MyCircuit, Dict[Qubit, Bit]]:\n"," \"\"\"Convert a pytket Circuit to a MyCircuit object and a measurement map.\n"," Supports Rz, Rx, Ry, and ZZMax gates, as well as end-of-circuit measurements.\n"," :param tkc: The Circuit to convert\n"," :type tkc: Circuit\n"," :return: An equivalent MyCircuit object and a map from measured Qubit to the Bit containing the result\n"," :rtype: Tuple[MyCircuit, Dict[Qubit, Bit]]\n"," \"\"\"\n"," circ = MyCircuit(tkc.qubits)\n"," measure_map = dict()\n"," measured_units = (\n"," set()\n"," ) # Track measured Qubits/used Bits to identify mid-circuit measurement\n"," for command in tkc:\n"," for u in command.args:\n"," if u in measured_units:\n"," raise ValueError(\"Circuit contains a mid-circuit measurement\")\n"," optype = command.op.type\n"," if optype == OpType.Rx:\n"," circ.add_gate(\n"," QubitPauliString(command.args[0], Pauli.X), np.pi * command.op.params[0]\n"," )\n"," elif optype == OpType.Ry:\n"," circ.add_gate(\n"," QubitPauliString(command.args[0], Pauli.Y), np.pi * command.op.params[0]\n"," )\n"," elif optype == OpType.Rz:\n"," circ.add_gate(\n"," QubitPauliString(command.args[0], Pauli.Z), np.pi * command.op.params[0]\n"," )\n"," elif optype == OpType.ZZMax:\n"," circ.add_gate(\n"," QubitPauliString(command.args, [Pauli.Z, Pauli.Z]), np.pi * 0.5\n"," )\n"," elif optype == OpType.Measure:\n"," measure_map[command.args[0]] = command.args[1]\n"," measured_units.add(command.args[0])\n"," measured_units.add(command.args[1])\n"," else:\n"," raise ValueError(\"Cannot convert optype to MyCircuit: \", optype)\n"," return circ, measure_map"]},{"cell_type":"markdown","metadata":{},"source":["To build a `Backend` subclass for this sampling simulator, we only need to change how we write `required_predicates` and `process_circuits`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.predicates import NoMidMeasurePredicate, NoClassicalControlPredicate\n","from pytket.utils.outcomearray import OutcomeArray"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["class MySampler(Backend):\n"," \"\"\"A pytket Backend wrapping around the MySimulator simulator with readout sampling\"\"\"\n"," _supports_shots = True\n"," _supports_counts = True\n"," _persistent_handles = False\n"," def __init__(self):\n"," \"\"\"Create a new instance of the MySampler class\"\"\"\n"," super().__init__()\n"," rebase_pass = rebase\n"," default_compilation_pass = default_compilation_pass\n"," _result_id_type = _result_id_type\n"," circuit_status = circuit_status\n"," @property\n"," def required_predicates(self) -> List[Predicate]:\n"," \"\"\"\n"," The minimum set of predicates that a circuit must satisfy before it can\n"," be successfully run on this backend.\n"," :return: Required predicates.\n"," :rtype: List[Predicate]\n"," \"\"\"\n"," preds = [\n"," NoClassicalControlPredicate(),\n"," NoMidMeasurePredicate(),\n"," GateSetPredicate(\n"," {\n"," OpType.Rx,\n"," OpType.Ry,\n"," OpType.Rz,\n"," OpType.ZZMax,\n"," OpType.Measure,\n"," }\n"," ),\n"," ]\n"," return preds\n"," def process_circuits(\n"," self,\n"," circuits: Iterable[Circuit],\n"," n_shots: Optional[int] = None,\n"," valid_check: bool = True,\n"," **kwargs: KwargTypes,\n"," ) -> List[ResultHandle]:\n"," \"\"\"\n"," Submit circuits to the backend for running. The results will be stored\n"," in the backend's result cache to be retrieved by the corresponding\n"," get_ method.\n"," Use keyword arguments to specify parameters to be used in submitting circuits\n"," See specific Backend derived class for available parameters, from the following\n"," list:\n"," * `seed`: RNG seed for simulators\n"," :param circuits: Circuits to process on the backend.\n"," :type circuits: Iterable[Circuit]\n"," :param n_shots: Number of shots to run per circuit. None is to be used\n"," for state/unitary simulators. Defaults to None.\n"," :type n_shots: Optional[int], optional\n"," :param valid_check: Explicitly check that all circuits satisfy all required\n"," predicates to run on the backend. Defaults to True\n"," :type valid_check: bool, optional\n"," :return: Handles to results for each input circuit, as an interable in\n"," the same order as the circuits.\n"," :rtype: List[ResultHandle]\n"," \"\"\"\n"," circuit_list = list(circuits)\n"," if valid_check:\n"," self._check_all_circuits(circuit_list)\n"," handle_list = []\n"," for circuit in circuit_list:\n"," handle = ResultHandle(str(uuid4()))\n"," mycirc, measure_map = tk_to_mymeasures(circuit)\n"," qubit_list, bit_list = zip(*measure_map.items())\n"," qubit_shots = sample_mycircuit(\n"," mycirc, set(qubit_list), n_shots, kwargs.get(\"seed\")\n"," )\n"," # Pad shot table with 0 columns for unused bits\n"," all_shots = np.zeros((n_shots, len(circuit.bits)), dtype=int)\n"," all_shots[:, : len(qubit_list)] = qubit_shots\n"," res_bits = [measure_map[q] for q in sorted(qubit_list, reverse=True)]\n"," for b in circuit.bits:\n"," if b not in bit_list:\n"," res_bits.append(b)\n"," res = BackendResult(\n"," c_bits=res_bits, shots=OutcomeArray.from_readouts(all_shots)\n"," )\n"," self._cache[handle] = {\"result\": res}\n"," handle_list.append(handle)\n"," return handle_list"]},{"cell_type":"markdown","metadata":{},"source":["Likewise, we run some basic tests to make sure it works."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def test_sampler_bell() -> None:\n"," c = Circuit(2, 2)\n"," c.H(0)\n"," c.CX(0, 1)\n"," c.measure_all()\n"," b = MySampler()\n"," c = b.get_compiled_circuit(c)\n"," res = b.run_circuit(c, n_shots=10, seed=3)\n"," assert res.get_shots().shape == (10, 2)\n"," assert res.get_counts() == {(0, 0): 5, (1, 1): 5}"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def test_sampler_basisorder() -> None:\n"," c = Circuit(2, 2)\n"," c.X(1)\n"," c.measure_all()\n"," b = MySampler()\n"," c = b.get_compiled_circuit(c)\n"," res = b.run_circuit(c, n_shots=10, seed=0)\n"," assert res.get_counts() == {(0, 1): 10}\n"," assert res.get_counts(basis=BasisOrder.dlo) == {(1, 0): 10}"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def test_sampler_compilation_pass() -> None:\n"," b = MySampler()\n"," for opt_level in range(3):\n"," c = Circuit(2)\n"," c.CX(0, 1)\n"," u = np.asarray([[0, 1], [-1j, 0]])\n"," c.add_unitary1qbox(Unitary1qBox(u), 1)\n"," c.CX(0, 1)\n"," c.add_gate(OpType.CRz, 0.35, [1, 0])\n"," c.measure_all()\n"," assert not (b.valid_circuit(c))\n"," c = b.get_compiled_circuit(c, optimisation_level=opt_level)\n"," assert b.valid_circuit(c)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def test_sampler_expectation_value() -> None:\n"," c = Circuit(2)\n"," c.H(0)\n"," c.CX(0, 1)\n"," op = QubitPauliOperator(\n"," {\n"," QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z}): 1.0,\n"," QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X}): 0.3,\n"," QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Y}): 0.8j,\n"," QubitPauliString({Qubit(0): Pauli.Y}): -0.4j,\n"," }\n"," )\n"," b = MySampler()\n"," c = b.get_compiled_circuit(c)\n"," expectation = get_operator_expectation_value(c, op, b, n_shots=2000, seed=0)\n"," assert (np.real(expectation), np.imag(expectation)) == pytest.approx(\n"," (1.3, 0.0), abs=0.1\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["test_sampler_bell()\n","test_sampler_basisorder()\n","test_sampler_compilation_pass()\n","test_sampler_expectation_value()"]},{"cell_type":"markdown","metadata":{},"source":["Exercises:
\n","- Add some extra gate definitions to the simulator and expand the accepted gate set of the backends. Start with some that are easily represented as exponentiated Pauli tensors like `OpType.YYPhase`. For a challenge, try adding `OpType.CCX` efficiently (it is possible to encode it using seven Pauli rotations).
\n","- Restrict the simulator to a limited qubit connectivity. Express this in the backends by modifying the `Architecture` property of the `BackendInfo` attribute object and adding to the `required_predicates`. Adjust the `default_compilation_pass` to solve for the connectivity.
\n","- The file `creating_backends_exercise.py` extends the simulators above to allow for mid-circuit measurement and conditional gates using a binary decision tree. Implement an appropriate converter and `Backend` class for this simulator."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/backends/qiskit_integration.ipynb b/docs/examples/backends/qiskit_integration.ipynb index 60d24c21..9e79a2b3 100644 --- a/docs/examples/backends/qiskit_integration.ipynb +++ b/docs/examples/backends/qiskit_integration.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Integrating `pytket` into Qiskit software"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In this tutorial, we will focus on:
\n", "- Using `pytket` for compilation or providing devices/simulators within Qiskit workflows;
\n", "- Adapting Qiskit code to use `pytket` directly."]}, {"cell_type": "markdown", "metadata": {}, "source": ["See the [pytket-qiskit docs](https://tket.quantinuum.com/extensions/pytket-qiskit/) for more information."]}, {"cell_type": "markdown", "metadata": {}, "source": ["This example assumes some familiarity with the Qiskit algorithms library. We have chosen a small variational quantum eigensolver (VQE) for our example, but the same principles apply to a wide range of quantum algorithms.
\n", "
\n", "To run this example, you will need `pytket-qiskit`, as well as the separate `qiskit-optimization` package. You will also need IBMQ credentials stored on your local machine.
\n", "
\n", "Qiskit has risen to prominence as the most popular platform for the development of quantum software, providing an open source, full-stack solution with a large feature list and extensive examples from the developers and community. For many researchers who have already invested in building a large codebase built on top of Qiskit, the idea of switching entirely to a new platform can look like a time-sink and may require reversion to take advantage of the new tools that get regularly added to Qiskit.
\n", "
\n", "The interoperability provided by `pytket-qiskit` allows Qiskit users to start taking advantage of some of the unique features of `pytket` without having to completely rewrite their software."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Let's take as an example an ansatz for computing the ground-state energy of a hydrogen molecule."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from qiskit.quantum_info import SparsePauliOp"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["H2_op = SparsePauliOp.from_list(\n", " [\n", " (\"II\", -1.052373245772859),\n", " (\"IZ\", 0.39793742484318045),\n", " (\"ZI\", -0.39793742484318045),\n", " (\"ZZ\", -0.01128010425623538),\n", " (\"XX\", 0.18093119978423156),\n", " ]\n", ")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["First let's use qiskit's NumPyEigensolver to compute the exact answer:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from qiskit.algorithms.eigensolvers import NumPyEigensolver"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["es = NumPyEigensolver(k=1)\n", "exact_result = es.compute_eigenvalues(H2_op).eigenvalues[0].real\n", "print(\"Exact result:\", exact_result)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The following function will attempt to find an approximation to this using VQE, given a qiskit BackendEstimator on which to run circuits:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from qiskit.algorithms.minimum_eigensolvers.vqe import VQE\n", "from qiskit.algorithms.optimizers import SPSA\n", "from qiskit.circuit.library import EfficientSU2"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def vqe_solve(op, maxiter, qestimator):\n", " optimizer = SPSA(maxiter=maxiter)\n", " ansatz = EfficientSU2(op.num_qubits, entanglement=\"linear\")\n", " vqe = VQE(estimator=qestimator, ansatz=ansatz, optimizer=optimizer)\n", " return vqe.compute_minimum_eigenvalue(op).eigenvalue"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We will run this on a pytket `IBMQEmulatorBackend`. This is a noisy simulator whose characteristics match those of the real device, in this case \"ibmq_belem\" (a 5-qubit machine). The characteristics are retrieved from the device when the backend is constructed, so we must first load our IBMQ account. Circuits will be compiled to match the connectivity of the device and simulated using a basic noise model [constructed from the device parameters](https://qiskit.org/documentation/apidoc/aer_noise.html)."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import IBMQEmulatorBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["b_emu = IBMQEmulatorBackend(\"ibmq_belem\", instance=\"ibm-q/open/main\")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Most qiskit algorithms require a qiskit `primitive` as input; this in turn is constructed from a `qiskit.providers.Backend`. The `TketBackend` class wraps a pytket backend as a `qiskit.providers.Backend`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit.tket_backend import TketBackend\n", "from qiskit.primitives import BackendEstimator"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["qis_backend = TketBackend(b_emu)\n", "qestimator = BackendEstimator(qis_backend, options={\"shots\": 8192})"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that we could have used any other pytket shots backend instead of `b_emu` here. The `pytket` extension modules provide an interface to a wide variety of devices and simulators from different quantum software platforms.
\n", "
\n", "We can now run the VQE algorithm. In this example we use only 50 iterations, but greater accuracy may be achieved by increasing this number:"]}, {"cell_type": "markdown", "metadata": {}, "source": ["#print(\"VQE result:\", vqe_solve(H2_op, 50, qestimator))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Another way to improve the accuracy of results is to apply optimisations to the circuit in an attempt to reduce the overall noise. When we construct our qiskit backend, we can pass in a pytket compilation pass as an additional parameter. There is a wide range of options here; we recommend the device-specific default compilation pass, provided by each tket backend. This pass will ensure that all the hardware constraints of the device are met. We can enable tket's most aggressive optimisation level by setting the parameter `optimisation_level=2`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["qis_backend2 = TketBackend(b_emu, b_emu.default_compilation_pass(optimisation_level=2))\n", "qestimator2 = BackendEstimator(qis_backend2, options={\"shots\": 8192})"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Let's run the optimisation again:"]}, {"cell_type": "markdown", "metadata": {}, "source": ["#print(\"VQE result (with optimisation):\", vqe_solve(H2_op, 50, qestimator2))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["These are small two-qubit circuits, so the improvement may be small, but with larger, more complex circuits, the reduction in noise from compilation will make a greater difference and allow VQE experiments to converge with fewer iterations."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Integrating `pytket` into Qiskit software"]},{"cell_type":"markdown","metadata":{},"source":["**Download this notebook - {nb-download}`qiskit_integration.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:
\n","- Using `pytket` for compilation or providing devices/simulators within Qiskit workflows;
\n","- Adapting Qiskit code to use `pytket` directly."]},{"cell_type":"markdown","metadata":{},"source":["See the [pytket-qiskit docs](https://tket.quantinuum.com/extensions/pytket-qiskit/) for more information."]},{"cell_type":"markdown","metadata":{},"source":["This example assumes some familiarity with the Qiskit algorithms library. We have chosen a small variational quantum eigensolver (VQE) for our example, but the same principles apply to a wide range of quantum algorithms.
\n","
\n","To run this example, you will need `pytket-qiskit`, as well as the separate `qiskit-optimization` package. You will also need IBMQ credentials stored on your local machine.
\n","
\n","Qiskit has risen to prominence as the most popular platform for the development of quantum software, providing an open source, full-stack solution with a large feature list and extensive examples from the developers and community. For many researchers who have already invested in building a large codebase built on top of Qiskit, the idea of switching entirely to a new platform can look like a time-sink and may require reversion to take advantage of the new tools that get regularly added to Qiskit.
\n","
\n","The interoperability provided by `pytket-qiskit` allows Qiskit users to start taking advantage of some of the unique features of `pytket` without having to completely rewrite their software."]},{"cell_type":"markdown","metadata":{},"source":["Let's take as an example an ansatz for computing the ground-state energy of a hydrogen molecule."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from qiskit.quantum_info import SparsePauliOp"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["H2_op = SparsePauliOp.from_list(\n"," [\n"," (\"II\", -1.052373245772859),\n"," (\"IZ\", 0.39793742484318045),\n"," (\"ZI\", -0.39793742484318045),\n"," (\"ZZ\", -0.01128010425623538),\n"," (\"XX\", 0.18093119978423156),\n"," ]\n",")"]},{"cell_type":"markdown","metadata":{},"source":["First let's use qiskit's NumPyEigensolver to compute the exact answer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from qiskit.algorithms.eigensolvers import NumPyEigensolver"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["es = NumPyEigensolver(k=1)\n","exact_result = es.compute_eigenvalues(H2_op).eigenvalues[0].real\n","print(\"Exact result:\", exact_result)"]},{"cell_type":"markdown","metadata":{},"source":["The following function will attempt to find an approximation to this using VQE, given a qiskit BackendEstimator on which to run circuits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from qiskit.algorithms.minimum_eigensolvers.vqe import VQE\n","from qiskit.algorithms.optimizers import SPSA\n","from qiskit.circuit.library import EfficientSU2"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def vqe_solve(op, maxiter, qestimator):\n"," optimizer = SPSA(maxiter=maxiter)\n"," ansatz = EfficientSU2(op.num_qubits, entanglement=\"linear\")\n"," vqe = VQE(estimator=qestimator, ansatz=ansatz, optimizer=optimizer)\n"," return vqe.compute_minimum_eigenvalue(op).eigenvalue"]},{"cell_type":"markdown","metadata":{},"source":["We will run this on a pytket `IBMQEmulatorBackend`. This is a noisy simulator whose characteristics match those of the real device, in this case \"ibmq_belem\" (a 5-qubit machine). The characteristics are retrieved from the device when the backend is constructed, so we must first load our IBMQ account. Circuits will be compiled to match the connectivity of the device and simulated using a basic noise model [constructed from the device parameters](https://qiskit.org/documentation/apidoc/aer_noise.html)."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import IBMQEmulatorBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b_emu = IBMQEmulatorBackend(\"ibmq_belem\", instance=\"ibm-q/open/main\")"]},{"cell_type":"markdown","metadata":{},"source":["Most qiskit algorithms require a qiskit `primitive` as input; this in turn is constructed from a `qiskit.providers.Backend`. The `TketBackend` class wraps a pytket backend as a `qiskit.providers.Backend`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit.tket_backend import TketBackend\n","from qiskit.primitives import BackendEstimator"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qis_backend = TketBackend(b_emu)\n","qestimator = BackendEstimator(qis_backend, options={\"shots\": 8192})"]},{"cell_type":"markdown","metadata":{},"source":["Note that we could have used any other pytket shots backend instead of `b_emu` here. The `pytket` extension modules provide an interface to a wide variety of devices and simulators from different quantum software platforms.
\n","
\n","We can now run the VQE algorithm. In this example we use only 50 iterations, but greater accuracy may be achieved by increasing this number:"]},{"cell_type":"markdown","metadata":{},"source":["#print(\"VQE result:\", vqe_solve(H2_op, 50, qestimator))"]},{"cell_type":"markdown","metadata":{},"source":["Another way to improve the accuracy of results is to apply optimisations to the circuit in an attempt to reduce the overall noise. When we construct our qiskit backend, we can pass in a pytket compilation pass as an additional parameter. There is a wide range of options here; we recommend the device-specific default compilation pass, provided by each tket backend. This pass will ensure that all the hardware constraints of the device are met. We can enable tket's most aggressive optimisation level by setting the parameter `optimisation_level=2`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qis_backend2 = TketBackend(b_emu, b_emu.default_compilation_pass(optimisation_level=2))\n","qestimator2 = BackendEstimator(qis_backend2, options={\"shots\": 8192})"]},{"cell_type":"markdown","metadata":{},"source":["Let's run the optimisation again:"]},{"cell_type":"markdown","metadata":{},"source":["#print(\"VQE result (with optimisation):\", vqe_solve(H2_op, 50, qestimator2))"]},{"cell_type":"markdown","metadata":{},"source":["These are small two-qubit circuits, so the improvement may be small, but with larger, more complex circuits, the reduction in noise from compilation will make a greater difference and allow VQE experiments to converge with fewer iterations."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/circuit_compilation/compilation_example.ipynb b/docs/examples/circuit_compilation/compilation_example.ipynb index 25bfb4af..fabf9f29 100644 --- a/docs/examples/circuit_compilation/compilation_example.ipynb +++ b/docs/examples/circuit_compilation/compilation_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Compilation passes"]}, {"cell_type": "markdown", "metadata": {}, "source": ["There are numerous ways to optimize circuits in `pytket`. In this notebook we will introduce the basics of compilation passes and how to combine and apply them.
\n", "
\n", "We assume familiarity with the `pytket` `Circuit` class. The objective is to transform one `Circuit` into another, equivalent, `Circuit`, that:
\n", "* satisfies the connectivity constraints of a given architecture;
\n", "* satisfies some further user-defined constraints (such as restricted gate sets);
\n", "* minimizes some cost function (such as CX count)."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Passes"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The basic mechanism of compilation is the 'pass', which is a transform that can be applied to a circuit. There is an extensive library of passes in `pytket`, and several standard ways in which they can be combined to form new passes. For example:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import DecomposeMultiQubitsCX"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pass1 = DecomposeMultiQubitsCX()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This pass converts all multi-qubit gates into CX and single-qubit gates. So let's create a circuit containing some non-CX multi-qubit gates:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import Circuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = Circuit(3)\n", "circ.CRz(0.5, 0, 1)\n", "circ.T(2)\n", "circ.CSWAP(2, 0, 1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In order to apply a pass to a circuit, we must first create a `CompilationUnit` from it. We can think of this as a 'bridge' between the circuit and the pass. The `CompilationUnit` is constructed from the circuit; the pass is applied to the `CompilationUnit`; and the transformed circuit is extracted from the `CompilationUnit`:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.predicates import CompilationUnit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cu = CompilationUnit(circ)\n", "pass1.apply(cu)\n", "circ1 = cu.circuit"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Let's have a look at the result of the transformation:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(circ1.get_commands())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Predicates"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Every `CompilationUnit` has associated with it a set of 'predicates', which describe target properties that can be checked against the circuit. There are many types of predicates available in `pytket`. For example, the `GateSetPredicate` checks whether all gates in a circuit belong to a particular set:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.predicates import GateSetPredicate\n", "from pytket.circuit import OpType"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pred1 = GateSetPredicate({OpType.Rz, OpType.T, OpType.Tdg, OpType.H, OpType.CX})"]}, {"cell_type": "markdown", "metadata": {}, "source": ["When we construct a `CompilationUnit`, we may pass a list of target predicates as well as the circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cu = CompilationUnit(circ, [pred1])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["To check whether the circuit associated to a `CompilationUnit` satisfies its target predicates, we can call the `check_all_predicates()` method:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cu.check_all_predicates()"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pass1.apply(cu)\n", "cu.check_all_predicates()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can also directly check whether a given circuit satisfies a given predicate, using the predicate's `verify()` method:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pred1.verify(circ1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["### In-place compilation"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The example above produced a new circuit, leaving the original circuit untouched. It is also possible to apply a pass to a circuit in-place:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["DecomposeMultiQubitsCX().apply(circ)\n", "print(circ.get_commands())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Combining passes"]}, {"cell_type": "markdown", "metadata": {}, "source": ["There are various ways to combine the elementary passes into more complex ones.
\n", "
\n", "To combine several passes in sequence, we use a `SequencePass`:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import SequencePass, OptimisePhaseGadgets"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["seqpass = SequencePass([DecomposeMultiQubitsCX(), OptimisePhaseGadgets()])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This pass will apply the two transforms in succession:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cu = CompilationUnit(circ)\n", "seqpass.apply(cu)\n", "circ1 = cu.circuit\n", "print(circ1.get_commands())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `apply()` method for an elementary pass returns a boolean indicating whether or not the pass had any effect on the circuit. For a `SequencePass`, the return value indicates whether _any_ of the constituent passes had some effect.
\n", "
\n", "A `RepeatPass` repeatedly calls `apply()` on a pass until it returns `False`, indicating that there was no effect:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import CommuteThroughMultis, RemoveRedundancies, RepeatPass"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["seqpass = SequencePass([CommuteThroughMultis(), RemoveRedundancies()])\n", "reppass = RepeatPass(seqpass)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This pass will repeatedly apply `CommuteThroughMultis` (which commutes single-qubit operations through multi-qubit operations where possible towards the start of the circuit) and `RemoveRedundancies` (which cancels inverse pairs, merges coaxial rotations and removes redundant gates before measurement) until neither pass has any effect on the circuit.
\n", "
\n", "Let's use `pytket`'s built-in visualizer to see the effect on a circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit.display import render_circuit_jupyter"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = Circuit(3)\n", "circ.X(0).Y(1).CX(0, 1).Z(0).Rx(1.3, 1).CX(0, 1).Rz(0.4, 0).Ry(0.53, 0).H(1).H(2).Rx(\n", " 1.5, 2\n", ").Rx(0.5, 2).H(2)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(circ)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cu = CompilationUnit(circ)\n", "reppass.apply(cu)\n", "circ1 = cu.circuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(circ1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["If we want to repeat a pass until the circuit satisfies some desired property, we first define a boolean function to test for that property, and then pass this function to the constructor of a `RepeatUntilSatisfied` pass:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import RepeatUntilSatisfiedPass"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def no_CX(circ):\n", " return circ.n_gates_of_type(OpType.CX) == 0"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = (\n", " Circuit(2)\n", " .CX(0, 1)\n", " .X(1)\n", " .CX(0, 1)\n", " .X(1)\n", " .CX(0, 1)\n", " .X(1)\n", " .CX(0, 1)\n", " .Z(1)\n", " .CX(1, 0)\n", " .Z(1)\n", " .CX(1, 0)\n", ")"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["custom_pass = RepeatUntilSatisfiedPass(seqpass, no_CX)\n", "cu = CompilationUnit(circ)\n", "custom_pass.apply(cu)\n", "circ1 = cu.circuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(circ1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `RepeatWithMetricPass` provides another way of generating more sophisticated passes. This is defined in terms of a cost function and another pass type; the pass is applied repeatedly until the cost function stops decreasing.
\n", "
\n", "For example, suppose we wish to associate a cost to each gate in out circuit, with $n$-qubit gates having a cost of $n^2$:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def cost(circ):\n", " return sum(pow(len(x.args), 2) for x in circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Let's construct a new circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = Circuit(2)\n", "circ.CX(0, 1).X(1).Y(0).CX(0, 1).X(1).Z(0).CX(0, 1).X(1).Y(0).CX(0, 1).Z(1).CX(1, 0).Z(\n", " 1\n", ").X(0).CX(1, 0)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We will repeatedly apply `CommuteThroughMultis`, `DecomposeMultiQubitsCX` and `RemoveRedundancies` until the `cost` function stops decreasing:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import RepeatWithMetricPass"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pass1 = SequencePass(\n", " [CommuteThroughMultis(), DecomposeMultiQubitsCX(), RemoveRedundancies()]\n", ")\n", "pass2 = RepeatWithMetricPass(pass1, cost)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cu = CompilationUnit(circ)\n", "pass2.apply(cu)\n", "print(cu.circuit.get_commands())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Targeting architectures"]}, {"cell_type": "markdown", "metadata": {}, "source": ["If we are given a target architecture, we can generate passes tailored to it.
\n", "
\n", "In `pytket` an architecture is defined by a connectivity graph, i.e. a list of pairs of qubits capable of executing two-qubit operations. For example, we can represent a 5-qubit linear architecture, with qubits labelled `n[i]`, as follows:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.architecture import Architecture\n", "from pytket.circuit import Node"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["n = [Node(\"n\", i) for i in range(5)]"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["arc = Architecture([[n[0], n[1]], [n[1], n[2]], [n[2], n[3]], [n[3], n[4]]])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Suppose we have a circuit that we wish to run on this architecture:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = Circuit(5)\n", "circ.CX(0, 1)\n", "circ.H(0)\n", "circ.Z(1)\n", "circ.CX(0, 3)\n", "circ.Rx(1.5, 3)\n", "circ.CX(2, 4)\n", "circ.X(2)\n", "circ.CX(1, 4)\n", "circ.CX(0, 4)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["A mapping pass lets us rewrite this circuit for our architecture:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import DefaultMappingPass"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["mapper = DefaultMappingPass(arc)\n", "cu = CompilationUnit(circ)\n", "mapper.apply(cu)\n", "circ1 = cu.circuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(circ1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["If we want to decompose all SWAP and BRIDGE gates to CX gates in the final circuit, we can use another pass:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import DecomposeSwapsToCXs"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["pass1 = DecomposeSwapsToCXs(arc)\n", "pass1.apply(cu)\n", "circ2 = cu.circuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(circ2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that the pass we just ran also performed some clean-up: the SWAP gate was decomposed into three CX gates, one of which was cancelled by a preceding CX gate; the cancelling gates were removed from the circuit.
\n", "
\n", "Every compilation pass has associated sets of preconditions and postconditions on the circuit. If all preconditions are satisfied before the pass, all postconditions are guaranteed to be satisfied afterwards. When we apply a pass to a circuit, we can optionally pass `SafetyMode.Audit` as the second parameter; this will tell the pass to check all preconditions explicitly. By default, there is only limited checking of preconditions and `pytket` relies on the programmer assuring these.
\n", "
\n", "For example, the `NoClassicalControl` predicate is a precondition of the `PauliSimp` pass. Let's add a classically controlled gate to our circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import PauliSimp, SafetyMode\n", "from pytket.circuit import Qubit, Bit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["q = [Qubit(\"q\", i) for i in range(5)]\n", "c = Bit(\"c\")\n", "circ.add_bit(c)\n", "circ.Measure(q[3], c)\n", "circ.CY(q[0], q[1], condition_bits=[c], condition_value=1)\n", "cu = CompilationUnit(circ)\n", "try:\n", " PauliSimp().apply(cu, safety_mode=SafetyMode.Audit)\n", "except RuntimeError as e:\n", " print(\"Error:\", str(e))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The preconditions and postconditions of all the elementary predicates are documented in their string representations:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["PauliSimp()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Backends and default passes"]}, {"cell_type": "markdown", "metadata": {}, "source": ["A `pytket` `Backend` may have a default compilation pass, which will guarantee that the circuit can run on it. This is given by the `default_compilation_pass` property. For example, the default pass for Qiskit's `AerBackend` just converts all gates to U1, U2, U3 and CX:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import AerBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["b = AerBackend()\n", "b.default_compilation_pass"]}, {"cell_type": "markdown", "metadata": {}, "source": ["To compile a circuit using the default pass of a `Backend` we can simply use the `get_compiled_circuit()` method:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = Circuit(2).X(0).Y(1).CRz(0.5, 1, 0)\n", "circ1 = b.get_compiled_circuit(circ)\n", "render_circuit_jupyter(circ1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Every `Backend` will have a certain set of requirements that must be met by any circuit in order to run. These are exposed via the `required_predicates` property:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["b.required_predicates"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can test whether a given circuit satisfies these requirements using the `valid_circuit()` method:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["b.valid_circuit(circ)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["b.valid_circuit(circ1)"]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Compilation passes\n","\n","**Download this notebook - {nb-download}`compilation_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["There are numerous ways to optimize circuits in `pytket`. In this notebook we will introduce the basics of compilation passes and how to combine and apply them.
\n","
\n","We assume familiarity with the `pytket` `Circuit` class. The objective is to transform one `Circuit` into another, equivalent, `Circuit`, that:
\n","* satisfies the connectivity constraints of a given architecture;
\n","* satisfies some further user-defined constraints (such as restricted gate sets);
\n","* minimizes some cost function (such as CX count)."]},{"cell_type":"markdown","metadata":{},"source":["## Passes"]},{"cell_type":"markdown","metadata":{},"source":["The basic mechanism of compilation is the 'pass', which is a transform that can be applied to a circuit. There is an extensive library of passes in `pytket`, and several standard ways in which they can be combined to form new passes. For example:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import DecomposeMultiQubitsCX"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pass1 = DecomposeMultiQubitsCX()"]},{"cell_type":"markdown","metadata":{},"source":["This pass converts all multi-qubit gates into CX and single-qubit gates. So let's create a circuit containing some non-CX multi-qubit gates:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(3)\n","circ.CRz(0.5, 0, 1)\n","circ.T(2)\n","circ.CSWAP(2, 0, 1)"]},{"cell_type":"markdown","metadata":{},"source":["In order to apply a pass to a circuit, we must first create a `CompilationUnit` from it. We can think of this as a 'bridge' between the circuit and the pass. The `CompilationUnit` is constructed from the circuit; the pass is applied to the `CompilationUnit`; and the transformed circuit is extracted from the `CompilationUnit`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.predicates import CompilationUnit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cu = CompilationUnit(circ)\n","pass1.apply(cu)\n","circ1 = cu.circuit"]},{"cell_type":"markdown","metadata":{},"source":["Let's have a look at the result of the transformation:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(circ1.get_commands())"]},{"cell_type":"markdown","metadata":{},"source":["## Predicates"]},{"cell_type":"markdown","metadata":{},"source":["Every `CompilationUnit` has associated with it a set of 'predicates', which describe target properties that can be checked against the circuit. There are many types of predicates available in `pytket`. For example, the `GateSetPredicate` checks whether all gates in a circuit belong to a particular set:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.predicates import GateSetPredicate\n","from pytket.circuit import OpType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pred1 = GateSetPredicate({OpType.Rz, OpType.T, OpType.Tdg, OpType.H, OpType.CX})"]},{"cell_type":"markdown","metadata":{},"source":["When we construct a `CompilationUnit`, we may pass a list of target predicates as well as the circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cu = CompilationUnit(circ, [pred1])"]},{"cell_type":"markdown","metadata":{},"source":["To check whether the circuit associated to a `CompilationUnit` satisfies its target predicates, we can call the `check_all_predicates()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cu.check_all_predicates()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pass1.apply(cu)\n","cu.check_all_predicates()"]},{"cell_type":"markdown","metadata":{},"source":["We can also directly check whether a given circuit satisfies a given predicate, using the predicate's `verify()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pred1.verify(circ1)"]},{"cell_type":"markdown","metadata":{},"source":["### In-place compilation"]},{"cell_type":"markdown","metadata":{},"source":["The example above produced a new circuit, leaving the original circuit untouched. It is also possible to apply a pass to a circuit in-place:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["DecomposeMultiQubitsCX().apply(circ)\n","print(circ.get_commands())"]},{"cell_type":"markdown","metadata":{},"source":["## Combining passes"]},{"cell_type":"markdown","metadata":{},"source":["There are various ways to combine the elementary passes into more complex ones.
\n","
\n","To combine several passes in sequence, we use a `SequencePass`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import SequencePass, OptimisePhaseGadgets"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["seqpass = SequencePass([DecomposeMultiQubitsCX(), OptimisePhaseGadgets()])"]},{"cell_type":"markdown","metadata":{},"source":["This pass will apply the two transforms in succession:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cu = CompilationUnit(circ)\n","seqpass.apply(cu)\n","circ1 = cu.circuit\n","print(circ1.get_commands())"]},{"cell_type":"markdown","metadata":{},"source":["The `apply()` method for an elementary pass returns a boolean indicating whether or not the pass had any effect on the circuit. For a `SequencePass`, the return value indicates whether _any_ of the constituent passes had some effect.
\n","
\n","A `RepeatPass` repeatedly calls `apply()` on a pass until it returns `False`, indicating that there was no effect:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import CommuteThroughMultis, RemoveRedundancies, RepeatPass"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["seqpass = SequencePass([CommuteThroughMultis(), RemoveRedundancies()])\n","reppass = RepeatPass(seqpass)"]},{"cell_type":"markdown","metadata":{},"source":["This pass will repeatedly apply `CommuteThroughMultis` (which commutes single-qubit operations through multi-qubit operations where possible towards the start of the circuit) and `RemoveRedundancies` (which cancels inverse pairs, merges coaxial rotations and removes redundant gates before measurement) until neither pass has any effect on the circuit.
\n","
\n","Let's use `pytket`'s built-in visualizer to see the effect on a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit.display import render_circuit_jupyter"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(3)\n","circ.X(0).Y(1).CX(0, 1).Z(0).Rx(1.3, 1).CX(0, 1).Rz(0.4, 0).Ry(0.53, 0).H(1).H(2).Rx(\n"," 1.5, 2\n",").Rx(0.5, 2).H(2)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cu = CompilationUnit(circ)\n","reppass.apply(cu)\n","circ1 = cu.circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circ1)"]},{"cell_type":"markdown","metadata":{},"source":["If we want to repeat a pass until the circuit satisfies some desired property, we first define a boolean function to test for that property, and then pass this function to the constructor of a `RepeatUntilSatisfied` pass:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import RepeatUntilSatisfiedPass"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def no_CX(circ):\n"," return circ.n_gates_of_type(OpType.CX) == 0"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = (\n"," Circuit(2)\n"," .CX(0, 1)\n"," .X(1)\n"," .CX(0, 1)\n"," .X(1)\n"," .CX(0, 1)\n"," .X(1)\n"," .CX(0, 1)\n"," .Z(1)\n"," .CX(1, 0)\n"," .Z(1)\n"," .CX(1, 0)\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["custom_pass = RepeatUntilSatisfiedPass(seqpass, no_CX)\n","cu = CompilationUnit(circ)\n","custom_pass.apply(cu)\n","circ1 = cu.circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circ1)"]},{"cell_type":"markdown","metadata":{},"source":["The `RepeatWithMetricPass` provides another way of generating more sophisticated passes. This is defined in terms of a cost function and another pass type; the pass is applied repeatedly until the cost function stops decreasing.
\n","
\n","For example, suppose we wish to associate a cost to each gate in out circuit, with $n$-qubit gates having a cost of $n^2$:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def cost(circ):\n"," return sum(pow(len(x.args), 2) for x in circ)"]},{"cell_type":"markdown","metadata":{},"source":["Let's construct a new circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(2)\n","circ.CX(0, 1).X(1).Y(0).CX(0, 1).X(1).Z(0).CX(0, 1).X(1).Y(0).CX(0, 1).Z(1).CX(1, 0).Z(\n"," 1\n",").X(0).CX(1, 0)"]},{"cell_type":"markdown","metadata":{},"source":["We will repeatedly apply `CommuteThroughMultis`, `DecomposeMultiQubitsCX` and `RemoveRedundancies` until the `cost` function stops decreasing:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import RepeatWithMetricPass"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pass1 = SequencePass(\n"," [CommuteThroughMultis(), DecomposeMultiQubitsCX(), RemoveRedundancies()]\n",")\n","pass2 = RepeatWithMetricPass(pass1, cost)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cu = CompilationUnit(circ)\n","pass2.apply(cu)\n","print(cu.circuit.get_commands())"]},{"cell_type":"markdown","metadata":{},"source":["## Targeting architectures"]},{"cell_type":"markdown","metadata":{},"source":["If we are given a target architecture, we can generate passes tailored to it.
\n","
\n","In `pytket` an architecture is defined by a connectivity graph, i.e. a list of pairs of qubits capable of executing two-qubit operations. For example, we can represent a 5-qubit linear architecture, with qubits labelled `n[i]`, as follows:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.architecture import Architecture\n","from pytket.circuit import Node"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["n = [Node(\"n\", i) for i in range(5)]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["arc = Architecture([[n[0], n[1]], [n[1], n[2]], [n[2], n[3]], [n[3], n[4]]])"]},{"cell_type":"markdown","metadata":{},"source":["Suppose we have a circuit that we wish to run on this architecture:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(5)\n","circ.CX(0, 1)\n","circ.H(0)\n","circ.Z(1)\n","circ.CX(0, 3)\n","circ.Rx(1.5, 3)\n","circ.CX(2, 4)\n","circ.X(2)\n","circ.CX(1, 4)\n","circ.CX(0, 4)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circ)"]},{"cell_type":"markdown","metadata":{},"source":["A mapping pass lets us rewrite this circuit for our architecture:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import DefaultMappingPass"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["mapper = DefaultMappingPass(arc)\n","cu = CompilationUnit(circ)\n","mapper.apply(cu)\n","circ1 = cu.circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circ1)"]},{"cell_type":"markdown","metadata":{},"source":["If we want to decompose all SWAP and BRIDGE gates to CX gates in the final circuit, we can use another pass:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import DecomposeSwapsToCXs"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pass1 = DecomposeSwapsToCXs(arc)\n","pass1.apply(cu)\n","circ2 = cu.circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circ2)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the pass we just ran also performed some clean-up: the SWAP gate was decomposed into three CX gates, one of which was cancelled by a preceding CX gate; the cancelling gates were removed from the circuit.
\n","
\n","Every compilation pass has associated sets of preconditions and postconditions on the circuit. If all preconditions are satisfied before the pass, all postconditions are guaranteed to be satisfied afterwards. When we apply a pass to a circuit, we can optionally pass `SafetyMode.Audit` as the second parameter; this will tell the pass to check all preconditions explicitly. By default, there is only limited checking of preconditions and `pytket` relies on the programmer assuring these.
\n","
\n","For example, the `NoClassicalControl` predicate is a precondition of the `PauliSimp` pass. Let's add a classically controlled gate to our circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import PauliSimp, SafetyMode\n","from pytket.circuit import Qubit, Bit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["q = [Qubit(\"q\", i) for i in range(5)]\n","c = Bit(\"c\")\n","circ.add_bit(c)\n","circ.Measure(q[3], c)\n","circ.CY(q[0], q[1], condition_bits=[c], condition_value=1)\n","cu = CompilationUnit(circ)\n","try:\n"," PauliSimp().apply(cu, safety_mode=SafetyMode.Audit)\n","except RuntimeError as e:\n"," print(\"Error:\", str(e))"]},{"cell_type":"markdown","metadata":{},"source":["The preconditions and postconditions of all the elementary predicates are documented in their string representations:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["PauliSimp()"]},{"cell_type":"markdown","metadata":{},"source":["## Backends and default passes"]},{"cell_type":"markdown","metadata":{},"source":["A `pytket` `Backend` may have a default compilation pass, which will guarantee that the circuit can run on it. This is given by the `default_compilation_pass` property. For example, the default pass for Qiskit's `AerBackend` just converts all gates to U1, U2, U3 and CX:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b = AerBackend()\n","b.default_compilation_pass"]},{"cell_type":"markdown","metadata":{},"source":["To compile a circuit using the default pass of a `Backend` we can simply use the `get_compiled_circuit()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(2).X(0).Y(1).CRz(0.5, 1, 0)\n","circ1 = b.get_compiled_circuit(circ)\n","render_circuit_jupyter(circ1)"]},{"cell_type":"markdown","metadata":{},"source":["Every `Backend` will have a certain set of requirements that must be met by any circuit in order to run. These are exposed via the `required_predicates` property:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b.required_predicates"]},{"cell_type":"markdown","metadata":{},"source":["We can test whether a given circuit satisfies these requirements using the `valid_circuit()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b.valid_circuit(circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b.valid_circuit(circ1)"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/circuit_compilation/contextual_optimization.ipynb b/docs/examples/circuit_compilation/contextual_optimisation.ipynb similarity index 100% rename from docs/examples/circuit_compilation/contextual_optimization.ipynb rename to docs/examples/circuit_compilation/contextual_optimisation.ipynb diff --git a/docs/examples/circuit_compilation/mapping_example.ipynb b/docs/examples/circuit_compilation/mapping_example.ipynb index 28d8e4d2..a004fda2 100644 --- a/docs/examples/circuit_compilation/mapping_example.ipynb +++ b/docs/examples/circuit_compilation/mapping_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Qubit mapping and routing"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In this tutorial we will show how the problem of mapping from logical quantum circuits to physically permitted circuits is solved automatically in TKET. The basic examples require only the installation of pytket, ```pip install pytket```."]}, {"cell_type": "markdown", "metadata": {}, "source": ["There is a wide variety of different blueprints for realising quantum computers, including the well known superconducting and ion trap devices. Different devices come with different constraints, such as a limited primitive gate set for universal quantum computing. Often this limited gate set accommodates an additional constraint, that two-qubit gates can not be executed between all pairs of qubits."]}, {"cell_type": "markdown", "metadata": {}, "source": ["In software, typically this constraint is presented as a \"connectivity\" graph where vertices connected by an edge represents pairs of physical qubits which two-qubit gates can be executed on. As programmers usually write logical quantum circuits with no sense of architecture (or may want to run their circuit on a range of hardware with different connectivity constraints), most quantum software development kits offer the means to automatically solve this constraint. One common way is to automatically add logical ```SWAP``` gates to a Circuit, changing the position of logical qubits on physical qubits until a two-qubit gate can be realised. This is an active area of research in quantum computing and a problem we discuss in our paper \"On The Qubit Routing Problem\" - arXiv:1902.08091."]}, {"cell_type": "markdown", "metadata": {}, "source": ["In TKET this constraint is represented by the ```Architecture``` class. An Architecture object requires a coupling map to be created, a list of pairs of qubits which defines where two-qubit primitives may be executed. A coupling map can be produced naively by the integer indexing of nodes and edges in some architecture."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.architecture import Architecture\n", "from pytket.circuit import Node"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["import networkx as nx\n", "from typing import List, Union, Tuple"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def draw_graph(coupling_map: List[Union[Tuple[int, int], Tuple[Node, Node]]]):\n", " coupling_graph = nx.Graph(coupling_map)\n", " nx.draw(coupling_graph, labels={node: node for node in coupling_graph.nodes()})"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["simple_coupling_map = [(0, 1), (1, 2), (2, 3)]\n", "simple_architecture = Architecture(simple_coupling_map)\n", "draw_graph(simple_coupling_map)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Alternatively we could use the `Node` class to assign our nodes - you will see why this can be helpful later. Lets create an Architecture with an identical graph:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["node_0 = Node(\"e0\", 0)\n", "node_1 = Node(\"e1\", 1)\n", "node_2 = Node(\"e2\", 2)\n", "node_3 = Node(\"e3\", 3)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["id_coupling_map = [(node_0, node_1), (node_1, node_2), (node_2, node_3)]\n", "id_architecture = Architecture(id_coupling_map)\n", "draw_graph(id_coupling_map)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can also create an ID with an arbitrary-dimensional index. Let us make a 2x2x2 cube:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["node_000 = Node(\"cube\", [0, 0, 0])\n", "node_001 = Node(\"cube\", [0, 0, 1])\n", "node_010 = Node(\"cube\", [0, 1, 0])\n", "node_011 = Node(\"cube\", [0, 1, 1])\n", "node_100 = Node(\"cube\", [1, 0, 0])\n", "node_101 = Node(\"cube\", [1, 0, 1])\n", "node_110 = Node(\"cube\", [1, 1, 0])\n", "node_111 = Node(\"cube\", [1, 1, 1])"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cube_coupling_map = [\n", " (node_000, node_001),\n", " (node_000, node_010),\n", " (node_010, node_011),\n", " (node_001, node_011),\n", " (node_000, node_100),\n", " (node_001, node_101),\n", " (node_010, node_110),\n", " (node_011, node_111),\n", " (node_100, node_101),\n", " (node_100, node_110),\n", " (node_110, node_111),\n", " (node_101, node_111),\n", "]"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cube_architecture = Architecture(cube_coupling_map)\n", "draw_graph(cube_coupling_map)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["To avoid that tedium though we could just use our SquareGrid Architecture:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.architecture import SquareGrid"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["alternative_cube_architecture = SquareGrid(2, 2, 2)\n", "draw_graph(alternative_cube_architecture.coupling)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The current range of quantum computers are commonly referred to as Noisy-Intermediate-Scale-Quantum devices i.e. NISQ devices. The impact of noise is a primary concern during compilation and incentivizes producing physically permitted circuits that have a minimal number of gates. For this reason benchmarking in this area is often completed by comparing the final number of two-qubit (or particularly SWAP gates) in compiled circuits."]}, {"cell_type": "markdown", "metadata": {}, "source": ["However it is important to remember that adding logical SWAP gates to minimise gate count is not the only way this constraint can be met, with large scale architecture-aware synthesis methods and fidelity aware methods amongst other approaches producing viable physically permitted circuits. It is likely that no SINGLE approach is better for all circuits, but the ability to use different approaches where best fitted will give the best results during compilation."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Producing physically valid circuits is completed via the `MappingManager` class, which aims to accomodate a wide range of approaches."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.mapping import MappingManager"]}, {"cell_type": "markdown", "metadata": {}, "source": ["A `MappingManager` object requires an `Architecture` object at construction."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["mapping_manager = MappingManager(id_architecture)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["All mapping is done through the `MappingManager.route_circuit` method. The `MappingManager.route_circuit` method has two arguments, the first a Circuit to be routed (which is mutated), the second a `List[RoutingMethodCircuit]` object that defines how the mapping is completed."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Later we will look at defining our own `RoutingMethodCircuit` objects, but initially lets consider one thats already available."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.mapping import LexiLabellingMethod, LexiRouteRoutingMethod"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["lexi_label = LexiLabellingMethod()\n", "lexi_route = LexiRouteRoutingMethod(10)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `lexi_route` object here is of little use outside `MappingManager`. Note that it takes a lookahead parameter, which will affect the performance of the method, defining the number of two-qubit gates it considers when finding `SWAP` gates to add."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit, OpType\n", "from pytket.circuit import display"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = (\n", " Circuit(4)\n", " .CX(0, 1)\n", " .CX(1, 2)\n", " .CX(0, 2)\n", " .CX(0, 3)\n", " .CX(2, 3)\n", " .CX(1, 3)\n", " .CX(0, 1)\n", " .measure_all()\n", ")\n", "display.render_circuit_jupyter(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can also look at which logical qubits are interacting."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.utils import Graph"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["Graph(c).get_qubit_graph()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["By running the `MappingManager.route_circuit` method on our circuit `c` with the `LexiLabellingMethod` and `LexiRouteRoutingMethod` objects as an argument, qubits in `c` with some physical requirements will be relabelled and the qubit graph modified (by the addition of SWAP gates and relabelling some CX as BRIDGE gates) such that the qubit graph is isomorphic to some subgraph of the full architecture."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["mapping_manager.route_circuit(c, [lexi_label, lexi_route])\n", "display.render_circuit_jupyter(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The graph:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["Graph(c).get_qubit_graph()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The resulting circuit may also change if we reduce the lookahead parameter."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = (\n", " Circuit(4)\n", " .CX(0, 1)\n", " .CX(1, 2)\n", " .CX(0, 2)\n", " .CX(0, 3)\n", " .CX(2, 3)\n", " .CX(1, 3)\n", " .CX(0, 1)\n", " .measure_all()\n", ")\n", "mapping_manager.route_circuit(c, [lexi_label, LexiRouteRoutingMethod(1)])\n", "display.render_circuit_jupyter(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can also pass multiple `RoutingMethod` options for Routing in a ranked List. Each `RoutingMethod` option has a function for checking whether it can usefully modify a subcircuit at a stage in Routing. To choose, each method in the List is checked in order until one returns True. This will be discussed more later."]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can aid the mapping procedure by relabelling qubits in advance. This can be completed using the `Placement` class."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.placement import Placement, LinePlacement, GraphPlacement"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The default ```Placement``` assigns logical qubits to physical qubits as they are encountered during routing. ```LinePlacement``` uses a strategy described in https://arxiv.org/abs/1902.08091. ```GraphPlacement``` is described in Section 7.1 of https://arxiv.org/abs/2003.10611. Lets look at how we can use the ```LinePlacement``` class.`"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["line_placement = LinePlacement(id_architecture)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = (\n", " Circuit(4)\n", " .CX(0, 1)\n", " .CX(1, 2)\n", " .CX(0, 2)\n", " .CX(0, 3)\n", " .CX(2, 3)\n", " .CX(1, 3)\n", " .CX(0, 1)\n", " .measure_all()\n", ")\n", "line_placement.place(c)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["display.render_circuit_jupyter(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that one qubit remains unplaced in this example. `LexiRouteRoutingMethod` will dynamically assign it during mapping."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Different placements will lead to different selections of SWAP gates being added. However each different routed circuit will preserve the original unitary action of the full circuit while respecting connectivity constraints."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["mapping_manager.route_circuit(c, [lexi_label, lexi_route])\n", "display.render_circuit_jupyter(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The graph:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["Graph(c).get_qubit_graph()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["However, small changes to the depth of lookahead or the original assignment of `Architecture` `Node` can greatly affect the resulting physical circuit for the `LexiRouteRoutingMethod` method. Considering this variance, it should be possible to easily throw additional computational resources at the problem if necessary, which is something TKET is leaning towards with the ability to define custom `RoutingCircuitMethod` objects."]}, {"cell_type": "markdown", "metadata": {}, "source": ["To define a new `RoutingMethodCircuit` method though, we first need to understand how it is used in `MappingManager` and routing. The `MappingManager.route_circuit` method treats the global problem of mapping to physical circuits as many sequential sub-problems. Consider the following problem."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.placement import place_with_map"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = Circuit(4).CX(0, 1).CX(1, 2).CX(0, 2).CX(0, 3).CX(2, 3).CX(1, 3).CX(0, 1)\n", "naive_map = {\n", " circ.qubits[0]: node_0,\n", " circ.qubits[1]: node_1,\n", " circ.qubits[2]: node_2,\n", " circ.qubits[3]: node_3,\n", "}\n", "place_with_map(circ, naive_map)\n", "Graph(circ).get_DAG()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["So what happens when we run the following?"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["mapping_manager.route_circuit(circ, [lexi_route])\n", "Graph(circ).get_DAG()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Sequential mapping typically works by partitioning the circuit into two, a first partition comprising a connected subcircuit that is physically permitted, a second partition that is not. Therefore, the first thing `MappingManager.route_circuit` does is find this partition for the passed circuit, by iterating through gates in the circuit."]}, {"cell_type": "markdown", "metadata": {}, "source": ["We will construct the partitions ourselves for illustrative purposes. Lets assume we are routing for the four qubit line architecture (qubits are connected to adjacent indices) \"simple_architecture\" we constructed earlier."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ_first_partition = Circuit(4).CX(0, 1).CX(1, 2)\n", "place_with_map(circ_first_partition, naive_map)\n", "Graph(circ_first_partition).get_DAG()"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ_second_partition = Circuit(4).CX(0, 2).CX(0, 3).CX(2, 3).CX(1, 3).CX(0, 1)\n", "place_with_map(circ_second_partition, naive_map)\n", "Graph(circ_second_partition).get_DAG()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that there are gates in the second partition that would be physically permitted, if they were not dependent on other gates that are not."]}, {"cell_type": "markdown", "metadata": {}, "source": ["The next step is to modify the second partition circuit to move it closer being physically permitted. Here the `LexiRouteRoutingMethod` as before will either insert a SWAP gate at the start of the partition, or will substitute a CX gate in the first slice of the partition with a BRIDGE gate."]}, {"cell_type": "markdown", "metadata": {}, "source": ["The option taken by `LexiRouteRoutingethod(1)` is to insert a SWAP gate between the first two nodes of the architecture, swapping their logical states. How does this change the second partition circuit?"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ_second_partition = (\n", " Circuit(4).SWAP(0, 1).CX(1, 2).CX(1, 3).CX(2, 3).CX(0, 3).CX(1, 0)\n", ")\n", "place_with_map(circ_second_partition, naive_map)\n", "Graph(circ_second_partition).get_DAG()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Leaving the full circuit as:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["full_circuit = (\n", " Circuit(4).CX(0, 1).CX(1, 2).SWAP(0, 1).CX(1, 2).CX(1, 3).CX(2, 3).CX(0, 3).CX(1, 0)\n", ")\n", "place_with_map(full_circuit, naive_map)\n", "Graph(full_circuit).get_DAG()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["After a modification is made the partition is updated."]}, {"cell_type": "markdown", "metadata": {}, "source": ["The first partition:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ_first_partition = Circuit(4).CX(0, 1).CX(1, 2).SWAP(0, 1).CX(1, 2)\n", "place_with_map(circ_first_partition, naive_map)\n", "Graph(circ_first_partition).get_DAG()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The second partition:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ_second_partition = Circuit(4).CX(1, 3).CX(2, 3).CX(0, 3).CX(1, 0)\n", "place_with_map(circ_second_partition, naive_map)\n", "Graph(circ_second_partition).get_DAG()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This pattern of modification and upating the partition is repeated until the partition has reached the end of the circuit, i.e. the back side of the partition has no gates in it. Also note that the process of updating the partition has been simplified for this example with \"physically permitted\" encapsulating two-qubit gate constraints only - in the future we expect other arity gates to provide constraints that need to be met. Also note that any modification to the second circuit can willfully modify the qubit labelling and a token swapping network will be automatically added to conform to the new labelling."]}, {"cell_type": "markdown", "metadata": {}, "source": ["We now enough about how `MappingManager` works to add our own `RoutingMethodCircuit`. While `LexiRouteRoutingMethod` is implemented in c++ TKET, giving it some advantages, via lambda functions we can define our own `RoutingMethodCircuit` in python."]}, {"cell_type": "markdown", "metadata": {}, "source": ["A python defined `RoutingMethodCircuit` requires three arguments. The first is a function that given a Circuit (the circuit after the partition) and an Architecture, returns a bool (determining whether the new circuit should be substitued in a full routing process), a new Circuit (a modification of the original circuit such as an added SWAP) a Dict between qubits reflecting any relabelling done in the method, and a Dict between qubits giving any implicit permutation of qubits (such as by adding a SWAP). For some clarity (we will write an example later), lets look at an example function declaration."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from typing import Dict"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def route_subcircuit_func(\n", " circuit: Circuit, architecture: Architecture\n", ") -> Tuple[bool, Circuit, Dict[Node, Node], Dict[Node, Node]]:\n", " return ()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The first return is a bool which detemrines if a given `RoutingMethodCircuit` is suitable for providing a solution at a given partition. `MappingManager.route_circuit` accepts a List of of `RoutingMethod` defining how solutions are found. At the point the partition circuit is modified, the circuit is passed to `RoutingMethodCircuit.routing_method` which additionally to finding a subcircuit substitution, should determine whether it can or can't helpfully modify the partition boundary circuit, and return True if it can. The first `RoutingMethodCircuit` to return True is then used for modification - meaning the ordering of List elements is important."]}, {"cell_type": "markdown", "metadata": {}, "source": ["The third argument sets the maximum number of gates given in the passed Circuit and the fourth argument sets the maximum depth in the passed Circuit."]}, {"cell_type": "markdown", "metadata": {}, "source": ["`LexiRouteRoutingMethod` will always return True, because it can always find some helpful SWAP to insert, and it can dynamically assign logical to physical qubits. Given this, lets construct a more specialised modification - an architecture-aware decomposition of a distance-2 CRy gate. Lets write our function type declarations for each method:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def distance2_CRy_decomp(\n", " circuit: Circuit, architecture: Architecture\n", ") -> Tuple[bool, Circuit, Dict[Node, Node], Dict[Node, Node]]:\n", " return (False, Circuit(), {}, {})"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Where do we start? Lets define a simple scope for our solution: for a single gate in the passed circuit (the circuit after the partition) that has OpType CRy, if the two qubits it's acting on are at distance 2 on the architecture, decompose the gate using BRIDGE gates."]}, {"cell_type": "markdown", "metadata": {}, "source": ["The first restriction is to only have a single gate from the first slice - we can achieve this by setting both the maximum depth and size parameters to 1."]}, {"cell_type": "markdown", "metadata": {}, "source": ["The second restriction is for the gate to have OpType CRy and for the qubits to be at distance 2 - we can check this restriction in a `distance2_CRy_check` method."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def distance2_CRy_check(circuit: Circuit, architecture: Architecture) -> bool:\n", " if circuit.n_gates != 1:\n", " raise ValueError(\n", " \"Circuit for CRy check should only have 1 gate, please change parameters of method declaration.\"\n", " )\n", " command = circuit.get_commands()[0]\n", " if command.op.type == OpType.CRy:\n", " # Architecture stores qubits under `Node` identifier\n", " n0 = Node(command.qubits[0].reg_name, command.qubits[0].index)\n", " n1 = Node(command.qubits[1].reg_name, command.qubits[1].index)\n", " # qubits could not be placed in circuit, so check before finding distance\n", " if n0 in architecture.nodes and n1 in architecture.nodes:\n", " # means we can run the decomposition\n", " if architecture.get_distance(n0, n1) == 2:\n", " return True\n", " return False"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `distance2_CRy_check` confirms whether the required restrictions are respected. Given this, if the `distance2_CRy_decomp` method is called we know where to add the decomposition."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["def distance2_CRy_decomp(\n", " circuit: Circuit, architecture: Architecture\n", ") -> Tuple[bool, Circuit, Dict[Node, Node], Dict[Node, Node]]:\n", " worthwhile_substitution = distance2_CRy_check(circuit, architecture)\n", " if worthwhile_substitution == False:\n", " return (False, Circuit(), {}, {})\n", " command = circuit.get_commands()[0]\n", " qubits = command.qubits\n", " # Architecture stores qubits under `Node` identifier\n", " n0 = Node(qubits[0].reg_name, qubits[0].index)\n", " n1 = Node(qubits[1].reg_name, qubits[1].index)\n\n", " # need to find connecting node for decomposition\n", " adjacent_nodes_0 = architecture.get_adjacent_nodes(n0)\n", " adjacent_nodes_1 = architecture.get_adjacent_nodes(n1)\n", " connecting_nodes = adjacent_nodes_0.intersection(adjacent_nodes_1)\n", " if len(connecting_nodes) == 0:\n", " raise ValueError(\"Qubits for distance-2 CRy decomp are not at distance 2.\")\n", " connecting_node = connecting_nodes.pop()\n", " c = Circuit()\n\n", " # the \"relabelling map\" empty, and the permutation map is qubit to qubit, so add here\n", " permutation_map = dict()\n", " for q in circuit.qubits:\n", " permutation_map[q] = q\n", " c.add_qubit(q)\n", " # rotation, can assume only parameter as CRy\n", " angle = command.op.params[0]\n", " c.Ry(angle, qubits[1])\n", " # distance-2 CX decomp\n", " c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1])\n", " c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1])\n", " # rotation\n", " c.Ry(-1 * angle, qubits[1])\n", " # distance-2 CX decomp\n", " c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1])\n", " c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1])\n\n", " # the \"relabelling map\" is just qubit to qubit\n", " return (True, c, {}, permutation_map)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Before turning this into a `RoutingMethod` we can try it ourselves."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["test_c = Circuit(4)\n", "test_c.CRy(0.6, 0, 2)\n", "place_with_map(test_c, naive_map)\n", "Graph(test_c).get_DAG()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["As we can see, our circuit has one CRy gate at distance two away."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(distance2_CRy_check(test_c, id_architecture))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Our method returns True, as expected! We should also test cases where it returns errors or False."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["test_c_false = Circuit(4)\n", "test_c_false.CRy(0.4, 0, 1)\n", "place_with_map(test_c_false, naive_map)\n", "print(distance2_CRy_check(test_c_false, id_architecture))"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["test_c_error = Circuit(4)\n", "test_c_error.CRy(0.6, 0, 2)\n", "test_c_error.CRy(0.4, 0, 1)\n", "place_with_map(test_c_error, naive_map)\n", "try:\n", " distance2_CRy_check(test_c_error, id_architecture)\n", "except ValueError:\n", " print(\"Error reached!\")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Does the decomposition work?"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["test_c = Circuit(4)\n", "test_c.CRy(0.6, 0, 2)\n", "place_with_map(test_c, naive_map)\n", "decomp = distance2_CRy_decomp(test_c, id_architecture)\n", "display.render_circuit_jupyter(decomp[1])"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Great! Our check function and decomposition method are both working. Lets wrap them into a `RoutingMethodCircuit` and try them out."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.mapping import RoutingMethodCircuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cry_rmc = RoutingMethodCircuit(distance2_CRy_decomp, 1, 1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can use our original `MappingManager` object as it is defined for the same architecture. Lets try it out on a range of circumstances."]}, {"cell_type": "markdown", "metadata": {}, "source": ["If we pass it a full CX circuit without `LexiRouteRoutingMethod`, we should find that `MappingManager` throws an error, as none of the passed methods can route for the given circuit."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = (\n", " Circuit(4)\n", " .CX(0, 1)\n", " .CX(1, 2)\n", " .CX(0, 2)\n", " .CX(0, 3)\n", " .CX(2, 3)\n", " .CX(1, 3)\n", " .CX(0, 1)\n", " .measure_all()\n", ")\n", "place_with_map(c, naive_map)\n", "try:\n", " mapping_manager.route_circuit(c, [cry_rmc])\n", "except RuntimeError:\n", " print(\"Error reached!\")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Alternatively, we can add `LexiRouteRoutingMethod` on top:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = (\n", " Circuit(4)\n", " .CX(0, 1)\n", " .CX(1, 2)\n", " .CX(0, 2)\n", " .CX(0, 3)\n", " .CX(2, 3)\n", " .CX(1, 3)\n", " .CX(0, 1)\n", " .measure_all()\n", ")\n", "place_with_map(c, naive_map)\n", "mapping_manager.route_circuit(c, [cry_rmc, LexiRouteRoutingMethod(10)])\n", "display.render_circuit_jupyter(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["However as there are no CRy gates our new method is unused. We can add one:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = (\n", " Circuit(4)\n", " .CRy(0.6, 0, 2)\n", " .CX(0, 1)\n", " .CX(1, 2)\n", " .CX(0, 2)\n", " .CX(0, 3)\n", " .CX(2, 3)\n", " .CX(1, 3)\n", " .CX(0, 1)\n", " .measure_all()\n", ")\n", "mapping_manager.route_circuit(c, [lexi_label, cry_rmc, LexiRouteRoutingMethod(10)])\n", "display.render_circuit_jupyter(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This time we can see our decomposition! If we reorder the methods though `LexiRouteRoutingMethod` is checked first (and returns True), so our new method is unused. The order is important!"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Finally, lets see what happens if the gate is not at the right distance initially."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = (\n", " Circuit(4)\n", " .CRy(0.6, 0, 3)\n", " .CX(0, 1)\n", " .CX(1, 2)\n", " .CX(0, 2)\n", " .CX(0, 3)\n", " .CX(2, 3)\n", " .CX(1, 3)\n", " .CX(0, 1)\n", " .measure_all()\n", ")\n", "mapping_manager.route_circuit(c, [lexi_label, cry_rmc, LexiRouteRoutingMethod(10)])\n", "display.render_circuit_jupyter(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Above a SWAP gate is inserted by `LexiRouteRoutingMethod` before anything else."]}, {"cell_type": "markdown", "metadata": {}, "source": ["For anyone interested, a simple extension exercise could be to extend this to additionally work for distance-2 CRx and CRz. Alternatively one could improve on the method itself - this approach always decomposes a CRy at distance-2, but is this a good idea?"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Also note that higher performance solutions are coded straight into the TKET c++ codebase. This provides advantages, including that Circuit construction and substitution is unncessary (as with python) as the circuit can be directly modified, however the ability to produce prototypes at the python level is very helpful. If you have a great python implementation but are finding some runtime bottlenecks, why not try implementing it straight into TKET (the code is open source at https://github.com/CQCL/tket)."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Besides the `LexiRouteRoutingMethod()` and the `LexiLabellingMethod()` there are other routing methods in pytket, such as the `AASRouteRoutingMethod()` and the corresponding `AASLabellingMethod()`, which are used to route phase-polynomial boxes using architecture-aware synthesis. Usually circuits contain non-phase-polynomial operations as well, so it is a good idea to combine them with the `LexiRouteRoutingMethod()`, as in the following example:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.mapping import AASRouteRoutingMethod, AASLabellingMethod\n", "from pytket.circuit import PhasePolyBox, Qubit\n", "import numpy as np"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(3, 3)\n", "n_qb = 3\n", "qubit_indices = {Qubit(0): 0, Qubit(1): 1, Qubit(2): 2}\n", "phase_polynomial = {(True, False, True): 0.333, (False, False, True): 0.05}\n", "linear_transformation = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])\n", "p_box = PhasePolyBox(n_qb, qubit_indices, phase_polynomial, linear_transformation)\n", "c.add_phasepolybox(p_box, [0, 1, 2])\n", "c.CX(0, 1).CX(0, 2).CX(1, 2)\n", "display.render_circuit_jupyter(c)\n", "nodes = [Node(\"test\", 0), Node(\"test\", 1), Node(\"test\", 2)]\n", "arch = Architecture([[nodes[0], nodes[1]], [nodes[1], nodes[2]]])\n", "mm = MappingManager(arch)\n", "mm.route_circuit(\n", " c,\n", " [\n", " AASRouteRoutingMethod(1),\n", " LexiLabellingMethod(),\n", " LexiRouteRoutingMethod(),\n", " AASLabellingMethod(),\n", " ],\n", ")\n", "display.render_circuit_jupyter(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In this case the order of the methods is not very relevant, because in each step of the routing only one of the methods is suitable. In the first part of the circuit the mapping is done without inserting swaps by the AAS method; in the second part one swap gate is added to the circuit."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Qubit mapping and routing\n","\n","**Download this notebook - {nb-download}`mapping_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial we will show how the problem of mapping from logical quantum circuits to physically permitted circuits is solved automatically in TKET. The basic examples require only the installation of pytket, ```pip install pytket```."]},{"cell_type":"markdown","metadata":{},"source":["There is a wide variety of different blueprints for realising quantum computers, including the well known superconducting and ion trap devices. Different devices come with different constraints, such as a limited primitive gate set for universal quantum computing. Often this limited gate set accommodates an additional constraint, that two-qubit gates can not be executed between all pairs of qubits."]},{"cell_type":"markdown","metadata":{},"source":["In software, typically this constraint is presented as a \"connectivity\" graph where vertices connected by an edge represents pairs of physical qubits which two-qubit gates can be executed on. As programmers usually write logical quantum circuits with no sense of architecture (or may want to run their circuit on a range of hardware with different connectivity constraints), most quantum software development kits offer the means to automatically solve this constraint. One common way is to automatically add logical ```SWAP``` gates to a Circuit, changing the position of logical qubits on physical qubits until a two-qubit gate can be realised. This is an active area of research in quantum computing and a problem we discuss in our paper \"On The Qubit Routing Problem\" - arXiv:1902.08091."]},{"cell_type":"markdown","metadata":{},"source":["In TKET this constraint is represented by the ```Architecture``` class. An Architecture object requires a coupling map to be created, a list of pairs of qubits which defines where two-qubit primitives may be executed. A coupling map can be produced naively by the integer indexing of nodes and edges in some architecture."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.architecture import Architecture\n","from pytket.circuit import Node"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import networkx as nx\n","from typing import List, Union, Tuple"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def draw_graph(coupling_map: List[Union[Tuple[int, int], Tuple[Node, Node]]]):\n"," coupling_graph = nx.Graph(coupling_map)\n"," nx.draw(coupling_graph, labels={node: node for node in coupling_graph.nodes()})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["simple_coupling_map = [(0, 1), (1, 2), (2, 3)]\n","simple_architecture = Architecture(simple_coupling_map)\n","draw_graph(simple_coupling_map)"]},{"cell_type":"markdown","metadata":{},"source":["Alternatively we could use the `Node` class to assign our nodes - you will see why this can be helpful later. Lets create an Architecture with an identical graph:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["node_0 = Node(\"e0\", 0)\n","node_1 = Node(\"e1\", 1)\n","node_2 = Node(\"e2\", 2)\n","node_3 = Node(\"e3\", 3)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["id_coupling_map = [(node_0, node_1), (node_1, node_2), (node_2, node_3)]\n","id_architecture = Architecture(id_coupling_map)\n","draw_graph(id_coupling_map)"]},{"cell_type":"markdown","metadata":{},"source":["We can also create an ID with an arbitrary-dimensional index. Let us make a 2x2x2 cube:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["node_000 = Node(\"cube\", [0, 0, 0])\n","node_001 = Node(\"cube\", [0, 0, 1])\n","node_010 = Node(\"cube\", [0, 1, 0])\n","node_011 = Node(\"cube\", [0, 1, 1])\n","node_100 = Node(\"cube\", [1, 0, 0])\n","node_101 = Node(\"cube\", [1, 0, 1])\n","node_110 = Node(\"cube\", [1, 1, 0])\n","node_111 = Node(\"cube\", [1, 1, 1])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cube_coupling_map = [\n"," (node_000, node_001),\n"," (node_000, node_010),\n"," (node_010, node_011),\n"," (node_001, node_011),\n"," (node_000, node_100),\n"," (node_001, node_101),\n"," (node_010, node_110),\n"," (node_011, node_111),\n"," (node_100, node_101),\n"," (node_100, node_110),\n"," (node_110, node_111),\n"," (node_101, node_111),\n","]"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cube_architecture = Architecture(cube_coupling_map)\n","draw_graph(cube_coupling_map)"]},{"cell_type":"markdown","metadata":{},"source":["To avoid that tedium though we could just use our SquareGrid Architecture:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.architecture import SquareGrid"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["alternative_cube_architecture = SquareGrid(2, 2, 2)\n","draw_graph(alternative_cube_architecture.coupling)"]},{"cell_type":"markdown","metadata":{},"source":["The current range of quantum computers are commonly referred to as Noisy-Intermediate-Scale-Quantum devices i.e. NISQ devices. The impact of noise is a primary concern during compilation and incentivizes producing physically permitted circuits that have a minimal number of gates. For this reason benchmarking in this area is often completed by comparing the final number of two-qubit (or particularly SWAP gates) in compiled circuits."]},{"cell_type":"markdown","metadata":{},"source":["However it is important to remember that adding logical SWAP gates to minimise gate count is not the only way this constraint can be met, with large scale architecture-aware synthesis methods and fidelity aware methods amongst other approaches producing viable physically permitted circuits. It is likely that no SINGLE approach is better for all circuits, but the ability to use different approaches where best fitted will give the best results during compilation."]},{"cell_type":"markdown","metadata":{},"source":["Producing physically valid circuits is completed via the `MappingManager` class, which aims to accomodate a wide range of approaches."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.mapping import MappingManager"]},{"cell_type":"markdown","metadata":{},"source":["A `MappingManager` object requires an `Architecture` object at construction."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["mapping_manager = MappingManager(id_architecture)"]},{"cell_type":"markdown","metadata":{},"source":["All mapping is done through the `MappingManager.route_circuit` method. The `MappingManager.route_circuit` method has two arguments, the first a Circuit to be routed (which is mutated), the second a `List[RoutingMethodCircuit]` object that defines how the mapping is completed."]},{"cell_type":"markdown","metadata":{},"source":["Later we will look at defining our own `RoutingMethodCircuit` objects, but initially lets consider one thats already available."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.mapping import LexiLabellingMethod, LexiRouteRoutingMethod"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["lexi_label = LexiLabellingMethod()\n","lexi_route = LexiRouteRoutingMethod(10)"]},{"cell_type":"markdown","metadata":{},"source":["The `lexi_route` object here is of little use outside `MappingManager`. Note that it takes a lookahead parameter, which will affect the performance of the method, defining the number of two-qubit gates it considers when finding `SWAP` gates to add."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit, OpType\n","from pytket.circuit import display"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = (\n"," Circuit(4)\n"," .CX(0, 1)\n"," .CX(1, 2)\n"," .CX(0, 2)\n"," .CX(0, 3)\n"," .CX(2, 3)\n"," .CX(1, 3)\n"," .CX(0, 1)\n"," .measure_all()\n",")\n","display.render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can also look at which logical qubits are interacting."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.utils import Graph"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Graph(c).get_qubit_graph()"]},{"cell_type":"markdown","metadata":{},"source":["By running the `MappingManager.route_circuit` method on our circuit `c` with the `LexiLabellingMethod` and `LexiRouteRoutingMethod` objects as an argument, qubits in `c` with some physical requirements will be relabelled and the qubit graph modified (by the addition of SWAP gates and relabelling some CX as BRIDGE gates) such that the qubit graph is isomorphic to some subgraph of the full architecture."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["mapping_manager.route_circuit(c, [lexi_label, lexi_route])\n","display.render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["The graph:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Graph(c).get_qubit_graph()"]},{"cell_type":"markdown","metadata":{},"source":["The resulting circuit may also change if we reduce the lookahead parameter."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = (\n"," Circuit(4)\n"," .CX(0, 1)\n"," .CX(1, 2)\n"," .CX(0, 2)\n"," .CX(0, 3)\n"," .CX(2, 3)\n"," .CX(1, 3)\n"," .CX(0, 1)\n"," .measure_all()\n",")\n","mapping_manager.route_circuit(c, [lexi_label, LexiRouteRoutingMethod(1)])\n","display.render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can also pass multiple `RoutingMethod` options for Routing in a ranked List. Each `RoutingMethod` option has a function for checking whether it can usefully modify a subcircuit at a stage in Routing. To choose, each method in the List is checked in order until one returns True. This will be discussed more later."]},{"cell_type":"markdown","metadata":{},"source":["We can aid the mapping procedure by relabelling qubits in advance. This can be completed using the `Placement` class."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.placement import Placement, LinePlacement, GraphPlacement"]},{"cell_type":"markdown","metadata":{},"source":["The default ```Placement``` assigns logical qubits to physical qubits as they are encountered during routing. ```LinePlacement``` uses a strategy described in https://arxiv.org/abs/1902.08091. ```GraphPlacement``` is described in Section 7.1 of https://arxiv.org/abs/2003.10611. Lets look at how we can use the ```LinePlacement``` class.`"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["line_placement = LinePlacement(id_architecture)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = (\n"," Circuit(4)\n"," .CX(0, 1)\n"," .CX(1, 2)\n"," .CX(0, 2)\n"," .CX(0, 3)\n"," .CX(2, 3)\n"," .CX(1, 3)\n"," .CX(0, 1)\n"," .measure_all()\n",")\n","line_placement.place(c)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["display.render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that one qubit remains unplaced in this example. `LexiRouteRoutingMethod` will dynamically assign it during mapping."]},{"cell_type":"markdown","metadata":{},"source":["Different placements will lead to different selections of SWAP gates being added. However each different routed circuit will preserve the original unitary action of the full circuit while respecting connectivity constraints."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["mapping_manager.route_circuit(c, [lexi_label, lexi_route])\n","display.render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["The graph:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Graph(c).get_qubit_graph()"]},{"cell_type":"markdown","metadata":{},"source":["However, small changes to the depth of lookahead or the original assignment of `Architecture` `Node` can greatly affect the resulting physical circuit for the `LexiRouteRoutingMethod` method. Considering this variance, it should be possible to easily throw additional computational resources at the problem if necessary, which is something TKET is leaning towards with the ability to define custom `RoutingCircuitMethod` objects."]},{"cell_type":"markdown","metadata":{},"source":["To define a new `RoutingMethodCircuit` method though, we first need to understand how it is used in `MappingManager` and routing. The `MappingManager.route_circuit` method treats the global problem of mapping to physical circuits as many sequential sub-problems. Consider the following problem."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.placement import place_with_map"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(4).CX(0, 1).CX(1, 2).CX(0, 2).CX(0, 3).CX(2, 3).CX(1, 3).CX(0, 1)\n","naive_map = {\n"," circ.qubits[0]: node_0,\n"," circ.qubits[1]: node_1,\n"," circ.qubits[2]: node_2,\n"," circ.qubits[3]: node_3,\n","}\n","place_with_map(circ, naive_map)\n","Graph(circ).get_DAG()"]},{"cell_type":"markdown","metadata":{},"source":["So what happens when we run the following?"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["mapping_manager.route_circuit(circ, [lexi_route])\n","Graph(circ).get_DAG()"]},{"cell_type":"markdown","metadata":{},"source":["Sequential mapping typically works by partitioning the circuit into two, a first partition comprising a connected subcircuit that is physically permitted, a second partition that is not. Therefore, the first thing `MappingManager.route_circuit` does is find this partition for the passed circuit, by iterating through gates in the circuit."]},{"cell_type":"markdown","metadata":{},"source":["We will construct the partitions ourselves for illustrative purposes. Lets assume we are routing for the four qubit line architecture (qubits are connected to adjacent indices) \"simple_architecture\" we constructed earlier."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ_first_partition = Circuit(4).CX(0, 1).CX(1, 2)\n","place_with_map(circ_first_partition, naive_map)\n","Graph(circ_first_partition).get_DAG()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ_second_partition = Circuit(4).CX(0, 2).CX(0, 3).CX(2, 3).CX(1, 3).CX(0, 1)\n","place_with_map(circ_second_partition, naive_map)\n","Graph(circ_second_partition).get_DAG()"]},{"cell_type":"markdown","metadata":{},"source":["Note that there are gates in the second partition that would be physically permitted, if they were not dependent on other gates that are not."]},{"cell_type":"markdown","metadata":{},"source":["The next step is to modify the second partition circuit to move it closer being physically permitted. Here the `LexiRouteRoutingMethod` as before will either insert a SWAP gate at the start of the partition, or will substitute a CX gate in the first slice of the partition with a BRIDGE gate."]},{"cell_type":"markdown","metadata":{},"source":["The option taken by `LexiRouteRoutingethod(1)` is to insert a SWAP gate between the first two nodes of the architecture, swapping their logical states. How does this change the second partition circuit?"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ_second_partition = (\n"," Circuit(4).SWAP(0, 1).CX(1, 2).CX(1, 3).CX(2, 3).CX(0, 3).CX(1, 0)\n",")\n","place_with_map(circ_second_partition, naive_map)\n","Graph(circ_second_partition).get_DAG()"]},{"cell_type":"markdown","metadata":{},"source":["Leaving the full circuit as:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["full_circuit = (\n"," Circuit(4).CX(0, 1).CX(1, 2).SWAP(0, 1).CX(1, 2).CX(1, 3).CX(2, 3).CX(0, 3).CX(1, 0)\n",")\n","place_with_map(full_circuit, naive_map)\n","Graph(full_circuit).get_DAG()"]},{"cell_type":"markdown","metadata":{},"source":["After a modification is made the partition is updated."]},{"cell_type":"markdown","metadata":{},"source":["The first partition:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ_first_partition = Circuit(4).CX(0, 1).CX(1, 2).SWAP(0, 1).CX(1, 2)\n","place_with_map(circ_first_partition, naive_map)\n","Graph(circ_first_partition).get_DAG()"]},{"cell_type":"markdown","metadata":{},"source":["The second partition:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ_second_partition = Circuit(4).CX(1, 3).CX(2, 3).CX(0, 3).CX(1, 0)\n","place_with_map(circ_second_partition, naive_map)\n","Graph(circ_second_partition).get_DAG()"]},{"cell_type":"markdown","metadata":{},"source":["This pattern of modification and upating the partition is repeated until the partition has reached the end of the circuit, i.e. the back side of the partition has no gates in it. Also note that the process of updating the partition has been simplified for this example with \"physically permitted\" encapsulating two-qubit gate constraints only - in the future we expect other arity gates to provide constraints that need to be met. Also note that any modification to the second circuit can willfully modify the qubit labelling and a token swapping network will be automatically added to conform to the new labelling."]},{"cell_type":"markdown","metadata":{},"source":["We now enough about how `MappingManager` works to add our own `RoutingMethodCircuit`. While `LexiRouteRoutingMethod` is implemented in c++ TKET, giving it some advantages, via lambda functions we can define our own `RoutingMethodCircuit` in python."]},{"cell_type":"markdown","metadata":{},"source":["A python defined `RoutingMethodCircuit` requires three arguments. The first is a function that given a Circuit (the circuit after the partition) and an Architecture, returns a bool (determining whether the new circuit should be substitued in a full routing process), a new Circuit (a modification of the original circuit such as an added SWAP) a Dict between qubits reflecting any relabelling done in the method, and a Dict between qubits giving any implicit permutation of qubits (such as by adding a SWAP). For some clarity (we will write an example later), lets look at an example function declaration."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from typing import Dict"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def route_subcircuit_func(\n"," circuit: Circuit, architecture: Architecture\n",") -> Tuple[bool, Circuit, Dict[Node, Node], Dict[Node, Node]]:\n"," return ()"]},{"cell_type":"markdown","metadata":{},"source":["The first return is a bool which detemrines if a given `RoutingMethodCircuit` is suitable for providing a solution at a given partition. `MappingManager.route_circuit` accepts a List of of `RoutingMethod` defining how solutions are found. At the point the partition circuit is modified, the circuit is passed to `RoutingMethodCircuit.routing_method` which additionally to finding a subcircuit substitution, should determine whether it can or can't helpfully modify the partition boundary circuit, and return True if it can. The first `RoutingMethodCircuit` to return True is then used for modification - meaning the ordering of List elements is important."]},{"cell_type":"markdown","metadata":{},"source":["The third argument sets the maximum number of gates given in the passed Circuit and the fourth argument sets the maximum depth in the passed Circuit."]},{"cell_type":"markdown","metadata":{},"source":["`LexiRouteRoutingMethod` will always return True, because it can always find some helpful SWAP to insert, and it can dynamically assign logical to physical qubits. Given this, lets construct a more specialised modification - an architecture-aware decomposition of a distance-2 CRy gate. Lets write our function type declarations for each method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def distance2_CRy_decomp(\n"," circuit: Circuit, architecture: Architecture\n",") -> Tuple[bool, Circuit, Dict[Node, Node], Dict[Node, Node]]:\n"," return (False, Circuit(), {}, {})"]},{"cell_type":"markdown","metadata":{},"source":["Where do we start? Lets define a simple scope for our solution: for a single gate in the passed circuit (the circuit after the partition) that has OpType CRy, if the two qubits it's acting on are at distance 2 on the architecture, decompose the gate using BRIDGE gates."]},{"cell_type":"markdown","metadata":{},"source":["The first restriction is to only have a single gate from the first slice - we can achieve this by setting both the maximum depth and size parameters to 1."]},{"cell_type":"markdown","metadata":{},"source":["The second restriction is for the gate to have OpType CRy and for the qubits to be at distance 2 - we can check this restriction in a `distance2_CRy_check` method."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def distance2_CRy_check(circuit: Circuit, architecture: Architecture) -> bool:\n"," if circuit.n_gates != 1:\n"," raise ValueError(\n"," \"Circuit for CRy check should only have 1 gate, please change parameters of method declaration.\"\n"," )\n"," command = circuit.get_commands()[0]\n"," if command.op.type == OpType.CRy:\n"," # Architecture stores qubits under `Node` identifier\n"," n0 = Node(command.qubits[0].reg_name, command.qubits[0].index)\n"," n1 = Node(command.qubits[1].reg_name, command.qubits[1].index)\n"," # qubits could not be placed in circuit, so check before finding distance\n"," if n0 in architecture.nodes and n1 in architecture.nodes:\n"," # means we can run the decomposition\n"," if architecture.get_distance(n0, n1) == 2:\n"," return True\n"," return False"]},{"cell_type":"markdown","metadata":{},"source":["The `distance2_CRy_check` confirms whether the required restrictions are respected. Given this, if the `distance2_CRy_decomp` method is called we know where to add the decomposition."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def distance2_CRy_decomp(\n"," circuit: Circuit, architecture: Architecture\n",") -> Tuple[bool, Circuit, Dict[Node, Node], Dict[Node, Node]]:\n"," worthwhile_substitution = distance2_CRy_check(circuit, architecture)\n"," if worthwhile_substitution == False:\n"," return (False, Circuit(), {}, {})\n"," command = circuit.get_commands()[0]\n"," qubits = command.qubits\n"," # Architecture stores qubits under `Node` identifier\n"," n0 = Node(qubits[0].reg_name, qubits[0].index)\n"," n1 = Node(qubits[1].reg_name, qubits[1].index)\n","\n"," # need to find connecting node for decomposition\n"," adjacent_nodes_0 = architecture.get_adjacent_nodes(n0)\n"," adjacent_nodes_1 = architecture.get_adjacent_nodes(n1)\n"," connecting_nodes = adjacent_nodes_0.intersection(adjacent_nodes_1)\n"," if len(connecting_nodes) == 0:\n"," raise ValueError(\"Qubits for distance-2 CRy decomp are not at distance 2.\")\n"," connecting_node = connecting_nodes.pop()\n"," c = Circuit()\n","\n"," # the \"relabelling map\" empty, and the permutation map is qubit to qubit, so add here\n"," permutation_map = dict()\n"," for q in circuit.qubits:\n"," permutation_map[q] = q\n"," c.add_qubit(q)\n"," # rotation, can assume only parameter as CRy\n"," angle = command.op.params[0]\n"," c.Ry(angle, qubits[1])\n"," # distance-2 CX decomp\n"," c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1])\n"," c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1])\n"," # rotation\n"," c.Ry(-1 * angle, qubits[1])\n"," # distance-2 CX decomp\n"," c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1])\n"," c.CX(qubits[0], connecting_node).CX(connecting_node, qubits[1])\n","\n"," # the \"relabelling map\" is just qubit to qubit\n"," return (True, c, {}, permutation_map)"]},{"cell_type":"markdown","metadata":{},"source":["Before turning this into a `RoutingMethod` we can try it ourselves."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["test_c = Circuit(4)\n","test_c.CRy(0.6, 0, 2)\n","place_with_map(test_c, naive_map)\n","Graph(test_c).get_DAG()"]},{"cell_type":"markdown","metadata":{},"source":["As we can see, our circuit has one CRy gate at distance two away."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(distance2_CRy_check(test_c, id_architecture))"]},{"cell_type":"markdown","metadata":{},"source":["Our method returns True, as expected! We should also test cases where it returns errors or False."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["test_c_false = Circuit(4)\n","test_c_false.CRy(0.4, 0, 1)\n","place_with_map(test_c_false, naive_map)\n","print(distance2_CRy_check(test_c_false, id_architecture))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["test_c_error = Circuit(4)\n","test_c_error.CRy(0.6, 0, 2)\n","test_c_error.CRy(0.4, 0, 1)\n","place_with_map(test_c_error, naive_map)\n","try:\n"," distance2_CRy_check(test_c_error, id_architecture)\n","except ValueError:\n"," print(\"Error reached!\")"]},{"cell_type":"markdown","metadata":{},"source":["Does the decomposition work?"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["test_c = Circuit(4)\n","test_c.CRy(0.6, 0, 2)\n","place_with_map(test_c, naive_map)\n","decomp = distance2_CRy_decomp(test_c, id_architecture)\n","display.render_circuit_jupyter(decomp[1])"]},{"cell_type":"markdown","metadata":{},"source":["Great! Our check function and decomposition method are both working. Lets wrap them into a `RoutingMethodCircuit` and try them out."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.mapping import RoutingMethodCircuit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cry_rmc = RoutingMethodCircuit(distance2_CRy_decomp, 1, 1)"]},{"cell_type":"markdown","metadata":{},"source":["We can use our original `MappingManager` object as it is defined for the same architecture. Lets try it out on a range of circumstances."]},{"cell_type":"markdown","metadata":{},"source":["If we pass it a full CX circuit without `LexiRouteRoutingMethod`, we should find that `MappingManager` throws an error, as none of the passed methods can route for the given circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = (\n"," Circuit(4)\n"," .CX(0, 1)\n"," .CX(1, 2)\n"," .CX(0, 2)\n"," .CX(0, 3)\n"," .CX(2, 3)\n"," .CX(1, 3)\n"," .CX(0, 1)\n"," .measure_all()\n",")\n","place_with_map(c, naive_map)\n","try:\n"," mapping_manager.route_circuit(c, [cry_rmc])\n","except RuntimeError:\n"," print(\"Error reached!\")"]},{"cell_type":"markdown","metadata":{},"source":["Alternatively, we can add `LexiRouteRoutingMethod` on top:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = (\n"," Circuit(4)\n"," .CX(0, 1)\n"," .CX(1, 2)\n"," .CX(0, 2)\n"," .CX(0, 3)\n"," .CX(2, 3)\n"," .CX(1, 3)\n"," .CX(0, 1)\n"," .measure_all()\n",")\n","place_with_map(c, naive_map)\n","mapping_manager.route_circuit(c, [cry_rmc, LexiRouteRoutingMethod(10)])\n","display.render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["However as there are no CRy gates our new method is unused. We can add one:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = (\n"," Circuit(4)\n"," .CRy(0.6, 0, 2)\n"," .CX(0, 1)\n"," .CX(1, 2)\n"," .CX(0, 2)\n"," .CX(0, 3)\n"," .CX(2, 3)\n"," .CX(1, 3)\n"," .CX(0, 1)\n"," .measure_all()\n",")\n","mapping_manager.route_circuit(c, [lexi_label, cry_rmc, LexiRouteRoutingMethod(10)])\n","display.render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["This time we can see our decomposition! If we reorder the methods though `LexiRouteRoutingMethod` is checked first (and returns True), so our new method is unused. The order is important!"]},{"cell_type":"markdown","metadata":{},"source":["Finally, lets see what happens if the gate is not at the right distance initially."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = (\n"," Circuit(4)\n"," .CRy(0.6, 0, 3)\n"," .CX(0, 1)\n"," .CX(1, 2)\n"," .CX(0, 2)\n"," .CX(0, 3)\n"," .CX(2, 3)\n"," .CX(1, 3)\n"," .CX(0, 1)\n"," .measure_all()\n",")\n","mapping_manager.route_circuit(c, [lexi_label, cry_rmc, LexiRouteRoutingMethod(10)])\n","display.render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["Above a SWAP gate is inserted by `LexiRouteRoutingMethod` before anything else."]},{"cell_type":"markdown","metadata":{},"source":["For anyone interested, a simple extension exercise could be to extend this to additionally work for distance-2 CRx and CRz. Alternatively one could improve on the method itself - this approach always decomposes a CRy at distance-2, but is this a good idea?"]},{"cell_type":"markdown","metadata":{},"source":["Also note that higher performance solutions are coded straight into the TKET c++ codebase. This provides advantages, including that Circuit construction and substitution is unncessary (as with python) as the circuit can be directly modified, however the ability to produce prototypes at the python level is very helpful. If you have a great python implementation but are finding some runtime bottlenecks, why not try implementing it straight into TKET (the code is open source at https://github.com/CQCL/tket)."]},{"cell_type":"markdown","metadata":{},"source":["Besides the `LexiRouteRoutingMethod()` and the `LexiLabellingMethod()` there are other routing methods in pytket, such as the `AASRouteRoutingMethod()` and the corresponding `AASLabellingMethod()`, which are used to route phase-polynomial boxes using architecture-aware synthesis. Usually circuits contain non-phase-polynomial operations as well, so it is a good idea to combine them with the `LexiRouteRoutingMethod()`, as in the following example:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.mapping import AASRouteRoutingMethod, AASLabellingMethod\n","from pytket.circuit import PhasePolyBox, Qubit\n","import numpy as np"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3, 3)\n","n_qb = 3\n","qubit_indices = {Qubit(0): 0, Qubit(1): 1, Qubit(2): 2}\n","phase_polynomial = {(True, False, True): 0.333, (False, False, True): 0.05}\n","linear_transformation = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])\n","p_box = PhasePolyBox(n_qb, qubit_indices, phase_polynomial, linear_transformation)\n","c.add_phasepolybox(p_box, [0, 1, 2])\n","c.CX(0, 1).CX(0, 2).CX(1, 2)\n","display.render_circuit_jupyter(c)\n","nodes = [Node(\"test\", 0), Node(\"test\", 1), Node(\"test\", 2)]\n","arch = Architecture([[nodes[0], nodes[1]], [nodes[1], nodes[2]]])\n","mm = MappingManager(arch)\n","mm.route_circuit(\n"," c,\n"," [\n"," AASRouteRoutingMethod(1),\n"," LexiLabellingMethod(),\n"," LexiRouteRoutingMethod(),\n"," AASLabellingMethod(),\n"," ],\n",")\n","display.render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["In this case the order of the methods is not very relevant, because in each step of the routing only one of the methods is suitable. In the first part of the circuit the mapping is done without inserting swaps by the AAS method; in the second part one swap gate is added to the circuit."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/circuit_compilation/symbolics_example.ipynb b/docs/examples/circuit_compilation/symbolics_example.ipynb index 1e5a4de1..27966456 100644 --- a/docs/examples/circuit_compilation/symbolics_example.ipynb +++ b/docs/examples/circuit_compilation/symbolics_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Symbolic compilation"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Motivation: in compilation, particularly of hybrid classical-quantum variational algorithms in which the structure of a circuit remains constant but the parameters of some gates change, it can be useful to compile using symbolic parameters and optimise the circuit without knowledge of what these parameters will be instantiated to afterwards.
\n", "
\n", "In this tutorial, we will show how to compile a circuit containing mathematical symbols, and then instantiate the symbols afterwards. To do this, you need to have `pytket` installed. Run:
\n", "
\n", "`pip install pytket`
\n", "
\n", "To begin, we will import the `Circuit` and `Transform` classes from `pytket`, and the `fresh_symbol` method from `pytket.circuit`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import Circuit, fresh_symbol\n", "from pytket.transform import Transform"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Now, we can construct a circuit containing symbols. You can ask for symbols by calling the `fresh_symbol` method with a string as an argument. This string represents the preferred symbol name; if this has already been used elsewhere, an appropriate suffix of the form `_x`, with `x` a natural number, will be added to generate a new symbol, as shown below:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["a = fresh_symbol(\"a\")\n", "a1 = fresh_symbol(\"a\")\n", "print(a)\n", "print(a1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We are going to make a circuit using just three 'phase gadgets': `Rz` gates surrounded by ladders of `CX` gates."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["b = fresh_symbol(\"b\")\n", "circ = Circuit(4)\n", "circ.CX(0, 1)\n", "circ.CX(1, 2)\n", "circ.CX(2, 3)\n", "circ.Rz(a, 3)\n", "circ.CX(2, 3)\n", "circ.CX(1, 2)\n", "circ.CX(0, 1)\n", "circ.CX(3, 2)\n", "circ.CX(2, 1)\n", "circ.CX(1, 0)\n", "circ.Rz(b, 0)\n", "circ.CX(1, 0)\n", "circ.CX(2, 1)\n", "circ.CX(3, 2)\n", "circ.CX(0, 1)\n", "circ.CX(1, 2)\n", "circ.CX(2, 3)\n", "circ.Rz(0.5, 3)\n", "circ.CX(2, 3)\n", "circ.CX(1, 2)\n", "circ.CX(0, 1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Now we can use the `render_circuit_jupyter` method to display the circuit."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit.display import render_circuit_jupyter"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Now let's use a transform to shrink the circuit. For more detail on transforms, see the `transform_example` notebook."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["Transform.OptimisePhaseGadgets().apply(circ)\n", "render_circuit_jupyter(circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that the type of gate has changed to `U1`, but the phase gadgets have been successfully combined. The `U1` gate is an IBM-specific gate that is equivalent to an `Rz`.
\n", "
\n", "We can now instantiate the symbols with some desired values. We make a dictionary, with each key a symbol name, and each value a double. Note that this value is in units of 'half-turns', in which a value of $1$ corresponds to a rotation of $\\pi$.
\n", "
\n", "Before instantiating our parameters we make a copy of the circuit, so that we can repeat the exercise without the need for recompilation."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["symbol_circ = circ.copy()"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["symbol_dict = {a: 0.5, b: 0.75}\n", "circ.symbol_substitution(symbol_dict)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Because this symbol substitution was called on the copy, we still have our original symbolic circuit."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(symbol_circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note: the expression tree for this symbolic expression is very small, consisting of only a couple of different operations, but tket is capable of handling large and complex expressions containing many different types of operation, such as trigonometric functions.
\n", "
\n", "It is usually possible to instantiate a symbolic circuit with specific values that allow further optimisation: for example, if we had chosen $a=1.5$ and $b=0$, this circuit would be reduce to the identity. If there are likely to be many parameters set to trivial values (such as $0$ or $1$), it can be beneficial to perform further optimisation after instantiation."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Symbolic compilation\n","\n","**Download this notebook - {nb-download}`symbolics_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["Motivation: in compilation, particularly of hybrid classical-quantum variational algorithms in which the structure of a circuit remains constant but the parameters of some gates change, it can be useful to compile using symbolic parameters and optimise the circuit without knowledge of what these parameters will be instantiated to afterwards.
\n","
\n","In this tutorial, we will show how to compile a circuit containing mathematical symbols, and then instantiate the symbols afterwards. To do this, you need to have `pytket` installed. Run:
\n","
\n","`pip install pytket`
\n","
\n","To begin, we will import the `Circuit` and `Transform` classes from `pytket`, and the `fresh_symbol` method from `pytket.circuit`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit, fresh_symbol\n","from pytket.transform import Transform"]},{"cell_type":"markdown","metadata":{},"source":["Now, we can construct a circuit containing symbols. You can ask for symbols by calling the `fresh_symbol` method with a string as an argument. This string represents the preferred symbol name; if this has already been used elsewhere, an appropriate suffix of the form `_x`, with `x` a natural number, will be added to generate a new symbol, as shown below:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = fresh_symbol(\"a\")\n","a1 = fresh_symbol(\"a\")\n","print(a)\n","print(a1)"]},{"cell_type":"markdown","metadata":{},"source":["We are going to make a circuit using just three 'phase gadgets': `Rz` gates surrounded by ladders of `CX` gates."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b = fresh_symbol(\"b\")\n","circ = Circuit(4)\n","circ.CX(0, 1)\n","circ.CX(1, 2)\n","circ.CX(2, 3)\n","circ.Rz(a, 3)\n","circ.CX(2, 3)\n","circ.CX(1, 2)\n","circ.CX(0, 1)\n","circ.CX(3, 2)\n","circ.CX(2, 1)\n","circ.CX(1, 0)\n","circ.Rz(b, 0)\n","circ.CX(1, 0)\n","circ.CX(2, 1)\n","circ.CX(3, 2)\n","circ.CX(0, 1)\n","circ.CX(1, 2)\n","circ.CX(2, 3)\n","circ.Rz(0.5, 3)\n","circ.CX(2, 3)\n","circ.CX(1, 2)\n","circ.CX(0, 1)"]},{"cell_type":"markdown","metadata":{},"source":["Now we can use the `render_circuit_jupyter` method to display the circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit.display import render_circuit_jupyter"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circ)"]},{"cell_type":"markdown","metadata":{},"source":["Now let's use a transform to shrink the circuit. For more detail on transforms, see the `transform_example` notebook."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.OptimisePhaseGadgets().apply(circ)\n","render_circuit_jupyter(circ)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the type of gate has changed to `U1`, but the phase gadgets have been successfully combined. The `U1` gate is an IBM-specific gate that is equivalent to an `Rz`.
\n","
\n","We can now instantiate the symbols with some desired values. We make a dictionary, with each key a symbol name, and each value a double. Note that this value is in units of 'half-turns', in which a value of $1$ corresponds to a rotation of $\\pi$.
\n","
\n","Before instantiating our parameters we make a copy of the circuit, so that we can repeat the exercise without the need for recompilation."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol_circ = circ.copy()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["symbol_dict = {a: 0.5, b: 0.75}\n","circ.symbol_substitution(symbol_dict)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(circ)"]},{"cell_type":"markdown","metadata":{},"source":["Because this symbol substitution was called on the copy, we still have our original symbolic circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(symbol_circ)"]},{"cell_type":"markdown","metadata":{},"source":["Note: the expression tree for this symbolic expression is very small, consisting of only a couple of different operations, but tket is capable of handling large and complex expressions containing many different types of operation, such as trigonometric functions.
\n","
\n","It is usually possible to instantiate a symbolic circuit with specific values that allow further optimisation: for example, if we had chosen $a=1.5$ and $b=0$, this circuit would be reduce to the identity. If there are likely to be many parameters set to trivial values (such as $0$ or $1$), it can be beneficial to perform further optimisation after instantiation."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/circuit_construction/circuit_analysis_example.ipynb b/docs/examples/circuit_construction/circuit_analysis_example.ipynb index 21b8507e..a5d05ebf 100644 --- a/docs/examples/circuit_construction/circuit_analysis_example.ipynb +++ b/docs/examples/circuit_construction/circuit_analysis_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Circuit analysis"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This notebook will introduce the basic methods of analysis and visualization of circuits available in `pytket`.
\n", "
\n", "It makes use of the modules `pytket_qiskit` and `pytket_cirq` for visualization; these need to be installed (with `pip`) in addition to `pytket`.
\n", "
\n", "We'll start by generating a small circuit to use as an example, and give it a name."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import Circuit, OpType"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(4, name=\"example\")\n", "c.add_gate(OpType.CU1, 0.5, [0, 1])\n", "c.H(0).X(1).Y(2).Z(3)\n", "c.X(0).CX(1, 2).Y(1).Z(2).H(3)\n", "c.Y(0).Z(1)\n", "c.add_gate(OpType.CU1, 0.5, [2, 3])\n", "c.H(2).X(3)\n", "c.Z(0).H(1).X(2).Y(3).CX(3, 0)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Basic statistics"]}, {"cell_type": "markdown", "metadata": {}, "source": ["From the circuit we can easily read off the number of qubits ..."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.n_qubits"]}, {"cell_type": "markdown", "metadata": {}, "source": ["... the name ..."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.name"]}, {"cell_type": "markdown", "metadata": {}, "source": ["... the overall depth of the circuit ..."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.depth()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["... and the depth by type of gate:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import OpType"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.depth_by_type(OpType.CU1), c.depth_by_type(OpType.H)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This last method counts the number of instances of the specified gate type that cannot be parallelized. Notice that although there are 4 H gates in the circuit, the H-depth is 2 because pairs of them can be parallelized (as will be clear from the visualizations below)."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Visualization"]}, {"cell_type": "markdown", "metadata": {}, "source": ["There are several ways to produce useful visualizations of circuits from `pytket`: we can use the methods in the `pytket.circuit.display` class; we can use the built-in `Graph` class to visualize the circuit as a directed acyclic graph (DAG); we can convert the circuit to either Qiskit or Cirq and use the tools provided by those modules; or we can export to Latex."]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Via the `render_circuit_jupyter` method"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit.display import render_circuit_jupyter"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["render_circuit_jupyter(c)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Notice that although the `render_circuit_jupyter` method is the recommended way to render a circuit as jupyter cell output, one of the other methods should be used when working with scripts or python shells."]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Via the `Graph` class"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.utils import Graph"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["G = Graph(c)\n", "G.get_DAG()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The small numbers (0 and 1) shown at the entry to and exit from the two-qubit gates represent \"port numbers\" on the gates; these allow us to track individual qubits, which may be placed in a different order on entry and exit in order to simplify the layout.
\n", "
\n", "The `Graph` class also has methods to save this image to a file and to open it in a PDF viewer.
\n", "
\n", "We can also view the qubit connectivity graph of a circuit:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["G.get_qubit_graph()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Via Qiskit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import tk_to_qiskit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(tk_to_qiskit(c))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Via Cirq"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.cirq import tk_to_cirq"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(tk_to_cirq(c))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["(Note that Cirq cannot represent all gates diagrammatically.)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Via Latex"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can create a Latex document containing a diagram of the circuit using the `to_latex_file()` method. This uses the `quantikz` library. The document can be viewed on its own or the Latex can easily be copied and pasted into a larger document."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.to_latex_file(\"c.tex\")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Commands"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We can retrieve a list of operations comprising a circuit, each represented as a `Command` object:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cmds = c.get_commands()\n", "print(cmds)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Each `Command` is defined by an operation and the qubits it operates on (as well as the classical bits and conditions, if any). For example:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["cmd0 = cmds[0]\n", "op0 = cmd0.op\n", "print(op0)\n", "qubits0 = cmd0.args\n", "print(qubits0)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["From the `Op` we can read off the string representation (in normal or Latex form), the parameters and the type:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["op0.get_name() # normal form"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["op0.get_name(latex=True) # Latex form"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["op0.type, op0.params"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that some compilation passes introduce implicit wire swaps at the end of the circuit, which are not represented in the command list. (The internal representation of the circuit as a directed acyclic graph reduces explicit permutations of qubits to implicit features of the graph.)"]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Circuit analysis\n","\n","**Download this notebook - {nb-download}`circuit_analysis_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["This notebook will introduce the basic methods of analysis and visualization of circuits available in `pytket`.
\n","
\n","It makes use of the modules `pytket_qiskit` and `pytket_cirq` for visualization; these need to be installed (with `pip`) in addition to `pytket`.
\n","
\n","We'll start by generating a small circuit to use as an example, and give it a name."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit, OpType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(4, name=\"example\")\n","c.add_gate(OpType.CU1, 0.5, [0, 1])\n","c.H(0).X(1).Y(2).Z(3)\n","c.X(0).CX(1, 2).Y(1).Z(2).H(3)\n","c.Y(0).Z(1)\n","c.add_gate(OpType.CU1, 0.5, [2, 3])\n","c.H(2).X(3)\n","c.Z(0).H(1).X(2).Y(3).CX(3, 0)"]},{"cell_type":"markdown","metadata":{},"source":["## Basic statistics"]},{"cell_type":"markdown","metadata":{},"source":["From the circuit we can easily read off the number of qubits ..."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.n_qubits"]},{"cell_type":"markdown","metadata":{},"source":["... the name ..."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.name"]},{"cell_type":"markdown","metadata":{},"source":["... the overall depth of the circuit ..."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.depth()"]},{"cell_type":"markdown","metadata":{},"source":["... and the depth by type of gate:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import OpType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.depth_by_type(OpType.CU1), c.depth_by_type(OpType.H)"]},{"cell_type":"markdown","metadata":{},"source":["This last method counts the number of instances of the specified gate type that cannot be parallelized. Notice that although there are 4 H gates in the circuit, the H-depth is 2 because pairs of them can be parallelized (as will be clear from the visualizations below)."]},{"cell_type":"markdown","metadata":{},"source":["## Visualization"]},{"cell_type":"markdown","metadata":{},"source":["There are several ways to produce useful visualizations of circuits from `pytket`: we can use the methods in the `pytket.circuit.display` class; we can use the built-in `Graph` class to visualize the circuit as a directed acyclic graph (DAG); we can convert the circuit to either Qiskit or Cirq and use the tools provided by those modules; or we can export to Latex."]},{"cell_type":"markdown","metadata":{},"source":["### Via the `render_circuit_jupyter` method"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit.display import render_circuit_jupyter"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["Notice that although the `render_circuit_jupyter` method is the recommended way to render a circuit as jupyter cell output, one of the other methods should be used when working with scripts or python shells."]},{"cell_type":"markdown","metadata":{},"source":["### Via the `Graph` class"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.utils import Graph"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["G = Graph(c)\n","G.get_DAG()"]},{"cell_type":"markdown","metadata":{},"source":["The small numbers (0 and 1) shown at the entry to and exit from the two-qubit gates represent \"port numbers\" on the gates; these allow us to track individual qubits, which may be placed in a different order on entry and exit in order to simplify the layout.
\n","
\n","The `Graph` class also has methods to save this image to a file and to open it in a PDF viewer.
\n","
\n","We can also view the qubit connectivity graph of a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["G.get_qubit_graph()"]},{"cell_type":"markdown","metadata":{},"source":["### Via Qiskit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import tk_to_qiskit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(tk_to_qiskit(c))"]},{"cell_type":"markdown","metadata":{},"source":["### Via Cirq"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.cirq import tk_to_cirq"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["print(tk_to_cirq(c))"]},{"cell_type":"markdown","metadata":{},"source":["(Note that Cirq cannot represent all gates diagrammatically.)"]},{"cell_type":"markdown","metadata":{},"source":["### Via Latex"]},{"cell_type":"markdown","metadata":{},"source":["We can create a Latex document containing a diagram of the circuit using the `to_latex_file()` method. This uses the `quantikz` library. The document can be viewed on its own or the Latex can easily be copied and pasted into a larger document."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.to_latex_file(\"c.tex\")"]},{"cell_type":"markdown","metadata":{},"source":["## Commands"]},{"cell_type":"markdown","metadata":{},"source":["We can retrieve a list of operations comprising a circuit, each represented as a `Command` object:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cmds = c.get_commands()\n","print(cmds)"]},{"cell_type":"markdown","metadata":{},"source":["Each `Command` is defined by an operation and the qubits it operates on (as well as the classical bits and conditions, if any). For example:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cmd0 = cmds[0]\n","op0 = cmd0.op\n","print(op0)\n","qubits0 = cmd0.args\n","print(qubits0)"]},{"cell_type":"markdown","metadata":{},"source":["From the `Op` we can read off the string representation (in normal or Latex form), the parameters and the type:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["op0.get_name() # normal form"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["op0.get_name(latex=True) # Latex form"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["op0.type, op0.params"]},{"cell_type":"markdown","metadata":{},"source":["Note that some compilation passes introduce implicit wire swaps at the end of the circuit, which are not represented in the command list. (The internal representation of the circuit as a directed acyclic graph reduces explicit permutations of qubits to implicit features of the graph.)"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/circuit_construction/circuit_generation_example.ipynb b/docs/examples/circuit_construction/circuit_generation_example.ipynb index aefe356a..e16386cc 100644 --- a/docs/examples/circuit_construction/circuit_generation_example.ipynb +++ b/docs/examples/circuit_construction/circuit_generation_example.ipynb @@ -1 +1 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Circuit generation"]},{"cell_type":"markdown","metadata":{},"source":["This notebook will provide a brief introduction to some of the more advanced methods of circuit generation available in `pytket`, including:
\n","* how to address wires and registers;
\n","* reading in circuits from QASM and Quipper ASCII files;
\n","* various types of 'boxes';
\n","* composition of circuits (both 'horizontally' and 'vertically');
\n","* use of symbolic gate parameters;
\n","* representation of classically controlled gates."]},{"cell_type":"markdown","metadata":{},"source":["## Wires, unit IDs and registers"]},{"cell_type":"markdown","metadata":{},"source":["Let's get started by constructing a circuit with 3 qubits and 2 classical bits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit\n","from pytket.circuit.display import render_circuit_jupyter as draw"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(1, 2)\n","print(circ.qubits)\n","print(circ.bits)"]},{"cell_type":"markdown","metadata":{},"source":["The qubits have automatically been assigned to a register with name `q` and indices 0, 1 and 2, while the bits have been assigned to a register with name `c` and indices 0 and 1.
\n","
\n","We can give these units arbitrary names and indices of arbitrary dimension:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Qubit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["new_q1 = Qubit(\"alpha\", 0)\n","new_q2 = Qubit(\"beta\", 2, 1)\n","circ.add_qubit(new_q1)\n","circ.add_qubit(new_q2)\n","print(circ.qubits)"]},{"cell_type":"markdown","metadata":{},"source":["We can also add a new register of qubits in one go:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["delta_reg = circ.add_q_register(\"delta\", 2)\n","print(circ.qubits)"]},{"cell_type":"markdown","metadata":{},"source":["Similar commands are available for classical bits.
\n","
\n","We can add gates to the circuit as follows:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.CX(delta_reg[0], delta_reg[1])"]},{"cell_type":"markdown","metadata":{},"source":["This command appends a CX gate with control `q[0]` and target `q[1]`. Note that the integer arguments are automatically converted to the default unit IDs. For simple circuits it is often easiest to stick to the default register and refer to the qubits by integers. To add gates to our own named units, we simply pass the `Qubit` (or classical `Bit`) as an argument. (We can't mix the two conventions in one command, however.)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.H(new_q1)\n","circ.CX(Qubit(\"q\", 0), new_q2)\n","circ.Rz(0.5, new_q2)"]},{"cell_type":"markdown","metadata":{},"source":["Let's have a look at our circuit using the interactive circuit renderer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit.display import render_circuit_jupyter as draw"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(circ)"]},{"cell_type":"markdown","metadata":{},"source":["## Exporting to and importing from standard formats"]},{"cell_type":"markdown","metadata":{},"source":["We can export a `Circuit` to a file in QASM format. Conversely, if we have such a file we can import it into `pytket`. There are some limitations on the circuits that can be converted: for example, multi-dimensional indices (as in `beta` and `gamma` above) are not allowed.
\n","
\n","Here is a simple example:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.qasm import circuit_from_qasm, circuit_to_qasm"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(3, 1)\n","circ.H(0)\n","circ.CX(0, 1)\n","circ.CX(1, 2)\n","circ.Rz(0.25, 2)\n","circ.Measure(2, 0)\n","draw(circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qasmfile = \"../qasm/c.qasm\"\n","circuit_to_qasm(circ, qasmfile)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["with open(qasmfile, encoding=\"utf-8\") as f:\n"," print(f.read())"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = circuit_from_qasm(qasmfile)\n","circ == c1"]},{"cell_type":"markdown","metadata":{},"source":["We can also import files in the Quipper ASCII format:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.quipper import circuit_from_quipper"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["quipfile = \"c.quip\"\n","with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n"," f.write(\n"," \"\"\"Inputs: 0:Qbit, 1:Qbit\n","QGate[\"W\"](0,1)\n","QGate[\"omega\"](1)\n","QGate[\"swap\"](0,1)\n","QGate[\"W\"]*(1,0)\n","Outputs: 0:Qbit, 1:Qbit\n","\"\"\"\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = circuit_from_quipper(quipfile)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the Quipper gates that are not supported directly in `pytket` (`W` and `omega`) are translated into equivalent sequences of `pytket` gates. See the [pytket.quipper](https://tket.quantinuum.com/api-docs/quipper.html) docs for more.
\n","
\n","Quipper subroutines are also supported, corresponding to `CircBox` operations in `pytket`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n"," f.write(\n"," \"\"\"Inputs: 0:Qbit, 1:Qbit, 2:Qbit\n","QGate[\"H\"](0)\n","Subroutine(x2)[\"sub\", shape \"([Q,Q],())\"] (2,1) -> (2,1)\n","QGate[\"H\"](1)\n","Outputs: 0:Qbit, 1:Qbit, 2:Qbit \\n\n","Subroutine: \"sub\"\n","Shape: \"([Q,Q],())\"\n","Controllable: no\n","Inputs: 0:Qbit, 1:Qbit\n","QGate[\"Y\"](0)\n","QGate[\"not\"](1) with controls=[+0]\n","QGate[\"Z\"](1)\n","Outputs: 0:Qbit, 1:Qbit\n","\"\"\"\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = circuit_from_quipper(quipfile)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Boxes in `pytket`"]},{"cell_type":"markdown","metadata":{},"source":["The `CircBox` is an example of a `pytket` 'box', which is a reusable encapsulation of a circuit inside another. We can recover the circuit 'inside' the box using the `get_circuit()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cmds = c.get_commands()\n","boxed_circuit = cmds[1].op.get_circuit()\n","draw(boxed_circuit)"]},{"cell_type":"markdown","metadata":{},"source":["The `CircBox` is the most general type of box, implementing an arbitrary circuit. But `pytket` supports several other useful box types:
\n","* [Unitary1qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary1qBox) (implementing an arbitrary $2 \\times 2$ unitary matrix);
\n","* [Unitary2qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary2qBox) (implementing an arbitrary $4 \\times 4$ unitary matrix);
\n","* [ExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.ExpBox) (implementing $e^{itA}$ for an arbitrary $4 \\times 4$ hermitian matrix $A$ and parameter $t$);
\n","* [PauliExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.PauliExpBox) (implementing $e^{-\\frac{1}{2} i \\pi t (\\sigma_0 \\otimes \\sigma_1 \\otimes \\cdots)}$ for arbitrary Pauli operators $\\sigma_i \\in \\{\\mathrm{I}, \\mathrm{X}, \\mathrm{Y}, \\mathrm{Z}\\}$ and parameter $t$)."]},{"cell_type":"markdown","metadata":{},"source":["An example will illustrate how these various box types are added to a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from math import sqrt"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import numpy as np\n","from pytket.circuit import CircBox, ExpBox, PauliExpBox, Unitary1qBox, Unitary2qBox\n","from pytket.pauli import Pauli"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["boxycirc = Circuit(3)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `CircBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["subcirc = Circuit(2, name=\"MY BOX\")\n","subcirc.X(0).Y(1).CZ(0, 1)\n","cbox = CircBox(subcirc)\n","boxycirc.add_gate(cbox, args=[Qubit(0), Qubit(1)])"]},{"cell_type":"markdown","metadata":{},"source":["Add a `Unitary1qBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["m1 = np.asarray([[1 / 2, sqrt(3) / 2], [sqrt(3) / 2, -1 / 2]])\n","m1box = Unitary1qBox(m1)\n","boxycirc.add_unitary1qbox(m1box, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `Unitary2qBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["m2 = np.asarray([[0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0]])\n","m2box = Unitary2qBox(m2)\n","boxycirc.add_unitary2qbox(m2box, 1, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Add an `ExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["A = np.asarray(\n"," [[1, 2, 3, 4 + 1j], [2, 0, 1j, -1], [3, -1j, 2, 1j], [4 - 1j, -1, -1j, 1]]\n",")\n","ebox = ExpBox(A, 0.5)\n","boxycirc.add_expbox(ebox, 0, 1)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `PauliExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pbox = PauliExpBox([Pauli.X, Pauli.Z, Pauli.X], 0.75)\n","boxycirc.add_gate(pbox, [0, 1, 2])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(boxycirc)"]},{"cell_type":"markdown","metadata":{},"source":["Try clicking on boxes in the diagram above to get information about the underlying subroutine."]},{"cell_type":"markdown","metadata":{},"source":["The `get_circuit()` method is available for all box types, and returns a `Circuit` object. For example if we look inside the `ExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(ebox.get_circuit())"]},{"cell_type":"markdown","metadata":{},"source":["## Circuit composition"]},{"cell_type":"markdown","metadata":{},"source":["For more discussion of circuit composition see the corresponding section of the [manual](https://tket.quantinuum.com/user-manual/manual_circuit.html#composing-circuits)."]},{"cell_type":"markdown","metadata":{},"source":["Circuits can be composed either serially, whereby wires are joined together, or in parallel, using the `append()` command.
\n","
\n","For a simple illustration of serial composition, let's create two circuits with compatible set of wires, and append the second to the first:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(2)\n","c.CX(0, 1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = Circuit(2)\n","c1.CZ(1, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["In the above example, there was a one-to-one match between the unit IDs in the two circuits, and they were matched up accordingly. The same applied with named unit IDs:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["x, y = Qubit(\"x\"), Qubit(\"y\")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","c.add_qubit(x)\n","c.add_qubit(y)\n","c.CX(x, y)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = Circuit()\n","c1.add_qubit(x)\n","c1.add_qubit(y)\n","c1.CZ(y, x)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["If either circuit contains wires not matching any wires in the other, those are added to the other circuit before composition:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["z = Qubit(\"z\")\n","c1.add_qubit(z)\n","c1.CY(y, z)\n","c.append(c1)\n","print(c.qubits)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["If the sets of unit IDs for the two circuits are disjoint, then the composition is entirely parallel.
\n","
\n","What if we want to serially compose two circuits having different sets of `Qubit`? In that case, we can use the `rename_units()` method on one or other of them to bring them into line. This method takes a dictionary mapping current unit IDs to new one:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c2 = Circuit()\n","c2.add_q_register(\"w\", 3)\n","w = [Qubit(\"w\", i) for i in range(3)]\n","c2.H(w[0]).CX(w[0], w[1]).CRz(0.25, w[1], w[2])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.rename_units({x: w[0], y: w[1], z: w[2]})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c2)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Symbolic parameters"]},{"cell_type":"markdown","metadata":{},"source":["Many of the gates supported by `pytket` are parametrized by one or more phase parameters, which represent rotations in multiples of $\\pi$. For example, $\\mathrm{Rz}(\\frac{1}{2})$ represents a quarter turn, i.e. a rotation of $\\pi/2$, about the Z axis. If we know the values of these parameters we can add the gates directly to our circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(1)\n","c.Rz(0.5, 0)"]},{"cell_type":"markdown","metadata":{},"source":["However, we may wish to construct and manipulate circuits containing such parametrized gates without specifying the values. This allows us to do calculations in a general setting, only later substituting values for the parameters.
\n","
\n","Thus `pytket` allows us to specify any of the parameters as symbols. All manipulations (such as combination and cancellation of gates) are performed on the symbolic representation:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from sympy import Symbol"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = Symbol(\"a\")\n","c.Rz(a, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["When we apply any transformation to this circuit, the symbolic parameter is preserved in the result:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.transform import Transform"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.RemoveRedundancies().apply(c)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["To substitute values for symbols, we use the `symbol_substitution()` method, supplying a dictionary from symbols to values:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.symbol_substitution({a: 0.75})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can also substitute symbols for other symbols:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b = Symbol(\"b\")\n","c = Circuit(1)\n","c.Rz(a + b, 0)\n","c.symbol_substitution({b: 2 * a})\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Custom gates"]},{"cell_type":"markdown","metadata":{},"source":["We can define custom parametrized gates in `pytket` by first setting up a circuit containing symbolic parameters and then converting this to a parametrized operation type:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import CustomGateDef"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = Symbol(\"a\")\n","b = Symbol(\"b\")\n","setup = Circuit(3)\n","setup.CX(0, 1)\n","setup.Rz(a + 0.5, 2)\n","setup.CRz(b, 0, 2)\n","my_gate = CustomGateDef.define(\"g\", setup, [a, b])\n","c = Circuit(4)\n","c.add_custom_gate(my_gate, [0.2, 1.3], [0, 3, 1])\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Custom gates can also receive symbolic parameters:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["x = Symbol(\"x\")\n","c.add_custom_gate(my_gate, [x, 1.0], [0, 1, 2])\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Decomposing boxes and custom gates"]},{"cell_type":"markdown","metadata":{},"source":["Having defined a circuit containing custom gates, we may now want to decompose it into elementary gates. The `DecomposeBoxes()` transform allows us to do this:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.DecomposeBoxes().apply(c)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["The same transform works on circuits composed of arbitrary boxes. Let's try it on a copy of the circuit we built up earlier out of various box types."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = boxycirc.copy()\n","Transform.DecomposeBoxes().apply(c)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the unitaries have been decomposed into elementary gates."]},{"cell_type":"markdown","metadata":{},"source":["## Classical controls"]},{"cell_type":"markdown","metadata":{},"source":["Most of the examples above involve only pure quantum gates. However, `pytket` can also represent gates whose operation is conditional on one or more classical inputs.
\n","
\n","For example, suppose we want to run the complex circuit `c` we've just constructed, then measure qubits 0 and 1, and finally apply an $\\mathrm{Rz}(\\frac{1}{2})$ rotation to qubit 2 if and only if the measurements were 0 and 1 respectively.
\n","
\n","First, we'll add two classical wires to the circuit to store the measurement results:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Bit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.add_c_register(\"m\", 2)\n","m = [Bit(\"m\", i) for i in range(2)]"]},{"cell_type":"markdown","metadata":{},"source":["Classically conditioned operations depend on all their inputs being 1. Since we want to condition on `m[0]` being 0, we must first apply an X gate to its qubit, and then measure:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["q = [Qubit(\"q\", i) for i in range(3)]\n","c.X(q[0])\n","c.Measure(q[0], m[0])\n","c.Measure(q[1], m[1])"]},{"cell_type":"markdown","metadata":{},"source":["Finally we add the classically conditioned Rz operation, using the `add_gate()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import OpType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.add_gate(OpType.Rz, [0.5], [q[2]], condition_bits=[m[0], m[1]], condition_value=3)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that many of the transforms and compilation passes will not accept circuits that contain classical controls."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Circuit generation\n","\n","**Download this notebook - {nb-download}`circuit_generation_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["This notebook will provide a brief introduction to some of the more advanced methods of circuit generation available in `pytket`, including:
\n","* how to address wires and registers;
\n","* reading in circuits from QASM and Quipper ASCII files;
\n","* various types of 'boxes';
\n","* composition of circuits (both 'horizontally' and 'vertically');
\n","* use of symbolic gate parameters;
\n","* representation of classically controlled gates."]},{"cell_type":"markdown","metadata":{},"source":["## Wires, unit IDs and registers"]},{"cell_type":"markdown","metadata":{},"source":["Let's get started by constructing a circuit with 3 qubits and 2 classical bits:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Circuit\n","from pytket.circuit.display import render_circuit_jupyter as draw"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(1, 2)\n","print(circ.qubits)\n","print(circ.bits)"]},{"cell_type":"markdown","metadata":{},"source":["The qubits have automatically been assigned to a register with name `q` and indices 0, 1 and 2, while the bits have been assigned to a register with name `c` and indices 0 and 1.
\n","
\n","We can give these units arbitrary names and indices of arbitrary dimension:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Qubit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["new_q1 = Qubit(\"alpha\", 0)\n","new_q2 = Qubit(\"beta\", 2, 1)\n","circ.add_qubit(new_q1)\n","circ.add_qubit(new_q2)\n","print(circ.qubits)"]},{"cell_type":"markdown","metadata":{},"source":["We can also add a new register of qubits in one go:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["delta_reg = circ.add_q_register(\"delta\", 2)\n","print(circ.qubits)"]},{"cell_type":"markdown","metadata":{},"source":["Similar commands are available for classical bits.
\n","
\n","We can add gates to the circuit as follows:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.CX(delta_reg[0], delta_reg[1])"]},{"cell_type":"markdown","metadata":{},"source":["This command appends a CX gate with control `q[0]` and target `q[1]`. Note that the integer arguments are automatically converted to the default unit IDs. For simple circuits it is often easiest to stick to the default register and refer to the qubits by integers. To add gates to our own named units, we simply pass the `Qubit` (or classical `Bit`) as an argument. (We can't mix the two conventions in one command, however.)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ.H(new_q1)\n","circ.CX(Qubit(\"q\", 0), new_q2)\n","circ.Rz(0.5, new_q2)"]},{"cell_type":"markdown","metadata":{},"source":["Let's have a look at our circuit using the interactive circuit renderer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit.display import render_circuit_jupyter as draw"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(circ)"]},{"cell_type":"markdown","metadata":{},"source":["## Exporting to and importing from standard formats"]},{"cell_type":"markdown","metadata":{},"source":["We can export a `Circuit` to a file in QASM format. Conversely, if we have such a file we can import it into `pytket`. There are some limitations on the circuits that can be converted: for example, multi-dimensional indices (as in `beta` and `gamma` above) are not allowed.
\n","
\n","Here is a simple example:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.qasm import circuit_from_qasm, circuit_to_qasm"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit(3, 1)\n","circ.H(0)\n","circ.CX(0, 1)\n","circ.CX(1, 2)\n","circ.Rz(0.25, 2)\n","circ.Measure(2, 0)\n","draw(circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qasmfile = \"../qasm/c.qasm\"\n","circuit_to_qasm(circ, qasmfile)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["with open(qasmfile, encoding=\"utf-8\") as f:\n"," print(f.read())"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = circuit_from_qasm(qasmfile)\n","circ == c1"]},{"cell_type":"markdown","metadata":{},"source":["We can also import files in the Quipper ASCII format:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.quipper import circuit_from_quipper"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["quipfile = \"c.quip\"\n","with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n"," f.write(\n"," \"\"\"Inputs: 0:Qbit, 1:Qbit\n","QGate[\"W\"](0,1)\n","QGate[\"omega\"](1)\n","QGate[\"swap\"](0,1)\n","QGate[\"W\"]*(1,0)\n","Outputs: 0:Qbit, 1:Qbit\n","\"\"\"\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = circuit_from_quipper(quipfile)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the Quipper gates that are not supported directly in `pytket` (`W` and `omega`) are translated into equivalent sequences of `pytket` gates. See the [pytket.quipper](https://tket.quantinuum.com/api-docs/quipper.html) docs for more.
\n","
\n","Quipper subroutines are also supported, corresponding to `CircBox` operations in `pytket`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["with open(quipfile, \"w\", encoding=\"utf-8\") as f:\n"," f.write(\n"," \"\"\"Inputs: 0:Qbit, 1:Qbit, 2:Qbit\n","QGate[\"H\"](0)\n","Subroutine(x2)[\"sub\", shape \"([Q,Q],())\"] (2,1) -> (2,1)\n","QGate[\"H\"](1)\n","Outputs: 0:Qbit, 1:Qbit, 2:Qbit \\n\n","Subroutine: \"sub\"\n","Shape: \"([Q,Q],())\"\n","Controllable: no\n","Inputs: 0:Qbit, 1:Qbit\n","QGate[\"Y\"](0)\n","QGate[\"not\"](1) with controls=[+0]\n","QGate[\"Z\"](1)\n","Outputs: 0:Qbit, 1:Qbit\n","\"\"\"\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = circuit_from_quipper(quipfile)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Boxes in `pytket`"]},{"cell_type":"markdown","metadata":{},"source":["The `CircBox` is an example of a `pytket` 'box', which is a reusable encapsulation of a circuit inside another. We can recover the circuit 'inside' the box using the `get_circuit()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["cmds = c.get_commands()\n","boxed_circuit = cmds[1].op.get_circuit()\n","draw(boxed_circuit)"]},{"cell_type":"markdown","metadata":{},"source":["The `CircBox` is the most general type of box, implementing an arbitrary circuit. But `pytket` supports several other useful box types:
\n","* [Unitary1qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary1qBox) (implementing an arbitrary $2 \\times 2$ unitary matrix);
\n","* [Unitary2qBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.Unitary2qBox) (implementing an arbitrary $4 \\times 4$ unitary matrix);
\n","* [ExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.ExpBox) (implementing $e^{itA}$ for an arbitrary $4 \\times 4$ hermitian matrix $A$ and parameter $t$);
\n","* [PauliExpBox](https://tket.quantinuum.com/api-docs/circuit.html#pytket.circuit.PauliExpBox) (implementing $e^{-\\frac{1}{2} i \\pi t (\\sigma_0 \\otimes \\sigma_1 \\otimes \\cdots)}$ for arbitrary Pauli operators $\\sigma_i \\in \\{\\mathrm{I}, \\mathrm{X}, \\mathrm{Y}, \\mathrm{Z}\\}$ and parameter $t$)."]},{"cell_type":"markdown","metadata":{},"source":["An example will illustrate how these various box types are added to a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from math import sqrt"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import numpy as np\n","from pytket.circuit import CircBox, ExpBox, PauliExpBox, Unitary1qBox, Unitary2qBox\n","from pytket.pauli import Pauli"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["boxycirc = Circuit(3)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `CircBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["subcirc = Circuit(2, name=\"MY BOX\")\n","subcirc.X(0).Y(1).CZ(0, 1)\n","cbox = CircBox(subcirc)\n","boxycirc.add_gate(cbox, args=[Qubit(0), Qubit(1)])"]},{"cell_type":"markdown","metadata":{},"source":["Add a `Unitary1qBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["m1 = np.asarray([[1 / 2, sqrt(3) / 2], [sqrt(3) / 2, -1 / 2]])\n","m1box = Unitary1qBox(m1)\n","boxycirc.add_unitary1qbox(m1box, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `Unitary2qBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["m2 = np.asarray([[0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0]])\n","m2box = Unitary2qBox(m2)\n","boxycirc.add_unitary2qbox(m2box, 1, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Add an `ExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["A = np.asarray(\n"," [[1, 2, 3, 4 + 1j], [2, 0, 1j, -1], [3, -1j, 2, 1j], [4 - 1j, -1, -1j, 1]]\n",")\n","ebox = ExpBox(A, 0.5)\n","boxycirc.add_expbox(ebox, 0, 1)"]},{"cell_type":"markdown","metadata":{},"source":["Add a `PauliExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pbox = PauliExpBox([Pauli.X, Pauli.Z, Pauli.X], 0.75)\n","boxycirc.add_gate(pbox, [0, 1, 2])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(boxycirc)"]},{"cell_type":"markdown","metadata":{},"source":["Try clicking on boxes in the diagram above to get information about the underlying subroutine."]},{"cell_type":"markdown","metadata":{},"source":["The `get_circuit()` method is available for all box types, and returns a `Circuit` object. For example if we look inside the `ExpBox`:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(ebox.get_circuit())"]},{"cell_type":"markdown","metadata":{},"source":["## Circuit composition"]},{"cell_type":"markdown","metadata":{},"source":["For more discussion of circuit composition see the corresponding section of the [manual](https://tket.quantinuum.com/user-manual/manual_circuit.html#composing-circuits)."]},{"cell_type":"markdown","metadata":{},"source":["Circuits can be composed either serially, whereby wires are joined together, or in parallel, using the `append()` command.
\n","
\n","For a simple illustration of serial composition, let's create two circuits with compatible set of wires, and append the second to the first:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(2)\n","c.CX(0, 1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = Circuit(2)\n","c1.CZ(1, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["In the above example, there was a one-to-one match between the unit IDs in the two circuits, and they were matched up accordingly. The same applied with named unit IDs:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["x, y = Qubit(\"x\"), Qubit(\"y\")"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","c.add_qubit(x)\n","c.add_qubit(y)\n","c.CX(x, y)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c1 = Circuit()\n","c1.add_qubit(x)\n","c1.add_qubit(y)\n","c1.CZ(y, x)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["If either circuit contains wires not matching any wires in the other, those are added to the other circuit before composition:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["z = Qubit(\"z\")\n","c1.add_qubit(z)\n","c1.CY(y, z)\n","c.append(c1)\n","print(c.qubits)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["If the sets of unit IDs for the two circuits are disjoint, then the composition is entirely parallel.
\n","
\n","What if we want to serially compose two circuits having different sets of `Qubit`? In that case, we can use the `rename_units()` method on one or other of them to bring them into line. This method takes a dictionary mapping current unit IDs to new one:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c2 = Circuit()\n","c2.add_q_register(\"w\", 3)\n","w = [Qubit(\"w\", i) for i in range(3)]\n","c2.H(w[0]).CX(w[0], w[1]).CRz(0.25, w[1], w[2])"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.rename_units({x: w[0], y: w[1], z: w[2]})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.append(c2)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Symbolic parameters"]},{"cell_type":"markdown","metadata":{},"source":["Many of the gates supported by `pytket` are parametrized by one or more phase parameters, which represent rotations in multiples of $\\pi$. For example, $\\mathrm{Rz}(\\frac{1}{2})$ represents a quarter turn, i.e. a rotation of $\\pi/2$, about the Z axis. If we know the values of these parameters we can add the gates directly to our circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(1)\n","c.Rz(0.5, 0)"]},{"cell_type":"markdown","metadata":{},"source":["However, we may wish to construct and manipulate circuits containing such parametrized gates without specifying the values. This allows us to do calculations in a general setting, only later substituting values for the parameters.
\n","
\n","Thus `pytket` allows us to specify any of the parameters as symbols. All manipulations (such as combination and cancellation of gates) are performed on the symbolic representation:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from sympy import Symbol"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = Symbol(\"a\")\n","c.Rz(a, 0)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["When we apply any transformation to this circuit, the symbolic parameter is preserved in the result:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.transform import Transform"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.RemoveRedundancies().apply(c)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["To substitute values for symbols, we use the `symbol_substitution()` method, supplying a dictionary from symbols to values:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.symbol_substitution({a: 0.75})"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can also substitute symbols for other symbols:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["b = Symbol(\"b\")\n","c = Circuit(1)\n","c.Rz(a + b, 0)\n","c.symbol_substitution({b: 2 * a})\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Custom gates"]},{"cell_type":"markdown","metadata":{},"source":["We can define custom parametrized gates in `pytket` by first setting up a circuit containing symbolic parameters and then converting this to a parametrized operation type:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import CustomGateDef"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["a = Symbol(\"a\")\n","b = Symbol(\"b\")\n","setup = Circuit(3)\n","setup.CX(0, 1)\n","setup.Rz(a + 0.5, 2)\n","setup.CRz(b, 0, 2)\n","my_gate = CustomGateDef.define(\"g\", setup, [a, b])\n","c = Circuit(4)\n","c.add_custom_gate(my_gate, [0.2, 1.3], [0, 3, 1])\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Custom gates can also receive symbolic parameters:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["x = Symbol(\"x\")\n","c.add_custom_gate(my_gate, [x, 1.0], [0, 1, 2])\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["## Decomposing boxes and custom gates"]},{"cell_type":"markdown","metadata":{},"source":["Having defined a circuit containing custom gates, we may now want to decompose it into elementary gates. The `DecomposeBoxes()` transform allows us to do this:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["Transform.DecomposeBoxes().apply(c)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["The same transform works on circuits composed of arbitrary boxes. Let's try it on a copy of the circuit we built up earlier out of various box types."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = boxycirc.copy()\n","Transform.DecomposeBoxes().apply(c)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that the unitaries have been decomposed into elementary gates."]},{"cell_type":"markdown","metadata":{},"source":["## Classical controls"]},{"cell_type":"markdown","metadata":{},"source":["Most of the examples above involve only pure quantum gates. However, `pytket` can also represent gates whose operation is conditional on one or more classical inputs.
\n","
\n","For example, suppose we want to run the complex circuit `c` we've just constructed, then measure qubits 0 and 1, and finally apply an $\\mathrm{Rz}(\\frac{1}{2})$ rotation to qubit 2 if and only if the measurements were 0 and 1 respectively.
\n","
\n","First, we'll add two classical wires to the circuit to store the measurement results:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import Bit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.add_c_register(\"m\", 2)\n","m = [Bit(\"m\", i) for i in range(2)]"]},{"cell_type":"markdown","metadata":{},"source":["Classically conditioned operations depend on all their inputs being 1. Since we want to condition on `m[0]` being 0, we must first apply an X gate to its qubit, and then measure:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["q = [Qubit(\"q\", i) for i in range(3)]\n","c.X(q[0])\n","c.Measure(q[0], m[0])\n","c.Measure(q[1], m[1])"]},{"cell_type":"markdown","metadata":{},"source":["Finally we add the classically conditioned Rz operation, using the `add_gate()` method:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import OpType"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.add_gate(OpType.Rz, [0.5], [q[2]], condition_bits=[m[0], m[1]], condition_value=3)\n","draw(c)"]},{"cell_type":"markdown","metadata":{},"source":["Note that many of the transforms and compilation passes will not accept circuits that contain classical controls."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} diff --git a/docs/examples/circuit_construction/conditional_gate_example.ipynb b/docs/examples/circuit_construction/conditional_gate_example.ipynb index baf0974d..7b2d1494 100644 --- a/docs/examples/circuit_construction/conditional_gate_example.ipynb +++ b/docs/examples/circuit_construction/conditional_gate_example.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Conditional gates"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Whilst any quantum process can be created by performing \"pure\" operations delaying all measurements to the end, this is not always practical and can greatly increase the resource requirements. It is much more convenient to alternate quantum gates and measurements, especially if we can use the measurement results to determine which gates to apply (we refer to this more generic circuit model as \"mixed\" circuits, against the usual \"pure\" circuits). This is especially crucial for error correcting codes, where the correction gates are applied only if an error is detected.
\n", "
\n", "Measurements on many NISQ devices are often slow and it is hard to maintain other qubits in a quantum state during the measurement operation. Hence they may only support a single round of measurements at the end of the circuit, removing the need for conditional gate support. However, the ability to work with mid-circuit measurement and conditional gates is a feature in high demand for the future, and tket is ready for it.
\n", "
\n", "Not every circuit language specification supports conditional gates in the same way. The most popular circuit model at the moment is that provided by the OpenQASM language. This permits a very restricted model of classical logic, where we can apply a gate conditionally on the exact value of a classical register. There is no facility in the current spec for Boolean logic or classical operations to apply any function to the value prior to the equality check.
\n", "
\n", "For example, quantum teleportation can be performed by the following QASM:
\n", "`OPENQASM 2.0;`
\n", "`include \"qelib1.inc\";`
\n", "`qreg a[2];`
\n", "`qreg b[1];`
\n", "`creg c[2];`
\n", "`// Bell state between Alice and Bob`
\n", "`h a[1];`
\n", "`cx a[1],b[0];`
\n", "`// Bell measurement of Alice's qubits`
\n", "`cx a[0],a[1];`
\n", "`h a[0];`
\n", "`measure a[0] -> c[0];`
\n", "`measure a[1] -> c[1];`
\n", "`// Correction of Bob's qubit`
\n", "`if(c==1) z b[0];`
\n", "`if(c==3) z b[0];`
\n", "`if(c==2) x b[0];`
\n", "`if(c==3) x b[0];`"]}, {"cell_type": "markdown", "metadata": {}, "source": ["tket supports a slightly more general form of conditional gates, where the gate is applied conditionally on the exact value of any list of bits. When adding a gate to a `Circuit` object, pass in the kwargs `condition_bits` and `condition_value` and the gate will only be applied if the state of the bits yields the binary representation of the value."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import Circuit\n", "from pytket.circuit.display import render_circuit_jupyter"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ = Circuit()\n", "alice = circ.add_q_register(\"a\", 2)\n", "bob = circ.add_q_register(\"b\", 1)\n", "cr = circ.add_c_register(\"c\", 2)\n", "# Bell state between Alice and Bob:\n", "circ.H(alice[1])\n", "circ.CX(alice[1], bob[0])\n", "# Bell measurement of Alice's qubits:\n", "circ.CX(alice[0], alice[1])\n", "circ.H(alice[0])\n", "circ.Measure(alice[0], cr[0])\n", "circ.Measure(alice[1], cr[1])\n", "# Correction of Bob's qubit:\n", "circ.Z(bob[0], condition_bits=[cr[0]], condition_value=1)\n", "circ.X(bob[0], condition_bits=[cr[1]], condition_value=1)\n", "render_circuit_jupyter(circ)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Performing individual gates conditionally is sufficient, but can get cumbersome for larger circuits. Fortunately, tket's Box structures can also be performed conditionally, enabling this to be applied to large circuits with ease.
\n", "
\n", "For the sake of example, assume our device struggles to perform $X$ gates. We can surround it by $CX$ gates onto an ancilla, so measuring the ancilla will either result in the identity or $X$ being applied to the target qubit. If we detect that the $X$ fails, we can retry."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.circuit import CircBox, Qubit, Bit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["checked_x = Circuit(2, 1)\n", "checked_x.CX(0, 1)\n", "checked_x.X(0)\n", "checked_x.CX(0, 1)\n", "checked_x.Measure(1, 0)\n", "x_box = CircBox(checked_x)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ2 = Circuit()\n", "target = Qubit(\"t\", 0)\n", "ancilla = Qubit(\"a\", 0)\n", "success = Bit(\"s\", 0)\n", "circ2.add_qubit(target)\n", "circ2.add_qubit(ancilla)\n", "circ2.add_bit(success)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Try the X gate:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ2.add_gate(x_box, args=[target, ancilla, success])\n", "# Try again if the X failed\n", "circ2.add_gate(\n", " x_box, args=[target, ancilla, success], condition_bits=[success], condition_value=0\n", ")\n", "render_circuit_jupyter(circ2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["tket is able to apply essential compilation passes on circuits containing conditional gates. This includes decomposing any boxes into primitive gates and rebasing to other gatesets whilst preserving the conditional data."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.passes import DecomposeBoxes, RebaseTket, SequencePass"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["comp_pass = SequencePass([DecomposeBoxes(), RebaseTket()])\n", "comp_pass.apply(circ2)\n", "render_circuit_jupyter(circ2)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["A tket circuit can be converted to OpenQASM or other languages following the same classical model (e.g. Qiskit) when all conditional gates are dependent on the exact state of a single, whole classical register."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import tk_to_qiskit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["qc = tk_to_qiskit(circ2)\n", "print(qc)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["This allows us to test our mixed programs using the `AerBackend`."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.qiskit import AerBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["circ3 = Circuit(2, 1)\n", "circ3.Rx(0.3, 0)\n", "circ3.Measure(0, 0)\n", "# Set qubit 1 to be the opposite result and measure\n", "circ3.X(1, condition_bits=[0], condition_value=0)\n", "circ3.Measure(1, 0)\n", "backend = AerBackend()\n", "compiled_circ = backend.get_compiled_circuit(circ3)\n", "render_circuit_jupyter(compiled_circ)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["counts = backend.run_circuit(compiled_circ, 1024).get_counts()\n", "print(counts)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Try out mid-circuit measurement and conditional gate support on the `AerBackend` simulator, or ask about accessing the `QuantinuumBackend` to try on a hardware device."]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Conditional gates\n","\n","**Download this notebook - {nb-download}`conditional_gate_example.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["Whilst any quantum process can be created by performing \"pure\" operations delaying all measurements to the end, this is not always practical and can greatly increase the resource requirements. It is much more convenient to alternate quantum gates and measurements, especially if we can use the measurement results to determine which gates to apply (we refer to this more generic circuit model as \"mixed\" circuits, against the usual \"pure\" circuits). This is especially crucial for error correcting codes, where the correction gates are applied only if an error is detected.
\n","
\n","Measurements on many NISQ devices are often slow and it is hard to maintain other qubits in a quantum state during the measurement operation. Hence they may only support a single round of measurements at the end of the circuit, removing the need for conditional gate support. However, the ability to work with mid-circuit measurement and conditional gates is a feature in high demand for the future, and tket is ready for it.
\n","
\n","Not every circuit language specification supports conditional gates in the same way. The most popular circuit model at the moment is that provided by the OpenQASM language. This permits a very restricted model of classical logic, where we can apply a gate conditionally on the exact value of a classical register. There is no facility in the current spec for Boolean logic or classical operations to apply any function to the value prior to the equality check.
\n","
\n","For example, quantum teleportation can be performed by the following QASM:
\n","`OPENQASM 2.0;`
\n","`include \"qelib1.inc\";`
\n","`qreg a[2];`
\n","`qreg b[1];`
\n","`creg c[2];`
\n","`// Bell state between Alice and Bob`
\n","`h a[1];`
\n","`cx a[1],b[0];`
\n","`// Bell measurement of Alice's qubits`
\n","`cx a[0],a[1];`
\n","`h a[0];`
\n","`measure a[0] -> c[0];`
\n","`measure a[1] -> c[1];`
\n","`// Correction of Bob's qubit`
\n","`if(c==1) z b[0];`
\n","`if(c==3) z b[0];`
\n","`if(c==2) x b[0];`
\n","`if(c==3) x b[0];`"]},{"cell_type":"markdown","metadata":{},"source":["tket supports a slightly more general form of conditional gates, where the gate is applied conditionally on the exact value of any list of bits. When adding a gate to a `Circuit` object, pass in the kwargs `condition_bits` and `condition_value` and the gate will only be applied if the state of the bits yields the binary representation of the value."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ = Circuit()\n","alice = circ.add_q_register(\"a\", 2)\n","bob = circ.add_q_register(\"b\", 1)\n","cr = circ.add_c_register(\"c\", 2)\n","# Bell state between Alice and Bob:\n","circ.H(alice[1])\n","circ.CX(alice[1], bob[0])\n","# Bell measurement of Alice's qubits:\n","circ.CX(alice[0], alice[1])\n","circ.H(alice[0])\n","circ.Measure(alice[0], cr[0])\n","circ.Measure(alice[1], cr[1])\n","# Correction of Bob's qubit:\n","circ.Z(bob[0], condition_bits=[cr[0]], condition_value=1)\n","circ.X(bob[0], condition_bits=[cr[1]], condition_value=1)\n","render_circuit_jupyter(circ)"]},{"cell_type":"markdown","metadata":{},"source":["Performing individual gates conditionally is sufficient, but can get cumbersome for larger circuits. Fortunately, tket's Box structures can also be performed conditionally, enabling this to be applied to large circuits with ease.
\n","
\n","For the sake of example, assume our device struggles to perform $X$ gates. We can surround it by $CX$ gates onto an ancilla, so measuring the ancilla will either result in the identity or $X$ being applied to the target qubit. If we detect that the $X$ fails, we can retry."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.circuit import CircBox, Qubit, Bit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["checked_x = Circuit(2, 1)\n","checked_x.CX(0, 1)\n","checked_x.X(0)\n","checked_x.CX(0, 1)\n","checked_x.Measure(1, 0)\n","x_box = CircBox(checked_x)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ2 = Circuit()\n","target = Qubit(\"t\", 0)\n","ancilla = Qubit(\"a\", 0)\n","success = Bit(\"s\", 0)\n","circ2.add_qubit(target)\n","circ2.add_qubit(ancilla)\n","circ2.add_bit(success)"]},{"cell_type":"markdown","metadata":{},"source":["Try the X gate:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ2.add_gate(x_box, args=[target, ancilla, success])\n","# Try again if the X failed\n","circ2.add_gate(\n"," x_box, args=[target, ancilla, success], condition_bits=[success], condition_value=0\n",")\n","render_circuit_jupyter(circ2)"]},{"cell_type":"markdown","metadata":{},"source":["tket is able to apply essential compilation passes on circuits containing conditional gates. This includes decomposing any boxes into primitive gates and rebasing to other gatesets whilst preserving the conditional data."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.passes import DecomposeBoxes, RebaseTket, SequencePass"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["comp_pass = SequencePass([DecomposeBoxes(), RebaseTket()])\n","comp_pass.apply(circ2)\n","render_circuit_jupyter(circ2)"]},{"cell_type":"markdown","metadata":{},"source":["A tket circuit can be converted to OpenQASM or other languages following the same classical model (e.g. Qiskit) when all conditional gates are dependent on the exact state of a single, whole classical register."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import tk_to_qiskit"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["qc = tk_to_qiskit(circ2)\n","print(qc)"]},{"cell_type":"markdown","metadata":{},"source":["This allows us to test our mixed programs using the `AerBackend`."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["circ3 = Circuit(2, 1)\n","circ3.Rx(0.3, 0)\n","circ3.Measure(0, 0)\n","# Set qubit 1 to be the opposite result and measure\n","circ3.X(1, condition_bits=[0], condition_value=0)\n","circ3.Measure(1, 0)\n","backend = AerBackend()\n","compiled_circ = backend.get_compiled_circuit(circ3)\n","render_circuit_jupyter(compiled_circ)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["counts = backend.run_circuit(compiled_circ, 1024).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["Try out mid-circuit measurement and conditional gate support on the `AerBackend` simulator, or ask about accessing the `QuantinuumBackend` to try on a hardware device."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} From 5678ddcedc9e0ae0e4b2c84e42b6522bf15b9ef1 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:33:47 +0100 Subject: [PATCH 56/76] allow execution of classification example --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 0e3b86dc..57d6d111 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,6 @@ "examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb", "examples/algorithms_and_protocols/spam_example.ipynb", "examples/algorithms_and_protocols/entanglement_swapping.ipynb", - "examples/algorithms_and_protocols/pytket-qujax-classification.ipynb", ] exclude_patterns = ["jupyter_execute/*", ".jupyter_cache", "manual/README.md"] From be15f3c60898409e5bce426805e15453d782f098 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Fri, 26 Jul 2024 14:11:10 +0100 Subject: [PATCH 57/76] change name of workflow --- .github/workflows/check-docs-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-docs-build.yml b/.github/workflows/check-docs-build.yml index 4d80ba32..ea8ba033 100644 --- a/.github/workflows/check-docs-build.yml +++ b/.github/workflows/check-docs-build.yml @@ -28,7 +28,7 @@ jobs: - '.github/**' check: - name: check manual + name: check docs build needs: changes if: github.event_name == 'schedule' || needs.changes.outputs.manual == 'true' runs-on: ubuntu-22.04 From 1faa8305fb1f5be388c8613f2ed08f07bf7de092 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Fri, 26 Jul 2024 14:59:17 +0100 Subject: [PATCH 58/76] try updating gitmodules file --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e2d9df30..cce7cbd1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ -[submodule "quantinuum-sphinx"] +[submodule "docs/quantinuum-sphinx"] path = docs/quantinuum-sphinx url = https://github.com/CQCL/quantinuum-sphinx.git branch = dist From 9e59a4362a9d7af755aa1bbbdf4d48b012069238 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:10:09 +0100 Subject: [PATCH 59/76] fix headings in classification example --- .../algorithms_and_protocols/pytket-qujax-classification.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/algorithms_and_protocols/pytket-qujax-classification.ipynb b/docs/examples/algorithms_and_protocols/pytket-qujax-classification.ipynb index 3d0e6836..e65925fa 100644 --- a/docs/examples/algorithms_and_protocols/pytket-qujax-classification.ipynb +++ b/docs/examples/algorithms_and_protocols/pytket-qujax-classification.ipynb @@ -1 +1 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Binary classification using `pytket-qujax`\n","\n","**Download this notebook - {nb-download}`pytket-qujax-classification.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html)."]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[],"source":["from jax import numpy as jnp, random, vmap, value_and_grad, jit\n","from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter\n","from pytket.extensions.qujax.qujax_convert import tk_to_qujax\n","import matplotlib.pyplot as plt"]},{"cell_type":"markdown","metadata":{},"source":["# Define the classification task\n","We'll try and learn a _donut_ binary classification function (i.e. a bivariate coordinate is labelled 1 if it is inside the donut and 0 if it is outside)"]},{"cell_type":"code","execution_count":2,"metadata":{},"outputs":[],"source":["inner_rad = 0.25\n","outer_rad = 0.75"]},{"cell_type":"code","execution_count":3,"metadata":{},"outputs":[],"source":["def classification_function(x, y):\n"," r = jnp.sqrt(x**2 + y**2)\n"," return jnp.where((r > inner_rad) * (r < outer_rad), 1, 0)"]},{"cell_type":"code","execution_count":4,"metadata":{},"outputs":[],"source":["linsp = jnp.linspace(-1, 1, 1000)\n","Z = vmap(lambda x: vmap(lambda y: classification_function(x, y))(linsp))(linsp)"]},{"cell_type":"code","execution_count":5,"metadata":{},"outputs":[{"data":{"text/plain":[""]},"execution_count":5,"metadata":{},"output_type":"execute_result"},{"data":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAAkcAAAGiCAYAAADtImJbAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/TGe4hAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA9EklEQVR4nO3de3hU1aH+8TcJkBAhCUiSIRjlZrlUIHJJCscWKXlIlLaiHEkoFsixoFagEESJB6FcbFA4SlEqarnYFuVixUurKCI8ntqUYCBFETiSolwnAWIykGiAZP/+8JdxhlyYJLPn+v08zzw6e9bes1Y2s+edtdbeO8QwDEMAAACQJIV6uwIAAAC+hHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADgwNRw9OGHH+qnP/2pEhISFBISotdff/2q6+zatUsDBw5UeHi4evbsqfXr19cps2rVKnXt2lURERFKSUlRfn6++ysPAACCkqnhqKKiQgMGDNCqVatcKn/06FGNHj1aI0aMUGFhoWbOnKlf/vKXevfdd+1lNm3apOzsbC1YsEB79+7VgAEDlJaWppKSErOaAQAAgkiIp248GxISoq1bt2rMmDENlnnkkUf0t7/9TZ9++ql9WWZmpsrKyrRt2zZJUkpKioYMGaJnn31WklRTU6PExERNnz5dc+fONbUNAAAg8LXydgUc5eXlKTU11WlZWlqaZs6cKUm6ePGiCgoKlJOTY389NDRUqampysvLa3C7VVVVqqqqsj+vqalRaWmprr32WoWEhLi3EQAAwBSGYej8+fNKSEhQaKh5g18+FY6sVqvi4+OdlsXHx8tms+nrr7/WV199perq6nrLHDp0qMHt5ubmauHChabUGQAAeNbx48d13XXXmbZ9nwpHZsnJyVF2drb9eXl5ua6//nr936EitW/f3os1AwAArjp//ry+17uH6d/dPhWOLBaLiouLnZYVFxcrKipKbdu2VVhYmMLCwuotY7FYGtxueHi4wsPD6yxv3769oqKi3FN5AADgEWZPifGp6xwNHTpUO3bscFq2fft2DR06VJLUpk0bDRo0yKlMTU2NduzYYS8DAADQEqaGowsXLqiwsFCFhYWSvj1Vv7CwUMeOHZP07XDXxIkT7eXvv/9+/fvf/9bDDz+sQ4cO6fe/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wNuaWr8vvvhCISEh9T62bNliL1ff6xs3bvREk1zSnP1y66231mnT/fff71Tm2LFjGj16tCIjIxUXF6c5c+bo8uXLZjalSZra7tLSUk2fPl29evVS27Ztdf3112vGjBkqLy93KueL+3vVqlXq2rWrIiIilJKSovz8/EbLb9myRb1791ZERIT69eunt99+2+l1Vz7vvqAp7X7xxRf1wx/+UB06dFCHDh2Umppap/zkyZPr7Nv09HSzm9FkTWn3+vXr67QpIiLCqUwg7u/6jmEhISEaPXq0vYyv7+8PP/xQP/3pT5WQkKCQkBC9/vrrV11n165dGjhwoMLDw9WzZ0+tX7++TpmmHi/qZQSg+fPnG0899ZSRnZ1tREdHu7TO0qVLjejoaOP11183/vWvfxk/+9nPjG7duhlff/21vUx6eroxYMAA45///Kfxv//7v0bPnj2N8ePHm9SKpmtq/S5fvmycPn3a6bFw4UKjXbt2xvnz5+3lJBnr1q1zKuf4d/G25uyX4cOHG1OmTHFqU3l5uf31y5cvGzfddJORmppq7Nu3z3j77beNTp06GTk5OWY3x2VNbfcnn3xi3HXXXcabb75pHDlyxNixY4dx4403GmPHjnUq52v7e+PGjUabNm2MtWvXGgcOHDCmTJlixMTEGMXFxfWW/+ijj4ywsDDjySefND777DNj3rx5RuvWrY1PPvnEXsaVz7u3NbXdP//5z41Vq1YZ+/btMw4ePGhMnjzZiI6ONk6cOGEvM2nSJCM9Pd1p35aWlnqqSS5parvXrVtnREVFObXJarU6lQnE/X3u3DmnNn/66adGWFiYsW7dOnsZX9/fb7/9tvHf//3fxmuvvWZIMrZu3dpo+X//+99GZGSkkZ2dbXz22WfGM888Y4SFhRnbtm2zl2nq37EhARmOaq1bt86lcFRTU2NYLBZj2bJl9mVlZWVGeHi48corrxiGYRifffaZIcnYs2ePvcw777xjhISEGCdPnnR73ZvKXfVLSkoy/uu//stpmSv/aL2lue0ePny48etf/7rB199++20jNDTU6SD73HPPGVFRUUZVVZVb6t4S7trfmzdvNtq0aWNcunTJvszX9ndycrLx4IMP2p9XV1cbCQkJRm5ubr3lx40bZ4wePdppWUpKinHfffcZhuHa590XNLXdV7p8+bLRvn1746WXXrIvmzRpknHH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","text/plain":["
"]},"metadata":{},"output_type":"display_data"}],"source":["plt.contourf(linsp, linsp, Z, cmap=\"Purples\")"]},{"cell_type":"markdown","metadata":{},"source":["Now let's generate some data for our quantum circuit to learn from"]},{"cell_type":"code","execution_count":6,"metadata":{},"outputs":[],"source":["n_data = 1000\n","x = random.uniform(random.PRNGKey(0), shape=(n_data, 2), minval=-1, maxval=1)\n","y = classification_function(x[:, 0], x[:, 1])"]},{"cell_type":"code","execution_count":7,"metadata":{},"outputs":[{"data":{"text/plain":[""]},"execution_count":7,"metadata":{},"output_type":"execute_result"},{"data":{"image/png":"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mzZBVVXs378fzzzzzIL7f+c738GuXbugqir27NmDJ554ouH1Zu7J1WYpbfz617+O3//930cmk0Emk8GBAwdm7X/XXXfNula33npru5sxL0tp32OPPTbr2FVVbdhnvV/DufoUjuNw22231fdZS9fwf/7nf/BHf/RHGBwcBMdx+MEPfrDoe5566im86lWvgqIo2LZtGx577LFZ+yz13l4yhNE0999/P/nSl75EPvShD5FUKtXUex544AGSSqXID37wA/K73/2O/PEf/zHZvHkzsW27vs+tt95Krr32WvLrX/+a/O///i/Ztm0bedvb3tamVszPUo8jCAIyPj7e8O9Tn/oUicfjxDTN+n4AyKOPPtqw36XtX02Wc65vuukmcvfddzccf6lUqr8eBAHZvXs3OXDgADl06BB54oknSE9PD7nvvvva3Zw5WWobn3/+efLmN7+Z/OhHPyInTpwgP//5z8n27dvJW97ylob9rsR1fPzxx4ksy+SRRx4hL774Irn77rtJOp0muVxuzv1/9atfEUEQyBe+8AVy5MgR8vGPf5xIkkSef/75+j7N3JOryVLb+Pa3v5189atfJYcOHSJHjx4ld911F0mlUuTChQv1fe68805y6623NlyrfD6/Wk1qYKnte/TRR0kymWw49omJiYZ91vs1nJmZaWjfCy+8QARBII8++mh9n7V0DZ944gnyD//wD+R73/seAUC+//3vL7j/qVOniK7r5EMf+hA5cuQI+cpXvkIEQSBPPvlkfZ+lnrPlwAKcZfDoo482FeBEUUT6+/vJF7/4xfq2YrFIFEUh//Ef/0EIIeTIkSMEAPnNb35T3+fHP/4x4TiOjI6OtvzY56NVx3HdddeRP//zP2/Y1swNsRost4033XQT+Zu/+Zt5X3/iiScIz/MNnfC//uu/kmQySVzXbcmxN0urruO3v/1tIssy8X2/vu1KXMfrr7+evO9976v/HYYhGRwcJJ///Ofn3P/P/uzPyG233dawbf/+/eQv/uIvCCHN3ZOrzVLbeDlBEJBEIkG+8Y1v1Lfdeeed5Pbbb2/1oS6LpbZvsf61E6/hP/3TP5FEIkHK5XJ921q6hpfSTD/w0Y9+lFxzzTUN29761reSW265pf73Ss9ZM7AlqjZy+vRpTExM4MCBA/VtqVQK+/fvx8GDBwEABw8eRDqdxmte85r6PgcOHADP83j66adX7VhbcRzPPvssDh8+jPe85z2zXnvf+96Hnp4eXH/99XjkkUeaKnXfalbSxm9+85vo6enB7t27cd9998GyrIbP3bNnD/r6+urbbrnlFhiGgRdffLH1DVmAVv2eSqUSkskkRLGxXN1qXkfP8/Dss8823D88z+PAgQP1++dyDh482LA/QK9Fbf9m7snVZDltvBzLsuD7Prq6uhq2P/XUU8hms9i5cyfe+973YmZmpqXH3gzLbV+5XMbGjRsxMjKC22+/veE+6sRr+PDDD+OOO+5ALBZr2L4WruFyWOw+bMU5a4ZXZLHN1WJiYgIAGh58tb9rr01MTCCbzTa8Looiurq66vusBq04jocffhhXXXUVbrzxxobtn/70p/EHf/AH0HUdP/3pT/FXf/VXKJfL+MAHPtCy42+G5bbx7W9/OzZu3IjBwUE899xz+NjHPoZjx47he9/7Xv1z57rGtddWk1Zcx+npaXzmM5/BPffc07B9ta/j9PQ0wjCc89y+9NJLc75nvmtx6f1W2zbfPqvJctp4OR/72McwODjY8LC49dZb8eY3vxmbN2/GyZMn8fd///d44xvfiIMHD0IQhJa2YSGW076dO3fikUcewd69e1EqlfDQQw/hxhtvxIsvvojh4eGOu4bPPPMMXnjhBTz88MMN29fKNVwO892HhmHAtm0UCoUV/+6b4RUf4Nx777148MEHF9zn6NGj2LVr1yodUWtptn0rxbZtfOtb38InPvGJWa9dum3fvn2oVCr44he/2LIHY7vbeOmDfs+ePRgYGMDNN9+MkydPYuvWrcv+3KWwWtfRMAzcdtttuPrqq/GP//iPDa+1+zoyls4DDzyAxx9/HE899VSDEPeOO+6o/3/Pnj3Yu3cvtm7diqeeego333zzlTjUprnhhhtwww031P++8cYbcdVVV+FrX/saPvOZz1zBI2sPDz/8MPbs2YPrr7++Yft6voZrhVd8gPPhD38Yd91114L7bNmyZVmf3d/fDwDI5XIYGBiob8/lcrjuuuvq+0xOTja8LwgC5PP5+vtXQrPtW+lxfPe734VlWXjXu9616L779+/HZz7zGbiu25I6JavVxhr79+8HAJw4cQJbt25Ff3//LPV/LpcDgJZcQ2B12miaJm699VYkEgl8//vfhyRJC+7f6ut4OT09PRAEoX4ua+RyuXnb0t/fv+D+zdyTq8ly2ljjoYcewgMPPICf/exn2Lt374L7btmyBT09PThx4sSqPhxX0r4akiRh3759OHHiBIDOuoaVSgWPP/44Pv3pTy/6PVfqGi6H+e7DZDIJTdMgCMKKfxdN0TI1zyuIpYqMH3roofq2Uqk0p8j4t7/9bX2fn/zkJ1dMZLzc47jppptmZd3Mx2c/+1mSyWSWfazLpVXn+pe//CUBQH73u98RQi6KjC9V/3/ta18jyWSSOI7TugY0wXLbWCqVyO/93u+Rm266iVQqlaa+azWu4/XXX0/e//731/8Ow5AMDQ0tKDL+wz/8w4ZtN9xwwyyR8UL35Gqz1DYSQsiDDz5IkskkOXjwYFPfcf78ecJxHPnhD3+44uNdKstp36UEQUB27txJ/vZv/5YQ0jnXkBD6LFEUhUxPTy/6HVfyGl4KmhQZ7969u2Hb2972tlki45X8Lpo61pZ90iuAs2fPkkOHDtVToQ8dOkQOHTrUkBK9c+dO8r3vfa/+9wMPPEDS6TT54Q9/SJ577jly++23z5kmvm/fPvL000+TX/7yl2T79u1XLE18oeO4cOEC2blzJ3n66acb3nf8+HHCcRz58Y9/POszf/SjH5Gvf/3r5PnnnyfHjx8n//Iv/0J0XSf3339/29szF0tt44kTJ8inP/1p8tvf/pacPn2a/PCHPyRbtmwhr3vd6+rvqaWJv+ENbyCHDx8mTz75JOnt7b2iaeJLaWOpVCL79+8ne/bsISdOnGhISw2CgBBy5a7j448/ThRFIY899hg5cuQIueeee0g6na5nrL3zne8k9957b33/X/3qV0QURfLQQw+Ro0ePkk9+8pNzpokvdk+uJktt4wMPPEBkWSbf/e53G65VrR8yTZP83d/9HTl48CA5ffo0+dnPfkZe9apXke3bt696wL2c9n3qU58iP/nJT8jJkyfJs88+S+644w6iqip58cUX6/us92tY47WvfS1561vfOmv7WruGpmnWn3cAyJe+9CVy6NAhcvbsWUIIIffeey955zvfWd+/lib+kY98hBw9epR89atfnTNNfKFz1gpYgLME7rzzTgJg1r9f/OIX9X1Q9QqpEUUR+cQnPkH6+vqIoijk5ptvJseOHWv43JmZGfK2t72NxONxkkwmybvf/e6GoGm1WOw4Tp8+Pau9hBBy3333kZGRERKG4azP/PGPf0yuu+46Eo/HSSwWI9deey35t3/7tzn3XQ2W2sZz586R173udaSrq4soikK2bdtGPvKRjzT44BBCyJkzZ8gb3/hGomka6enpIR/+8IcbUqxXk6W28Re/+MWcv2sA5PTp04SQK3sdv/KVr5ANGzYQWZbJ9ddfT37961/XX7vpppvInXfe2bD/t7/9bbJjxw4iyzK55ppryH/+5382vN7MPbnaLKWNGzdunPNaffKTnySEEGJZFnnDG95Aent7iSRJZOPGjeTuu+9u6YNjqSylfR/84Afr+/b19ZE3velN5P/+7/8aPm+9X0NCCHnppZcIAPLTn/501mettWs4Xx9Ra9Odd95Jbrrpplnvue6664gsy2TLli0Nz8UaC52zVsARcgXydRkMBoPBYDDaCPPBYTAYDAaD0XGwAIfBYDAYDEbHwQIcBoPBYDAYHQcLcBgMBoPBYHQcLMBhMBgMBoPRcbAAh8FgMBgMRsfBAhwGg8FgMBgdBwtwGAwGg8FgdBwswGEwGAwGg9FxsACHwWAwGAxGx8ECHAaDwWAwGB0HC3AYDAaDwWB0HP8f4XHWfbcCd6UAAAAASUVORK5CYII=","text/plain":["
"]},"metadata":{},"output_type":"display_data"}],"source":["plt.scatter(x[:, 0], x[:, 1], alpha=jnp.where(y, 1, 0.2), s=10)"]},{"cell_type":"markdown","metadata":{},"source":["# Quantum circuit time\n","We'll use a variant of data re-uploading [Pérez-Salinas et al](https://doi.org/10.22331/q-2020-02-06-226) to encode the input data, alongside some variational parameters within a quantum circuit classifier"]},{"cell_type":"code","execution_count":8,"metadata":{},"outputs":[],"source":["n_qubits = 3\n","depth = 5"]},{"cell_type":"code","execution_count":9,"metadata":{},"outputs":[],"source":["c = Circuit(n_qubits)"]},{"cell_type":"code","execution_count":10,"metadata":{},"outputs":[],"source":["for layer in range(depth):\n"," for qi in range(n_qubits):\n"," c.Rz(0.0, qi)\n"," c.Ry(0.0, qi)\n"," c.Rz(0.0, qi)\n"," if layer < (depth - 1):\n"," for qi in range(layer, layer + n_qubits - 1, 2):\n"," c.CZ(qi % n_qubits, (qi + 1) % n_qubits)\n"," c.add_barrier(range(n_qubits))"]},{"cell_type":"code","execution_count":11,"metadata":{},"outputs":[{"data":{"text/html":["\n","\n","\n","\n","\n","\n","
\n"," \n","
\n","\n"],"text/plain":[""]},"metadata":{},"output_type":"display_data"}],"source":["render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can use `pytket-qujax` to generate our angles-to-statetensor function."]},{"cell_type":"code","execution_count":12,"metadata":{},"outputs":[],"source":["angles_to_st = tk_to_qujax(c)"]},{"cell_type":"markdown","metadata":{},"source":["We'll parameterise each angle as\n","\n","$$\n","\\begin{equation}\n","\\theta_k = b_k + w_k \\, x_k\n","\\end{equation}\n","$$\n","\n","where $b_k, w_k$ are variational parameters to be learnt and $x_k = x_0$ if $k$ even, $x_k = x_1$ if $k$ odd for a single bivariate input point $(x_0, x_1)$."]},{"cell_type":"code","execution_count":13,"metadata":{},"outputs":[],"source":["n_angles = 3 * n_qubits * depth\n","n_params = 2 * n_angles"]},{"cell_type":"code","execution_count":14,"metadata":{},"outputs":[],"source":["def param_and_x_to_angles(param, x_single):\n"," biases = param[:n_angles]\n"," weights = param[n_angles:]\n"," weights_times_data = jnp.where(\n"," jnp.arange(n_angles) % 2 == 0, weights * x_single[0], weights * x_single[1]\n"," )\n"," angles = biases + weights_times_data\n"," return angles"]},{"cell_type":"code","execution_count":15,"metadata":{},"outputs":[],"source":["param_and_x_to_st = lambda param, x_single: angles_to_st(\n"," param_and_x_to_angles(param, x_single)\n",")"]},{"cell_type":"markdown","metadata":{},"source":["We'll measure the first qubit only (if its 1 we label _donut_, if its 0 we label _not donut_)"]},{"cell_type":"code","execution_count":16,"metadata":{},"outputs":[],"source":["def param_and_x_to_probability(param, x_single):\n"," st = param_and_x_to_st(param, x_single)\n"," all_probs = jnp.square(jnp.abs(st))\n"," first_qubit_probs = jnp.sum(all_probs, axis=range(1, n_qubits))\n"," return first_qubit_probs[1]"]},{"cell_type":"markdown","metadata":{},"source":["For binary classification, the likelihood for our full data set $(x_{1:N}, y_{1:N})$ is\n","\n","$$\n","\\begin{equation}\n","p(y_{1:N} \\mid b, w, x_{1:N}) = \\prod_{i=1}^N p(y_i \\mid b, w, x_i) = \\prod_{i=1}^N (1 - q_{(b,w)}(x_i))^{I[y_i = 0]}q_{(b,w)}(x_i)^{I[y_i = 1]},\n","\\end{equation}\n","$$\n","\n","where $q_{(b, w)}(x)$ is the probability the quantum circuit classifies input $x$ as donut given variational parameter vectors $(b, w)$. This gives log-likelihood\n","\n","$$\n","\\begin{equation}\n"," \\log p(y_{1:N} \\mid b, w, x_{1:N}) = \\sum_{i=1}^N I[y_i = 0] \\log(1 - q_{(b,w)}(x_i)) + I[y_i = 1] \\log q_{(b,w)}(x_i),\n","\\end{equation}\n","$$\n","\n","which we would like to maximise.\n","\n","Unfortunately, the log-likelihood **cannot** be approximated unbiasedly using shots, that is we can approximate $q_{(b,w)}(x_i)$ unbiasedly but not $\\log(q_{(b,w)}(x_i))$.\n","Note that in qujax simulations we can use the statetensor to calculate this exactly, but it is still good to keep in mind loss functions that can also be used with shots from a quantum device.\n","\n","Instead we can minimise an expected distance between shots and data\n","\n","$$\n","\\begin{equation}\n","C(b, w, x, y) = E_{p(y' \\mid q_{(b, w)}(x))}[\\ell(y', y)] = (1 - q_{(b, w)}(x)) \\ell(0, y) + q_{(b, w)}(x)\\ell(1, y),\n","\\end{equation}\n","$$\n","\n","where $y'$ is a shot, $y$ is a data label and $\\ell$ is some distance between bitstrings - here we simply set $\\ell(0, 0) = \\ell(1, 1) = 0$ and $\\ell(0, 1) = \\ell(1, 0) = 1$ (which coincides with the Hamming distance for this binary example).\n","\n"," The full batch cost function is\n","\n","$$\n","\\begin{equation}\n"," C(b, w) = \\frac1N \\sum_{i=1}^N C(b,\\, w,\\, x_i,\\, y_i).\n","\\end{equation}\n","$$"]},{"cell_type":"markdown","metadata":{},"source":["Note that to calculate the cost function we need to evaluate the statetensor for every input point $x_i$. If the dataset becomes too large, we can easily minibatch."]},{"cell_type":"code","execution_count":17,"metadata":{},"outputs":[],"source":["def param_to_cost(param):\n"," donut_probs = vmap(param_and_x_to_probability, in_axes=(None, 0))(param, x)\n"," costs = jnp.where(y, 1 - donut_probs, donut_probs)\n"," return costs.mean()"]},{"cell_type":"markdown","metadata":{},"source":["# Ready to descend some gradients?\n","We'll just use vanilla gradient descent here"]},{"cell_type":"code","execution_count":18,"metadata":{},"outputs":[],"source":["param_to_cost_and_grad = jit(value_and_grad(param_to_cost))"]},{"cell_type":"code","execution_count":19,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["999 Cost: 0.25950647\r"]}],"source":["n_iter = 1000\n","stepsize = 1e-1\n","param = random.uniform(random.PRNGKey(1), shape=(n_params,), minval=0, maxval=2)\n","costs = jnp.zeros(n_iter)\n","for i in range(n_iter):\n"," cost, grad = param_to_cost_and_grad(param)\n"," costs = costs.at[i].set(cost)\n"," param = param - stepsize * grad\n"," print(i, \"Cost: \", cost, end=\"\\r\")"]},{"cell_type":"code","execution_count":20,"metadata":{},"outputs":[{"data":{"text/plain":["Text(0, 0.5, 'Cost')"]},"execution_count":20,"metadata":{},"output_type":"execute_result"},{"data":{"image/png":"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z6a/3iKYOBwOPfLII4xC+QF97R/0s3/Qz/5DX/uH2f3MyA8AAAgqjPwAAICgQvgBAABBhfADAACCCuEHAAAEFcKPH82bN0/p6ekKCwtTZmamVq1aZXZJ7Upubq5GjBih6OhoJSYmavz48dq6datPm+rqat1xxx3q1KmToqKidO211x73CpX8/HxdfvnlioiIUGJion7/+9+rvr7en5fSrsyZM0cWi0UzZszwbqOfW8e+fft0ww03qFOnTgoPD9fAgQO1Zs0a737DMDRz5kylpKQoPDxcWVlZ2rZtm885Dh8+rEmTJikmJkaxsbG66aabVF5e7u9LCVhut1sPP/ywunfvrvDwcPXs2VOPPfaYz7uf6Ofm+cc//qErr7xSqampslgsev/99332t1a/btq0SRdccIHCwsKUlpamJ598suXFG/CLRYsWGXa73XjppZeMr7/+2pg+fboRGxtrFBYWml1au5GdnW389a9/NbZs2WJs2LDBuOyyy4yuXbsa5eXl3ja33nqrkZaWZuTl5Rlr1qwxzj33XGP06NHe/fX19caAAQOMrKwsY/369cZHH31kJCQkGA888IAZlxTwVq1aZaSnpxuDBg0y7r77bu92+rnlDh8+bHTr1s248cYbjZUrVxo//PCD8cknnxjbt2/3tpkzZ47hdDqN999/39i4caPxi1/8wujevbtRVVXlbTN27Fhj8ODBxldffWV8+eWXRq9evYzrr7/ejEsKSLNnzzY6depkfPjhh8bOnTuNt99+24iKijKefvppbxv6uXk++ugj46GHHjLeffddQ5Lx3nvv+exvjX4tLS01kpKSjEmTJhlbtmwx3nzzTSM8PNz485//3KLaCT9+MnLkSOOOO+7w/ux2u43U1FQjNzfXxKrat4MHDxqSjGXLlhmGYRglJSVGaGio8fbbb3vbfPvtt4YkY8WKFYZhNPzHarVajYKCAm+bF154wYiJiTFqamr8ewEBrqyszOjdu7fx6aefGj/72c+84Yd+bh333Xefcf755590v8fjMZKTk40//elP3m0lJSWGw+Ew3nzzTcMwDOObb74xJBmrV6/2tvn4448Ni8Vi7Nu3r+2Kb0cuv/xy49e//rXPtmuuucaYNGmSYRj0c2v5cfhprX59/vnnjbi4OJ9/N+677z6jT58+LaqXaS8/qK2t1dq1a5WVleXdZrValZWVpRUrVphYWftWWloqSYqPj5ckrV27VnV1dT793LdvX3Xt2tXbzytWrNDAgQO9r1CRpOzsbLlcLn399dd+rD7w3XHHHbr88st9+lOin1vL//zP/ygjI0P/9m//psTERA0dOlQLFizw7t+5c6cKCgp8+tnpdCozM9Onn2NjY31e8pyVlSWr1aqVK1f672IC2OjRo5WXl6fvv/9ekrRx40YtX75c48aNk0Q/t5XW6tcVK1bowgsvlN1u97bJzs7W1q1bdeTIkWbXZ+q7vYJFUVGR3G63zx8CSUpKStJ3331nUlXtm8fj0YwZM3TeeedpwIABkqSCggLZ7XbFxsb6tE1KSvK+BLegoOCE/zs07UODRYsWad26dVq9evVx++jn1vHDDz/ohRdeUE5Ojh588EGtXr1av/nNb2S32zV16lRvP52oH4/t58TERJ/9ISEhio+Pp58b3X///XK5XOrbt69sNpvcbrdmz56tSZMmSRL93EZaq18LCgrUvXv3487RtC8uLq5Z9RF+0C7dcccd2rJli5YvX252KR3Onj17dPfdd+vTTz9VWFiY2eV0WB6PRxkZGfrjH/8oSRo6dKi2bNmi+fPna+rUqSZX13G89dZbev311/XGG2/onHPO0YYNGzRjxgylpqbSz0GMaS8/SEhIkM1mO+5umMLCQiUnJ5tUVft155136sMPP9QXX3yhs846y7s9OTlZtbW1Kikp8Wl/bD8nJyef8H+Hpn1omNY6ePCghg0bppCQEIWEhGjZsmV65plnFBISoqSkJPq5FaSkpKh///4+2/r166f8/HxJR/vpVP9uJCcn6+DBgz776+vrdfjwYfq50e9//3vdf//9mjhxogYOHKjJkyfrt7/9rXJzcyXRz22ltfq1rf4tIfz4gd1u1/Dhw5WXl+fd5vF4lJeXp1GjRplYWftiGIbuvPNOvffee/r888+PGwodPny4QkNDffp569atys/P9/bzqFGjtHnzZp//4D799FPFxMQc94coWI0ZM0abN2/Whg0bvJ+MjAxNmjTJ+z393HLnnXfecY9q+P7779WtWzdJUvfu3ZWcnOzTzy6XSytXrvTp55KSEq1du9bb5vPPP5fH41FmZqYfriLwVVZWymr1/VNns9nk8Xgk0c9tpbX6ddSoUfrHP/6huro6b5tPP/1Uffr0afaUlyRudfeXRYsWGQ6Hw3j5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","text/plain":["
"]},"metadata":{},"output_type":"display_data"}],"source":["plt.plot(costs)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]},{"cell_type":"markdown","metadata":{},"source":["# Visualise trained classifier"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["linsp = jnp.linspace(-1, 1, 100)\n","Z = vmap(\n"," lambda a: vmap(lambda b: param_and_x_to_probability(param, jnp.array([a, b])))(\n"," linsp\n"," )\n",")(linsp)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["plt.contourf(linsp, linsp, Z, cmap=\"Purples\", alpha=0.8)\n","circle_linsp = jnp.linspace(0, 2 * jnp.pi, 100)\n","plt.plot(inner_rad * jnp.cos(circle_linsp), inner_rad * jnp.sin(circle_linsp), c=\"red\")\n","plt.plot(outer_rad * jnp.cos(circle_linsp), outer_rad * jnp.sin(circle_linsp), c=\"red\")"]},{"cell_type":"markdown","metadata":{},"source":["Looks good, it has clearly grasped the donut shape. Sincerest apologies if you are now hungry! 🍩"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.1"}},"nbformat":4,"nbformat_minor":2} +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Binary classification using `pytket-qujax`\n","\n","**Download this notebook - {nb-download}`pytket-qujax-classification.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["See the docs for [qujax](https://cqcl.github.io/qujax/) and [pytket-qujax](https://cqcl.github.io/pytket-qujax/api/index.html)."]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[],"source":["from jax import numpy as jnp, random, vmap, value_and_grad, jit\n","from pytket import Circuit\n","from pytket.circuit.display import render_circuit_jupyter\n","from pytket.extensions.qujax.qujax_convert import tk_to_qujax\n","import matplotlib.pyplot as plt"]},{"cell_type":"markdown","metadata":{},"source":["## Define the classification task\n","We'll try and learn a _donut_ binary classification function (i.e. a bivariate coordinate is labelled 1 if it is inside the donut and 0 if it is outside)"]},{"cell_type":"code","execution_count":2,"metadata":{},"outputs":[],"source":["inner_rad = 0.25\n","outer_rad = 0.75"]},{"cell_type":"code","execution_count":3,"metadata":{},"outputs":[],"source":["def classification_function(x, y):\n"," r = jnp.sqrt(x**2 + y**2)\n"," return jnp.where((r > inner_rad) * (r < outer_rad), 1, 0)"]},{"cell_type":"code","execution_count":4,"metadata":{},"outputs":[],"source":["linsp = jnp.linspace(-1, 1, 1000)\n","Z = vmap(lambda x: vmap(lambda y: classification_function(x, y))(linsp))(linsp)"]},{"cell_type":"code","execution_count":5,"metadata":{},"outputs":[{"data":{"text/plain":[""]},"execution_count":5,"metadata":{},"output_type":"execute_result"},{"data":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAAkcAAAGiCAYAAADtImJbAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/TGe4hAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA9EklEQVR4nO3de3hU1aH+8TcJkBAhCUiSIRjlZrlUIHJJCscWKXlIlLaiHEkoFsixoFagEESJB6FcbFA4SlEqarnYFuVixUurKCI8ntqUYCBFETiSolwnAWIykGiAZP/+8JdxhlyYJLPn+v08zzw6e9bes1Y2s+edtdbeO8QwDEMAAACQJIV6uwIAAAC+hHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADggHAEAADgwNRw9OGHH+qnP/2pEhISFBISotdff/2q6+zatUsDBw5UeHi4evbsqfXr19cps2rVKnXt2lURERFKSUlRfn6++ysPAACCkqnhqKKiQgMGDNCqVatcKn/06FGNHj1aI0aMUGFhoWbOnKlf/vKXevfdd+1lNm3apOzsbC1YsEB79+7VgAEDlJaWppKSErOaAQAAgkiIp248GxISoq1bt2rMmDENlnnkkUf0t7/9TZ9++ql9WWZmpsrKyrRt2zZJUkpKioYMGaJnn31WklRTU6PExERNnz5dc+fONbUNAAAg8LXydgUc5eXlKTU11WlZWlqaZs6cKUm6ePGiCgoKlJOTY389NDRUqampysvLa3C7VVVVqqqqsj+vqalRaWmprr32WoWEhLi3EQAAwBSGYej8+fNKSEhQaKh5g18+FY6sVqvi4+OdlsXHx8tms+nrr7/WV199perq6nrLHDp0qMHt5ubmauHChabUGQAAeNbx48d13XXXmbZ9nwpHZsnJyVF2drb9eXl5ua6//nr936EitW/f3os1AwAArjp//ry+17uH6d/dPhWOLBaLiouLnZYVFxcrKipKbdu2VVhYmMLCwuotY7FYGtxueHi4wsPD6yxv3769oqKi3FN5AADgEWZPifGp6xwNHTpUO3bscFq2fft2DR06VJLUpk0bDRo0yKlMTU2NduzYYS8DAADQEqaGowsXLqiwsFCFhYWSvj1Vv7CwUMeOHZP07XDXxIkT7eXvv/9+/fvf/9bDDz+sQ4cO6fe//702b96sWbNm2ctkZ2frxRdf1EsvvaSDBw/qgQceUEVFhbKyssxsCgAACBKmDqt9/PHHGjFihP157byfSZMmaf369Tp9+rQ9KElSt27d9Le//U2zZs3S7373O1133XX6wx/+oLS0NHuZjIwMnTlzRvPnz5fValVSUpK2bdtWZ5I2AABAc3jsOke+xGazKTo6WqdPljDnCAAAP2Gz2dS5S5zKy8tN/f72qTlHAAAA3kY4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcEA4AgAAcOCRcLRq1Sp17dpVERERSklJUX5+foNlb731VoWEhNR5jB492l5m8uTJdV5PT0/3RFMAAECAa2X2G2zatEnZ2dlavXq1UlJStGLFCqWlpenw4cOKi4urU/61117TxYsX7c/PnTunAQMG6O6773Yql56ernXr1tmfh4eHm9cIAAAQNEzvOXrqqac0ZcoUZWVlqW/fvlq9erUiIyO1du3aest37NhRFovF/ti+fbsiIyPrhKPw8HCnch06dDC7KQAAIAiY2nN08eJFFRQUKCcnx74sNDRUqampysvLc2kba9asUWZmpq655hqn5bt27VJcXJw6dOigH//4x1qyZImuvfbaerdRVVWlqqoq+3ObzdaM1gDwtDPnKq9a5sHk513a1sCsJN37QMpVy8VeG+nS9gAELlPD0dmzZ1VdXa34+Hin5fHx8Tp06NBV18/Pz9enn36qNWvWOC1PT0/XXXfdpW7duqmoqEiPPvqobrvtNuXl5SksLKzOdnJzc7Vw4cKWNQaA28y4c4Os+0s8+p571xVq77rCFm/H0j9OK7dOaHmFAPgs0+cctcSaNWvUr18/JScnOy3PzMy0/3+/fv3Uv39/9ejRQ7t27dLIkSPrbCcnJ0fZ2dn25zabTYmJieZVHAhSZ85V6oP3/k9Hi75ySxDxRdb9JRrX4+kGXx+YlSRJ6tajgzLGJ3mmUgDcytRw1KlTJ4WFham4uNhpeXFxsSwWS6PrVlRUaOPGjVq0aNFV36d79+7q1KmTjhw5Um84Cg8PZ8I24CZnzlXqiy/LtOzuTd6uik+qDYV7Jf1l3s46r8/ZkqGuN8QwfAf4MFPDUZs2bTRo0CDt2LFDY8aMkSTV1NRox44dmjZtWqPrbtmyRVVVVbrnnnuu+j4nTpzQuXPn1LlzZ3dUG8D/t+mVwnq/4NF8DYVKhusA32H6sFp2drYmTZqkwYMHKzk5WStWrFBFRYWysrIkSRMnTlSXLl2Um5vrtN6aNWs0ZsyYOpOsL1y4oIULF2rs2LGyWCwqKirSww8/rJ49eyotLc3s5gABa+mSb0NQoA6H+borh+tqh+fmzhvhpRoBwcv0cJSRkaEzZ85o/vz5slqtSkpK0rZt2+yTtI8dO6bQUOcrChw+fFh///vf9d5779XZXlhYmPbv36+XXnpJZWVlSkhI0KhRo7R48WKGzgAX7dl7Sn9avNPjk6LhutqQOu6KsDpnS4aGDEzwfIWAIBJiGIbh7Up4ms1mU3R0tE6fLFFUVJS3qwN4RGOTiOG/NhfN8nYVAI+x2Wzq3CVO5eXlpn5/+/TZagCazxuny8PzHEOvpX+cfvHYCHqWgBYiHAEBgsnTsO4vcZrwPXbJCC4nADQDw2oMq8FPnTlX6fLVoQFJWpV/H5cQgF9jWA1AvZg7hOZyDNNM7AYaRjgCfBzDZTCD4/DbwKwkLhkAOGBYjWE1+KA9e09xBWp4BT1K8GUMqwFBiCEzeJtjKCcoIVgRjgAvW7pkJ1elhk9yDEpcTwnBhHAEeAHDZvA3tb2azE9CMGDOEXOO4EEMmyGQ0JsET2POERBACEUIRLX/rglJCDSEI8AknIKPYMGQGwINw2oMq8HN6CUCuBo3zMGwGuBnCEXAd2qvxs2QG/wR4QhoAc46AxrHkBv8EcNqDKuhmegpApqOC0uiJRhWA3wUoQhovtqeVobb4MsIR4CLCEWA+3AZAPgywhFwFYQiwDy1ny+G2+BLCEdAA7jnGeA5DLfBlxCOgCtwBhrgPeN6PC1L/zit3DrB21VBECMcAQ4YQgO8z7q/RON6PK2xS0YoY3ySt6uDIMSp/JzKDxGKAF/GUBtqcSo/4AGEIsD3cWYbPC3U2xUAvIVgBPiXcT2e1qZXCr1dDQQBhtUYVgs6hCLA/3Fj2+DkqWE1eo4QNDa9UkgwAgLEg8nPa8adG7xdDQQo5hwhKBCKgMBTe1Ybc5HgboQjBLQZd26QdX+Jt6sBwERM2Ia7MayGgDWux9MEIyCI0EMMdyEcIeCcOVfJQRIIUpzRBnfgbDXOVgsohCIAtRhmCzycrQY0EcEIgCOOCWguwhH8HsNoABoyrsfTWrpkp7erAT/DsBrDan6NUATAVQyz+b+AGlZbtWqVunbtqoiICKWkpCg/P7/BsuvXr1dISIjTIyIiwqmMYRiaP3++OnfurLZt2yo1NVWff/652c2AjyEYAWgKjhlwlenhaNOmTcrOztaCBQu0d+9eDRgwQGlpaSopafgU66ioKJ0+fdr++PLLL51ef/LJJ7Vy5UqtXr1au3fv1jXXXKO0tDR98803ZjcHPmDP3lMc5AA0C8NscIXpw2opKSkaMmSInn32WUlSTU2NEhMTNX36dM2dO7dO+fXr12vmzJkqKyurd3uGYSghIUGzZ8/WQw89JEkqLy9XfHy81q9fr8zMzKvWiWE1/0UoAuAuDLP5n4AYVrt48aIKCgqUmpr63RuGhio1NVV5eXkNrnfhwgXdcMMNSkxM1B133KEDBw7YXzt69KisVqvTNqOjo5WSktLgNquqqmSz2Zwe8D8EIwDuxDEFDTE1HJ09e1bV1dWKj493Wh4fHy+r1VrvOr169dLatWv1xhtv6M9//rNqamo0bNgwnThxQpLs6zVlm7m5uYqOjrY/EhMTW9o0eBgHMQBm4NiC+vjcqfxDhw7VxIkTlZSUpOHDh+u1115TbGysnn/++WZvMycnR+Xl5fbH8ePH3VhjmI2DFwAzjevxtPbsPeXtasCHmBqOOnXqpLCwMBUXFzstLy4ulsVicWkbrVu31s0336wjR45Ikn29pmwzPDxcUVFRTg/4Pq5fBMBTlt29iYnasDM1HLVp00aDBg3Sjh077Mtqamq0Y8cODR061KVtVFdX65NPPlHnzp0lSd26dZPFYnHaps1m0+7du13eJnzfnr2n9GBy83sLAaCp9q4r1Iw7N3i7GvABrcx+g+zsbE2aNEmDBw9WcnKyVqxYoYqKCmVlZUmSJk6cqC5duig3N1eStGjRIv3gBz9Qz549VVZWpmXLlunLL7/UL3/5S0lSSEiIZs6cqSVLlujGG29Ut27d9NhjjykhIUFjxowxuznwAHqLAHiLdX+JxvV4mjPZgpzp4SgjI0NnzpzR/PnzZbValZSUpG3bttknVB87dkyhod91YH311VeaMmWKrFarOnTooEGDBukf//iH+vbtay/z8MMPq6KiQlOnTlVZWZluueUWbdu2rc7FIuF/+NUGwBcQkIIbtw9h/pHPoMcIgK8hIPmWgLjOEeAqghEAX8SxKTgRjuB1DKUB8GUEpOBDOIJXjevxtKz7G77PHgD4AgJScCEcwWs42ADwJxyzggfhCF7BQQaAP+LYFRwIR/A4rkILwJ8RkAIf4QgeNa7H09q7rtDb1QCAFiEgBTbCETyGgwmAQMIxLXARjuARDKUBCEQEpMBEOILp9uw9xVAagIDFtdoCD+EIplt29yZvVwEATGPdX6I9e095uxpwI8IRTEWXM4BgwI/AwEI4gmkIRgCCCce8wEE4gik4SAAIRhz7AgPhCG7H5EQAwYyA5P8IR3A7biQLINgxQdu/EY7gVvxiAgAmaPs7whHchmAEAN/hmOi/CEdwi02vFHq7CgDgcwhI/olwBLf4yzxuDwIA9WH+kf8hHKHF+GUEAA1bdvcmnTlX6e1qoAkIR2gRghEAXN2Dyc97uwpoAsIRmo2uYgBwHdeA8x+EIzQbp6oCgOus+0sYXvMThCM0C8NpANB0DK/5B8IRmozhNABoPobXfB/hCE3GcBoANB+3WPJ9hCM0Cb94AKDlmJrg2whHaBJ+8QCAezA523cRjuAyfukAgPswOdt3EY4AAPASTnDxTYQjuIReIwBwP05w8U2EI1wVv2wAwDxLl3Djbl9DOMJV8csGAMyzd10hk7N9DOEIjeIXDQCYj8nZvoVwhEbtXVfo7SoAQFCg98h3eCQcrVq1Sl27dlVERIRSUlKUn5/fYNkXX3xRP/zhD9WhQwd16NBBqampdcpPnjxZISEhTo/09HSzmxF0uOAjAHgOvUe+w/RwtGnTJmVnZ2vBggXau3evBgwYoLS0NJWU1H8xwV27dmn8+PHauXOn8vLylJiYqFGjRunkyZNO5dLT03X69Gn745VXXjG7KUGHCz4CgGfRe+QbQgzDMMx8g5SUFA0ZMkTPPvusJKmmpkaJiYmaPn265s6de9X1q6ur1aFDBz377LOaOHGipG97jsrKyvT666+7VIeqqipVVVXZn9tsNiUmJur0yRJFRUU1vVFBYMadGwhHAOAFm4tmebsKPstms6lzlziVl5eb+v1tas/RxYsXVVBQoNTU1O/eMDRUqampysvLc2kblZWVunTpkjp27Oi0fNeuXYqLi1OvXr30wAMP6Ny5cw1uIzc3V9HR0fZHYmJi8xoURAhGAOAd9B55XyszN3727FlVV1crPj7eaXl8fLwOHTrk0jYeeeQRJSQkOAWs9PR03XXXXerWrZuKior06KOP6rbbblNeXp7CwsLqbCMnJ0fZ2dn257U9R6gfc43gi+ZsyZAkDRmY4Jbt1V6/a8fbhznxAD7lweTn6T3yMlPDUUstXbpUGzdu1K5duxQREWFfnpmZaf//fv36qX///urRo4d27dqlkSNH1tlOeHi4wsPDPVLnQECvEbxpYFaS5s4bYfr71IasIQMTpCveb+mSnQQmIIiZGo46deqksLAwFRcXOy0vLi6WxWJpdN3ly5dr6dKlev/999W/f/9Gy3bv3l2dOnXSkSNH6g1HcB3dufA0S/84rdw6wdvVcDJ33ginwLRn7ykuhgqPWrpkp0d+JKB+poajNm3aaNCgQdqxY4fGjBkj6dsJ2Tt27NC0adMaXO/JJ5/U448/rnfffVeDBw++6vucOHFC586dU+fOnd1V9aDFqaTwhFX59yn22khvV8NlQwYm2Ic5zpyr1OJfbqWHFabau66wTo8mPMf0YbXs7GxNmjRJgwcPVnJyslasWKGKigplZWVJkiZOnKguXbooNzdXkvTEE09o/vz5evnll9W1a1dZrVZJUrt27dSuXTtduHBBCxcu1NixY2WxWFRUVKSHH35YPXv2VFpamtnNAdBMY5eMUMb4JG9Xo8Vir4106unipswwy5lzlX71IyKQmB6OMjIydObMGc2fP19Wq1VJSUnatm2bfZL2sWPHFBr63Ulzzz33nC5evKj//M//dNrOggUL9Jvf/EZhYWHav3+/XnrpJZWVlSkhIUGjRo3S4sWLmVfUQkzEhrv54pCZu9X2KDH0BndjYrb3mH6dI19ks9kUHR3NdY6uwC9guEuwH9D5LMFdgv2zdKWAuM4R/Ac3mIU7zNmSwcFc336h8XeAOxC0vcOnT+WH55wqOOXtKsCPEQTqV/t34QsO8C+EI0ji2kZoHkKRawhJgH9hWA0MqaFZCEZNx98MzUGo9jx6jsCVgNEkfMG3DL1IgO+j5wiAywhG7rO5aJYGZiV5uxoA6kE4CnIMqcEVnIVmjrnzRvB3hUvoafQswhGARm0ummW/SSvMQUACfAvhKMgx3wiN4UvbcxhmA3wH4SiIMaSGxhCMPI+7sKMxDK15DuEIgJOBWUkEIy/aXDRLq/Lv83Y1gKBGOApiDKmhPvReeF/stZEau4T9AHgL4QiAHT1GviNjfJIs/eO8XQ0gKBGOghTzjXAlgpHvWbl1grerAB/DvCPPIBwBIBj5MPYN4HmEoyDFfCPU4svX97GPAM8iHAFBbM6WDG9XAS4iIAGeQzgCgpSlfxxXvvYzhFnAMwhHQYjJ2JCY7OuPCLOQmJTtCYQjIAjRA+G/GF4DzEc4CkJMxg5uA7OS6IHwc4RbwFyEIyDIcAVs/0e4BcxFOAKCCLekCBwMrwHmIRwFGSZjB7eM8UnergIAN2BStrkIRwDgp+gJBMxBOAKCxKr8+7xdBbgZPYGAOQhHQJCIvTbS21UAAL9AOAoynMYfnBh+CVxMzAbcj3AEBAGGXwDAdYQjAAAAB4QjIMAxpBb4mGwPuBfhCAAAwAHhCAhwzDcKfJyJCLgX4QgAAMAB4SiIbHql0NtVAAC4CbcQMY9HwtGqVavUtWtXRUREKCUlRfn5+Y2W37Jli3r37q2IiAj169dPb7/9ttPrhmFo/vz56ty5s9q2bavU1FR9/vnnZjYhIBwt+srbVYCHMRk7eDApG3Af08PRpk2blJ2drQULFmjv3r0aMGCA0tLSVFJSUm/5f/zjHxo/frzuvfde7du3T2PGjNGYMWP06aef2ss8+eSTWrlypVavXq3du3frmmuuUVpamr755huzmwMAAAKc6eHoqaee0pQpU5SVlaW+fftq9erVioyM1Nq1a+st/7vf/U7p6emaM2eO+vTpo8WLF2vgwIF69tlnJX3ba7RixQrNmzdPd9xxh/r3768//vGPOnXqlF5//fV6t1lVVSWbzeb0AAAAqI+p4ejixYsqKChQamrqd28YGqrU1FTl5eXVu05eXp5TeUlKS0uzlz969KisVqtTmejoaKWkpDS4zdzcXEVHR9sfiYmJLW0aAAAIUKaGo7Nnz6q6ulrx8fFOy+Pj42W1Wutdx2q1Nlq+9r9N2WZOTo7Ky8vtj+PHjzerPQAAIPC18nYFPCE8PFzh4eHergYAAPADpvYcderUSWFhYSouLnZaXlxcLIvFUu86Foul0fK1/23KNgEAAFxlajhq06aNBg0apB07dtiX1dTUaMeOHRo6dGi96wwdOtSpvCRt377dXr5bt26yWCxOZWw2m3bv3t3gNvGtkbf38nYV4GF/mbfT21WAhzyY/Ly3qwAPs/SP83YVApbpw2rZ2dmaNGmSBg8erOTkZK1YsUIVFRXKysqSJE2cOFFdunRRbm6uJOnXv/61hg8frv/5n//R6NGjtXHjRn388cd64YUXJEkhISGaOXOmlixZohtvvFHdunXTY489poSEBI0ZM8bs5vi1IQMTvF0FAICbrNw6wdtVCFimh6OMjAydOXNG8+fPl9VqVVJSkrZt22afUH3s2DGFhn7XgTVs2DC9/PLLmjdvnh599FHdeOONev3113XTTTfZyzz88MOqqKjQ1KlTVVZWpltuuUXbtm1TRESE2c0BAAABLsQwDMPblfA0m82m6OhonT5ZoqioKG9Xx6O43HzwGbtkBDefDXBnzlUyrBaENhfN8nYVPM5ms6lzlziVl5eb+v3NvdUAAAAcEI6AAMek7MBHrxHgXoQjAAAAB4QjIAhseqXQ21UAAL9BOAoyA7OSvF0FeAFDa4GLkywA9yMcAUFiz95T3q4CAPgFwhEQJJbdvcnbVYCbLV1CjyBgBsIRAPipvesKvV0FICARjoLM3HkjvF0FeBETs4HAEIwXgPQkwhEQRJiYHTiYiA2Yh3AEBJkZd27wdhXQQvQAAuYiHAUhTucPbtb9JZy55ufoAQTMRTgCghBnrvkvhtMA8xGOghCTsiHxJeuPGE6DxGRsTyAcAUGM4TX/wnAa4BmEIyCIMbzmP+jpAzyHcBSkmJSNWnzp+j72EeBZhCMAfPn6MPYN4HmEoyDFpGxciS9h38M+wZWYjO0ZhCMAdnwZ+w5uKgt4D+EoiDHvCPXhCtret2fvKW4qC3hRK29XAIBvse4v0bgeT9N97yX03gHeR89REGPeERrDl7Tn0WuHxvCDxXMIR0GOoTU0hoDkOeN6PC3r/hJvVwOACEcArmJcj6e5bYXJCKGAbwkxDMPwdiU8zWazKTo6WqdPligqKsrb1fE6DsxwFd367jXjzg30FsElfPa+ZbPZ1LlLnMrLy039/qbnCIDLCNLuwzAa4Ls4Ww0amJXEacNwWW1A4pds8xAwAd9HzxE4aw3NMq7H0zpzrtLb1fAbZ85VEozQLPwQ8Tx6jiBJsvSPo4sfTfZg8vOSOHhfDaEI8C+EI0iSEgYlEI7QbLVf/mOXjFDG+CTvVsZHnDlXaQ+PAPwLZ6txtpodv27hLqvy71PstZHerobX8FmCu9Ar68xTZ6vRcwQ7htbgLo49JsFycN/0SqH+Mo+bxQKBgJ4jeo6c8IsXZgnEITeGzmCmYO+BrQ89R/AKeo9glr/M22nvWZmzJUNDBiZ4uUbNs2fvKf1p8U4+JzAdwch7TD2Vv7S0VBMmTFBUVJRiYmJ077336sKFC42Wnz59unr16qW2bdvq+uuv14wZM1ReXu5ULiQkpM5j48aNZjYlaKzcOsHbVUAQWHb3Jo3r8bT94es2vVJor+uyuzcRjGC6sUu4xIo3mdpzNGHCBJ0+fVrbt2/XpUuXlJWVpalTp+rll1+ut/ypU6d06tQpLV++XH379tWXX36p+++/X6dOndKrr77qVHbdunVKT0+3P4+JiTGzKQBM5BiQLP3jvB7Sua0HvC3QhqD9jWlzjg4ePKi+fftqz549Gjx4sCRp27Ztuv3223XixAklJLjWpb5lyxbdc889qqioUKtW32a5kJAQbd26VWPGjGlW3Zhz1LilS3ZyxWz4nNpf0u760qi9me5Hmw8QhOBzguVEhqby1Jwj08LR2rVrNXv2bH311Vf2ZZcvX1ZERIS2bNmiO++806Xt/OEPf1BOTo7OnDljXxYSEqKEhARVVVWpe/fuuv/++5WVlaWQkJB6t1FVVaWqqir7c5vNpsTERMJRI/xhqAMAAhETsRvm9zeetVqtiouLc1rWqlUrdezYUVar1aVtnD17VosXL9bUqVOdli9atEibN2/W9u3bNXbsWP3qV7/SM8880+B2cnNzFR0dbX8kJiY2vUFBxtI/7uqFAABuRzDyviaHo7lz59Y7IdrxcejQoRZXzGazafTo0erbt69+85vfOL322GOP6T/+4z90880365FHHtHDDz+sZcuWNbitnJwclZeX2x/Hjx9vcf0CnbfnfABAMFqVf5+3qwA1Y0L27NmzNXny5EbLdO/eXRaLRSUlzuP4ly9fVmlpqSwWS6Prnz9/Xunp6Wrfvr22bt2q1q1bN1o+JSVFixcvVlVVlcLDw+u8Hh4eXu9yNI7T+gHAs+g18g1NDkexsbGKjY29armhQ4eqrKxMBQUFGjRokCTpgw8+UE1NjVJSUhpcz2azKS0tTeHh4XrzzTcVERFx1fcqLCxUhw4dCEButnLrBOYeAYCH0GvkO0ybc9SnTx+lp6drypQpys/P10cffaRp06YpMzPTfqbayZMn1bt3b+Xn50v6NhiNGjVKFRUVWrNmjWw2m6xWq6xWq6qrqyVJb731lv7whz/o008/1ZEjR/Tcc8/pt7/9raZPn25WU4Ia19oAAM+g18h3mHqdow0bNmjatGkaOXKkQkNDNXbsWK1cudL++qVLl3T48GFVVlZKkvbu3avdu3dLknr27Om0raNHj6pr165q3bq1Vq1apVmzZskwDPXs2VNPPfWUpkyZYmZTglbG+CTuFwUAJqPXyLdwbzVO5b8q7h8FAOYJxPsOmsXvT+VH4KCrFwDMQzDyPYQjuISrtQKA+zGc5psIRwAAeAk9876JcASX0XsEAO5Dr5HvIhyhSQZmJXm7CgAQEOg18l2EIzTJ3Hlc9wgAWoqeeN9GOEKTzdmS4e0qAIDfogfe9xGO0GRDBiZ4uwoA4Lfogfd9hCM0C13CANB0TML2D4QjNBsfcgBw3cCsJCZh+wnCEZqNDzkAuI7hNP9BOEKLMLwGAFdHT7t/IRyhxQhIANCwOVsy6Gn3M4QjuMXYJXQXA8CVLP3jOMPXDxGO4BbcVRoA6lq5dYK3q4BmIBzBbRheA4DvcEz0X4QjuBUHAwDgTgL+jnAEt+PS+ACCHfOM/BvhCG7HtTwABDN60P0f4Qim4OAAIBhx7AsMhCOYhoMEgGDCMS9wEI5gKg4WAIIBE7ADC+EIpuOy+QAC2cCsJCZgBxjCEUwXe20kV9AGELA4CSXwEI7gERnjk2TpH+ftagCAWzF1IDARjuAxXEYfQCAhGAUuwhE8anPRLIbYAPg9glFgIxzB4xhiA+DPCEaBj3AEr2CIDYA/IhgFB8IRvIaDDAB/wjEreBCO4FWbi2Zxo1oAPo9gFFwIR/A6rhECwJcRjIIP4Qg+gYMPAF/EsSk4EY7gMxhiA+BLCEbBi3AEn8IQGwBfQDAKbqaGo9LSUk2YMEFRUVGKiYnRvffeqwsXLjS6zq233qqQkBCnx/333+9U5tixYxo9erQiIyMVFxenOXPm6PLly2Y2BR60uWgWN6sF4BUDs5IIRjA3HE2YMEEHDhzQ9u3b9de//lUffvihpk6detX1pkyZotOnT9sfTz75pP216upqjR49WhcvXtQ//vEPvfTSS1q/fr3mz59vZlPgYbHXRhKQAHjUwKwkeq8hSQoxDMMwY8MHDx5U3759tWfPHg0ePFiStG3bNt1+++06ceKEEhIS6l3v1ltvVVJSklasWFHv6++8845+8pOf6NSpU4qPj5ckrV69Wo888ojOnDmjNm3aXLVuNptN0dHROn2yRFFRUc1rIDxmXI+nvV0FAAFuzpYMDRlY//cSfIfNZlPnLnEqLy839fvbtJ6jvLw8xcTE2IORJKWmpio0NFS7d+9udN0NGzaoU6dOuummm5STk6PKykqn7fbr188ejCQpLS1NNptNBw4cqHd7VVVVstlsTg/4D7q4AZhpc9EsghGcmBaOrFar4uKc75/VqlUrdezYUVartcH1fv7zn+vPf/6zdu7cqZycHP3pT3/SPffc47Rdx2Akyf68oe3m5uYqOjra/khMTGxus+AlBCQAZuDYgvo0ORzNnTu3zoTpKx+HDh1qdoWmTp2qtLQ09evXTxMmTNAf//hHbd26VUVFRc3eZk5OjsrLy+2P48ePN3tb8J7NRbO4YS0AtyEYoSGtmrrC7NmzNXny5EbLdO/eXRaLRSUlJU7LL1++rNLSUlksFpffLyUlRZJ05MgR9ejRQxaLRfn5+U5liouLJanB7YaHhys8PNzl94TvWrl1gs6cq9SDyc97uyoA/NTYJSOUMT7J29WAD2tyOIqNjVVsbOxVyw0dOlRlZWUqKCjQoEGDJEkffPCBampq7IHHFYWFhZKkzp0727f7+OOPq6SkxD5st337dkVFRalv375NbA38Uey1kdpcNIuJ2gCajN4iuMK0OUd9+vRRenq6pkyZovz8fH300UeaNm2aMjMz7WeqnTx5Ur1797b3BBUVFWnx4sUqKCjQF198oTfffFMTJ07Uj370I/Xv31+SNGrUKPXt21e/+MUv9K9//Uvvvvuu5s2bpwcffJDeoSDDMBuApiAYwVWmXudow4YN6t27t0aOHKnbb79dt9xyi1544QX765cuXdLhw4ftZ6O1adNG77//vkaNGqXevXtr9uzZGjt2rN566y37OmFhYfrrX/+qsLAwDR06VPfcc48mTpyoRYsWmdkU+KiVWydwwAPQqLFLRnCcQJOYdp0jX8Z1jgITw2wArkQoCix+f50jwNMYZgPgiGCE5mryhGzAl63cOkESvUhAMONsNLQUPUcISPxiBILT5qJZBCO0GOEIAWtz0SwNzErydjUAeAg/iuAuTMhmQnZQYJgNCFyEouDBhGzAjTYXzdKcLRnergYANxqYlUQwgimYkI2gMWRgAlfWBgIEoQhmIhwh6NQeVAlJgP+ZsyVDQwYmeLsaCHAMqyFocV0kwH9Y+sdpc9EsghE8gp4jBDWuiwT4PobQ4GmEI0AMtQG+iCE0eAvhCHCwuWiWzpyr1IPJz3u7KkDQGpiVpLnzRni7GghihCPgCrHXRmpz0SxteqVQf5m309vVAYIKQ2jwBUzIBhqQMZ5rqACesir/Pj5v8Bn0HAFXwXwkwDwEIvgiwhHgIkIS4D6EIvgywhHQRIQkoPlW5d+n2GsjvV0NoFGEI6CZOLMNcN3YJSOUMT7J29UAXEI4Alqg9sw2SZpx5wZZ95d4uUaAb2H4DP6IcAS4Se3VtglJAKEI/o1wBLhZbUjas/eUlt29ycu1ATyHoTMECsIRYJIhAxOYvI2gQC8RAg3hCPAA5iUhEBGKEKgIR4AH1Q65cZYb/BVDZwgGhCPACxzPclu6ZKf2riv0boWAq6CXCMEkxDAMw9uV8DSbzabo6GidPlmiqKgob1cHsGNuEnwJF2yEr7HZbOrcJU7l5eWmfn/TcwT4kNpf5wy7wVsIRADhCPBJjsNuXBIAZmMeEeCMcAT4OMdLAkgMvcE96CECGkY4AvwMQQnNxaRqwDWEI8CPOX7ZMfyGK83ZkqEhAxO8XQ3A7xCOgABx5fAblwgIPgOzknTvAykMlwEtxKn8nMqPIMEQXGBiqAzBhFP5AbiV45fomXOVWvPcbnqW/BATqQHz0XNEzxEgSdr0SqEk6S/zdnq3IpD07en1kjjFHnDgqZ4jU8NRaWmppk+frrfeekuhoaEaO3asfve736ldu3b1lv/iiy/UrVu3el/bvHmz7r777m8rHRJS5/VXXnlFmZmZLtWLcAS4Zs/eU/rT4p3cLNdklv5xShiUoLnzRni7KoBPC4hwdNttt+n06dN6/vnndenSJWVlZWnIkCF6+eWX6y1fXV2tM2fOOC174YUXtGzZMp0+fdoeqkJCQrRu3Tqlp6fby8XExCgiIsKlehGOgOY7c65SkriCdzOtyr9PkhgaA5rB7+ccHTx4UNu2bdOePXs0ePBgSdIzzzyj22+/XcuXL1dCQt3TS8PCwmSxWJyWbd26VePGjavT2xQTE1OnLADz1X6pNzQRONiH52qHw7r3iuM0esBPmdZztHbtWs2ePVtfffWVfdnly5cVERGhLVu26M4777zqNgoKCjR48GB99NFHGjZs2HeVDglRQkKCqqqq1L17d91///3Kysqqd7hNkqqqqlRVVWV/brPZlJiYSM8R4CUz7tzg9Nwfhu0s/ePs/79y6wQv1gQIXn7fc2S1WhUXF+e0rFWrVurYsaOsVqtL21izZo369OnjFIwkadGiRfrxj3+syMhIvffee/rVr36lCxcuaMaMGfVuJzc3VwsXLmxeQwC4nTvDhauXKOD+YQBc1eRwNHfuXD3xxBONljl48GCzK1Tr66+/1ssvv6zHHnuszmuOy26++WZVVFRo2bJlDYajnJwcZWdn25/X9hwB8H9c5weAuzU5HM2ePVuTJ09utEz37t1lsVhUUuLcVX758mWVlpa6NFfo1VdfVWVlpSZOnHjVsikpKVq8eLGqqqoUHh5e5/Xw8PB6lwMAAFypyeEoNjZWsbGxVy03dOhQlZWVqaCgQIMGDZIkffDBB6qpqVFKSspV11+zZo1+9rOfufRehYWF6tChAwEIAAC0mGlzjvr06aP09HRNmTJFq1ev1qVLlzRt2jRlZmbaz1Q7efKkRo4cqT/+8Y9KTk62r3vkyBF9+OGHevvtt+ts96233lJxcbF+8IMfKCIiQtu3b9dvf/tbPfTQQ2Y1BQAABBFTbx+yYcMGTZs2TSNHjrRfBHLlypX21y9duqTDhw+rsrLSab21a9fquuuu06hRo+pss3Xr1lq1apVmzZolwzDUs2dPPfXUU5oyZYqZTQEAAEGC24dwKj8AAH7BU6fyh5q2ZQAAAD9EOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBAOAIAAHBgWjh6/PHHNWzYMEVGRiomJsaldQzD0Pz589W5c2e1bdtWqamp+vzzz53KlJaWasKECYqKilJMTIzuvfdeXbhwwYQWAACAYGRaOLp48aLuvvtuPfDAAy6v8+STT2rlypVavXq1du/erWuuuUZpaWn65ptv7GUmTJigAwcOaPv27frrX/+qDz/8UFOnTjWjCQAAIAiFGIZhmPkG69ev18yZM1VWVtZoOcMwlJCQoNmzZ+uhhx6SJJWXlys+Pl7r169XZmamDh48qL59+2rPnj0aPHiwJGnbtm26/fbbdeLECSUkJNS77aqqKlVVVdmfl5eX6/rrr9f/HSpS+/bt3dNQAABgqvPnz+t7vXuorKxM0dHRpr1PK9O23ERHjx6V1WpVamqqfVl0dLRSUlKUl5enzMxM5eXlKSYmxh6MJCk1NVWhoaHavXu37rzzznq3nZubq4ULF9ZZ/r3ePdzfEAAAYKpz584FRziyWq2SpPj4eKfl8fHx9tesVqvi4uKcXm/VqpU6duxoL1OfnJwcZWdn25+XlZXphhtu0LFjx0z94/oam82mxMREHT9+XFFRUd6ujsfQbtodDGg37Q4GtSM/HTt2NPV9mhSO5s6dqyeeeKLRMgcPHlTv3r1bVCl3Cw8PV3h4eJ3l0dHRQfWPqlZUVBTtDiK0O7jQ7uASrO0ODTX3ZPsmhaPZs2dr8uTJjZbp3r17sypisVgkScXFxercubN9eXFxsZKSkuxlSkpKnNa7fPmySktL7esDAAC0RJPCUWxsrGJjY02pSLdu3WSxWLRjxw57GLLZbNq9e7f9jLehQ4eqrKxMBQUFGjRokCTpgw8+UE1NjVJSUkypFwAACC6m9UsdO3ZMhYWFOnbsmKqrq1VYWKjCwkKnaxL17t1bW7dulSSFhIRo5syZWrJkid5880198sknmjhxohISEjRmzBhJUp8+fZSenq4pU6YoPz9fH330kaZNm6bMzMwGz1SrT3h4uBYsWFDvUFsgo920OxjQbtodDGi3ue027VT+yZMn66WXXqqzfOfOnbr11lu/ffOQEK1bt84+VGcYhhYsWKAXXnhBZWVluuWWW/T73/9e3/ve9+zrl5aWatq0aXrrrbcUGhqqsWPHauXKlWrXrp0ZzQAAAEHG9OscAQAA+BPurQYAAOCAcAQAAOCAcAQAAOCAcAQAAOAgIMPR448/rmHDhikyMlIxMTEurWMYhubPn6/OnTurbdu2Sk1N1eeff+5UprS0VBMmTFBUVJRiYmJ07733Ol2awNuaWr8vvvhCISEh9T62bNliL1ff6xs3bvREk1zSnP1y66231mnT/fff71Tm2LFjGj16tCIjIxUXF6c5c+bo8uXLZjalSZra7tLSUk2fPl29evVS27Ztdf3112vGjBkqLy93KueL+3vVqlXq2rWrIiIilJKSovz8/EbLb9myRb1791ZERIT69eunt99+2+l1Vz7vvqAp7X7xxRf1wx/+UB06dFCHDh2Umppap/zkyZPr7Nv09HSzm9FkTWn3+vXr67QpIiLCqUwg7u/6jmEhISEaPXq0vYyv7+8PP/xQP/3pT5WQkKCQkBC9/vrrV11n165dGjhwoMLDw9WzZ0+tX7++TpmmHi/qZQSg+fPnG0899ZSRnZ1tREdHu7TO0qVLjejoaOP11183/vWvfxk/+9nPjG7duhlff/21vUx6eroxYMAA45///Kfxv//7v0bPnj2N8ePHm9SKpmtq/S5fvmycPn3a6bFw4UKjXbt2xvnz5+3lJBnr1q1zKuf4d/G25uyX4cOHG1OmTHFqU3l5uf31y5cvGzfddJORmppq7Nu3z3j77beNTp06GTk5OWY3x2VNbfcnn3xi3HXXXcabb75pHDlyxNixY4dx4403GmPHjnUq52v7e+PGjUabNm2MtWvXGgcOHDCmTJlixMTEGMXFxfWW/+ijj4ywsDDjySefND777DNj3rx5RuvWrY1PPvnEXsaVz7u3NbXdP//5z41Vq1YZ+/btMw4ePGhMnjzZiI6ONk6cOGEvM2nSJCM9Pd1p35aWlnqqSS5parvXrVtnREVFObXJarU6lQnE/X3u3DmnNn/66adGWFiYsW7dOnsZX9/fb7/9tvHf//3fxmuvvWZIMrZu3dpo+X//+99GZGSkkZ2dbXz22WfGM888Y4SFhRnbtm2zl2nq37EhARmOaq1bt86lcFRTU2NYLBZj2bJl9mVlZWVGeHi48corrxiGYRifffaZIcnYs2ePvcw777xjhISEGCdPnnR73ZvKXfVLSkoy/uu//stpmSv/aL2lue0ePny48etf/7rB199++20jNDTU6SD73HPPGVFRUUZVVZVb6t4S7trfmzdvNtq0aWNcunTJvszX9ndycrLx4IMP2p9XV1cbCQkJRm5ubr3lx40bZ4wePdppWUpKinHfffcZhuHa590XNLXdV7p8+bLRvn1746WXXrIvmzRpknHH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","text/plain":["
"]},"metadata":{},"output_type":"display_data"}],"source":["plt.contourf(linsp, linsp, Z, cmap=\"Purples\")"]},{"cell_type":"markdown","metadata":{},"source":["Now let's generate some data for our quantum circuit to learn from"]},{"cell_type":"code","execution_count":6,"metadata":{},"outputs":[],"source":["n_data = 1000\n","x = random.uniform(random.PRNGKey(0), shape=(n_data, 2), minval=-1, maxval=1)\n","y = classification_function(x[:, 0], x[:, 1])"]},{"cell_type":"code","execution_count":7,"metadata":{},"outputs":[{"data":{"text/plain":[""]},"execution_count":7,"metadata":{},"output_type":"execute_result"},{"data":{"image/png":"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mzZBVVXs378fzzzzzIL7f+c738GuXbugqir27NmDJ554ouH1Zu7J1WYpbfz617+O3//930cmk0Emk8GBAwdm7X/XXXfNula33npru5sxL0tp32OPPTbr2FVVbdhnvV/DufoUjuNw22231fdZS9fwf/7nf/BHf/RHGBwcBMdx+MEPfrDoe5566im86lWvgqIo2LZtGx577LFZ+yz13l4yhNE0999/P/nSl75EPvShD5FUKtXUex544AGSSqXID37wA/K73/2O/PEf/zHZvHkzsW27vs+tt95Krr32WvLrX/+a/O///i/Ztm0bedvb3tamVszPUo8jCAIyPj7e8O9Tn/oUicfjxDTN+n4AyKOPPtqw36XtX02Wc65vuukmcvfddzccf6lUqr8eBAHZvXs3OXDgADl06BB54oknSE9PD7nvvvva3Zw5WWobn3/+efLmN7+Z/OhHPyInTpwgP//5z8n27dvJW97ylob9rsR1fPzxx4ksy+SRRx4hL774Irn77rtJOp0muVxuzv1/9atfEUEQyBe+8AVy5MgR8vGPf5xIkkSef/75+j7N3JOryVLb+Pa3v5189atfJYcOHSJHjx4ld911F0mlUuTChQv1fe68805y6623NlyrfD6/Wk1qYKnte/TRR0kymWw49omJiYZ91vs1nJmZaWjfCy+8QARBII8++mh9n7V0DZ944gnyD//wD+R73/seAUC+//3vL7j/qVOniK7r5EMf+hA5cuQI+cpXvkIEQSBPPvlkfZ+lnrPlwAKcZfDoo482FeBEUUT6+/vJF7/4xfq2YrFIFEUh//Ef/0EIIeTIkSMEAPnNb35T3+fHP/4x4TiOjI6OtvzY56NVx3HdddeRP//zP2/Y1swNsRost4033XQT+Zu/+Zt5X3/iiScIz/MNnfC//uu/kmQySVzXbcmxN0urruO3v/1tIssy8X2/vu1KXMfrr7+evO9976v/HYYhGRwcJJ///Ofn3P/P/uzPyG233dawbf/+/eQv/uIvCCHN3ZOrzVLbeDlBEJBEIkG+8Y1v1Lfdeeed5Pbbb2/1oS6LpbZvsf61E6/hP/3TP5FEIkHK5XJ921q6hpfSTD/w0Y9+lFxzzTUN29761reSW265pf73Ss9ZM7AlqjZy+vRpTExM4MCBA/VtqVQK+/fvx8GDBwEABw8eRDqdxmte85r6PgcOHADP83j66adX7VhbcRzPPvssDh8+jPe85z2zXnvf+96Hnp4eXH/99XjkkUeaKnXfalbSxm9+85vo6enB7t27cd9998GyrIbP3bNnD/r6+urbbrnlFhiGgRdffLH1DVmAVv2eSqUSkskkRLGxXN1qXkfP8/Dss8823D88z+PAgQP1++dyDh482LA/QK9Fbf9m7snVZDltvBzLsuD7Prq6uhq2P/XUU8hms9i5cyfe+973YmZmpqXH3gzLbV+5XMbGjRsxMjKC22+/veE+6sRr+PDDD+OOO+5ALBZr2L4WruFyWOw+bMU5a4ZXZLHN1WJiYgIAGh58tb9rr01MTCCbzTa8Looiurq66vusBq04jocffhhXXXUVbrzxxobtn/70p/EHf/AH0HUdP/3pT/FXf/VXKJfL+MAHPtCy42+G5bbx7W9/OzZu3IjBwUE899xz+NjHPoZjx47he9/7Xv1z57rGtddWk1Zcx+npaXzmM5/BPffc07B9ta/j9PQ0wjCc89y+9NJLc75nvmtx6f1W2zbfPqvJctp4OR/72McwODjY8LC49dZb8eY3vxmbN2/GyZMn8fd///d44xvfiIMHD0IQhJa2YSGW076dO3fikUcewd69e1EqlfDQQw/hxhtvxIsvvojh4eGOu4bPPPMMXnjhBTz88MMN29fKNVwO892HhmHAtm0UCoUV/+6b4RUf4Nx777148MEHF9zn6NGj2LVr1yodUWtptn0rxbZtfOtb38InPvGJWa9dum3fvn2oVCr44he/2LIHY7vbeOmDfs+ePRgYGMDNN9+MkydPYuvWrcv+3KWwWtfRMAzcdtttuPrqq/GP//iPDa+1+zoyls4DDzyAxx9/HE899VSDEPeOO+6o/3/Pnj3Yu3cvtm7diqeeego333zzlTjUprnhhhtwww031P++8cYbcdVVV+FrX/saPvOZz1zBI2sPDz/8MPbs2YPrr7++Yft6voZrhVd8gPPhD38Yd91114L7bNmyZVmf3d/fDwDI5XIYGBiob8/lcrjuuuvq+0xOTja8LwgC5PP5+vtXQrPtW+lxfPe734VlWXjXu9616L779+/HZz7zGbiu25I6JavVxhr79+8HAJw4cQJbt25Ff3//LPV/LpcDgJZcQ2B12miaJm699VYkEgl8//vfhyRJC+7f6ut4OT09PRAEoX4ua+RyuXnb0t/fv+D+zdyTq8ly2ljjoYcewgMPPICf/exn2Lt374L7btmyBT09PThx4sSqPhxX0r4akiRh3759OHHiBIDOuoaVSgWPP/44Pv3pTy/6PVfqGi6H+e7DZDIJTdMgCMKKfxdN0TI1zyuIpYqMH3roofq2Uqk0p8j4t7/9bX2fn/zkJ1dMZLzc47jppptmZd3Mx2c/+1mSyWSWfazLpVXn+pe//CUBQH73u98RQi6KjC9V/3/ta18jyWSSOI7TugY0wXLbWCqVyO/93u+Rm266iVQqlaa+azWu4/XXX0/e//731/8Ow5AMDQ0tKDL+wz/8w4ZtN9xwwyyR8UL35Gqz1DYSQsiDDz5IkskkOXjwYFPfcf78ecJxHPnhD3+44uNdKstp36UEQUB27txJ/vZv/5YQ0jnXkBD6LFEUhUxPTy/6HVfyGl4KmhQZ7969u2Hb2972tlki45X8Lpo61pZ90iuAs2fPkkOHDtVToQ8dOkQOHTrUkBK9c+dO8r3vfa/+9wMPPEDS6TT54Q9/SJ577jly++23z5kmvm/fPvL000+TX/7yl2T79u1XLE18oeO4cOEC2blzJ3n66acb3nf8+HHCcRz58Y9/POszf/SjH5Gvf/3r5PnnnyfHjx8n//Iv/0J0XSf3339/29szF0tt44kTJ8inP/1p8tvf/pacPn2a/PCHPyRbtmwhr3vd6+rvqaWJv+ENbyCHDx8mTz75JOnt7b2iaeJLaWOpVCL79+8ne/bsISdOnGhISw2CgBBy5a7j448/ThRFIY899hg5cuQIueeee0g6na5nrL3zne8k9957b33/X/3qV0QURfLQQw+Ro0ePkk9+8pNzpokvdk+uJktt4wMPPEBkWSbf/e53G65VrR8yTZP83d/9HTl48CA5ffo0+dnPfkZe9apXke3bt696wL2c9n3qU58iP/nJT8jJkyfJs88+S+644w6iqip58cUX6/us92tY47WvfS1561vfOmv7WruGpmnWn3cAyJe+9CVy6NAhcvbsWUIIIffeey955zvfWd+/lib+kY98hBw9epR89atfnTNNfKFz1gpYgLME7rzzTgJg1r9f/OIX9X1Q9QqpEUUR+cQnPkH6+vqIoijk5ptvJseOHWv43JmZGfK2t72NxONxkkwmybvf/e6GoGm1WOw4Tp8+Pau9hBBy3333kZGRERKG4azP/PGPf0yuu+46Eo/HSSwWI9deey35t3/7tzn3XQ2W2sZz586R173udaSrq4soikK2bdtGPvKRjzT44BBCyJkzZ8gb3/hGomka6enpIR/+8IcbUqxXk6W28Re/+MWcv2sA5PTp04SQK3sdv/KVr5ANGzYQWZbJ9ddfT37961/XX7vpppvInXfe2bD/t7/9bbJjxw4iyzK55ppryH/+5382vN7MPbnaLKWNGzdunPNaffKTnySEEGJZFnnDG95Aent7iSRJZOPGjeTuu+9u6YNjqSylfR/84Afr+/b19ZE3velN5P/+7/8aPm+9X0NCCHnppZcIAPLTn/501mettWs4Xx9Ra9Odd95Jbrrpplnvue6664gsy2TLli0Nz8UaC52zVsARcgXydRkMBoPBYDDaCPPBYTAYDAaD0XGwAIfBYDAYDEbHwQIcBoPBYDAYHQcLcBgMBoPBYHQcLMBhMBgMBoPRcbAAh8FgMBgMRsfBAhwGg8FgMBgdBwtwGAwGg8FgdBwswGEwGAwGg9FxsACHwWAwGAxGx8ECHAaDwWAwGB0HC3AYDAaDwWB0HP8f4XHWfbcCd6UAAAAASUVORK5CYII=","text/plain":["
"]},"metadata":{},"output_type":"display_data"}],"source":["plt.scatter(x[:, 0], x[:, 1], alpha=jnp.where(y, 1, 0.2), s=10)"]},{"cell_type":"markdown","metadata":{},"source":["## Quantum circuit time\n","We'll use a variant of data re-uploading [Pérez-Salinas et al](https://doi.org/10.22331/q-2020-02-06-226) to encode the input data, alongside some variational parameters within a quantum circuit classifier"]},{"cell_type":"code","execution_count":8,"metadata":{},"outputs":[],"source":["n_qubits = 3\n","depth = 5"]},{"cell_type":"code","execution_count":9,"metadata":{},"outputs":[],"source":["c = Circuit(n_qubits)"]},{"cell_type":"code","execution_count":10,"metadata":{},"outputs":[],"source":["for layer in range(depth):\n"," for qi in range(n_qubits):\n"," c.Rz(0.0, qi)\n"," c.Ry(0.0, qi)\n"," c.Rz(0.0, qi)\n"," if layer < (depth - 1):\n"," for qi in range(layer, layer + n_qubits - 1, 2):\n"," c.CZ(qi % n_qubits, (qi + 1) % n_qubits)\n"," c.add_barrier(range(n_qubits))"]},{"cell_type":"code","execution_count":11,"metadata":{},"outputs":[{"data":{"text/html":["\n","\n","\n","\n","\n","\n","
\n"," \n","
\n","\n"],"text/plain":[""]},"metadata":{},"output_type":"display_data"}],"source":["render_circuit_jupyter(c)"]},{"cell_type":"markdown","metadata":{},"source":["We can use `pytket-qujax` to generate our angles-to-statetensor function."]},{"cell_type":"code","execution_count":12,"metadata":{},"outputs":[],"source":["angles_to_st = tk_to_qujax(c)"]},{"cell_type":"markdown","metadata":{},"source":["We'll parameterise each angle as\n","\n","$$\n","\\begin{equation}\n","\\theta_k = b_k + w_k \\, x_k\n","\\end{equation}\n","$$\n","\n","where $b_k, w_k$ are variational parameters to be learnt and $x_k = x_0$ if $k$ even, $x_k = x_1$ if $k$ odd for a single bivariate input point $(x_0, x_1)$."]},{"cell_type":"code","execution_count":13,"metadata":{},"outputs":[],"source":["n_angles = 3 * n_qubits * depth\n","n_params = 2 * n_angles"]},{"cell_type":"code","execution_count":14,"metadata":{},"outputs":[],"source":["def param_and_x_to_angles(param, x_single):\n"," biases = param[:n_angles]\n"," weights = param[n_angles:]\n"," weights_times_data = jnp.where(\n"," jnp.arange(n_angles) % 2 == 0, weights * x_single[0], weights * x_single[1]\n"," )\n"," angles = biases + weights_times_data\n"," return angles"]},{"cell_type":"code","execution_count":15,"metadata":{},"outputs":[],"source":["param_and_x_to_st = lambda param, x_single: angles_to_st(\n"," param_and_x_to_angles(param, x_single)\n",")"]},{"cell_type":"markdown","metadata":{},"source":["We'll measure the first qubit only (if its 1 we label _donut_, if its 0 we label _not donut_)"]},{"cell_type":"code","execution_count":16,"metadata":{},"outputs":[],"source":["def param_and_x_to_probability(param, x_single):\n"," st = param_and_x_to_st(param, x_single)\n"," all_probs = jnp.square(jnp.abs(st))\n"," first_qubit_probs = jnp.sum(all_probs, axis=range(1, n_qubits))\n"," return first_qubit_probs[1]"]},{"cell_type":"markdown","metadata":{},"source":["For binary classification, the likelihood for our full data set $(x_{1:N}, y_{1:N})$ is\n","\n","$$\n","\\begin{equation}\n","p(y_{1:N} \\mid b, w, x_{1:N}) = \\prod_{i=1}^N p(y_i \\mid b, w, x_i) = \\prod_{i=1}^N (1 - q_{(b,w)}(x_i))^{I[y_i = 0]}q_{(b,w)}(x_i)^{I[y_i = 1]},\n","\\end{equation}\n","$$\n","\n","where $q_{(b, w)}(x)$ is the probability the quantum circuit classifies input $x$ as donut given variational parameter vectors $(b, w)$. This gives log-likelihood\n","\n","$$\n","\\begin{equation}\n"," \\log p(y_{1:N} \\mid b, w, x_{1:N}) = \\sum_{i=1}^N I[y_i = 0] \\log(1 - q_{(b,w)}(x_i)) + I[y_i = 1] \\log q_{(b,w)}(x_i),\n","\\end{equation}\n","$$\n","\n","which we would like to maximise.\n","\n","Unfortunately, the log-likelihood **cannot** be approximated unbiasedly using shots, that is we can approximate $q_{(b,w)}(x_i)$ unbiasedly but not $\\log(q_{(b,w)}(x_i))$.\n","Note that in qujax simulations we can use the statetensor to calculate this exactly, but it is still good to keep in mind loss functions that can also be used with shots from a quantum device.\n","\n","Instead we can minimise an expected distance between shots and data\n","\n","$$\n","\\begin{equation}\n","C(b, w, x, y) = E_{p(y' \\mid q_{(b, w)}(x))}[\\ell(y', y)] = (1 - q_{(b, w)}(x)) \\ell(0, y) + q_{(b, w)}(x)\\ell(1, y),\n","\\end{equation}\n","$$\n","\n","where $y'$ is a shot, $y$ is a data label and $\\ell$ is some distance between bitstrings - here we simply set $\\ell(0, 0) = \\ell(1, 1) = 0$ and $\\ell(0, 1) = \\ell(1, 0) = 1$ (which coincides with the Hamming distance for this binary example).\n","\n"," The full batch cost function is\n","\n","$$\n","\\begin{equation}\n"," C(b, w) = \\frac1N \\sum_{i=1}^N C(b,\\, w,\\, x_i,\\, y_i).\n","\\end{equation}\n","$$"]},{"cell_type":"markdown","metadata":{},"source":["Note that to calculate the cost function we need to evaluate the statetensor for every input point $x_i$. If the dataset becomes too large, we can easily minibatch."]},{"cell_type":"code","execution_count":17,"metadata":{},"outputs":[],"source":["def param_to_cost(param):\n"," donut_probs = vmap(param_and_x_to_probability, in_axes=(None, 0))(param, x)\n"," costs = jnp.where(y, 1 - donut_probs, donut_probs)\n"," return costs.mean()"]},{"cell_type":"markdown","metadata":{},"source":["## Ready to descend some gradients?\n","We'll just use vanilla gradient descent here"]},{"cell_type":"code","execution_count":18,"metadata":{},"outputs":[],"source":["param_to_cost_and_grad = jit(value_and_grad(param_to_cost))"]},{"cell_type":"code","execution_count":19,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["999 Cost: 0.25950647\r"]}],"source":["n_iter = 1000\n","stepsize = 1e-1\n","param = random.uniform(random.PRNGKey(1), shape=(n_params,), minval=0, maxval=2)\n","costs = jnp.zeros(n_iter)\n","for i in range(n_iter):\n"," cost, grad = param_to_cost_and_grad(param)\n"," costs = costs.at[i].set(cost)\n"," param = param - stepsize * grad\n"," print(i, \"Cost: \", cost, end=\"\\r\")"]},{"cell_type":"code","execution_count":20,"metadata":{},"outputs":[{"data":{"text/plain":["Text(0, 0.5, 'Cost')"]},"execution_count":20,"metadata":{},"output_type":"execute_result"},{"data":{"image/png":"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z6a/3iKYOBwOPfLII4xC+QF97R/0s3/Qz/5DX/uH2f3MyA8AAAgqjPwAAICgQvgBAABBhfADAACCCuEHAAAEFcKPH82bN0/p6ekKCwtTZmamVq1aZXZJ7Upubq5GjBih6OhoJSYmavz48dq6datPm+rqat1xxx3q1KmToqKidO211x73CpX8/HxdfvnlioiIUGJion7/+9+rvr7en5fSrsyZM0cWi0UzZszwbqOfW8e+fft0ww03qFOnTgoPD9fAgQO1Zs0a737DMDRz5kylpKQoPDxcWVlZ2rZtm885Dh8+rEmTJikmJkaxsbG66aabVF5e7u9LCVhut1sPP/ywunfvrvDwcPXs2VOPPfaYz7uf6Ofm+cc//qErr7xSqampslgsev/99332t1a/btq0SRdccIHCwsKUlpamJ598suXFG/CLRYsWGXa73XjppZeMr7/+2pg+fboRGxtrFBYWml1au5GdnW389a9/NbZs2WJs2LDBuOyyy4yuXbsa5eXl3ja33nqrkZaWZuTl5Rlr1qwxzj33XGP06NHe/fX19caAAQOMrKwsY/369cZHH31kJCQkGA888IAZlxTwVq1aZaSnpxuDBg0y7r77bu92+rnlDh8+bHTr1s248cYbjZUrVxo//PCD8cknnxjbt2/3tpkzZ47hdDqN999/39i4caPxi1/8wujevbtRVVXlbTN27Fhj8ODBxldffWV8+eWXRq9evYzrr7/ejEsKSLNnzzY6depkfPjhh8bOnTuNt99+24iKijKefvppbxv6uXk++ugj46GHHjLeffddQ5Lx3nvv+exvjX4tLS01kpKSjEmTJhlbtmwx3nzzTSM8PNz485//3KLaCT9+MnLkSOOOO+7w/ux2u43U1FQjNzfXxKrat4MHDxqSjGXLlhmGYRglJSVGaGio8fbbb3vbfPvtt4YkY8WKFYZhNPzHarVajYKCAm+bF154wYiJiTFqamr8ewEBrqyszOjdu7fx6aefGj/72c+84Yd+bh333Xefcf755590v8fjMZKTk40//elP3m0lJSWGw+Ew3nzzTcMwDOObb74xJBmrV6/2tvn4448Ni8Vi7Nu3r+2Kb0cuv/xy49e//rXPtmuuucaYNGmSYRj0c2v5cfhprX59/vnnjbi4OJ9/N+677z6jT58+LaqXaS8/qK2t1dq1a5WVleXdZrValZWVpRUrVphYWftWWloqSYqPj5ckrV27VnV1dT793LdvX3Xt2tXbzytWrNDAgQO9r1CRpOzsbLlcLn399dd+rD7w3XHHHbr88st9+lOin1vL//zP/ygjI0P/9m//psTERA0dOlQLFizw7t+5c6cKCgp8+tnpdCozM9Onn2NjY31e8pyVlSWr1aqVK1f672IC2OjRo5WXl6fvv/9ekrRx40YtX75c48aNk0Q/t5XW6tcVK1bowgsvlN1u97bJzs7W1q1bdeTIkWbXZ+q7vYJFUVGR3G63zx8CSUpKStJ3331nUlXtm8fj0YwZM3TeeedpwIABkqSCggLZ7XbFxsb6tE1KSvK+BLegoOCE/zs07UODRYsWad26dVq9evVx++jn1vHDDz/ohRdeUE5Ojh588EGtXr1av/nNb2S32zV16lRvP52oH4/t58TERJ/9ISEhio+Pp58b3X///XK5XOrbt69sNpvcbrdmz56tSZMmSRL93EZaq18LCgrUvXv3487RtC8uLq5Z9RF+0C7dcccd2rJli5YvX252KR3Onj17dPfdd+vTTz9VWFiY2eV0WB6PRxkZGfrjH/8oSRo6dKi2bNmi+fPna+rUqSZX13G89dZbev311/XGG2/onHPO0YYNGzRjxgylpqbSz0GMaS8/SEhIkM1mO+5umMLCQiUnJ5tUVft155136sMPP9QXX3yhs846y7s9OTlZtbW1Kikp8Wl/bD8nJyef8H+Hpn1omNY6ePCghg0bppCQEIWEhGjZsmV65plnFBISoqSkJPq5FaSkpKh///4+2/r166f8/HxJR/vpVP9uJCcn6+DBgz776+vrdfjwYfq50e9//3vdf//9mjhxogYOHKjJkyfrt7/9rXJzcyXRz22ltfq1rf4tIfz4gd1u1/Dhw5WXl+fd5vF4lJeXp1GjRplYWftiGIbuvPNOvffee/r888+PGwodPny4QkNDffp569atys/P9/bzqFGjtHnzZp//4D799FPFxMQc94coWI0ZM0abN2/Whg0bvJ+MjAxNmjTJ+z393HLnnXfecY9q+P7779WtWzdJUvfu3ZWcnOzTzy6XSytXrvTp55KSEq1du9bb5vPPP5fH41FmZqYfriLwVVZWymr1/VNns9nk8Xgk0c9tpbX6ddSoUfrHP/6huro6b5tPP/1Uffr0afaUlyRudfeXRYsWGQ6Hw3j5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","text/plain":["
"]},"metadata":{},"output_type":"display_data"}],"source":["plt.plot(costs)\n","plt.xlabel(\"Iteration\")\n","plt.ylabel(\"Cost\")"]},{"cell_type":"markdown","metadata":{},"source":["## Visualise trained classifier"]},{"cell_type":"code","execution_count":21,"metadata":{},"outputs":[],"source":["linsp = jnp.linspace(-1, 1, 100)\n","Z = vmap(\n"," lambda a: vmap(lambda b: param_and_x_to_probability(param, jnp.array([a, b])))(\n"," linsp\n"," )\n",")(linsp)"]},{"cell_type":"code","execution_count":22,"metadata":{},"outputs":[{"data":{"text/plain":["[]"]},"execution_count":22,"metadata":{},"output_type":"execute_result"},{"data":{"image/png":"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","text/plain":["
"]},"metadata":{},"output_type":"display_data"}],"source":["plt.contourf(linsp, linsp, Z, cmap=\"Purples\", alpha=0.8)\n","circle_linsp = jnp.linspace(0, 2 * jnp.pi, 100)\n","plt.plot(inner_rad * jnp.cos(circle_linsp), inner_rad * jnp.sin(circle_linsp), c=\"red\")\n","plt.plot(outer_rad * jnp.cos(circle_linsp), outer_rad * jnp.sin(circle_linsp), c=\"red\")"]},{"cell_type":"markdown","metadata":{},"source":["Looks good, it has clearly grasped the donut shape. Sincerest apologies if you are now hungry! 🍩"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.1"}},"nbformat":4,"nbformat_minor":2} From a510eee68f98813ba286bd5a64623cbc2f68f452 Mon Sep 17 00:00:00 2001 From: aidanCQ Date: Mon, 29 Jul 2024 11:03:26 +0100 Subject: [PATCH 60/76] Fix submodule. --- docs/quantinuum-sphinx | 1 + docs/quantinuum-sphinx/.nojekyll | 0 .../_static/assets/github.svg | 3 - .../_static/assets/slack.svg | 1 - .../_static/assets/stack.svg | 1 - .../quantinuum-sphinx/_static/assets/test.svg | 0 .../quantinuum-sphinx/_static/index.global.js | 142 - docs/quantinuum-sphinx/_static/styles.css | 16 - docs/quantinuum-sphinx/_static/tailwind.css | 3054 ----------------- docs/quantinuum-sphinx/_static/tokens.css | 63 - docs/quantinuum-sphinx/_templates/page.html | 313 -- 11 files changed, 1 insertion(+), 3593 deletions(-) create mode 160000 docs/quantinuum-sphinx delete mode 100644 docs/quantinuum-sphinx/.nojekyll delete mode 100644 docs/quantinuum-sphinx/_static/assets/github.svg delete mode 100644 docs/quantinuum-sphinx/_static/assets/slack.svg delete mode 100644 docs/quantinuum-sphinx/_static/assets/stack.svg delete mode 100644 docs/quantinuum-sphinx/_static/assets/test.svg delete mode 100644 docs/quantinuum-sphinx/_static/index.global.js delete mode 100644 docs/quantinuum-sphinx/_static/styles.css delete mode 100644 docs/quantinuum-sphinx/_static/tailwind.css delete mode 100644 docs/quantinuum-sphinx/_static/tokens.css delete mode 100644 docs/quantinuum-sphinx/_templates/page.html diff --git a/docs/quantinuum-sphinx b/docs/quantinuum-sphinx new file mode 160000 index 00000000..30f30c39 --- /dev/null +++ b/docs/quantinuum-sphinx @@ -0,0 +1 @@ +Subproject commit 30f30c390e9f29e00e8f8ae5d4096e244b7b117c diff --git a/docs/quantinuum-sphinx/.nojekyll b/docs/quantinuum-sphinx/.nojekyll deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/quantinuum-sphinx/_static/assets/github.svg b/docs/quantinuum-sphinx/_static/assets/github.svg deleted file mode 100644 index a8d11740..00000000 --- a/docs/quantinuum-sphinx/_static/assets/github.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/docs/quantinuum-sphinx/_static/assets/slack.svg b/docs/quantinuum-sphinx/_static/assets/slack.svg deleted file mode 100644 index 4f101e0f..00000000 --- a/docs/quantinuum-sphinx/_static/assets/slack.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/quantinuum-sphinx/_static/assets/stack.svg b/docs/quantinuum-sphinx/_static/assets/stack.svg deleted file mode 100644 index 74d5333c..00000000 --- a/docs/quantinuum-sphinx/_static/assets/stack.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/quantinuum-sphinx/_static/assets/test.svg b/docs/quantinuum-sphinx/_static/assets/test.svg deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/quantinuum-sphinx/_static/index.global.js b/docs/quantinuum-sphinx/_static/index.global.js deleted file mode 100644 index 97f0a295..00000000 --- a/docs/quantinuum-sphinx/_static/index.global.js +++ /dev/null @@ -1,142 +0,0 @@ -"use strict";(()=>{var Fy=Object.create;var $s=Object.defineProperty,By=Object.defineProperties,Ny=Object.getOwnPropertyDescriptor,zy=Object.getOwnPropertyDescriptors,Uy=Object.getOwnPropertyNames,El=Object.getOwnPropertySymbols,qy=Object.getPrototypeOf,Ks=Object.prototype.hasOwnProperty,Sc=Object.prototype.propertyIsEnumerable;var Ic=(e,t,a)=>t in e?$s(e,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[t]=a,w=(e,t)=>{for(var a in t||(t={}))Ks.call(t,a)&&Ic(e,a,t[a]);if(El)for(var a of El(t))Sc.call(t,a)&&Ic(e,a,t[a]);return e},b=(e,t)=>By(e,zy(t));var A=(e,t)=>{var a={};for(var r in e)Ks.call(e,r)&&t.indexOf(r)<0&&(a[r]=e[r]);if(e!=null&&El)for(var r of El(e))t.indexOf(r)<0&&Sc.call(e,r)&&(a[r]=e[r]);return a};var ka=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Hy=(e,t,a,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Uy(t))!Ks.call(e,o)&&o!==a&&$s(e,o,{get:()=>t[o],enumerable:!(r=Ny(t,o))||r.enumerable});return e};var N=(e,t,a)=>(a=e!=null?Fy(qy(e)):{},Hy(t||!e||!e.__esModule?$s(a,"default",{value:e,enumerable:!0}):a,e));var Pe=(e,t,a)=>new Promise((r,o)=>{var n=s=>{try{u(a.next(s))}catch(i){o(i)}},l=s=>{try{u(a.throw(s))}catch(i){o(i)}},u=s=>s.done?r(s.value):Promise.resolve(s.value).then(n,l);u((a=a.apply(e,t)).next())});var Fc=ka(Y=>{"use strict";var mn=Symbol.for("react.element"),Vy=Symbol.for("react.portal"),jy=Symbol.for("react.fragment"),Wy=Symbol.for("react.strict_mode"),Gy=Symbol.for("react.profiler"),Zy=Symbol.for("react.provider"),$y=Symbol.for("react.context"),Ky=Symbol.for("react.forward_ref"),Xy=Symbol.for("react.suspense"),Qy=Symbol.for("react.memo"),Yy=Symbol.for("react.lazy"),kc=Symbol.iterator;function Jy(e){return e===null||typeof e!="object"?null:(e=kc&&e[kc]||e["@@iterator"],typeof e=="function"?e:null)}var Tc={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},_c=Object.assign,Rc={};function po(e,t,a){this.props=e,this.context=t,this.refs=Rc,this.updater=a||Tc}po.prototype.isReactComponent={};po.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};po.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Dc(){}Dc.prototype=po.prototype;function Qs(e,t,a){this.props=e,this.context=t,this.refs=Rc,this.updater=a||Tc}var Ys=Qs.prototype=new Dc;Ys.constructor=Qs;_c(Ys,po.prototype);Ys.isPureReactComponent=!0;var Pc=Array.isArray,bc=Object.prototype.hasOwnProperty,Js={current:null},Ec={key:!0,ref:!0,__self:!0,__source:!0};function Ac(e,t,a){var r,o={},n=null,l=null;if(t!=null)for(r in t.ref!==void 0&&(l=t.ref),t.key!==void 0&&(n=""+t.key),t)bc.call(t,r)&&!Ec.hasOwnProperty(r)&&(o[r]=t[r]);var u=arguments.length-2;if(u===1)o.children=a;else if(1{"use strict";Bc.exports=Fc()});var Zc=ka(ie=>{"use strict";function oi(e,t){var a=e.length;e.push(t);e:for(;0>>1,o=e[r];if(0>>1;rBl(u,a))sBl(i,u)?(e[r]=i,e[s]=a,r=s):(e[r]=u,e[l]=a,r=l);else if(sBl(i,a))e[r]=i,e[s]=a,r=s;else break e}}return t}function Bl(e,t){var a=e.sortIndex-t.sortIndex;return a!==0?a:e.id-t.id}typeof performance=="object"&&typeof performance.now=="function"?(Nc=performance,ie.unstable_now=function(){return Nc.now()}):(ti=Date,zc=ti.now(),ie.unstable_now=function(){return ti.now()-zc});var Nc,ti,zc,ia=[],Ga=[],oL=1,bt=null,je=3,Ul=!1,kr=!1,gn=!1,Hc=typeof setTimeout=="function"?setTimeout:null,Vc=typeof clearTimeout=="function"?clearTimeout:null,Uc=typeof setImmediate!="undefined"?setImmediate:null;typeof navigator!="undefined"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function ni(e){for(var t=Gt(Ga);t!==null;){if(t.callback===null)zl(Ga);else if(t.startTime<=e)zl(Ga),t.sortIndex=t.expirationTime,oi(ia,t);else break;t=Gt(Ga)}}function li(e){if(gn=!1,ni(e),!kr)if(Gt(ia)!==null)kr=!0,si(ui);else{var t=Gt(Ga);t!==null&&ii(li,t.startTime-e)}}function ui(e,t){kr=!1,gn&&(gn=!1,Vc(vn),vn=-1),Ul=!0;var a=je;try{for(ni(t),bt=Gt(ia);bt!==null&&(!(bt.expirationTime>t)||e&&!Gc());){var r=bt.callback;if(typeof r=="function"){bt.callback=null,je=bt.priorityLevel;var o=r(bt.expirationTime<=t);t=ie.unstable_now(),typeof o=="function"?bt.callback=o:bt===Gt(ia)&&zl(ia),ni(t)}else zl(ia);bt=Gt(ia)}if(bt!==null)var n=!0;else{var l=Gt(Ga);l!==null&&ii(li,l.startTime-t),n=!1}return n}finally{bt=null,je=a,Ul=!1}}var ql=!1,Nl=null,vn=-1,jc=5,Wc=-1;function Gc(){return!(ie.unstable_now()-Wce||125r?(e.sortIndex=a,oi(Ga,e),Gt(ia)===null&&e===Gt(Ga)&&(gn?(Vc(vn),vn=-1):gn=!0,ii(li,a-r))):(e.sortIndex=o,oi(ia,e),kr||Ul||(kr=!0,si(ui))),e};ie.unstable_shouldYield=Gc;ie.unstable_wrapCallback=function(e){var t=je;return function(){var a=je;je=t;try{return e.apply(this,arguments)}finally{je=a}}}});var Kc=ka((VS,$c)=>{"use strict";$c.exports=Zc()});var Jh=ka(Pt=>{"use strict";var nL=G(),St=Kc();function _(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,a=1;at}return!1}function rt(e,t,a,r,o,n,l){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=o,this.mustUseProperty=a,this.propertyName=e,this.type=t,this.sanitizeURL=n,this.removeEmptyString=l}var He={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){He[e]=new rt(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];He[t]=new rt(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){He[e]=new rt(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){He[e]=new rt(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){He[e]=new rt(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){He[e]=new rt(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){He[e]=new rt(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){He[e]=new rt(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){He[e]=new rt(e,5,!1,e.toLowerCase(),null,!1,!1)});var Sd=/[\-:]([a-z])/g;function kd(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Sd,kd);He[t]=new rt(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Sd,kd);He[t]=new rt(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Sd,kd);He[t]=new rt(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){He[e]=new rt(e,1,!1,e.toLowerCase(),null,!1,!1)});He.xlinkHref=new rt("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){He[e]=new rt(e,1,!1,e.toLowerCase(),null,!0,!0)});function Pd(e,t,a,r){var o=He.hasOwnProperty(t)?He[t]:null;(o!==null?o.type!==0:r||!(2u||o[l]!==n[u]){var s=` -`+o[l].replace(" at new "," at ");return e.displayName&&s.includes("")&&(s=s.replace("",e.displayName)),s}while(1<=l&&0<=u);break}}}finally{fi=!1,Error.prepareStackTrace=a}return(e=e?e.displayName||e.name:"")?Pn(e):""}function dL(e){switch(e.tag){case 5:return Pn(e.type);case 16:return Pn("Lazy");case 13:return Pn("Suspense");case 19:return Pn("SuspenseList");case 0:case 2:case 15:return e=ci(e.type,!1),e;case 11:return e=ci(e.type.render,!1),e;case 1:return e=ci(e.type,!0),e;default:return""}}function Fi(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case vo:return"Fragment";case go:return"Portal";case Ei:return"Profiler";case Md:return"StrictMode";case Ai:return"Suspense";case Oi:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case om:return(e.displayName||"Context")+".Consumer";case rm:return(e._context.displayName||"Context")+".Provider";case Td:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case _d:return t=e.displayName||null,t!==null?t:Fi(e.type)||"Memo";case $a:t=e._payload,e=e._init;try{return Fi(e(t))}catch(a){}}return null}function fL(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Fi(t);case 8:return t===Md?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function sr(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function lm(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function cL(e){var t=lm(e)?"checked":"value",a=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof a!="undefined"&&typeof a.get=="function"&&typeof a.set=="function"){var o=a.get,n=a.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(l){r=""+l,n.call(this,l)}}),Object.defineProperty(e,t,{enumerable:a.enumerable}),{getValue:function(){return r},setValue:function(l){r=""+l},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Vl(e){e._valueTracker||(e._valueTracker=cL(e))}function um(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var a=t.getValue(),r="";return e&&(r=lm(e)?e.checked?"true":"false":e.value),e=r,e!==a?(t.setValue(e),!0):!1}function vu(e){if(e=e||(typeof document!="undefined"?document:void 0),typeof e=="undefined")return null;try{return e.activeElement||e.body}catch(t){return e.body}}function Bi(e,t){var a=t.checked;return Ie({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:a!=null?a:e._wrapperState.initialChecked})}function Jc(e,t){var a=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;a=sr(t.value!=null?t.value:a),e._wrapperState={initialChecked:r,initialValue:a,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function sm(e,t){t=t.checked,t!=null&&Pd(e,"checked",t,!1)}function Ni(e,t){sm(e,t);var a=sr(t.value),r=t.type;if(a!=null)r==="number"?(a===0&&e.value===""||e.value!=a)&&(e.value=""+a):e.value!==""+a&&(e.value=""+a);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?zi(e,t.type,a):t.hasOwnProperty("defaultValue")&&zi(e,t.type,sr(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function ep(e,t,a){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,a||t===e.value||(e.value=t),e.defaultValue=t}a=e.name,a!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,a!==""&&(e.name=a)}function zi(e,t,a){(t!=="number"||vu(e.ownerDocument)!==e)&&(a==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+a&&(e.defaultValue=""+a))}var Mn=Array.isArray;function To(e,t,a,r){if(e=e.options,t){t={};for(var o=0;o"+t.valueOf().toString()+"",t=jl.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Un(e,t){if(t){var a=e.firstChild;if(a&&a===e.lastChild&&a.nodeType===3){a.nodeValue=t;return}}e.textContent=t}var Rn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},pL=["Webkit","ms","Moz","O"];Object.keys(Rn).forEach(function(e){pL.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Rn[t]=Rn[e]})});function cm(e,t,a){return t==null||typeof t=="boolean"||t===""?"":a||typeof t!="number"||t===0||Rn.hasOwnProperty(e)&&Rn[e]?(""+t).trim():t+"px"}function pm(e,t){e=e.style;for(var a in t)if(t.hasOwnProperty(a)){var r=a.indexOf("--")===0,o=cm(a,t[a],r);a==="float"&&(a="cssFloat"),r?e.setProperty(a,o):e[a]=o}}var mL=Ie({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Hi(e,t){if(t){if(mL[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(_(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(_(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(_(61))}if(t.style!=null&&typeof t.style!="object")throw Error(_(62))}}function Vi(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ji=null;function Rd(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Wi=null,_o=null,Ro=null;function rp(e){if(e=ol(e)){if(typeof Wi!="function")throw Error(_(280));var t=e.stateNode;t&&(t=Wu(t),Wi(e.stateNode,e.type,t))}}function mm(e){_o?Ro?Ro.push(e):Ro=[e]:_o=e}function hm(){if(_o){var e=_o,t=Ro;if(Ro=_o=null,rp(e),t)for(e=0;e>>=0,e===0?32:31-(kL(e)/PL|0)|0}var Wl=64,Gl=4194304;function Tn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function wu(e,t){var a=e.pendingLanes;if(a===0)return 0;var r=0,o=e.suspendedLanes,n=e.pingedLanes,l=a&268435455;if(l!==0){var u=l&~o;u!==0?r=Tn(u):(n&=l,n!==0&&(r=Tn(n)))}else l=a&~o,l!==0?r=Tn(l):n!==0&&(r=Tn(n));if(r===0)return 0;if(t!==0&&t!==r&&!(t&o)&&(o=r&-r,n=t&-t,o>=n||o===16&&(n&4194240)!==0))return t;if(r&4&&(r|=a&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0a;a++)t.push(e);return t}function al(e,t,a){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Qt(t),e[t]=a}function RL(e,t){var a=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=bn),cp=" ",pp=!1;function Om(e,t){switch(e){case"keyup":return ow.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Fm(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var xo=!1;function lw(e,t){switch(e){case"compositionend":return Fm(t);case"keypress":return t.which!==32?null:(pp=!0,cp);case"textInput":return e=t.data,e===cp&&pp?null:e;default:return null}}function uw(e,t){if(xo)return e==="compositionend"||!Nd&&Om(e,t)?(e=Em(),su=Od=Ya=null,xo=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:a,offset:t-e};e=r}e:{for(;a;){if(a.nextSibling){a=a.nextSibling;break e}a=a.parentNode}a=void 0}a=gp(a)}}function Um(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Um(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function qm(){for(var e=window,t=vu();t instanceof e.HTMLIFrameElement;){try{var a=typeof t.contentWindow.location.href=="string"}catch(r){a=!1}if(a)e=t.contentWindow;else break;t=vu(e.document)}return t}function zd(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function gw(e){var t=qm(),a=e.focusedElem,r=e.selectionRange;if(t!==a&&a&&a.ownerDocument&&Um(a.ownerDocument.documentElement,a)){if(r!==null&&zd(a)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in a)a.selectionStart=t,a.selectionEnd=Math.min(e,a.value.length);else if(e=(t=a.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var o=a.textContent.length,n=Math.min(r.start,o);r=r.end===void 0?n:Math.min(r.end,o),!e.extend&&n>r&&(o=r,r=n,n=o),o=vp(a,n);var l=vp(a,r);o&&l&&(e.rangeCount!==1||e.anchorNode!==o.node||e.anchorOffset!==o.offset||e.focusNode!==l.node||e.focusOffset!==l.offset)&&(t=t.createRange(),t.setStart(o.node,o.offset),e.removeAllRanges(),n>r?(e.addRange(t),e.extend(l.node,l.offset)):(t.setEnd(l.node,l.offset),e.addRange(t)))}}for(t=[],e=a;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof a.focus=="function"&&a.focus(),a=0;a=document.documentMode,yo=null,Qi=null,An=null,Yi=!1;function xp(e,t,a){var r=a.window===a?a.document:a.nodeType===9?a:a.ownerDocument;Yi||yo==null||yo!==vu(r)||(r=yo,"selectionStart"in r&&zd(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),An&&Gn(An,r)||(An=r,r=Su(Qi,"onSelect"),0Co||(e.current=od[Co],od[Co]=null,Co--)}function de(e,t){Co++,od[Co]=e.current,e.current=t}var ir={},$e=fr(ir),dt=fr(!1),Er=ir;function Oo(e,t){var a=e.type.contextTypes;if(!a)return ir;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var o={},n;for(n in a)o[n]=t[n];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function ft(e){return e=e.childContextTypes,e!=null}function Pu(){he(dt),he($e)}function Mp(e,t,a){if($e.current!==ir)throw Error(_(168));de($e,t),de(dt,a)}function Xm(e,t,a){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return a;r=r.getChildContext();for(var o in r)if(!(o in t))throw Error(_(108,fL(e)||"Unknown",o));return Ie({},a,r)}function Mu(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||ir,Er=$e.current,de($e,e),de(dt,dt.current),!0}function Tp(e,t,a){var r=e.stateNode;if(!r)throw Error(_(169));a?(e=Xm(e,t,Er),r.__reactInternalMemoizedMergedChildContext=e,he(dt),he($e),de($e,e)):he(dt),de(dt,a)}var Ma=null,Gu=!1,Ii=!1;function Qm(e){Ma===null?Ma=[e]:Ma.push(e)}function Pw(e){Gu=!0,Qm(e)}function cr(){if(!Ii&&Ma!==null){Ii=!0;var e=0,t=ne;try{var a=Ma;for(ne=1;e>=l,o-=l,Ta=1<<32-Qt(t)+o|a<S?(M=k,k=null):M=k.sibling;var P=p(m,k,h[S],y);if(P===null){k===null&&(k=M);break}e&&k&&P.alternate===null&&t(m,k),c=n(P,c,S),I===null?C=P:I.sibling=P,I=P,k=M}if(S===h.length)return a(m,k),Le&&Pr(m,S),C;if(k===null){for(;SS?(M=k,k=null):M=k.sibling;var U=p(m,k,P.value,y);if(U===null){k===null&&(k=M);break}e&&k&&U.alternate===null&&t(m,k),c=n(U,c,S),I===null?C=U:I.sibling=U,I=U,k=M}if(P.done)return a(m,k),Le&&Pr(m,S),C;if(k===null){for(;!P.done;S++,P=h.next())P=f(m,P.value,y),P!==null&&(c=n(P,c,S),I===null?C=P:I.sibling=P,I=P);return Le&&Pr(m,S),C}for(k=r(m,k);!P.done;S++,P=h.next())P=v(k,m,S,P.value,y),P!==null&&(e&&P.alternate!==null&&k.delete(P.key===null?S:P.key),c=n(P,c,S),I===null?C=P:I.sibling=P,I=P);return e&&k.forEach(function(j){return t(m,j)}),Le&&Pr(m,S),C}function L(m,c,h,y){if(typeof h=="object"&&h!==null&&h.type===vo&&h.key===null&&(h=h.props.children),typeof h=="object"&&h!==null){switch(h.$$typeof){case Hl:e:{for(var C=h.key,I=c;I!==null;){if(I.key===C){if(C=h.type,C===vo){if(I.tag===7){a(m,I.sibling),c=o(I,h.props.children),c.return=m,m=c;break e}}else if(I.elementType===C||typeof C=="object"&&C!==null&&C.$$typeof===$a&&Dp(C)===I.type){a(m,I.sibling),c=o(I,h.props),c.ref=Cn(m,I,h),c.return=m,m=c;break e}a(m,I);break}else t(m,I);I=I.sibling}h.type===vo?(c=br(h.props.children,m.mode,y,h.key),c.return=m,m=c):(y=gu(h.type,h.key,h.props,null,m.mode,y),y.ref=Cn(m,c,h),y.return=m,m=y)}return l(m);case go:e:{for(I=h.key;c!==null;){if(c.key===I)if(c.tag===4&&c.stateNode.containerInfo===h.containerInfo&&c.stateNode.implementation===h.implementation){a(m,c.sibling),c=o(c,h.children||[]),c.return=m,m=c;break e}else{a(m,c);break}else t(m,c);c=c.sibling}c=Di(h,m.mode,y),c.return=m,m=c}return l(m);case $a:return I=h._init,L(m,c,I(h._payload),y)}if(Mn(h))return x(m,c,h,y);if(xn(h))return g(m,c,h,y);ru(m,h)}return typeof h=="string"&&h!==""||typeof h=="number"?(h=""+h,c!==null&&c.tag===6?(a(m,c.sibling),c=o(c,h),c.return=m,m=c):(a(m,c),c=Ri(h,m.mode,y),c.return=m,m=c),l(m)):a(m,c)}return L}var Bo=th(!0),ah=th(!1),Ru=fr(null),Du=null,ko=null,Vd=null;function jd(){Vd=ko=Du=null}function Wd(e){var t=Ru.current;he(Ru),e._currentValue=t}function ud(e,t,a){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===a)break;e=e.return}}function bo(e,t){Du=e,Vd=ko=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(it=!0),e.firstContext=null)}function Bt(e){var t=e._currentValue;if(Vd!==e)if(e={context:e,memoizedValue:t,next:null},ko===null){if(Du===null)throw Error(_(308));ko=e,Du.dependencies={lanes:0,firstContext:e}}else ko=ko.next=e;return t}var _r=null;function Gd(e){_r===null?_r=[e]:_r.push(e)}function rh(e,t,a,r){var o=t.interleaved;return o===null?(a.next=a,Gd(t)):(a.next=o.next,o.next=a),t.interleaved=a,Ea(e,r)}function Ea(e,t){e.lanes|=t;var a=e.alternate;for(a!==null&&(a.lanes|=t),a=e,e=e.return;e!==null;)e.childLanes|=t,a=e.alternate,a!==null&&(a.childLanes|=t),a=e,e=e.return;return a.tag===3?a.stateNode:null}var Ka=!1;function Zd(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function oh(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ra(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function or(e,t,a){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,re&2){var o=r.pending;return o===null?t.next=t:(t.next=o.next,o.next=t),r.pending=t,Ea(e,a)}return o=r.interleaved,o===null?(t.next=t,Gd(r)):(t.next=o.next,o.next=t),r.interleaved=t,Ea(e,a)}function du(e,t,a){if(t=t.updateQueue,t!==null&&(t=t.shared,(a&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,a|=r,t.lanes=a,bd(e,a)}}function bp(e,t){var a=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,a===r)){var o=null,n=null;if(a=a.firstBaseUpdate,a!==null){do{var l={eventTime:a.eventTime,lane:a.lane,tag:a.tag,payload:a.payload,callback:a.callback,next:null};n===null?o=n=l:n=n.next=l,a=a.next}while(a!==null);n===null?o=n=t:n=n.next=t}else o=n=t;a={baseState:r.baseState,firstBaseUpdate:o,lastBaseUpdate:n,shared:r.shared,effects:r.effects},e.updateQueue=a;return}e=a.lastBaseUpdate,e===null?a.firstBaseUpdate=t:e.next=t,a.lastBaseUpdate=t}function bu(e,t,a,r){var o=e.updateQueue;Ka=!1;var n=o.firstBaseUpdate,l=o.lastBaseUpdate,u=o.shared.pending;if(u!==null){o.shared.pending=null;var s=u,i=s.next;s.next=null,l===null?n=i:l.next=i,l=s;var d=e.alternate;d!==null&&(d=d.updateQueue,u=d.lastBaseUpdate,u!==l&&(u===null?d.firstBaseUpdate=i:u.next=i,d.lastBaseUpdate=s))}if(n!==null){var f=o.baseState;l=0,d=i=s=null,u=n;do{var p=u.lane,v=u.eventTime;if((r&p)===p){d!==null&&(d=d.next={eventTime:v,lane:0,tag:u.tag,payload:u.payload,callback:u.callback,next:null});e:{var x=e,g=u;switch(p=t,v=a,g.tag){case 1:if(x=g.payload,typeof x=="function"){f=x.call(v,f,p);break e}f=x;break e;case 3:x.flags=x.flags&-65537|128;case 0:if(x=g.payload,p=typeof x=="function"?x.call(v,f,p):x,p==null)break e;f=Ie({},f,p);break e;case 2:Ka=!0}}u.callback!==null&&u.lane!==0&&(e.flags|=64,p=o.effects,p===null?o.effects=[u]:p.push(u))}else v={eventTime:v,lane:p,tag:u.tag,payload:u.payload,callback:u.callback,next:null},d===null?(i=d=v,s=f):d=d.next=v,l|=p;if(u=u.next,u===null){if(u=o.shared.pending,u===null)break;p=u,u=p.next,p.next=null,o.lastBaseUpdate=p,o.shared.pending=null}}while(!0);if(d===null&&(s=f),o.baseState=s,o.firstBaseUpdate=i,o.lastBaseUpdate=d,t=o.shared.interleaved,t!==null){o=t;do l|=o.lane,o=o.next;while(o!==t)}else n===null&&(o.shared.lanes=0);Fr|=l,e.lanes=l,e.memoizedState=f}}function Ep(e,t,a){if(e=t.effects,t.effects=null,e!==null)for(t=0;ta?a:4,e(!0);var r=ki.transition;ki.transition={};try{e(!1),t()}finally{ne=a,ki.transition=r}}function wh(){return Nt().memoizedState}function Rw(e,t,a){var r=lr(e);if(a={lane:r,action:a,hasEagerState:!1,eagerState:null,next:null},Ch(e))Ih(t,a);else if(a=rh(e,t,a,r),a!==null){var o=at();Yt(a,e,r,o),Sh(a,t,r)}}function Dw(e,t,a){var r=lr(e),o={lane:r,action:a,hasEagerState:!1,eagerState:null,next:null};if(Ch(e))Ih(t,o);else{var n=e.alternate;if(e.lanes===0&&(n===null||n.lanes===0)&&(n=t.lastRenderedReducer,n!==null))try{var l=t.lastRenderedState,u=n(l,a);if(o.hasEagerState=!0,o.eagerState=u,Jt(u,l)){var s=t.interleaved;s===null?(o.next=o,Gd(t)):(o.next=s.next,s.next=o),t.interleaved=o;return}}catch(i){}finally{}a=rh(e,t,o,r),a!==null&&(o=at(),Yt(a,e,r,o),Sh(a,t,r))}}function Ch(e){var t=e.alternate;return e===Ce||t!==null&&t===Ce}function Ih(e,t){On=Au=!0;var a=e.pending;a===null?t.next=t:(t.next=a.next,a.next=t),e.pending=t}function Sh(e,t,a){if(a&4194240){var r=t.lanes;r&=e.pendingLanes,a|=r,t.lanes=a,bd(e,a)}}var Ou={readContext:Bt,useCallback:We,useContext:We,useEffect:We,useImperativeHandle:We,useInsertionEffect:We,useLayoutEffect:We,useMemo:We,useReducer:We,useRef:We,useState:We,useDebugValue:We,useDeferredValue:We,useTransition:We,useMutableSource:We,useSyncExternalStore:We,useId:We,unstable_isNewReconciler:!1},bw={readContext:Bt,useCallback:function(e,t){return fa().memoizedState=[e,t===void 0?null:t],e},useContext:Bt,useEffect:Op,useImperativeHandle:function(e,t,a){return a=a!=null?a.concat([e]):null,cu(4194308,4,gh.bind(null,t,e),a)},useLayoutEffect:function(e,t){return cu(4194308,4,e,t)},useInsertionEffect:function(e,t){return cu(4,2,e,t)},useMemo:function(e,t){var a=fa();return t=t===void 0?null:t,e=e(),a.memoizedState=[e,t],e},useReducer:function(e,t,a){var r=fa();return t=a!==void 0?a(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Rw.bind(null,Ce,e),[r.memoizedState,e]},useRef:function(e){var t=fa();return e={current:e},t.memoizedState=e},useState:Ap,useDebugValue:tf,useDeferredValue:function(e){return fa().memoizedState=e},useTransition:function(){var e=Ap(!1),t=e[0];return e=_w.bind(null,e[1]),fa().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,a){var r=Ce,o=fa();if(Le){if(a===void 0)throw Error(_(407));a=a()}else{if(a=t(),Be===null)throw Error(_(349));Or&30||sh(r,t,a)}o.memoizedState=a;var n={value:a,getSnapshot:t};return o.queue=n,Op(dh.bind(null,r,n,e),[e]),r.flags|=2048,el(9,ih.bind(null,r,n,a,t),void 0,null),a},useId:function(){var e=fa(),t=Be.identifierPrefix;if(Le){var a=_a,r=Ta;a=(r&~(1<<32-Qt(r)-1)).toString(32)+a,t=":"+t+"R"+a,a=Yn++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=l.createElement(a,{is:r.is}):(e=l.createElement(a),a==="select"&&(l=e,r.multiple?l.multiple=!0:r.size&&(l.size=r.size))):e=l.createElementNS(e,a),e[ca]=t,e[Kn]=r,Ah(e,t,!1,!1),t.stateNode=e;e:{switch(l=Vi(a,r),a){case"dialog":me("cancel",e),me("close",e),o=r;break;case"iframe":case"object":case"embed":me("load",e),o=r;break;case"video":case"audio":for(o=0;o<_n.length;o++)me(_n[o],e);o=r;break;case"source":me("error",e),o=r;break;case"img":case"image":case"link":me("error",e),me("load",e),o=r;break;case"details":me("toggle",e),o=r;break;case"input":Jc(e,r),o=Bi(e,r),me("invalid",e);break;case"option":o=r;break;case"select":e._wrapperState={wasMultiple:!!r.multiple},o=Ie({},r,{value:void 0}),me("invalid",e);break;case"textarea":tp(e,r),o=Ui(e,r),me("invalid",e);break;default:o=r}Hi(a,o),u=o;for(n in u)if(u.hasOwnProperty(n)){var s=u[n];n==="style"?pm(e,s):n==="dangerouslySetInnerHTML"?(s=s?s.__html:void 0,s!=null&&fm(e,s)):n==="children"?typeof s=="string"?(a!=="textarea"||s!=="")&&Un(e,s):typeof s=="number"&&Un(e,""+s):n!=="suppressContentEditableWarning"&&n!=="suppressHydrationWarning"&&n!=="autoFocus"&&(zn.hasOwnProperty(n)?s!=null&&n==="onScroll"&&me("scroll",e):s!=null&&Pd(e,n,s,l))}switch(a){case"input":Vl(e),ep(e,r,!1);break;case"textarea":Vl(e),ap(e);break;case"option":r.value!=null&&e.setAttribute("value",""+sr(r.value));break;case"select":e.multiple=!!r.multiple,n=r.value,n!=null?To(e,!!r.multiple,n,!1):r.defaultValue!=null&&To(e,!!r.multiple,r.defaultValue,!0);break;default:typeof o.onClick=="function"&&(e.onclick=ku)}switch(a){case"button":case"input":case"select":case"textarea":r=!!r.autoFocus;break e;case"img":r=!0;break e;default:r=!1}}r&&(t.flags|=4)}t.ref!==null&&(t.flags|=512,t.flags|=2097152)}return Ge(t),null;case 6:if(e&&t.stateNode!=null)Fh(e,t,e.memoizedProps,r);else{if(typeof r!="string"&&t.stateNode===null)throw Error(_(166));if(a=Rr(Qn.current),Rr(ma.current),au(t)){if(r=t.stateNode,a=t.memoizedProps,r[ca]=t,(n=r.nodeValue!==a)&&(e=It,e!==null))switch(e.tag){case 3:tu(r.nodeValue,a,(e.mode&1)!==0);break;case 5:e.memoizedProps.suppressHydrationWarning!==!0&&tu(r.nodeValue,a,(e.mode&1)!==0)}n&&(t.flags|=4)}else r=(a.nodeType===9?a:a.ownerDocument).createTextNode(r),r[ca]=t,t.stateNode=r}return Ge(t),null;case 13:if(he(we),r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(Le&&Ct!==null&&t.mode&1&&!(t.flags&128))eh(),Fo(),t.flags|=98560,n=!1;else if(n=au(t),r!==null&&r.dehydrated!==null){if(e===null){if(!n)throw Error(_(318));if(n=t.memoizedState,n=n!==null?n.dehydrated:null,!n)throw Error(_(317));n[ca]=t}else Fo(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Ge(t),n=!1}else Xt!==null&&(Id(Xt),Xt=null),n=!0;if(!n)return t.flags&65536?t:null}return t.flags&128?(t.lanes=a,t):(r=r!==null,r!==(e!==null&&e.memoizedState!==null)&&r&&(t.child.flags|=8192,t.mode&1&&(e===null||we.current&1?Oe===0&&(Oe=3):sf())),t.updateQueue!==null&&(t.flags|=4),Ge(t),null);case 4:return No(),hd(e,t),e===null&&Zn(t.stateNode.containerInfo),Ge(t),null;case 10:return Wd(t.type._context),Ge(t),null;case 17:return ft(t.type)&&Pu(),Ge(t),null;case 19:if(he(we),n=t.memoizedState,n===null)return Ge(t),null;if(r=(t.flags&128)!==0,l=n.rendering,l===null)if(r)In(n,!1);else{if(Oe!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(l=Eu(e),l!==null){for(t.flags|=128,In(n,!1),r=l.updateQueue,r!==null&&(t.updateQueue=r,t.flags|=4),t.subtreeFlags=0,r=a,a=t.child;a!==null;)n=a,e=r,n.flags&=14680066,l=n.alternate,l===null?(n.childLanes=0,n.lanes=e,n.child=null,n.subtreeFlags=0,n.memoizedProps=null,n.memoizedState=null,n.updateQueue=null,n.dependencies=null,n.stateNode=null):(n.childLanes=l.childLanes,n.lanes=l.lanes,n.child=l.child,n.subtreeFlags=0,n.deletions=null,n.memoizedProps=l.memoizedProps,n.memoizedState=l.memoizedState,n.updateQueue=l.updateQueue,n.type=l.type,e=l.dependencies,n.dependencies=e===null?null:{lanes:e.lanes,firstContext:e.firstContext}),a=a.sibling;return de(we,we.current&1|2),t.child}e=e.sibling}n.tail!==null&&Te()>Uo&&(t.flags|=128,r=!0,In(n,!1),t.lanes=4194304)}else{if(!r)if(e=Eu(l),e!==null){if(t.flags|=128,r=!0,a=e.updateQueue,a!==null&&(t.updateQueue=a,t.flags|=4),In(n,!0),n.tail===null&&n.tailMode==="hidden"&&!l.alternate&&!Le)return Ge(t),null}else 2*Te()-n.renderingStartTime>Uo&&a!==1073741824&&(t.flags|=128,r=!0,In(n,!1),t.lanes=4194304);n.isBackwards?(l.sibling=t.child,t.child=l):(a=n.last,a!==null?a.sibling=l:t.child=l,n.last=l)}return n.tail!==null?(t=n.tail,n.rendering=t,n.tail=t.sibling,n.renderingStartTime=Te(),t.sibling=null,a=we.current,de(we,r?a&1|2:a&1),t):(Ge(t),null);case 22:case 23:return uf(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?wt&1073741824&&(Ge(t),t.subtreeFlags&6&&(t.flags|=8192)):Ge(t),null;case 24:return null;case 25:return null}throw Error(_(156,t.tag))}function Uw(e,t){switch(qd(t),t.tag){case 1:return ft(t.type)&&Pu(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return No(),he(dt),he($e),Xd(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Kd(t),null;case 13:if(he(we),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(_(340));Fo()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return he(we),null;case 4:return No(),null;case 10:return Wd(t.type._context),null;case 22:case 23:return uf(),null;case 24:return null;default:return null}}var nu=!1,Ze=!1,qw=typeof WeakSet=="function"?WeakSet:Set,B=null;function Po(e,t){var a=e.ref;if(a!==null)if(typeof a=="function")try{a(null)}catch(r){Me(e,t,r)}else a.current=null}function gd(e,t,a){try{a()}catch(r){Me(e,t,r)}}var Gp=!1;function Hw(e,t){if(Ji=Cu,e=qm(),zd(e)){if("selectionStart"in e)var a={start:e.selectionStart,end:e.selectionEnd};else e:{a=(a=e.ownerDocument)&&a.defaultView||window;var r=a.getSelection&&a.getSelection();if(r&&r.rangeCount!==0){a=r.anchorNode;var o=r.anchorOffset,n=r.focusNode;r=r.focusOffset;try{a.nodeType,n.nodeType}catch(y){a=null;break e}var l=0,u=-1,s=-1,i=0,d=0,f=e,p=null;t:for(;;){for(var v;f!==a||o!==0&&f.nodeType!==3||(u=l+o),f!==n||r!==0&&f.nodeType!==3||(s=l+r),f.nodeType===3&&(l+=f.nodeValue.length),(v=f.firstChild)!==null;)p=f,f=v;for(;;){if(f===e)break t;if(p===a&&++i===o&&(u=l),p===n&&++d===r&&(s=l),(v=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=v}a=u===-1||s===-1?null:{start:u,end:s}}else a=null}a=a||{start:0,end:0}}else a=null;for(ed={focusedElem:e,selectionRange:a},Cu=!1,B=t;B!==null;)if(t=B,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,B=e;else for(;B!==null;){t=B;try{var x=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(x!==null){var g=x.memoizedProps,L=x.memoizedState,m=t.stateNode,c=m.getSnapshotBeforeUpdate(t.elementType===t.type?g:$t(t.type,g),L);m.__reactInternalSnapshotBeforeUpdate=c}break;case 3:var h=t.stateNode.containerInfo;h.nodeType===1?h.textContent="":h.nodeType===9&&h.documentElement&&h.removeChild(h.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(_(163))}}catch(y){Me(t,t.return,y)}if(e=t.sibling,e!==null){e.return=t.return,B=e;break}B=t.return}return x=Gp,Gp=!1,x}function Fn(e,t,a){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var o=r=r.next;do{if((o.tag&e)===e){var n=o.destroy;o.destroy=void 0,n!==void 0&&gd(t,a,n)}o=o.next}while(o!==r)}}function Ku(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var a=t=t.next;do{if((a.tag&e)===e){var r=a.create;a.destroy=r()}a=a.next}while(a!==t)}}function vd(e){var t=e.ref;if(t!==null){var a=e.stateNode;switch(e.tag){case 5:e=a;break;default:e=a}typeof t=="function"?t(e):t.current=e}}function Bh(e){var t=e.alternate;t!==null&&(e.alternate=null,Bh(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[ca],delete t[Kn],delete t[rd],delete t[Sw],delete t[kw])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Nh(e){return e.tag===5||e.tag===3||e.tag===4}function Zp(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Nh(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function xd(e,t,a){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?a.nodeType===8?a.parentNode.insertBefore(e,t):a.insertBefore(e,t):(a.nodeType===8?(t=a.parentNode,t.insertBefore(e,a)):(t=a,t.appendChild(e)),a=a._reactRootContainer,a!=null||t.onclick!==null||(t.onclick=ku));else if(r!==4&&(e=e.child,e!==null))for(xd(e,t,a),e=e.sibling;e!==null;)xd(e,t,a),e=e.sibling}function yd(e,t,a){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?a.insertBefore(e,t):a.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(yd(e,t,a),e=e.sibling;e!==null;)yd(e,t,a),e=e.sibling}var Ue=null,Kt=!1;function Za(e,t,a){for(a=a.child;a!==null;)zh(e,t,a),a=a.sibling}function zh(e,t,a){if(pa&&typeof pa.onCommitFiberUnmount=="function")try{pa.onCommitFiberUnmount(qu,a)}catch(u){}switch(a.tag){case 5:Ze||Po(a,t);case 6:var r=Ue,o=Kt;Ue=null,Za(e,t,a),Ue=r,Kt=o,Ue!==null&&(Kt?(e=Ue,a=a.stateNode,e.nodeType===8?e.parentNode.removeChild(a):e.removeChild(a)):Ue.removeChild(a.stateNode));break;case 18:Ue!==null&&(Kt?(e=Ue,a=a.stateNode,e.nodeType===8?Ci(e.parentNode,a):e.nodeType===1&&Ci(e,a),jn(e)):Ci(Ue,a.stateNode));break;case 4:r=Ue,o=Kt,Ue=a.stateNode.containerInfo,Kt=!0,Za(e,t,a),Ue=r,Kt=o;break;case 0:case 11:case 14:case 15:if(!Ze&&(r=a.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){o=r=r.next;do{var n=o,l=n.destroy;n=n.tag,l!==void 0&&(n&2||n&4)&&gd(a,t,l),o=o.next}while(o!==r)}Za(e,t,a);break;case 1:if(!Ze&&(Po(a,t),r=a.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=a.memoizedProps,r.state=a.memoizedState,r.componentWillUnmount()}catch(u){Me(a,t,u)}Za(e,t,a);break;case 21:Za(e,t,a);break;case 22:a.mode&1?(Ze=(r=Ze)||a.memoizedState!==null,Za(e,t,a),Ze=r):Za(e,t,a);break;default:Za(e,t,a)}}function $p(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var a=e.stateNode;a===null&&(a=e.stateNode=new qw),t.forEach(function(r){var o=Qw.bind(null,e,r);a.has(r)||(a.add(r),r.then(o,o))})}}function Zt(e,t){var a=t.deletions;if(a!==null)for(var r=0;ro&&(o=l),r&=~n}if(r=o,r=Te()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*jw(r/1960))-r,10e?16:e,Ja===null)var r=!1;else{if(e=Ja,Ja=null,Nu=0,re&6)throw Error(_(331));var o=re;for(re|=4,B=e.current;B!==null;){var n=B,l=n.child;if(B.flags&16){var u=n.deletions;if(u!==null){for(var s=0;sTe()-nf?Dr(e,0):of|=a),ct(e,t)}function Zh(e,t){t===0&&(e.mode&1?(t=Gl,Gl<<=1,!(Gl&130023424)&&(Gl=4194304)):t=1);var a=at();e=Ea(e,t),e!==null&&(al(e,t,a),ct(e,a))}function Xw(e){var t=e.memoizedState,a=0;t!==null&&(a=t.retryLane),Zh(e,a)}function Qw(e,t){var a=0;switch(e.tag){case 13:var r=e.stateNode,o=e.memoizedState;o!==null&&(a=o.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(_(314))}r!==null&&r.delete(t),Zh(e,a)}var $h;$h=function(e,t,a){if(e!==null)if(e.memoizedProps!==t.pendingProps||dt.current)it=!0;else{if(!(e.lanes&a)&&!(t.flags&128))return it=!1,Nw(e,t,a);it=!!(e.flags&131072)}else it=!1,Le&&t.flags&1048576&&Ym(t,_u,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;pu(e,t),e=t.pendingProps;var o=Oo(t,$e.current);bo(t,a),o=Yd(null,t,r,e,o,a);var n=Jd();return t.flags|=1,typeof o=="object"&&o!==null&&typeof o.render=="function"&&o.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,ft(r)?(n=!0,Mu(t)):n=!1,t.memoizedState=o.state!==null&&o.state!==void 0?o.state:null,Zd(t),o.updater=$u,t.stateNode=o,o._reactInternals=t,id(t,r,e,a),t=cd(null,t,r,!0,n,a)):(t.tag=0,Le&&n&&Ud(t),tt(null,t,o,a),t=t.child),t;case 16:r=t.elementType;e:{switch(pu(e,t),e=t.pendingProps,o=r._init,r=o(r._payload),t.type=r,o=t.tag=Jw(r),e=$t(r,e),o){case 0:t=fd(null,t,r,e,a);break e;case 1:t=Vp(null,t,r,e,a);break e;case 11:t=qp(null,t,r,e,a);break e;case 14:t=Hp(null,t,r,$t(r.type,e),a);break e}throw Error(_(306,r,""))}return t;case 0:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:$t(r,o),fd(e,t,r,o,a);case 1:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:$t(r,o),Vp(e,t,r,o,a);case 3:e:{if(Dh(t),e===null)throw Error(_(387));r=t.pendingProps,n=t.memoizedState,o=n.element,oh(e,t),bu(t,r,null,a);var l=t.memoizedState;if(r=l.element,n.isDehydrated)if(n={element:r,isDehydrated:!1,cache:l.cache,pendingSuspenseBoundaries:l.pendingSuspenseBoundaries,transitions:l.transitions},t.updateQueue.baseState=n,t.memoizedState=n,t.flags&256){o=zo(Error(_(423)),t),t=jp(e,t,r,a,o);break e}else if(r!==o){o=zo(Error(_(424)),t),t=jp(e,t,r,a,o);break e}else for(Ct=rr(t.stateNode.containerInfo.firstChild),It=t,Le=!0,Xt=null,a=ah(t,null,r,a),t.child=a;a;)a.flags=a.flags&-3|4096,a=a.sibling;else{if(Fo(),r===o){t=Aa(e,t,a);break e}tt(e,t,r,a)}t=t.child}return t;case 5:return nh(t),e===null&&ld(t),r=t.type,o=t.pendingProps,n=e!==null?e.memoizedProps:null,l=o.children,td(r,o)?l=null:n!==null&&td(r,n)&&(t.flags|=32),Rh(e,t),tt(e,t,l,a),t.child;case 6:return e===null&&ld(t),null;case 13:return bh(e,t,a);case 4:return $d(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=Bo(t,null,r,a):tt(e,t,r,a),t.child;case 11:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:$t(r,o),qp(e,t,r,o,a);case 7:return tt(e,t,t.pendingProps,a),t.child;case 8:return tt(e,t,t.pendingProps.children,a),t.child;case 12:return tt(e,t,t.pendingProps.children,a),t.child;case 10:e:{if(r=t.type._context,o=t.pendingProps,n=t.memoizedProps,l=o.value,de(Ru,r._currentValue),r._currentValue=l,n!==null)if(Jt(n.value,l)){if(n.children===o.children&&!dt.current){t=Aa(e,t,a);break e}}else for(n=t.child,n!==null&&(n.return=t);n!==null;){var u=n.dependencies;if(u!==null){l=n.child;for(var s=u.firstContext;s!==null;){if(s.context===r){if(n.tag===1){s=Ra(-1,a&-a),s.tag=2;var i=n.updateQueue;if(i!==null){i=i.shared;var d=i.pending;d===null?s.next=s:(s.next=d.next,d.next=s),i.pending=s}}n.lanes|=a,s=n.alternate,s!==null&&(s.lanes|=a),ud(n.return,a,t),u.lanes|=a;break}s=s.next}}else if(n.tag===10)l=n.type===t.type?null:n.child;else if(n.tag===18){if(l=n.return,l===null)throw Error(_(341));l.lanes|=a,u=l.alternate,u!==null&&(u.lanes|=a),ud(l,a,t),l=n.sibling}else l=n.child;if(l!==null)l.return=n;else for(l=n;l!==null;){if(l===t){l=null;break}if(n=l.sibling,n!==null){n.return=l.return,l=n;break}l=l.return}n=l}tt(e,t,o.children,a),t=t.child}return t;case 9:return o=t.type,r=t.pendingProps.children,bo(t,a),o=Bt(o),r=r(o),t.flags|=1,tt(e,t,r,a),t.child;case 14:return r=t.type,o=$t(r,t.pendingProps),o=$t(r.type,o),Hp(e,t,r,o,a);case 15:return Th(e,t,t.type,t.pendingProps,a);case 17:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:$t(r,o),pu(e,t),t.tag=1,ft(r)?(e=!0,Mu(t)):e=!1,bo(t,a),kh(t,r,o),id(t,r,o,a),cd(null,t,r,!0,e,a);case 19:return Eh(e,t,a);case 22:return _h(e,t,a)}throw Error(_(156,t.tag))};function Kh(e,t){return Cm(e,t)}function Yw(e,t,a,r){this.tag=e,this.key=a,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ot(e,t,a,r){return new Yw(e,t,a,r)}function df(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Jw(e){if(typeof e=="function")return df(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Td)return 11;if(e===_d)return 14}return 2}function ur(e,t){var a=e.alternate;return a===null?(a=Ot(e.tag,t,e.key,e.mode),a.elementType=e.elementType,a.type=e.type,a.stateNode=e.stateNode,a.alternate=e,e.alternate=a):(a.pendingProps=t,a.type=e.type,a.flags=0,a.subtreeFlags=0,a.deletions=null),a.flags=e.flags&14680064,a.childLanes=e.childLanes,a.lanes=e.lanes,a.child=e.child,a.memoizedProps=e.memoizedProps,a.memoizedState=e.memoizedState,a.updateQueue=e.updateQueue,t=e.dependencies,a.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},a.sibling=e.sibling,a.index=e.index,a.ref=e.ref,a}function gu(e,t,a,r,o,n){var l=2;if(r=e,typeof e=="function")df(e)&&(l=1);else if(typeof e=="string")l=5;else e:switch(e){case vo:return br(a.children,o,n,t);case Md:l=8,o|=8;break;case Ei:return e=Ot(12,a,t,o|2),e.elementType=Ei,e.lanes=n,e;case Ai:return e=Ot(13,a,t,o),e.elementType=Ai,e.lanes=n,e;case Oi:return e=Ot(19,a,t,o),e.elementType=Oi,e.lanes=n,e;case nm:return Qu(a,o,n,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case rm:l=10;break e;case om:l=9;break e;case Td:l=11;break e;case _d:l=14;break e;case $a:l=16,r=null;break e}throw Error(_(130,e==null?e:typeof e,""))}return t=Ot(l,a,t,o),t.elementType=e,t.type=r,t.lanes=n,t}function br(e,t,a,r){return e=Ot(7,e,r,t),e.lanes=a,e}function Qu(e,t,a,r){return e=Ot(22,e,r,t),e.elementType=nm,e.lanes=a,e.stateNode={isHidden:!1},e}function Ri(e,t,a){return e=Ot(6,e,null,t),e.lanes=a,e}function Di(e,t,a){return t=Ot(4,e.children!==null?e.children:[],e.key,t),t.lanes=a,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function eC(e,t,a,r,o){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=mi(0),this.expirationTimes=mi(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=mi(0),this.identifierPrefix=r,this.onRecoverableError=o,this.mutableSourceEagerHydrationData=null}function ff(e,t,a,r,o,n,l,u,s){return e=new eC(e,t,a,u,s),t===1?(t=1,n===!0&&(t|=8)):t=0,n=Ot(3,null,null,t),e.current=n,n.stateNode=e,n.memoizedState={element:r,isDehydrated:a,cache:null,transitions:null,pendingSuspenseBoundaries:null},Zd(n),e}function tC(e,t,a){var r=3{"use strict";function eg(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__=="undefined"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(eg)}catch(e){console.error(e)}}eg(),tg.exports=Jh()});var rg=ka(hf=>{"use strict";var ag=Vo();hf.createRoot=ag.createRoot,hf.hydrateRoot=ag.hydrateRoot;var GS});var ng=ka(as=>{"use strict";var lC=G(),uC=Symbol.for("react.element"),sC=Symbol.for("react.fragment"),iC=Object.prototype.hasOwnProperty,dC=lC.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,fC={key:!0,ref:!0,__self:!0,__source:!0};function og(e,t,a){var r,o={},n=null,l=null;a!==void 0&&(n=""+a),t.key!==void 0&&(n=""+t.key),t.ref!==void 0&&(l=t.ref);for(r in t)iC.call(t,r)&&!fC.hasOwnProperty(r)&&(o[r]=t[r]);if(e&&e.defaultProps)for(r in t=e.defaultProps,t)o[r]===void 0&&(o[r]=t[r]);return{$$typeof:uC,type:e,key:n,ref:l,props:o,_owner:dC.current}}as.Fragment=sC;as.jsx=og;as.jsxs=og});var Ke=ka((KS,lg)=>{"use strict";lg.exports=ng()});var Ay=N(rg());var pt=function(){return pt=Object.assign||function(e){for(var t,a=1,r=arguments.length;ae.forEach(a=>function(r,o){typeof r=="function"?r(o):r!=null&&(r.current=o)}(a,t))}function se(...e){return(0,sg.useCallback)(Fa(...e),e)}var ha=(0,ke.forwardRef)((e,t)=>{let l=e,{children:a}=l,r=A(l,["children"]),o=ke.Children.toArray(a),n=o.find(pC);if(n){let u=n.props.children,s=o.map(i=>i===n?ke.Children.count(u)>1?ke.Children.only(null):(0,ke.isValidElement)(u)?u.props.children:null:i);return(0,ke.createElement)(gf,E({},r,{ref:t}),(0,ke.isValidElement)(u)?(0,ke.cloneElement)(u,void 0,s):null)}return(0,ke.createElement)(gf,E({},r,{ref:t}),a)});ha.displayName="Slot";var gf=(0,ke.forwardRef)((e,t)=>{let o=e,{children:a}=o,r=A(o,["children"]);return(0,ke.isValidElement)(a)?(0,ke.cloneElement)(a,b(w({},mC(r,a.props)),{ref:t?Fa(t,a.ref):a.ref})):ke.Children.count(a)>1?ke.Children.only(null):null});gf.displayName="SlotClone";var cC=({children:e})=>(0,ke.createElement)(ke.Fragment,null,e);function pC(e){return(0,ke.isValidElement)(e)&&e.type===cC}function mC(e,t){let a=w({},t);for(let r in t){let o=e[r],n=t[r];/^on[A-Z]/.test(r)?o&&n?a[r]=(...l)=>{n(...l),o(...l)}:o&&(a[r]=o):r==="style"?a[r]=w(w({},o),n):r==="className"&&(a[r]=[o,n].filter(Boolean).join(" "))}return w(w({},e),a)}function ig(e){var t,a,r="";if(typeof e=="string"||typeof e=="number")r+=e;else if(typeof e=="object")if(Array.isArray(e))for(t=0;ttypeof e=="boolean"?"".concat(e):e===0?"0":e,fg=rs,os=(e,t)=>a=>{var r;if((t==null?void 0:t.variants)==null)return fg(e,a==null?void 0:a.class,a==null?void 0:a.className);let{variants:o,defaultVariants:n}=t,l=Object.keys(o).map(i=>{let d=a==null?void 0:a[i],f=n==null?void 0:n[i];if(d===null)return null;let p=dg(d)||dg(f);return o[i][p]}),u=a&&Object.entries(a).reduce((i,d)=>{let[f,p]=d;return p===void 0||(i[f]=p),i},{}),s=t==null||(r=t.compoundVariants)===null||r===void 0?void 0:r.reduce((i,d)=>{let x=d,{class:f,className:p}=x,v=A(x,["class","className"]);return Object.entries(v).every(g=>{let[L,m]=g;return Array.isArray(m)?m.includes(w(w({},n),u)[L]):w(w({},n),u)[L]===m})?[...i,f,p]:i},[]);return fg(e,l,s,a==null?void 0:a.class,a==null?void 0:a.className)};var xf="-";function hC(e){let t=function(o){let{theme:n,prefix:l}=o,u={nextPart:new Map,validators:[]};return function(i,d){return d?i.map(([f,p])=>[f,p.map(v=>typeof v=="string"?d+v:typeof v=="object"?Object.fromEntries(Object.entries(v).map(([x,g])=>[d+x,g])):v)]):i}(Object.entries(o.classGroups),l).forEach(([i,d])=>{vf(d,u,i,n)}),u}(e),{conflictingClassGroups:a,conflictingClassGroupModifiers:r}=e;return{getClassGroupId:function(o){let n=o.split(xf);return n[0]===""&&n.length!==1&&n.shift(),mg(n,t)||function(l){if(cg.test(l)){let u=cg.exec(l)[1],s=u==null?void 0:u.substring(0,u.indexOf(":"));if(s)return"arbitrary.."+s}}(o)},getConflictingClassGroupIds:function(o,n){let l=a[o]||[];return n&&r[o]?[...l,...r[o]]:l}}}function mg(e,t){var l;if(e.length===0)return t.classGroupId;let a=e[0],r=t.nextPart.get(a),o=r?mg(e.slice(1),r):void 0;if(o)return o;if(t.validators.length===0)return;let n=e.join(xf);return(l=t.validators.find(({validator:u})=>u(n)))==null?void 0:l.classGroupId}var cg=/^\[(.+)\]$/;function vf(e,t,a,r){e.forEach(o=>{if(typeof o!="string"){if(typeof o=="function")return o.isThemeGetter?void vf(o(r),t,a,r):void t.validators.push({validator:o,classGroupId:a});Object.entries(o).forEach(([n,l])=>{vf(l,pg(t,n),a,r)})}else(o===""?t:pg(t,o)).classGroupId=a})}function pg(e,t){let a=e;return t.split(xf).forEach(r=>{a.nextPart.has(r)||a.nextPart.set(r,{nextPart:new Map,validators:[]}),a=a.nextPart.get(r)}),a}function gC(e){if(e<1)return{get:()=>{},set:()=>{}};let t=0,a=new Map,r=new Map;function o(n,l){a.set(n,l),t++,t>e&&(t=0,r=a,a=new Map)}return{get(n){let l=a.get(n);return l!==void 0?l:(l=r.get(n))!==void 0?(o(n,l),l):void 0},set(n,l){a.has(n)?a.set(n,l):o(n,l)}}}var hg="!";function vC(e){let t=e.separator,a=t.length===1,r=t[0],o=t.length;return function(n){let l=[],u,s=0,i=0;for(let p=0;pi?u-i:void 0}}}var xC=/\s+/;function yC(){let e,t,a=0,r="";for(;ad(i),e());return a=function(i){return w({cache:gC(i.cacheSize),splitModifiers:vC(i)},hC(i))}(s),r=a.cache.get,o=a.cache.set,n=l,l(u)};function l(u){let s=r(u);if(s)return s;let i=function(d,f){let{splitModifiers:p,getClassGroupId:v,getConflictingClassGroupIds:x}=f,g=new Set;return d.trim().split(xC).map(L=>{let{modifiers:m,hasImportantModifier:c,baseClassName:h,maybePostfixModifierPosition:y}=p(L),C=v(y?h.substring(0,y):h),I=!!y;if(!C){if(!y)return{isTailwindClass:!1,originalClassName:L};if(C=v(h),!C)return{isTailwindClass:!1,originalClassName:L};I=!1}let k=function(S){if(S.length<=1)return S;let M=[],P=[];return S.forEach(U=>{U[0]==="["?(M.push(...P.sort(),U),P=[]):P.push(U)}),M.push(...P.sort()),M}(m).join(":");return{isTailwindClass:!0,modifierId:c?k+hg:k,classGroupId:C,originalClassName:L,hasPostfixModifier:I}}).reverse().filter(L=>{if(!L.isTailwindClass)return!0;let{modifierId:m,classGroupId:c,hasPostfixModifier:h}=L,y=m+c;return!g.has(y)&&(g.add(y),x(c,h).forEach(C=>g.add(m+C)),!0)}).reverse().map(L=>L.originalClassName).join(" ")}(u,a);return o(u,i),i}return function(){return n(yC.apply(null,arguments))}}function ge(e){let t=a=>a[e]||[];return t.isThemeGetter=!0,t}var vg=/^\[(?:([a-z-]+):)?(.+)\]$/i,wC=/^\d+\/\d+$/,CC=new Set(["px","full","screen"]),IC=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,SC=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,kC=/^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,PC=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/;function ea(e){return Ur(e)||CC.has(e)||wC.test(e)}function pr(e){return jo(e,"length",AC)}function Ur(e){return!!e&&!Number.isNaN(Number(e))}function ns(e){return jo(e,"number",Ur)}function ll(e){return!!e&&Number.isInteger(Number(e))}function MC(e){return e.endsWith("%")&&Ur(e.slice(0,-1))}function X(e){return vg.test(e)}function mr(e){return IC.test(e)}var TC=new Set(["length","size","percentage"]);function _C(e){return jo(e,TC,xg)}function RC(e){return jo(e,"position",xg)}var DC=new Set(["image","url"]);function bC(e){return jo(e,DC,FC)}function EC(e){return jo(e,"",OC)}function ul(){return!0}function jo(e,t,a){let r=vg.exec(e);return!!r&&(r[1]?typeof t=="string"?r[1]===t:t.has(r[1]):a(r[2]))}function AC(e){return SC.test(e)}function xg(){return!1}function OC(e){return kC.test(e)}function FC(e){return PC.test(e)}function BC(){let e=ge("colors"),t=ge("spacing"),a=ge("blur"),r=ge("brightness"),o=ge("borderColor"),n=ge("borderRadius"),l=ge("borderSpacing"),u=ge("borderWidth"),s=ge("contrast"),i=ge("grayscale"),d=ge("hueRotate"),f=ge("invert"),p=ge("gap"),v=ge("gradientColorStops"),x=ge("gradientColorStopPositions"),g=ge("inset"),L=ge("margin"),m=ge("opacity"),c=ge("padding"),h=ge("saturate"),y=ge("scale"),C=ge("sepia"),I=ge("skew"),k=ge("space"),S=ge("translate"),M=()=>["auto",X,t],P=()=>[X,t],U=()=>["",ea,pr],j=()=>["auto",Ur,X],ae=()=>["","0",X],Z=()=>[Ur,ns],ue=()=>[Ur,X];return{cacheSize:500,separator:":",theme:{colors:[ul],spacing:[ea,pr],blur:["none","",mr,X],brightness:Z(),borderColor:[e],borderRadius:["none","","full",mr,X],borderSpacing:P(),borderWidth:U(),contrast:Z(),grayscale:ae(),hueRotate:ue(),invert:ae(),gap:P(),gradientColorStops:[e],gradientColorStopPositions:[MC,pr],inset:M(),margin:M(),opacity:Z(),padding:P(),saturate:Z(),scale:Z(),sepia:ae(),skew:ue(),space:P(),translate:P()},classGroups:{aspect:[{aspect:["auto","square","video",X]}],container:["container"],columns:[{columns:[mr]}],"break-after":[{"break-after":["auto","avoid","all","avoid-page","page","left","right","column"]}],"break-before":[{"break-before":["auto","avoid","all","avoid-page","page","left","right","column"]}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],float:[{float:["right","left","none"]}],clear:[{clear:["left","right","both","none"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:["bottom","center","left","left-bottom","left-top","right","right-bottom","right-top","top",X]}],overflow:[{overflow:["auto","hidden","clip","visible","scroll"]}],"overflow-x":[{"overflow-x":["auto","hidden","clip","visible","scroll"]}],"overflow-y":[{"overflow-y":["auto","hidden","clip","visible","scroll"]}],overscroll:[{overscroll:["auto","contain","none"]}],"overscroll-x":[{"overscroll-x":["auto","contain","none"]}],"overscroll-y":[{"overscroll-y":["auto","contain","none"]}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:[g]}],"inset-x":[{"inset-x":[g]}],"inset-y":[{"inset-y":[g]}],start:[{start:[g]}],end:[{end:[g]}],top:[{top:[g]}],right:[{right:[g]}],bottom:[{bottom:[g]}],left:[{left:[g]}],visibility:["visible","invisible","collapse"],z:[{z:["auto",ll,X]}],basis:[{basis:M()}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["wrap","wrap-reverse","nowrap"]}],flex:[{flex:["1","auto","initial","none",X]}],grow:[{grow:ae()}],shrink:[{shrink:ae()}],order:[{order:["first","last","none",ll,X]}],"grid-cols":[{"grid-cols":[ul]}],"col-start-end":[{col:["auto",{span:["full",ll,X]},X]}],"col-start":[{"col-start":j()}],"col-end":[{"col-end":j()}],"grid-rows":[{"grid-rows":[ul]}],"row-start-end":[{row:["auto",{span:[ll,X]},X]}],"row-start":[{"row-start":j()}],"row-end":[{"row-end":j()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":["auto","min","max","fr",X]}],"auto-rows":[{"auto-rows":["auto","min","max","fr",X]}],gap:[{gap:[p]}],"gap-x":[{"gap-x":[p]}],"gap-y":[{"gap-y":[p]}],"justify-content":[{justify:["normal","start","end","center","between","around","evenly","stretch"]}],"justify-items":[{"justify-items":["start","end","center","stretch"]}],"justify-self":[{"justify-self":["auto","start","end","center","stretch"]}],"align-content":[{content:["normal","start","end","center","between","around","evenly","stretch","baseline"]}],"align-items":[{items:["start","end","center","baseline","stretch"]}],"align-self":[{self:["auto","start","end","center","stretch","baseline"]}],"place-content":[{"place-content":["start","end","center","between","around","evenly","stretch","baseline"]}],"place-items":[{"place-items":["start","end","center","baseline","stretch"]}],"place-self":[{"place-self":["auto","start","end","center","stretch"]}],p:[{p:[c]}],px:[{px:[c]}],py:[{py:[c]}],ps:[{ps:[c]}],pe:[{pe:[c]}],pt:[{pt:[c]}],pr:[{pr:[c]}],pb:[{pb:[c]}],pl:[{pl:[c]}],m:[{m:[L]}],mx:[{mx:[L]}],my:[{my:[L]}],ms:[{ms:[L]}],me:[{me:[L]}],mt:[{mt:[L]}],mr:[{mr:[L]}],mb:[{mb:[L]}],ml:[{ml:[L]}],"space-x":[{"space-x":[k]}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":[k]}],"space-y-reverse":["space-y-reverse"],w:[{w:["auto","min","max","fit",X,t]}],"min-w":[{"min-w":["min","max","fit",X,ea]}],"max-w":[{"max-w":["0","none","full","min","max","fit","prose",{screen:[mr]},mr,X]}],h:[{h:[X,t,"auto","min","max","fit"]}],"min-h":[{"min-h":["min","max","fit",ea,X]}],"max-h":[{"max-h":[X,t,"min","max","fit"]}],"font-size":[{text:["base",mr,pr]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:["thin","extralight","light","normal","medium","semibold","bold","extrabold","black",ns]}],"font-family":[{font:[ul]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractons"],tracking:[{tracking:["tighter","tight","normal","wide","wider","widest",X]}],"line-clamp":[{"line-clamp":["none",Ur,ns]}],leading:[{leading:["none","tight","snug","normal","relaxed","loose",ea,X]}],"list-image":[{"list-image":["none",X]}],"list-style-type":[{list:["none","disc","decimal",X]}],"list-style-position":[{list:["inside","outside"]}],"placeholder-color":[{placeholder:[e]}],"placeholder-opacity":[{"placeholder-opacity":[m]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"text-color":[{text:[e]}],"text-opacity":[{"text-opacity":[m]}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:["solid","dashed","dotted","double","none","wavy"]}],"text-decoration-thickness":[{decoration:["auto","from-font",ea,pr]}],"underline-offset":[{"underline-offset":["auto",ea,X]}],"text-decoration-color":[{decoration:[e]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],indent:[{indent:P()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",X]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",X]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-opacity":[{"bg-opacity":[m]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:["bottom","center","left","left-bottom","left-top","right","right-bottom","right-top","top",RC]}],"bg-repeat":[{bg:["no-repeat",{repeat:["","x","y","round","space"]}]}],"bg-size":[{bg:["auto","cover","contain",_C]}],"bg-image":[{bg:["none",{"gradient-to":["t","tr","r","br","b","bl","l","tl"]},bC]}],"bg-color":[{bg:[e]}],"gradient-from-pos":[{from:[x]}],"gradient-via-pos":[{via:[x]}],"gradient-to-pos":[{to:[x]}],"gradient-from":[{from:[v]}],"gradient-via":[{via:[v]}],"gradient-to":[{to:[v]}],rounded:[{rounded:[n]}],"rounded-s":[{"rounded-s":[n]}],"rounded-e":[{"rounded-e":[n]}],"rounded-t":[{"rounded-t":[n]}],"rounded-r":[{"rounded-r":[n]}],"rounded-b":[{"rounded-b":[n]}],"rounded-l":[{"rounded-l":[n]}],"rounded-ss":[{"rounded-ss":[n]}],"rounded-se":[{"rounded-se":[n]}],"rounded-ee":[{"rounded-ee":[n]}],"rounded-es":[{"rounded-es":[n]}],"rounded-tl":[{"rounded-tl":[n]}],"rounded-tr":[{"rounded-tr":[n]}],"rounded-br":[{"rounded-br":[n]}],"rounded-bl":[{"rounded-bl":[n]}],"border-w":[{border:[u]}],"border-w-x":[{"border-x":[u]}],"border-w-y":[{"border-y":[u]}],"border-w-s":[{"border-s":[u]}],"border-w-e":[{"border-e":[u]}],"border-w-t":[{"border-t":[u]}],"border-w-r":[{"border-r":[u]}],"border-w-b":[{"border-b":[u]}],"border-w-l":[{"border-l":[u]}],"border-opacity":[{"border-opacity":[m]}],"border-style":[{border:["solid","dashed","dotted","double","none","hidden"]}],"divide-x":[{"divide-x":[u]}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":[u]}],"divide-y-reverse":["divide-y-reverse"],"divide-opacity":[{"divide-opacity":[m]}],"divide-style":[{divide:["solid","dashed","dotted","double","none"]}],"border-color":[{border:[o]}],"border-color-x":[{"border-x":[o]}],"border-color-y":[{"border-y":[o]}],"border-color-t":[{"border-t":[o]}],"border-color-r":[{"border-r":[o]}],"border-color-b":[{"border-b":[o]}],"border-color-l":[{"border-l":[o]}],"divide-color":[{divide:[o]}],"outline-style":[{outline:["","solid","dashed","dotted","double","none"]}],"outline-offset":[{"outline-offset":[ea,X]}],"outline-w":[{outline:[ea,pr]}],"outline-color":[{outline:[e]}],"ring-w":[{ring:U()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:[e]}],"ring-opacity":[{"ring-opacity":[m]}],"ring-offset-w":[{"ring-offset":[ea,pr]}],"ring-offset-color":[{"ring-offset":[e]}],shadow:[{shadow:["","inner","none",mr,EC]}],"shadow-color":[{shadow:[ul]}],opacity:[{opacity:[m]}],"mix-blend":[{"mix-blend":["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity","plus-lighter"]}],"bg-blend":[{"bg-blend":["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity","plus-lighter"]}],filter:[{filter:["","none"]}],blur:[{blur:[a]}],brightness:[{brightness:[r]}],contrast:[{contrast:[s]}],"drop-shadow":[{"drop-shadow":["","none",mr,X]}],grayscale:[{grayscale:[i]}],"hue-rotate":[{"hue-rotate":[d]}],invert:[{invert:[f]}],saturate:[{saturate:[h]}],sepia:[{sepia:[C]}],"backdrop-filter":[{"backdrop-filter":["","none"]}],"backdrop-blur":[{"backdrop-blur":[a]}],"backdrop-brightness":[{"backdrop-brightness":[r]}],"backdrop-contrast":[{"backdrop-contrast":[s]}],"backdrop-grayscale":[{"backdrop-grayscale":[i]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[d]}],"backdrop-invert":[{"backdrop-invert":[f]}],"backdrop-opacity":[{"backdrop-opacity":[m]}],"backdrop-saturate":[{"backdrop-saturate":[h]}],"backdrop-sepia":[{"backdrop-sepia":[C]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":[l]}],"border-spacing-x":[{"border-spacing-x":[l]}],"border-spacing-y":[{"border-spacing-y":[l]}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["none","all","","colors","opacity","shadow","transform",X]}],duration:[{duration:ue()}],ease:[{ease:["linear","in","out","in-out",X]}],delay:[{delay:ue()}],animate:[{animate:["none","spin","ping","pulse","bounce",X]}],transform:[{transform:["","gpu","none"]}],scale:[{scale:[y]}],"scale-x":[{"scale-x":[y]}],"scale-y":[{"scale-y":[y]}],rotate:[{rotate:[ll,X]}],"translate-x":[{"translate-x":[S]}],"translate-y":[{"translate-y":[S]}],"skew-x":[{"skew-x":[I]}],"skew-y":[{"skew-y":[I]}],"transform-origin":[{origin:["center","top","top-right","right","bottom-right","bottom","bottom-left","left","top-left",X]}],accent:[{accent:["auto",e]}],appearance:["appearance-none"],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",X]}],"caret-color":[{caret:[e]}],"pointer-events":[{"pointer-events":["none","auto"]}],resize:[{resize:["none","y","x",""]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":P()}],"scroll-mx":[{"scroll-mx":P()}],"scroll-my":[{"scroll-my":P()}],"scroll-ms":[{"scroll-ms":P()}],"scroll-me":[{"scroll-me":P()}],"scroll-mt":[{"scroll-mt":P()}],"scroll-mr":[{"scroll-mr":P()}],"scroll-mb":[{"scroll-mb":P()}],"scroll-ml":[{"scroll-ml":P()}],"scroll-p":[{"scroll-p":P()}],"scroll-px":[{"scroll-px":P()}],"scroll-py":[{"scroll-py":P()}],"scroll-ps":[{"scroll-ps":P()}],"scroll-pe":[{"scroll-pe":P()}],"scroll-pt":[{"scroll-pt":P()}],"scroll-pr":[{"scroll-pr":P()}],"scroll-pb":[{"scroll-pb":P()}],"scroll-pl":[{"scroll-pl":P()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",X]}],fill:[{fill:[e,"none"]}],"stroke-w":[{stroke:[ea,pr,ns]}],stroke:[{stroke:[e,"none"]}],sr:["sr-only","not-sr-only"]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]}}}var yg=LC(BC);function De(...e){return yg(rs(e))}var Cg=os("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"bg-primary text-primary-foreground shadow hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",outline:"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-9 px-4 py-2",sm:"h-8 rounded-md px-3 text-xs",lg:"h-10 rounded-md px-8",icon:"h-9 w-9"}},defaultVariants:{variant:"default",size:"default"}}),ls=wg.forwardRef((e,t)=>{var{className:a,variant:r,size:o,asChild:n=!1}=e,l=Se(e,["className","variant","size","asChild"]);return(0,Lg.jsx)(n?ha:"button",Object.assign({className:De(Cg({variant:r,size:o,className:a})),ref:t},l))});ls.displayName="Button";var ta=N(G(),1);function zt(e,t=[]){let a=[],r=()=>{let o=a.map(n=>(0,ta.createContext)(n));return function(n){let l=(n==null?void 0:n[e])||o;return(0,ta.useMemo)(()=>({[`__scope${e}`]:b(w({},n),{[e]:l})}),[n,l])}};return r.scopeName=e,[function(o,n){let l=(0,ta.createContext)(n),u=a.length;function s(i){let g=i,{scope:d,children:f}=g,p=A(g,["scope","children"]),v=(d==null?void 0:d[e][u])||l,x=(0,ta.useMemo)(()=>p,Object.values(p));return(0,ta.createElement)(v.Provider,{value:x},f)}return a=[...a,n],s.displayName=o+"Provider",[s,function(i,d){let f=(d==null?void 0:d[e][u])||l,p=(0,ta.useContext)(f);if(p)return p;if(n!==void 0)return n;throw new Error(`\`${i}\` must be used within \`${o}\``)}]},NC(r,...t)]}function NC(...e){let t=e[0];if(e.length===1)return t;let a=()=>{let r=e.map(o=>({useScope:o(),scopeName:o.scopeName}));return function(o){let n=r.reduce((l,{useScope:u,scopeName:s})=>w(w({},l),u(o)[`__scope${s}`]),{});return(0,ta.useMemo)(()=>({[`__scope${t.scopeName}`]:n}),[n])}};return a.scopeName=t.scopeName,a}var aa=N(G(),1);function qr(e){let t=e+"CollectionProvider",[a,r]=zt(t),[o,n]=a(t,{collectionRef:{current:null},itemMap:new Map}),l=e+"CollectionSlot",u=e+"CollectionItemSlot",s="data-radix-collection-item";return[{Provider:i=>{let{scope:d,children:f}=i,p=aa.default.useRef(null),v=aa.default.useRef(new Map).current;return aa.default.createElement(o,{scope:d,itemMap:v,collectionRef:p},f)},Slot:aa.default.forwardRef((i,d)=>{let{scope:f,children:p}=i,v=n(l,f),x=se(d,v.collectionRef);return aa.default.createElement(ha,{ref:x},p)}),ItemSlot:aa.default.forwardRef((i,d)=>{let m=i,{scope:f,children:p}=m,v=A(m,["scope","children"]),x=aa.default.useRef(null),g=se(d,x),L=n(u,f);return aa.default.useEffect(()=>(L.itemMap.set(x,w({ref:x},v)),()=>{L.itemMap.delete(x)})),aa.default.createElement(ha,{[s]:"",ref:g},p)})},function(i){let d=n(e+"CollectionConsumer",i);return aa.default.useCallback(()=>{let f=d.collectionRef.current;if(!f)return[];let p=Array.from(f.querySelectorAll(`[${s}]`));return Array.from(d.itemMap.values()).sort((v,x)=>p.indexOf(v.ref.current)-p.indexOf(x.ref.current))},[d.collectionRef,d.itemMap])},r]}function q(e,t,{checkForDefaultPrevented:a=!0}={}){return function(r){if(e==null||e(r),a===!1||!r.defaultPrevented)return t==null?void 0:t(r)}}var hr=N(G(),1);var Wo=N(G(),1);function fe(e){let t=(0,Wo.useRef)(e);return(0,Wo.useEffect)(()=>{t.current=e}),(0,Wo.useMemo)(()=>(...a)=>{var r;return(r=t.current)===null||r===void 0?void 0:r.call(t,...a)},[])}function Go({prop:e,defaultProp:t,onChange:a=()=>{}}){let[r,o]=function({defaultProp:s,onChange:i}){let d=(0,hr.useState)(s),[f]=d,p=(0,hr.useRef)(f),v=fe(i);return(0,hr.useEffect)(()=>{p.current!==f&&(v(f),p.current=f)},[f,p,v]),d}({defaultProp:t,onChange:a}),n=e!==void 0,l=n?e:r,u=fe(a);return[l,(0,hr.useCallback)(s=>{if(n){let i=typeof s=="function"?s(e):s;i!==e&&u(i)}else o(s)},[n,e,o,u])]}var Zo=N(G(),1),Ig=N(Vo(),1);var oe=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","span","svg","ul"].reduce((e,t)=>{let a=(0,Zo.forwardRef)((r,o)=>{let s=r,{asChild:n}=s,l=A(s,["asChild"]),u=n?ha:t;return(0,Zo.useEffect)(()=>{window[Symbol.for("radix-ui")]=!0},[]),(0,Zo.createElement)(u,E({},l,{ref:o}))});return a.displayName=`Primitive.${t}`,b(w({},e),{[t]:a})},{});function Hr(e,t){e&&(0,Ig.flushSync)(()=>e.dispatchEvent(t))}var Sg=N(G(),1),mt=globalThis!=null&&globalThis.document?Sg.useLayoutEffect:()=>{};var Xe=N(G(),1),kg=N(Vo(),1);var ga=e=>{let{present:t,children:a}=e,r=function(l){let[u,s]=(0,Xe.useState)(),i=(0,Xe.useRef)({}),d=(0,Xe.useRef)(l),f=(0,Xe.useRef)("none"),p=l?"mounted":"unmounted",[v,x]=function(g,L){return(0,Xe.useReducer)((m,c)=>{let h=L[m][c];return h!=null?h:m},g)}(p,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return(0,Xe.useEffect)(()=>{let g=us(i.current);f.current=v==="mounted"?g:"none"},[v]),mt(()=>{let g=i.current,L=d.current;if(L!==l){let m=f.current,c=us(g);l?x("MOUNT"):c==="none"||(g==null?void 0:g.display)==="none"?x("UNMOUNT"):x(L&&m!==c?"ANIMATION_OUT":"UNMOUNT"),d.current=l}},[l,x]),mt(()=>{if(u){let g=m=>{let c=us(i.current).includes(m.animationName);m.target===u&&c&&(0,kg.flushSync)(()=>x("ANIMATION_END"))},L=m=>{m.target===u&&(f.current=us(i.current))};return u.addEventListener("animationstart",L),u.addEventListener("animationcancel",g),u.addEventListener("animationend",g),()=>{u.removeEventListener("animationstart",L),u.removeEventListener("animationcancel",g),u.removeEventListener("animationend",g)}}x("ANIMATION_END")},[u,x]),{isPresent:["mounted","unmountSuspended"].includes(v),ref:(0,Xe.useCallback)(g=>{g&&(i.current=getComputedStyle(g)),s(g)},[])}}(t),o=typeof a=="function"?a({present:r.isPresent}):Xe.Children.only(a),n=se(r.ref,o.ref);return typeof a=="function"||r.isPresent?(0,Xe.cloneElement)(o,{ref:n}):null};function us(e){return(e==null?void 0:e.animationName)||"none"}ga.displayName="Presence";var ss=N(G(),1);var zC=ss.useId||(()=>{}),UC=0;function gr(e){let[t,a]=ss.useState(zC());return mt(()=>{e||a(r=>r!=null?r:String(UC++))},[e]),e||(t?`radix-${t}`:"")}var is=N(G(),1),qC=(0,is.createContext)(void 0);function $o(e){let t=(0,is.useContext)(qC);return e||t||"ltr"}var J=N(G(),1);function ra(e,t){if(e==null)return{};var a,r,o={},n=Object.keys(e);for(r=0;r=0||(o[a]=e[a]);return o}var HC=["color"],z1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,HC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M4.93179 5.43179C4.75605 5.60753 4.75605 5.89245 4.93179 6.06819C5.10753 6.24392 5.39245 6.24392 5.56819 6.06819L7.49999 4.13638L9.43179 6.06819C9.60753 6.24392 9.89245 6.24392 10.0682 6.06819C10.2439 5.89245 10.2439 5.60753 10.0682 5.43179L7.81819 3.18179C7.73379 3.0974 7.61933 3.04999 7.49999 3.04999C7.38064 3.04999 7.26618 3.0974 7.18179 3.18179L4.93179 5.43179ZM10.0682 9.56819C10.2439 9.39245 10.2439 9.10753 10.0682 8.93179C9.89245 8.75606 9.60753 8.75606 9.43179 8.93179L7.49999 10.8636L5.56819 8.93179C5.39245 8.75606 5.10753 8.75606 4.93179 8.93179C4.75605 9.10753 4.75605 9.39245 4.93179 9.56819L7.18179 11.8182C7.35753 11.9939 7.64245 11.9939 7.81819 11.8182L10.0682 9.56819Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),VC=["color"],Pg=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,VC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),jC=["color"],Mg=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,jC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),WC=["color"],U1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,WC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M8.84182 3.13514C9.04327 3.32401 9.05348 3.64042 8.86462 3.84188L5.43521 7.49991L8.86462 11.1579C9.05348 11.3594 9.04327 11.6758 8.84182 11.8647C8.64036 12.0535 8.32394 12.0433 8.13508 11.8419L4.38508 7.84188C4.20477 7.64955 4.20477 7.35027 4.38508 7.15794L8.13508 3.15794C8.32394 2.95648 8.64036 2.94628 8.84182 3.13514Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),GC=["color"],Tg=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,GC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M6.1584 3.13508C6.35985 2.94621 6.67627 2.95642 6.86514 3.15788L10.6151 7.15788C10.7954 7.3502 10.7954 7.64949 10.6151 7.84182L6.86514 11.8418C6.67627 12.0433 6.35985 12.0535 6.1584 11.8646C5.95694 11.6757 5.94673 11.3593 6.1356 11.1579L9.565 7.49985L6.1356 3.84182C5.94673 3.64036 5.95694 3.32394 6.1584 3.13508Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),ZC=["color"],q1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,ZC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M3.13523 8.84197C3.3241 9.04343 3.64052 9.05363 3.84197 8.86477L7.5 5.43536L11.158 8.86477C11.3595 9.05363 11.6759 9.04343 11.8648 8.84197C12.0536 8.64051 12.0434 8.32409 11.842 8.13523L7.84197 4.38523C7.64964 4.20492 7.35036 4.20492 7.15803 4.38523L3.15803 8.13523C2.95657 8.32409 2.94637 8.64051 3.13523 8.84197Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),$C=["color"],H1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,$C);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),KC=["color"],_g=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,KC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M9.875 7.5C9.875 8.81168 8.81168 9.875 7.5 9.875C6.18832 9.875 5.125 8.81168 5.125 7.5C5.125 6.18832 6.18832 5.125 7.5 5.125C8.81168 5.125 9.875 6.18832 9.875 7.5Z",fill:r}))}),XC=["color"],V1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,XC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M3.625 7.5C3.625 8.12132 3.12132 8.625 2.5 8.625C1.87868 8.625 1.375 8.12132 1.375 7.5C1.375 6.87868 1.87868 6.375 2.5 6.375C3.12132 6.375 3.625 6.87868 3.625 7.5ZM8.625 7.5C8.625 8.12132 8.12132 8.625 7.5 8.625C6.87868 8.625 6.375 8.12132 6.375 7.5C6.375 6.87868 6.87868 6.375 7.5 6.375C8.12132 6.375 8.625 6.87868 8.625 7.5ZM12.5 8.625C13.1213 8.625 13.625 8.12132 13.625 7.5C13.625 6.87868 13.1213 6.375 12.5 6.375C11.8787 6.375 11.375 6.87868 11.375 7.5C11.375 8.12132 11.8787 8.625 12.5 8.625Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),QC=["color"],j1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,QC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M5.5 4.625C6.12132 4.625 6.625 4.12132 6.625 3.5C6.625 2.87868 6.12132 2.375 5.5 2.375C4.87868 2.375 4.375 2.87868 4.375 3.5C4.375 4.12132 4.87868 4.625 5.5 4.625ZM9.5 4.625C10.1213 4.625 10.625 4.12132 10.625 3.5C10.625 2.87868 10.1213 2.375 9.5 2.375C8.87868 2.375 8.375 2.87868 8.375 3.5C8.375 4.12132 8.87868 4.625 9.5 4.625ZM10.625 7.5C10.625 8.12132 10.1213 8.625 9.5 8.625C8.87868 8.625 8.375 8.12132 8.375 7.5C8.375 6.87868 8.87868 6.375 9.5 6.375C10.1213 6.375 10.625 6.87868 10.625 7.5ZM5.5 8.625C6.12132 8.625 6.625 8.12132 6.625 7.5C6.625 6.87868 6.12132 6.375 5.5 6.375C4.87868 6.375 4.375 6.87868 4.375 7.5C4.375 8.12132 4.87868 8.625 5.5 8.625ZM10.625 11.5C10.625 12.1213 10.1213 12.625 9.5 12.625C8.87868 12.625 8.375 12.1213 8.375 11.5C8.375 10.8787 8.87868 10.375 9.5 10.375C10.1213 10.375 10.625 10.8787 10.625 11.5ZM5.5 12.625C6.12132 12.625 6.625 12.1213 6.625 11.5C6.625 10.8787 6.12132 10.375 5.5 10.375C4.87868 10.375 4.375 10.8787 4.375 11.5C4.375 12.1213 4.87868 12.625 5.5 12.625Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))}),YC=["color"],W1=(0,J.forwardRef)(function(e,t){var a=e.color,r=a===void 0?"currentColor":a,o=ra(e,YC);return(0,J.createElement)("svg",Object.assign({width:"15",height:"15",viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg"},o,{ref:t}),(0,J.createElement)("path",{d:"M10 6.5C10 8.433 8.433 10 6.5 10C4.567 10 3 8.433 3 6.5C3 4.567 4.567 3 6.5 3C8.433 3 10 4.567 10 6.5ZM9.30884 10.0159C8.53901 10.6318 7.56251 11 6.5 11C4.01472 11 2 8.98528 2 6.5C2 4.01472 4.01472 2 6.5 2C8.98528 2 11 4.01472 11 6.5C11 7.56251 10.6318 8.53901 10.0159 9.30884L12.8536 12.1464C13.0488 12.3417 13.0488 12.6583 12.8536 12.8536C12.6583 13.0488 12.3417 13.0488 12.1464 12.8536L9.30884 10.0159Z",fill:r,fillRule:"evenodd",clipRule:"evenodd"}))});var ce=N(G(),1);var Rg=N(G(),1);function Dg(e,t=globalThis==null?void 0:globalThis.document){let a=fe(e);(0,Rg.useEffect)(()=>{let r=o=>{o.key==="Escape"&&a(o)};return t.addEventListener("keydown",r),()=>t.removeEventListener("keydown",r)},[a,t])}var yf="dismissableLayer.update",JC="dismissableLayer.pointerDownOutside",eI="dismissableLayer.focusOutside",bg,Og=(0,ce.createContext)({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),ds=(0,ce.forwardRef)((e,t)=>{var a;let S=e,{disableOutsidePointerEvents:r=!1,onEscapeKeyDown:o,onPointerDownOutside:n,onFocusOutside:l,onInteractOutside:u,onDismiss:s}=S,i=A(S,["disableOutsidePointerEvents","onEscapeKeyDown","onPointerDownOutside","onFocusOutside","onInteractOutside","onDismiss"]),d=(0,ce.useContext)(Og),[f,p]=(0,ce.useState)(null),v=(a=f==null?void 0:f.ownerDocument)!==null&&a!==void 0?a:globalThis==null?void 0:globalThis.document,[,x]=(0,ce.useState)({}),g=se(t,M=>p(M)),L=Array.from(d.layers),[m]=[...d.layersWithOutsidePointerEventsDisabled].slice(-1),c=L.indexOf(m),h=f?L.indexOf(f):-1,y=d.layersWithOutsidePointerEventsDisabled.size>0,C=h>=c,I=function(M,P=globalThis==null?void 0:globalThis.document){let U=fe(M),j=(0,ce.useRef)(!1),ae=(0,ce.useRef)(()=>{});return(0,ce.useEffect)(()=>{let Z=Q=>{if(Q.target&&!j.current){let Je=function(){Ag(JC,U,le,{discrete:!0})};var xe=Je;let le={originalEvent:Q};Q.pointerType==="touch"?(P.removeEventListener("click",ae.current),ae.current=Je,P.addEventListener("click",ae.current,{once:!0})):Je()}else P.removeEventListener("click",ae.current);j.current=!1},ue=window.setTimeout(()=>{P.addEventListener("pointerdown",Z)},0);return()=>{window.clearTimeout(ue),P.removeEventListener("pointerdown",Z),P.removeEventListener("click",ae.current)}},[P,U]),{onPointerDownCapture:()=>j.current=!0}}(M=>{let P=M.target,U=[...d.branches].some(j=>j.contains(P));C&&!U&&(n==null||n(M),u==null||u(M),M.defaultPrevented||s==null||s())},v),k=function(M,P=globalThis==null?void 0:globalThis.document){let U=fe(M),j=(0,ce.useRef)(!1);return(0,ce.useEffect)(()=>{let ae=Z=>{Z.target&&!j.current&&Ag(eI,U,{originalEvent:Z},{discrete:!1})};return P.addEventListener("focusin",ae),()=>P.removeEventListener("focusin",ae)},[P,U]),{onFocusCapture:()=>j.current=!0,onBlurCapture:()=>j.current=!1}}(M=>{let P=M.target;[...d.branches].some(U=>U.contains(P))||(l==null||l(M),u==null||u(M),M.defaultPrevented||s==null||s())},v);return Dg(M=>{h===d.layers.size-1&&(o==null||o(M),!M.defaultPrevented&&s&&(M.preventDefault(),s()))},v),(0,ce.useEffect)(()=>{if(f)return r&&(d.layersWithOutsidePointerEventsDisabled.size===0&&(bg=v.body.style.pointerEvents,v.body.style.pointerEvents="none"),d.layersWithOutsidePointerEventsDisabled.add(f)),d.layers.add(f),Eg(),()=>{r&&d.layersWithOutsidePointerEventsDisabled.size===1&&(v.body.style.pointerEvents=bg)}},[f,v,r,d]),(0,ce.useEffect)(()=>()=>{f&&(d.layers.delete(f),d.layersWithOutsidePointerEventsDisabled.delete(f),Eg())},[f,d]),(0,ce.useEffect)(()=>{let M=()=>x({});return document.addEventListener(yf,M),()=>document.removeEventListener(yf,M)},[]),(0,ce.createElement)(oe.div,E({},i,{ref:g,style:w({pointerEvents:y?C?"auto":"none":void 0},e.style),onFocusCapture:q(e.onFocusCapture,k.onFocusCapture),onBlurCapture:q(e.onBlurCapture,k.onBlurCapture),onPointerDownCapture:q(e.onPointerDownCapture,I.onPointerDownCapture)}))}),tk=(0,ce.forwardRef)((e,t)=>{let a=(0,ce.useContext)(Og),r=(0,ce.useRef)(null),o=se(t,r);return(0,ce.useEffect)(()=>{let n=r.current;if(n)return a.branches.add(n),()=>{a.branches.delete(n)}},[a.branches]),(0,ce.createElement)(oe.div,E({},e,{ref:o}))});function Eg(){let e=new CustomEvent(yf);document.dispatchEvent(e)}function Ag(e,t,a,{discrete:r}){let o=a.originalEvent.target,n=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:a});t&&o.addEventListener(e,t,{once:!0}),r?Hr(o,n):o.dispatchEvent(n)}var ht=N(G(),1);var Lf="focusScope.autoFocusOnMount",wf="focusScope.autoFocusOnUnmount",Fg={bubbles:!1,cancelable:!0},qg=(0,ht.forwardRef)((e,t)=>{let g=e,{loop:a=!1,trapped:r=!1,onMountAutoFocus:o,onUnmountAutoFocus:n}=g,l=A(g,["loop","trapped","onMountAutoFocus","onUnmountAutoFocus"]),[u,s]=(0,ht.useState)(null),i=fe(o),d=fe(n),f=(0,ht.useRef)(null),p=se(t,L=>s(L)),v=(0,ht.useRef)({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;(0,ht.useEffect)(()=>{if(r){let h=function(k){if(v.paused||!u)return;let S=k.target;u.contains(S)?f.current=S:vr(f.current,{select:!0})},y=function(k){if(v.paused||!u)return;let S=k.relatedTarget;S!==null&&(u.contains(S)||vr(f.current,{select:!0}))},C=function(k){if(document.activeElement===document.body)for(let S of k)S.removedNodes.length>0&&vr(u)};var L=h,m=y,c=C;document.addEventListener("focusin",h),document.addEventListener("focusout",y);let I=new MutationObserver(C);return u&&I.observe(u,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",h),document.removeEventListener("focusout",y),I.disconnect()}}},[r,u,v.paused]),(0,ht.useEffect)(()=>{if(u){zg.add(v);let m=document.activeElement;if(!u.contains(m)){let c=new CustomEvent(Lf,Fg);u.addEventListener(Lf,i),u.dispatchEvent(c),c.defaultPrevented||(function(h,{select:y=!1}={}){let C=document.activeElement;for(let I of h)if(vr(I,{select:y}),document.activeElement!==C)return}((L=Bg(u),L.filter(h=>h.tagName!=="A")),{select:!0}),document.activeElement===m&&vr(u))}return()=>{u.removeEventListener(Lf,i),setTimeout(()=>{let c=new CustomEvent(wf,Fg);u.addEventListener(wf,d),u.dispatchEvent(c),c.defaultPrevented||vr(m!=null?m:document.body,{select:!0}),u.removeEventListener(wf,d),zg.remove(v)},0)}}var L},[u,i,d,v]);let x=(0,ht.useCallback)(L=>{if(!a&&!r||v.paused)return;let m=L.key==="Tab"&&!L.altKey&&!L.ctrlKey&&!L.metaKey,c=document.activeElement;if(m&&c){let h=L.currentTarget,[y,C]=function(I){let k=Bg(I),S=Ng(k,I),M=Ng(k.reverse(),I);return[S,M]}(h);y&&C?L.shiftKey||c!==C?L.shiftKey&&c===y&&(L.preventDefault(),a&&vr(C,{select:!0})):(L.preventDefault(),a&&vr(y,{select:!0})):c===h&&L.preventDefault()}},[a,r,v.paused]);return(0,ht.createElement)(oe.div,E({tabIndex:-1},l,{ref:p,onKeyDown:x}))});function Bg(e){let t=[],a=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:r=>{let o=r.tagName==="INPUT"&&r.type==="hidden";return r.disabled||r.hidden||o?NodeFilter.FILTER_SKIP:r.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;a.nextNode();)t.push(a.currentNode);return t}function Ng(e,t){for(let a of e)if(!tI(a,{upTo:t}))return a}function tI(e,{upTo:t}){if(getComputedStyle(e).visibility==="hidden")return!0;for(;e;){if(t!==void 0&&e===t)return!1;if(getComputedStyle(e).display==="none")return!0;e=e.parentElement}return!1}function vr(e,{select:t=!1}={}){if(e&&e.focus){let a=document.activeElement;e.focus({preventScroll:!0}),e!==a&&function(r){return r instanceof HTMLInputElement&&"select"in r}(e)&&t&&e.select()}}var zg=function(){let e=[];return{add(t){let a=e[0];t!==a&&(a==null||a.pause()),e=Ug(e,t),e.unshift(t)},remove(t){var a;e=Ug(e,t),(a=e[0])===null||a===void 0||a.resume()}}}();function Ug(e,t){let a=[...e],r=a.indexOf(t);return r!==-1&&a.splice(r,1),a}var Vg=N(G(),1),Cf=0;function jg(){(0,Vg.useEffect)(()=>{var e,t;let a=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",(e=a[0])!==null&&e!==void 0?e:Hg()),document.body.insertAdjacentElement("beforeend",(t=a[1])!==null&&t!==void 0?t:Hg()),Cf++,()=>{Cf===1&&document.querySelectorAll("[data-radix-focus-guard]").forEach(r=>r.remove()),Cf--}},[])}function Hg(){let e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.cssText="outline: none; opacity: 0; position: fixed; pointer-events: none",e}var hs=N(G(),1);var Qe=N(G(),1);var Ko="right-scroll-bar-position",Xo="width-before-scroll-bar",Wg="with-scroll-bars-hidden",Gg="--removed-body-scroll-bar-size";function Zg(e,t){return typeof e=="function"?e(t):e&&(e.current=t),e}var $g=N(G(),1);function Kg(e,t){var a=(0,$g.useState)(function(){return{value:e,callback:t,facade:{get current(){return a.value},set current(r){var o=a.value;o!==r&&(a.value=r,a.callback(r,o))}}}})[0];return a.callback=t,a.facade}function Xg(e,t){return Kg(t||null,function(a){return e.forEach(function(r){return Zg(r,a)})})}var wk=N(G(),1);function aI(e){return e}function Qg(e){e===void 0&&(e={});var t=function(a,r){r===void 0&&(r=aI);var o=[],n=!1;return{read:function(){if(n)throw new Error("Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.");return o.length?o[o.length-1]:a},useMedium:function(l){var u=r(l,n);return o.push(u),function(){o=o.filter(function(s){return s!==u})}},assignSyncMedium:function(l){for(n=!0;o.length;){var u=o;o=[],u.forEach(l)}o={push:function(s){return l(s)},filter:function(){return o}}},assignMedium:function(l){n=!0;var u=[];if(o.length){var s=o;o=[],s.forEach(l),u=o}var i=function(){var f=u;u=[],f.forEach(l)},d=function(){return Promise.resolve().then(i)};d(),o={push:function(f){u.push(f),d()},filter:function(f){return u=u.filter(f),o}}}}}(null);return t.options=pt({async:!0,ssr:!1},e),t}var Yg=N(G(),1),Jg=function(e){var t=e.sideCar,a=Se(e,["sideCar"]);if(!t)throw new Error("Sidecar: please provide `sideCar` property to import the right car");var r=t.read();if(!r)throw new Error("Sidecar medium not found");return Yg.createElement(r,pt({},a))};function ev(e,t){return e.useMedium(t),Jg}Jg.isSideCarExport=!0;var fs=Qg();var If=function(){},sl=Qe.forwardRef(function(e,t){var a=Qe.useRef(null),r=Qe.useState({onScrollCapture:If,onWheelCapture:If,onTouchMoveCapture:If}),o=r[0],n=r[1],l=e.forwardProps,u=e.children,s=e.className,i=e.removeScrollBar,d=e.enabled,f=e.shards,p=e.sideCar,v=e.noIsolation,x=e.inert,g=e.allowPinchZoom,L=e.as,m=L===void 0?"div":L,c=Se(e,["forwardProps","children","className","removeScrollBar","enabled","shards","sideCar","noIsolation","inert","allowPinchZoom","as"]),h=p,y=Xg([a,t]),C=pt(pt({},c),o);return Qe.createElement(Qe.Fragment,null,d&&Qe.createElement(h,{sideCar:fs,removeScrollBar:i,shards:f,noIsolation:v,inert:x,setCallbacks:n,allowPinchZoom:!!g,lockRef:a}),l?Qe.cloneElement(Qe.Children.only(u),pt(pt({},C),{ref:y})):Qe.createElement(m,pt({},C,{className:s,ref:y}),u))});sl.defaultProps={enabled:!0,removeScrollBar:!0,inert:!1},sl.classNames={fullWidth:Xo,zeroRight:Ko};var Kk=N(G(),1);var ve=N(G(),1);var ps=N(G(),1);var rv=N(G(),1);var tv=function(){if(typeof __webpack_nonce__!="undefined")return __webpack_nonce__};var av=function(){var e=0,t=null;return{add:function(a){var r,o;e==0&&(t=function(){if(!document)return null;var n=document.createElement("style");n.type="text/css";var l=tv();return l&&n.setAttribute("nonce",l),n}())&&(o=a,(r=t).styleSheet?r.styleSheet.cssText=o:r.appendChild(document.createTextNode(o)),function(n){(document.head||document.getElementsByTagName("head")[0]).appendChild(n)}(t)),e++},remove:function(){!--e&&t&&(t.parentNode&&t.parentNode.removeChild(t),t=null)}}};var ov=function(){var e=av();return function(t,a){rv.useEffect(function(){return e.add(t),function(){e.remove()}},[t&&a])}};var cs=function(){var e=ov();return function(t){var a=t.styles,r=t.dynamic;return e(a,r),null}};var rI={left:0,top:0,right:0,gap:0},Sf=function(e){return parseInt(e||"",10)||0},nv=function(e){if(e===void 0&&(e="margin"),typeof window=="undefined")return rI;var t=function(o){var n=window.getComputedStyle(document.body),l=n[o==="padding"?"paddingLeft":"marginLeft"],u=n[o==="padding"?"paddingTop":"marginTop"],s=n[o==="padding"?"paddingRight":"marginRight"];return[Sf(l),Sf(u),Sf(s)]}(e),a=document.documentElement.clientWidth,r=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,r-a+t[2]-t[0])}};var oI=cs(),nI=function(e,t,a,r){var o=e.left,n=e.top,l=e.right,u=e.gap;return a===void 0&&(a="margin"),` - .`.concat(Wg,` { - overflow: hidden `).concat(r,`; - padding-right: `).concat(u,"px ").concat(r,`; - } - body { - overflow: hidden `).concat(r,`; - overscroll-behavior: contain; - `).concat([t&&"position: relative ".concat(r,";"),a==="margin"&&` - padding-left: `.concat(o,`px; - padding-top: `).concat(n,`px; - padding-right: `).concat(l,`px; - margin-left:0; - margin-top:0; - margin-right: `).concat(u,"px ").concat(r,`; - `),a==="padding"&&"padding-right: ".concat(u,"px ").concat(r,";")].filter(Boolean).join(""),` - } - - .`).concat(Ko,` { - right: `).concat(u,"px ").concat(r,`; - } - - .`).concat(Xo,` { - margin-right: `).concat(u,"px ").concat(r,`; - } - - .`).concat(Ko," .").concat(Ko,` { - right: 0 `).concat(r,`; - } - - .`).concat(Xo," .").concat(Xo,` { - margin-right: 0 `).concat(r,`; - } - - body { - `).concat(Gg,": ").concat(u,`px; - } -`)},lv=function(e){var t=e.noRelative,a=e.noImportant,r=e.gapMode,o=r===void 0?"margin":r,n=ps.useMemo(function(){return nv(o)},[o]);return ps.createElement(oI,{styles:nI(n,!t,o,a?"":"!important")})};var kf=!1;if(typeof window!="undefined")try{il=Object.defineProperty({},"passive",{get:function(){return kf=!0,!0}}),window.addEventListener("test",il,il),window.removeEventListener("test",il,il)}catch(e){kf=!1}var il,Vr=!!kf&&{passive:!1};var uv=function(e,t){var a=window.getComputedStyle(e);return a[t]!=="hidden"&&!(a.overflowY===a.overflowX&&!function(r){return r.tagName==="TEXTAREA"}(e)&&a[t]==="visible")},Pf=function(e,t){var a=t;do{if(typeof ShadowRoot!="undefined"&&a instanceof ShadowRoot&&(a=a.host),sv(e,a)){var r=iv(e,a);if(r[1]>r[2])return!0}a=a.parentNode}while(a&&a!==document.body);return!1},sv=function(e,t){return e==="v"?function(a){return uv(a,"overflowY")}(t):function(a){return uv(a,"overflowX")}(t)},iv=function(e,t){return e==="v"?[(a=t).scrollTop,a.scrollHeight,a.clientHeight]:function(r){return[r.scrollLeft,r.scrollWidth,r.clientWidth]}(t);var a},dv=function(e,t,a,r,o){var n=function(L,m){return L==="h"&&m==="rtl"?-1:1}(e,window.getComputedStyle(t).direction),l=n*r,u=a.target,s=t.contains(u),i=!1,d=l>0,f=0,p=0;do{var v=iv(e,u),x=v[0],g=v[1]-v[2]-n*x;(x||g)&&sv(e,u)&&(f+=g,p+=x),u=u.parentNode}while(!s&&u!==document.body||s&&(t.contains(u)||t===u));return(d&&(o&&f===0||!o&&l>f)||!d&&(o&&p===0||!o&&-l>p))&&(i=!0),i};var ms=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},fv=function(e){return[e.deltaX,e.deltaY]},cv=function(e){return e&&"current"in e?e.current:e},lI=function(e){return` - .block-interactivity-`.concat(e,` {pointer-events: none;} - .allow-interactivity-`).concat(e,` {pointer-events: all;} -`)},uI=0,Qo=[];function pv(e){var t=ve.useRef([]),a=ve.useRef([0,0]),r=ve.useRef(),o=ve.useState(uI++)[0],n=ve.useState(function(){return cs()})[0],l=ve.useRef(e);ve.useEffect(function(){l.current=e},[e]),ve.useEffect(function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var g=ug([e.lockRef.current],(e.shards||[]).map(cv),!0).filter(Boolean);return g.forEach(function(L){return L.classList.add("allow-interactivity-".concat(o))}),function(){document.body.classList.remove("block-interactivity-".concat(o)),g.forEach(function(L){return L.classList.remove("allow-interactivity-".concat(o))})}}},[e.inert,e.lockRef.current,e.shards]);var u=ve.useCallback(function(g,L){if("touches"in g&&g.touches.length===2)return!l.current.allowPinchZoom;var m,c=ms(g),h=a.current,y="deltaX"in g?g.deltaX:h[0]-c[0],C="deltaY"in g?g.deltaY:h[1]-c[1],I=g.target,k=Math.abs(y)>Math.abs(C)?"h":"v";if("touches"in g&&k==="h"&&I.type==="range")return!1;var S=Pf(k,I);if(!S)return!0;if(S?m=k:(m=k==="v"?"h":"v",S=Pf(k,I)),!S)return!1;if(!r.current&&"changedTouches"in g&&(y||C)&&(r.current=m),!m)return!0;var M=r.current||m;return dv(M,L,g,M==="h"?y:C,!0)},[]),s=ve.useCallback(function(g){var L=g;if(Qo.length&&Qo[Qo.length-1]===n){var m="deltaY"in L?fv(L):ms(L),c=t.current.filter(function(y){return y.name===L.type&&y.target===L.target&&(C=y.delta,I=m,C[0]===I[0]&&C[1]===I[1]);var C,I})[0];if(c&&c.should)L.cancelable&&L.preventDefault();else if(!c){var h=(l.current.shards||[]).map(cv).filter(Boolean).filter(function(y){return y.contains(L.target)});(h.length>0?u(L,h[0]):!l.current.noIsolation)&&L.cancelable&&L.preventDefault()}}},[]),i=ve.useCallback(function(g,L,m,c){var h={name:g,delta:L,target:m,should:c};t.current.push(h),setTimeout(function(){t.current=t.current.filter(function(y){return y!==h})},1)},[]),d=ve.useCallback(function(g){a.current=ms(g),r.current=void 0},[]),f=ve.useCallback(function(g){i(g.type,fv(g),g.target,u(g,e.lockRef.current))},[]),p=ve.useCallback(function(g){i(g.type,ms(g),g.target,u(g,e.lockRef.current))},[]);ve.useEffect(function(){return Qo.push(n),e.setCallbacks({onScrollCapture:f,onWheelCapture:f,onTouchMoveCapture:p}),document.addEventListener("wheel",s,Vr),document.addEventListener("touchmove",s,Vr),document.addEventListener("touchstart",d,Vr),function(){Qo=Qo.filter(function(g){return g!==n}),document.removeEventListener("wheel",s,Vr),document.removeEventListener("touchmove",s,Vr),document.removeEventListener("touchstart",d,Vr)}},[]);var v=e.removeScrollBar,x=e.inert;return ve.createElement(ve.Fragment,null,x?ve.createElement(n,{styles:lI(o)}):null,v?ve.createElement(lv,{gapMode:"margin"}):null)}var mv=ev(fs,pv);var Mf=hs.forwardRef(function(e,t){return hs.createElement(sl,pt({},e,{ref:t,sideCar:mv}))});Mf.classNames=sl.classNames;var Yo=new WeakMap,gs=new WeakMap,vs={},hv=0,gv=function(e){return e&&(e.host||gv(e.parentNode))},sI=function(e,t,a,r){var o=function(f,p){return p.map(function(v){if(f.contains(v))return v;var x=gv(v);return x&&f.contains(x)?x:(console.error("aria-hidden",v,"in not contained inside",f,". Doing nothing"),null)}).filter(function(v){return!!v})}(t,Array.isArray(e)?e:[e]);vs[a]||(vs[a]=new WeakMap);var n=vs[a],l=[],u=new Set,s=new Set(o),i=function(f){f&&!u.has(f)&&(u.add(f),i(f.parentNode))};o.forEach(i);var d=function(f){f&&!s.has(f)&&Array.prototype.forEach.call(f.children,function(p){if(u.has(p))d(p);else{var v=p.getAttribute(r),x=v!==null&&v!=="false",g=(Yo.get(p)||0)+1,L=(n.get(p)||0)+1;Yo.set(p,g),n.set(p,L),l.push(p),g===1&&x&&gs.set(p,!0),L===1&&p.setAttribute(a,"true"),x||p.setAttribute(r,"true")}})};return d(t),u.clear(),hv++,function(){l.forEach(function(f){var p=Yo.get(f)-1,v=n.get(f)-1;Yo.set(f,p),n.set(f,v),p||(gs.has(f)||f.removeAttribute(r),gs.delete(f)),v||f.removeAttribute(a)}),--hv||(Yo=new WeakMap,Yo=new WeakMap,gs=new WeakMap,vs={})}},vv=function(e,t,a){a===void 0&&(a="data-aria-hidden");var r=Array.from(Array.isArray(e)?e:[e]),o=t||function(n){return typeof document=="undefined"?null:(Array.isArray(n)?n[0]:n).ownerDocument.body}(e);return o?(r.push.apply(r,Array.from(o.querySelectorAll("[aria-live]"))),sI(r,o,a,"aria-hidden")):function(){return null}};var xs=N(G(),1);function xv(e){let t=(0,xs.useRef)({value:e,previous:e});return(0,xs.useMemo)(()=>(t.current.value!==e&&(t.current.previous=t.current.value,t.current.value=e),t.current.previous),[e])}var yv=N(G(),1);function Lv(e){let[t,a]=(0,yv.useState)(void 0);return mt(()=>{if(e){a({width:e.offsetWidth,height:e.offsetHeight});let r=new ResizeObserver(o=>{if(!Array.isArray(o)||!o.length)return;let n=o[0],l,u;if("borderBoxSize"in n){let s=n.borderBoxSize,i=Array.isArray(s)?s[0]:s;l=i.inlineSize,u=i.blockSize}else l=e.offsetWidth,u=e.offsetHeight;a({width:l,height:u})});return r.observe(e,{box:"border-box"}),()=>r.unobserve(e)}a(void 0)},[e]),t}var R=N(G(),1);var Ne=N(G(),1);var wv=["top","right","bottom","left"],va=Math.min,ot=Math.max,fl=Math.round,cl=Math.floor,Ba=e=>({x:e,y:e}),iI={left:"right",right:"left",bottom:"top",top:"bottom"},dI={start:"end",end:"start"};function ys(e,t,a){return ot(e,va(t,a))}function xa(e,t){return typeof e=="function"?e(t):e}function ya(e){return e.split("-")[0]}function jr(e){return e.split("-")[1]}function Ls(e){return e==="x"?"y":"x"}function ws(e){return e==="y"?"height":"width"}function Wr(e){return["top","bottom"].includes(ya(e))?"y":"x"}function Cs(e){return Ls(Wr(e))}function Cv(e,t,a){a===void 0&&(a=!1);let r=jr(e),o=Cs(e),n=ws(o),l=o==="x"?r===(a?"end":"start")?"right":"left":r==="start"?"bottom":"top";return t.reference[n]>t.floating[n]&&(l=dl(l)),[l,dl(l)]}function Iv(e){let t=dl(e);return[Tf(e),t,Tf(t)]}function Tf(e){return e.replace(/start|end/g,t=>dI[t])}function Sv(e,t,a,r){let o=jr(e),n=function(l,u,s){let i=["left","right"],d=["right","left"],f=["top","bottom"],p=["bottom","top"];switch(l){case"top":case"bottom":return s?u?d:i:u?i:d;case"left":case"right":return u?f:p;default:return[]}}(ya(e),a==="start",r);return o&&(n=n.map(l=>l+"-"+o),t&&(n=n.concat(n.map(Tf)))),n}function dl(e){return e.replace(/left|right|bottom|top/g,t=>iI[t])}function fI(e){return w({top:0,right:0,bottom:0,left:0},e)}function _f(e){return typeof e!="number"?fI(e):{top:e,right:e,bottom:e,left:e}}function Jo(e){return b(w({},e),{top:e.y,left:e.x,right:e.x+e.width,bottom:e.y+e.height})}function kv(e,t,a){let{reference:r,floating:o}=e,n=Wr(t),l=Cs(t),u=ws(l),s=ya(t),i=n==="y",d=r.x+r.width/2-o.width/2,f=r.y+r.height/2-o.height/2,p=r[u]/2-o[u]/2,v;switch(s){case"top":v={x:d,y:r.y-o.height};break;case"bottom":v={x:d,y:r.y+r.height};break;case"right":v={x:r.x+r.width,y:f};break;case"left":v={x:r.x-o.width,y:f};break;default:v={x:r.x,y:r.y}}switch(jr(t)){case"start":v[l]-=p*(a&&i?-1:1);break;case"end":v[l]+=p*(a&&i?-1:1)}return v}var Tv=(e,t,a)=>Pe(void 0,null,function*(){let{placement:r="bottom",strategy:o="absolute",middleware:n=[],platform:l}=a,u=n.filter(Boolean),s=yield l.isRTL==null?void 0:l.isRTL(t),i=yield l.getElementRects({reference:e,floating:t,strategy:o}),{x:d,y:f}=kv(i,r,s),p=r,v={},x=0;for(let g=0;g({name:"arrow",options:e,fn(a){return Pe(this,null,function*(){let{x:r,y:o,placement:n,rects:l,platform:u,elements:s,middlewareData:i}=a,{element:d,padding:f=0}=xa(e,a)||{};if(d==null)return{};let p=_f(f),v={x:r,y:o},x=Cs(n),g=ws(x),L=yield u.getDimensions(d),m=x==="y",c=m?"top":"left",h=m?"bottom":"right",y=m?"clientHeight":"clientWidth",C=l.reference[g]+l.reference[x]-v[x]-l.floating[g],I=v[x]-l.reference[x],k=yield u.getOffsetParent==null?void 0:u.getOffsetParent(d),S=k?k[y]:0;S&&(yield u.isElement==null?void 0:u.isElement(k))||(S=s.floating[y]||l.floating[g]);let M=C/2-I/2,P=S/2-L[g]/2-1,U=va(p[c],P),j=va(p[h],P),ae=U,Z=S-L[g]-j,ue=S/2-L[g]/2+M,Q=ys(ae,ue,Z),xe=!i.arrow&&jr(n)!=null&&ue!=Q&&l.reference[g]/2-(ueZ<=0)){var P,U;let Z=(((P=l.flip)==null?void 0:P.index)||0)+1,ue=I[Z];if(ue)return{data:{index:Z,overflows:M},reset:{placement:ue}};let Q=(U=M.filter(xe=>xe.overflows[0]<=0).sort((xe,le)=>xe.overflows[1]-le.overflows[1])[0])==null?void 0:U.placement;if(!Q)switch(x){case"bestFit":{var j;let xe=(j=M.map(le=>[le.placement,le.overflows.filter(Je=>Je>0).reduce((Je,W)=>Je+W,0)]).sort((le,Je)=>le[1]-Je[1])[0])==null?void 0:j[0];xe&&(Q=xe);break}case"initialPlacement":Q=s}if(n!==Q)return{reset:{placement:Q}}}return{}})}}};function Pv(e,t){return{top:e.top-t.height,right:e.right-t.width,bottom:e.bottom-t.height,left:e.left-t.width}}function Mv(e){return wv.some(t=>e[t]>=0)}var Rv=function(e){return e===void 0&&(e={}),{name:"hide",options:e,fn(a){return Pe(this,null,function*(){let{rects:r}=a,l=xa(e,a),{strategy:o="referenceHidden"}=l,n=A(l,["strategy"]);switch(o){case"referenceHidden":{let u=Pv(yield pl(a,b(w({},n),{elementContext:"reference"})),r.reference);return{data:{referenceHiddenOffsets:u,referenceHidden:Mv(u)}}}case"escaped":{let u=Pv(yield pl(a,b(w({},n),{altBoundary:!0})),r.floating);return{data:{escapedOffsets:u,escaped:Mv(u)}}}default:return{}}})}}},Dv=function(e){return e===void 0&&(e=0),{name:"offset",options:e,fn(a){return Pe(this,null,function*(){var r,o;let{x:n,y:l,placement:u,middlewareData:s}=a,i=yield function(d,f){return Pe(this,null,function*(){let{placement:p,platform:v,elements:x}=d,g=yield v.isRTL==null?void 0:v.isRTL(x.floating),L=ya(p),m=jr(p),c=Wr(p)==="y",h=["left","top"].includes(L)?-1:1,y=g&&c?-1:1,C=xa(f,d),{mainAxis:I,crossAxis:k,alignmentAxis:S}=typeof C=="number"?{mainAxis:C,crossAxis:0,alignmentAxis:null}:w({mainAxis:0,crossAxis:0,alignmentAxis:null},C);return m&&typeof S=="number"&&(k=m==="end"?-1*S:S),c?{x:k*y,y:I*h}:{x:I*h,y:k*y}})}(a,e);return u===((r=s.offset)==null?void 0:r.placement)&&(o=s.arrow)!=null&&o.alignmentOffset?{}:{x:n+i.x,y:l+i.y,data:b(w({},i),{placement:u})}})}}},bv=function(e){return e===void 0&&(e={}),{name:"shift",options:e,fn(a){return Pe(this,null,function*(){let{x:r,y:o,placement:n}=a,m=xa(e,a),{mainAxis:l=!0,crossAxis:u=!1,limiter:s={fn:c=>{let{x:h,y}=c;return{x:h,y}}}}=m,i=A(m,["mainAxis","crossAxis","limiter"]),d={x:r,y:o},f=yield pl(a,i),p=Wr(ya(n)),v=Ls(p),x=d[v],g=d[p];if(l){let c=v==="y"?"bottom":"right",h=x+f[v==="y"?"top":"left"],y=x-f[c];x=ys(h,x,y)}if(u){let c=p==="y"?"bottom":"right",h=g+f[p==="y"?"top":"left"],y=g-f[c];g=ys(h,g,y)}let L=s.fn(b(w({},a),{[v]:x,[p]:g}));return b(w({},L),{data:{x:L.x-r,y:L.y-o}})})}}},Ev=function(e){return e===void 0&&(e={}),{options:e,fn(t){let{x:a,y:r,placement:o,rects:n,middlewareData:l}=t,{offset:u=0,mainAxis:s=!0,crossAxis:i=!0}=xa(e,t),d={x:a,y:r},f=Wr(o),p=Ls(f),v=d[p],x=d[f],g=xa(u,t),L=typeof g=="number"?{mainAxis:g,crossAxis:0}:w({mainAxis:0,crossAxis:0},g);if(s){let h=p==="y"?"height":"width",y=n.reference[p]-n.floating[h]+L.mainAxis,C=n.reference[p]+n.reference[h]-L.mainAxis;vC&&(v=C)}if(i){var m,c;let h=p==="y"?"width":"height",y=["top","left"].includes(ya(o)),C=n.reference[f]-n.floating[h]+(y&&((m=l.offset)==null?void 0:m[f])||0)+(y?0:L.crossAxis),I=n.reference[f]+n.reference[h]+(y?0:((c=l.offset)==null?void 0:c[f])||0)-(y?L.crossAxis:0);xI&&(x=I)}return{[p]:v,[f]:x}}}},Av=function(e){return e===void 0&&(e={}),{name:"size",options:e,fn(a){return Pe(this,null,function*(){let{placement:r,rects:o,platform:n,elements:l}=a,k=xa(e,a),{apply:u=()=>{}}=k,s=A(k,["apply"]),i=yield pl(a,s),d=ya(r),f=jr(r),p=Wr(r)==="y",{width:v,height:x}=o.floating,g,L;d==="top"||d==="bottom"?(g=d,L=f===((yield n.isRTL==null?void 0:n.isRTL(l.floating))?"start":"end")?"left":"right"):(L=d,g=f==="end"?"top":"bottom");let m=x-i[g],c=v-i[L],h=!a.middlewareData.shift,y=m,C=c;if(p){let S=v-i.left-i.right;C=f||h?va(c,S):S}else{let S=x-i.top-i.bottom;y=f||h?va(m,S):S}if(h&&!f){let S=ot(i.left,0),M=ot(i.right,0),P=ot(i.top,0),U=ot(i.bottom,0);p?C=v-2*(S!==0||M!==0?S+M:ot(i.left,i.right)):y=x-2*(P!==0||U!==0?P+U:ot(i.top,i.bottom))}yield u(b(w({},a),{availableWidth:C,availableHeight:y}));let I=yield n.getDimensions(l.floating);return v!==I.width||x!==I.height?{reset:{rects:!0}}:{}})}}};function Na(e){return Fv(e)?(e.nodeName||"").toLowerCase():"#document"}function gt(e){var t;return(e==null||(t=e.ownerDocument)==null?void 0:t.defaultView)||window}function La(e){var t;return(t=(Fv(e)?e.ownerDocument:e.document)||window.document)==null?void 0:t.documentElement}function Fv(e){return e instanceof Node||e instanceof gt(e).Node}function wa(e){return e instanceof Element||e instanceof gt(e).Element}function oa(e){return e instanceof HTMLElement||e instanceof gt(e).HTMLElement}function Ov(e){return typeof ShadowRoot!="undefined"&&(e instanceof ShadowRoot||e instanceof gt(e).ShadowRoot)}function tn(e){let{overflow:t,overflowX:a,overflowY:r,display:o}=Mt(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+a)&&!["inline","contents"].includes(o)}function Bv(e){return["table","td","th"].includes(Na(e))}function Is(e){let t=Ss(),a=Mt(e);return a.transform!=="none"||a.perspective!=="none"||!!a.containerType&&a.containerType!=="normal"||!t&&!!a.backdropFilter&&a.backdropFilter!=="none"||!t&&!!a.filter&&a.filter!=="none"||["transform","perspective","filter"].some(r=>(a.willChange||"").includes(r))||["paint","layout","strict","content"].some(r=>(a.contain||"").includes(r))}function Nv(e){let t=Gr(e);for(;oa(t)&&!ml(t);){if(Is(t))return t;t=Gr(t)}return null}function Ss(){return!(typeof CSS=="undefined"||!CSS.supports)&&CSS.supports("-webkit-backdrop-filter","none")}function ml(e){return["html","body","#document"].includes(Na(e))}function Mt(e){return gt(e).getComputedStyle(e)}function hl(e){return wa(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Gr(e){if(Na(e)==="html")return e;let t=e.assignedSlot||e.parentNode||Ov(e)&&e.host||La(e);return Ov(t)?t.host:t}function zv(e){let t=Gr(e);return ml(t)?e.ownerDocument?e.ownerDocument.body:e.body:oa(t)&&tn(t)?t:zv(t)}function en(e,t,a){var r;t===void 0&&(t=[]),a===void 0&&(a=!0);let o=zv(e),n=o===((r=e.ownerDocument)==null?void 0:r.body),l=gt(o);return n?t.concat(l,l.visualViewport||[],tn(o)?o:[],l.frameElement&&a?en(l.frameElement):[]):t.concat(o,en(o,[],a))}function Vv(e){let t=Mt(e),a=parseFloat(t.width)||0,r=parseFloat(t.height)||0,o=oa(e),n=o?e.offsetWidth:a,l=o?e.offsetHeight:r,u=fl(a)!==n||fl(r)!==l;return u&&(a=n,r=l),{width:a,height:r,$:u}}function Df(e){return wa(e)?e:e.contextElement}function an(e){let t=Df(e);if(!oa(t))return Ba(1);let a=t.getBoundingClientRect(),{width:r,height:o,$:n}=Vv(t),l=(n?fl(a.width):a.width)/r,u=(n?fl(a.height):a.height)/o;return l&&Number.isFinite(l)||(l=1),u&&Number.isFinite(u)||(u=1),{x:l,y:u}}var cI=Ba(0);function jv(e){let t=gt(e);return Ss()&&t.visualViewport?{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}:cI}function Zr(e,t,a,r){t===void 0&&(t=!1),a===void 0&&(a=!1);let o=e.getBoundingClientRect(),n=Df(e),l=Ba(1);t&&(r?wa(r)&&(l=an(r)):l=an(e));let u=function(p,v,x){return v===void 0&&(v=!1),!(!x||v&&x!==gt(p))&&v}(n,a,r)?jv(n):Ba(0),s=(o.left+u.x)/l.x,i=(o.top+u.y)/l.y,d=o.width/l.x,f=o.height/l.y;if(n){let p=gt(n),v=r&&wa(r)?gt(r):r,x=p.frameElement;for(;x&&r&&v!==p;){let g=an(x),L=x.getBoundingClientRect(),m=Mt(x),c=L.left+(x.clientLeft+parseFloat(m.paddingLeft))*g.x,h=L.top+(x.clientTop+parseFloat(m.paddingTop))*g.y;s*=g.x,i*=g.y,d*=g.x,f*=g.y,s+=c,i+=h,x=gt(x).frameElement}}return Jo({width:d,height:f,x:s,y:i})}function Wv(e){return Zr(La(e)).left+hl(e).scrollLeft}function Uv(e,t,a){let r;if(t==="viewport")r=function(o,n){let l=gt(o),u=La(o),s=l.visualViewport,i=u.clientWidth,d=u.clientHeight,f=0,p=0;if(s){i=s.width,d=s.height;let v=Ss();(!v||v&&n==="fixed")&&(f=s.offsetLeft,p=s.offsetTop)}return{width:i,height:d,x:f,y:p}}(e,a);else if(t==="document")r=function(o){let n=La(o),l=hl(o),u=o.ownerDocument.body,s=ot(n.scrollWidth,n.clientWidth,u.scrollWidth,u.clientWidth),i=ot(n.scrollHeight,n.clientHeight,u.scrollHeight,u.clientHeight),d=-l.scrollLeft+Wv(o),f=-l.scrollTop;return Mt(u).direction==="rtl"&&(d+=ot(n.clientWidth,u.clientWidth)-s),{width:s,height:i,x:d,y:f}}(La(e));else if(wa(t))r=function(o,n){let l=Zr(o,!0,n==="fixed"),u=l.top+o.clientTop,s=l.left+o.clientLeft,i=oa(o)?an(o):Ba(1);return{width:o.clientWidth*i.x,height:o.clientHeight*i.y,x:s*i.x,y:u*i.y}}(t,a);else{let o=jv(e);r=b(w({},t),{x:t.x-o.x,y:t.y-o.y})}return Jo(r)}function Gv(e,t){let a=Gr(e);return!(a===t||!wa(a)||ml(a))&&(Mt(a).position==="fixed"||Gv(a,t))}function pI(e,t,a){let r=oa(t),o=La(t),n=a==="fixed",l=Zr(e,!0,n,t),u={scrollLeft:0,scrollTop:0},s=Ba(0);if(r||!r&&!n)if((Na(t)!=="body"||tn(o))&&(u=hl(t)),r){let i=Zr(t,!0,n,t);s.x=i.x+t.clientLeft,s.y=i.y+t.clientTop}else o&&(s.x=Wv(o));return{x:l.left+u.scrollLeft-s.x,y:l.top+u.scrollTop-s.y,width:l.width,height:l.height}}function qv(e,t){return oa(e)&&Mt(e).position!=="fixed"?t?t(e):e.offsetParent:null}function Hv(e,t){let a=gt(e);if(!oa(e))return a;let r=qv(e,t);for(;r&&Bv(r)&&Mt(r).position==="static";)r=qv(r,t);return r&&(Na(r)==="html"||Na(r)==="body"&&Mt(r).position==="static"&&!Is(r))?a:r||Nv(e)||a}var mI={convertOffsetParentRelativeRectToViewportRelativeRect:function(e){let{rect:t,offsetParent:a,strategy:r}=e,o=oa(a),n=La(a);if(a===n)return t;let l={scrollLeft:0,scrollTop:0},u=Ba(1),s=Ba(0);if((o||!o&&r!=="fixed")&&((Na(a)!=="body"||tn(n))&&(l=hl(a)),oa(a))){let i=Zr(a);u=an(a),s.x=i.x+a.clientLeft,s.y=i.y+a.clientTop}return{width:t.width*u.x,height:t.height*u.y,x:t.x*u.x-l.scrollLeft*u.x+s.x,y:t.y*u.y-l.scrollTop*u.y+s.y}},getDocumentElement:La,getClippingRect:function(e){let{element:t,boundary:a,rootBoundary:r,strategy:o}=e,n=[...a==="clippingAncestors"?function(s,i){let d=i.get(s);if(d)return d;let f=en(s,[],!1).filter(g=>wa(g)&&Na(g)!=="body"),p=null,v=Mt(s).position==="fixed",x=v?Gr(s):s;for(;wa(x)&&!ml(x);){let g=Mt(x),L=Is(x);L||g.position!=="fixed"||(p=null),(v?!L&&!p:!L&&g.position==="static"&&p&&["absolute","fixed"].includes(p.position)||tn(x)&&!L&&Gv(s,x))?f=f.filter(m=>m!==x):p=g,x=Gr(x)}return i.set(s,f),f}(t,this._c):[].concat(a),r],l=n[0],u=n.reduce((s,i)=>{let d=Uv(t,i,o);return s.top=ot(d.top,s.top),s.right=va(d.right,s.right),s.bottom=va(d.bottom,s.bottom),s.left=ot(d.left,s.left),s},Uv(t,l,o));return{width:u.right-u.left,height:u.bottom-u.top,x:u.left,y:u.top}},getOffsetParent:Hv,getElementRects:function(e){return Pe(this,null,function*(){let{reference:t,floating:a,strategy:r}=e,o=this.getOffsetParent||Hv,n=this.getDimensions;return{reference:pI(t,yield o(a),r),floating:w({x:0,y:0},yield n(a))}})},getClientRects:function(e){return Array.from(e.getClientRects())},getDimensions:function(e){return Vv(e)},getScale:an,isElement:wa,isRTL:function(e){return Mt(e).direction==="rtl"}};function Zv(e,t,a,r){r===void 0&&(r={});let{ancestorScroll:o=!0,ancestorResize:n=!0,elementResize:l=typeof ResizeObserver=="function",layoutShift:u=typeof IntersectionObserver=="function",animationFrame:s=!1}=r,i=Df(e),d=o||n?[...i?en(i):[],...en(t)]:[];d.forEach(L=>{o&&L.addEventListener("scroll",a,{passive:!0}),n&&L.addEventListener("resize",a)});let f=i&&u?function(L,m){let c,h=null,y=La(L);function C(){clearTimeout(c),h&&h.disconnect(),h=null}return function I(k,S){k===void 0&&(k=!1),S===void 0&&(S=1),C();let{left:M,top:P,width:U,height:j}=L.getBoundingClientRect();if(k||m(),!U||!j)return;let ae={rootMargin:-cl(P)+"px "+-cl(y.clientWidth-(M+U))+"px "+-cl(y.clientHeight-(P+j))+"px "+-cl(M)+"px",threshold:ot(0,va(1,S))||1},Z=!0;function ue(Q){let xe=Q[0].intersectionRatio;if(xe!==S){if(!Z)return I();xe?I(!1,xe):c=setTimeout(()=>{I(!1,1e-7)},100)}Z=!1}try{h=new IntersectionObserver(ue,b(w({},ae),{root:y.ownerDocument}))}catch(Q){h=new IntersectionObserver(ue,ae)}h.observe(L)}(!0),C}(i,a):null,p,v=-1,x=null;l&&(x=new ResizeObserver(L=>{let[m]=L;m&&m.target===i&&x&&(x.unobserve(t),cancelAnimationFrame(v),v=requestAnimationFrame(()=>{x&&x.observe(t)})),a()}),i&&!s&&x.observe(i),x.observe(t));let g=s?Zr(e):null;return s&&function L(){let m=Zr(e);!g||m.x===g.x&&m.y===g.y&&m.width===g.width&&m.height===g.height||a(),g=m,p=requestAnimationFrame(L)}(),a(),()=>{d.forEach(L=>{o&&L.removeEventListener("scroll",a),n&&L.removeEventListener("resize",a)}),f&&f(),x&&x.disconnect(),x=null,s&&cancelAnimationFrame(p)}}var $v=(e,t,a)=>{let r=new Map,o=w({platform:mI},a),n=b(w({},o.platform),{_c:r});return Tv(e,t,b(w({},o),{platform:n}))};var be=N(G(),1),Ms=N(G(),1),Qv=N(Vo(),1);var Yv=e=>({name:"arrow",options:e,fn(t){let{element:a,padding:r}=typeof e=="function"?e(t):e;return a&&(o=a,{}.hasOwnProperty.call(o,"current"))?a.current!=null?Rf({element:a.current,padding:r}).fn(t):{}:a?Rf({element:a,padding:r}).fn(t):{};var o}}),ks=typeof document!="undefined"?Ms.useLayoutEffect:Ms.useEffect;function Ps(e,t){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(typeof e=="function"&&e.toString()===t.toString())return!0;let a,r,o;if(e&&t&&typeof e=="object"){if(Array.isArray(e)){if(a=e.length,a!=t.length)return!1;for(r=a;r--!=0;)if(!Ps(e[r],t[r]))return!1;return!0}if(o=Object.keys(e),a=o.length,a!==Object.keys(t).length)return!1;for(r=a;r--!=0;)if(!{}.hasOwnProperty.call(t,o[r]))return!1;for(r=a;r--!=0;){let n=o[r];if((n!=="_owner"||!e.$$typeof)&&!Ps(e[n],t[n]))return!1}return!0}return e!=e&&t!=t}function Jv(e){return typeof window=="undefined"?1:(e.ownerDocument.defaultView||window).devicePixelRatio||1}function Kv(e,t){let a=Jv(e);return Math.round(t*a)/a}function Xv(e){let t=be.useRef(e);return ks(()=>{t.current=e}),t}function ex(e){e===void 0&&(e={});let{placement:t="bottom",strategy:a="absolute",middleware:r=[],platform:o,elements:{reference:n,floating:l}={},transform:u=!0,whileElementsMounted:s,open:i}=e,[d,f]=be.useState({x:0,y:0,strategy:a,placement:t,middlewareData:{},isPositioned:!1}),[p,v]=be.useState(r);Ps(p,r)||v(r);let[x,g]=be.useState(null),[L,m]=be.useState(null),c=be.useCallback(Q=>{Q!=I.current&&(I.current=Q,g(Q))},[g]),h=be.useCallback(Q=>{Q!==k.current&&(k.current=Q,m(Q))},[m]),y=n||x,C=l||L,I=be.useRef(null),k=be.useRef(null),S=be.useRef(d),M=Xv(s),P=Xv(o),U=be.useCallback(()=>{if(!I.current||!k.current)return;let Q={placement:t,strategy:a,middleware:p};P.current&&(Q.platform=P.current),$v(I.current,k.current,Q).then(xe=>{let le=b(w({},xe),{isPositioned:!0});j.current&&!Ps(S.current,le)&&(S.current=le,Qv.flushSync(()=>{f(le)}))})},[p,t,a,P]);ks(()=>{i===!1&&S.current.isPositioned&&(S.current.isPositioned=!1,f(Q=>b(w({},Q),{isPositioned:!1})))},[i]);let j=be.useRef(!1);ks(()=>(j.current=!0,()=>{j.current=!1}),[]),ks(()=>{if(y&&(I.current=y),C&&(k.current=C),y&&C){if(M.current)return M.current(y,C,U);U()}},[y,C,U,M]);let ae=be.useMemo(()=>({reference:I,floating:k,setReference:c,setFloating:h}),[c,h]),Z=be.useMemo(()=>({reference:y,floating:C}),[y,C]),ue=be.useMemo(()=>{let Q={position:a,left:0,top:0};if(!Z.floating)return Q;let xe=Kv(Z.floating,d.x),le=Kv(Z.floating,d.y);return u?w(b(w({},Q),{transform:"translate("+xe+"px, "+le+"px)"}),Jv(Z.floating)>=1.5&&{willChange:"transform"}):{position:a,left:xe,top:le}},[a,u,Z.floating,d.x,d.y]);return be.useMemo(()=>b(w({},d),{update:U,refs:ae,elements:Z,floatingStyles:ue}),[d,U,ae,Z,ue])}var tx="Popper",[ax,bf]=zt(tx),[hI,rx]=ax(tx),gI=e=>{let{__scopePopper:t,children:a}=e,[r,o]=(0,Ne.useState)(null);return(0,Ne.createElement)(hI,{scope:t,anchor:r,onAnchorChange:o},a)},vI=(0,Ne.forwardRef)((e,t)=>{let s=e,{__scopePopper:a,virtualRef:r}=s,o=A(s,["__scopePopper","virtualRef"]),n=rx("PopperAnchor",a),l=(0,Ne.useRef)(null),u=se(t,l);return(0,Ne.useEffect)(()=>{n.onAnchorChange((r==null?void 0:r.current)||l.current)}),r?null:(0,Ne.createElement)(oe.div,E({},o,{ref:u}))}),ox="PopperContent",[xI,EP]=ax(ox),yI=(0,Ne.forwardRef)((e,t)=>{var a,r,o,n,l,u,s,i;let ja=e,{__scopePopper:d,side:f="bottom",sideOffset:p=0,align:v="center",alignOffset:x=0,arrowPadding:g=0,avoidCollisions:L=!0,collisionBoundary:m=[],collisionPadding:c=0,sticky:h="partial",hideWhenDetached:y=!1,updatePositionStrategy:C="optimized",onPlaced:I}=ja,k=A(ja,["__scopePopper","side","sideOffset","align","alignOffset","arrowPadding","avoidCollisions","collisionBoundary","collisionPadding","sticky","hideWhenDetached","updatePositionStrategy","onPlaced"]),S=rx(ox,d),[M,P]=(0,Ne.useState)(null),U=se(t,Lt=>P(Lt)),[j,ae]=(0,Ne.useState)(null),Z=Lv(j),ue=(a=Z==null?void 0:Z.width)!==null&&a!==void 0?a:0,Q=(r=Z==null?void 0:Z.height)!==null&&r!==void 0?r:0,xe=f+(v!=="center"?"-"+v:""),le=typeof c=="number"?c:w({top:0,right:0,bottom:0,left:0},c),Je=Array.isArray(m)?m:[m],W=Je.length>0,ye={padding:le,boundary:Je.filter(LI),altBoundary:W},{refs:yt,floatingStyles:Dt,placement:sa,isPositioned:jt,middlewareData:Ve}=ex({strategy:"fixed",placement:xe,whileElementsMounted:(...Lt)=>Zv(...Lt,{animationFrame:C==="always"}),elements:{reference:S.anchor},middleware:[Dv({mainAxis:p+Q,alignmentAxis:x}),L&&bv(w({mainAxis:!0,crossAxis:!1,limiter:h==="partial"?Ev():void 0},ye)),L&&_v(w({},ye)),Av(b(w({},ye),{apply:({elements:Lt,rects:Rl,availableWidth:Dl,availableHeight:Wa})=>{let{width:Zs,height:Oy}=Rl.reference,bl=Lt.floating.style;bl.setProperty("--radix-popper-available-width",`${Dl}px`),bl.setProperty("--radix-popper-available-height",`${Wa}px`),bl.setProperty("--radix-popper-anchor-width",`${Zs}px`),bl.setProperty("--radix-popper-anchor-height",`${Oy}px`)}})),j&&Yv({element:j,padding:g}),wI({arrowWidth:ue,arrowHeight:Q}),y&&Rv(w({strategy:"referenceHidden"},ye))]}),[lt,Sa]=nx(sa),_e=fe(I);mt(()=>{jt&&(_e==null||_e())},[jt,_e]);let ut=(o=Ve.arrow)===null||o===void 0?void 0:o.x,Wt=(n=Ve.arrow)===null||n===void 0?void 0:n.y,Cr=((l=Ve.arrow)===null||l===void 0?void 0:l.centerOffset)!==0,[Ir,Sr]=(0,Ne.useState)();return mt(()=>{M&&Sr(window.getComputedStyle(M).zIndex)},[M]),(0,Ne.createElement)("div",{ref:yt.setFloating,"data-radix-popper-content-wrapper":"",style:b(w({},Dt),{transform:jt?Dt.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:Ir,"--radix-popper-transform-origin":[(u=Ve.transformOrigin)===null||u===void 0?void 0:u.x,(s=Ve.transformOrigin)===null||s===void 0?void 0:s.y].join(" ")}),dir:e.dir},(0,Ne.createElement)(xI,{scope:d,placedSide:lt,onArrowChange:ae,arrowX:ut,arrowY:Wt,shouldHideArrow:Cr},(0,Ne.createElement)(oe.div,E({"data-side":lt,"data-align":Sa},k,{ref:U,style:b(w({},k.style),{animation:jt?void 0:"none",opacity:(i=Ve.hide)!==null&&i!==void 0&&i.referenceHidden?0:void 0})}))))});function LI(e){return e!==null}var wI=e=>({name:"transformOrigin",options:e,fn(t){var a,r,o,n,l;let{placement:u,rects:s,middlewareData:i}=t,d=((a=i.arrow)===null||a===void 0?void 0:a.centerOffset)!==0,f=d?0:e.arrowWidth,p=d?0:e.arrowHeight,[v,x]=nx(u),g={start:"0%",center:"50%",end:"100%"}[x],L=((r=(o=i.arrow)===null||o===void 0?void 0:o.x)!==null&&r!==void 0?r:0)+f/2,m=((n=(l=i.arrow)===null||l===void 0?void 0:l.y)!==null&&n!==void 0?n:0)+p/2,c="",h="";return v==="bottom"?(c=d?g:`${L}px`,h=-p+"px"):v==="top"?(c=d?g:`${L}px`,h=`${s.floating.height+p}px`):v==="right"?(c=-p+"px",h=d?g:`${m}px`):v==="left"&&(c=`${s.floating.width+p}px`,h=d?g:`${m}px`),{data:{x:c,y:h}}}});function nx(e){let[t,a="center"]=e.split("-");return[t,a]}var lx=gI,ux=vI,sx=yI;var pe=N(G(),1);var Ef="rovingFocusGroup.onEntryFocus",CI={bubbles:!1,cancelable:!0},Of="RovingFocusGroup",[Af,ix,II]=qr(Of),[SI,Ff]=zt(Of,[II]),[kI,PI]=SI(Of),MI=(0,pe.forwardRef)((e,t)=>(0,pe.createElement)(Af.Provider,{scope:e.__scopeRovingFocusGroup},(0,pe.createElement)(Af.Slot,{scope:e.__scopeRovingFocusGroup},(0,pe.createElement)(TI,E({},e,{ref:t}))))),TI=(0,pe.forwardRef)((e,t)=>{let k=e,{__scopeRovingFocusGroup:a,orientation:r,loop:o=!1,dir:n,currentTabStopId:l,defaultCurrentTabStopId:u,onCurrentTabStopIdChange:s,onEntryFocus:i}=k,d=A(k,["__scopeRovingFocusGroup","orientation","loop","dir","currentTabStopId","defaultCurrentTabStopId","onCurrentTabStopIdChange","onEntryFocus"]),f=(0,pe.useRef)(null),p=se(t,f),v=$o(n),[x=null,g]=Go({prop:l,defaultProp:u,onChange:s}),[L,m]=(0,pe.useState)(!1),c=fe(i),h=ix(a),y=(0,pe.useRef)(!1),[C,I]=(0,pe.useState)(0);return(0,pe.useEffect)(()=>{let S=f.current;if(S)return S.addEventListener(Ef,c),()=>S.removeEventListener(Ef,c)},[c]),(0,pe.createElement)(kI,{scope:a,orientation:r,dir:v,loop:o,currentTabStopId:x,onItemFocus:(0,pe.useCallback)(S=>g(S),[g]),onItemShiftTab:(0,pe.useCallback)(()=>m(!0),[]),onFocusableItemAdd:(0,pe.useCallback)(()=>I(S=>S+1),[]),onFocusableItemRemove:(0,pe.useCallback)(()=>I(S=>S-1),[])},(0,pe.createElement)(oe.div,E({tabIndex:L||C===0?-1:0,"data-orientation":r},d,{ref:p,style:w({outline:"none"},e.style),onMouseDown:q(e.onMouseDown,()=>{y.current=!0}),onFocus:q(e.onFocus,S=>{let M=!y.current;if(S.target===S.currentTarget&&M&&!L){let P=new CustomEvent(Ef,CI);if(S.currentTarget.dispatchEvent(P),!P.defaultPrevented){let U=h().filter(j=>j.focusable);dx([U.find(j=>j.active),U.find(j=>j.id===x),...U].filter(Boolean).map(j=>j.ref.current))}}y.current=!1}),onBlur:q(e.onBlur,()=>m(!1))})))}),_I=(0,pe.forwardRef)((e,t)=>{let x=e,{__scopeRovingFocusGroup:a,focusable:r=!0,active:o=!1,tabStopId:n}=x,l=A(x,["__scopeRovingFocusGroup","focusable","active","tabStopId"]),u=gr(),s=n||u,i=PI("RovingFocusGroupItem",a),d=i.currentTabStopId===s,f=ix(a),{onFocusableItemAdd:p,onFocusableItemRemove:v}=i;return(0,pe.useEffect)(()=>{if(r)return p(),()=>v()},[r,p,v]),(0,pe.createElement)(Af.ItemSlot,{scope:a,id:s,focusable:r,active:o},(0,pe.createElement)(oe.span,E({tabIndex:d?0:-1,"data-orientation":i.orientation},l,{ref:t,onMouseDown:q(e.onMouseDown,g=>{r?i.onItemFocus(s):g.preventDefault()}),onFocus:q(e.onFocus,()=>i.onItemFocus(s)),onKeyDown:q(e.onKeyDown,g=>{if(g.key==="Tab"&&g.shiftKey)return void i.onItemShiftTab();if(g.target!==g.currentTarget)return;let L=function(h,y,C){let I=function(k,S){return S!=="rtl"?k:k==="ArrowLeft"?"ArrowRight":k==="ArrowRight"?"ArrowLeft":k}(h.key,C);return y==="vertical"&&["ArrowLeft","ArrowRight"].includes(I)||y==="horizontal"&&["ArrowUp","ArrowDown"].includes(I)?void 0:RI[I]}(g,i.orientation,i.dir);if(L!==void 0){g.preventDefault();let h=f().filter(y=>y.focusable).map(y=>y.ref.current);if(L==="last")h.reverse();else if(L==="prev"||L==="next"){L==="prev"&&h.reverse();let y=h.indexOf(g.currentTarget);h=i.loop?(c=y+1,(m=h).map((C,I)=>m[(c+I)%m.length])):h.slice(y+1)}setTimeout(()=>dx(h))}var m,c})})))}),RI={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function dx(e){let t=document.activeElement;for(let a of e)if(a===t||(a.focus(),document.activeElement!==t))return}var fx=MI,cx=_I;var Bf=["Enter"," "],mx=["ArrowUp","PageDown","End"],DI=["ArrowDown","PageUp","Home",...mx],bI={ltr:[...Bf,"ArrowRight"],rtl:[...Bf,"ArrowLeft"]},EI={ltr:["ArrowLeft"],rtl:["ArrowRight"]},Rs="Menu",[gl,AI,OI]=qr(Rs),[$r,zf]=zt(Rs,[OI,bf,Ff]),Uf=bf(),hx=Ff(),[FI,rn]=$r(Rs),[BI,xl]=$r(Rs),NI=e=>{let{__scopeMenu:t,open:a=!1,children:r,dir:o,onOpenChange:n,modal:l=!0}=e,u=Uf(t),[s,i]=(0,R.useState)(null),d=(0,R.useRef)(!1),f=fe(n),p=$o(o);return(0,R.useEffect)(()=>{let v=()=>{d.current=!0,document.addEventListener("pointerdown",x,{capture:!0,once:!0}),document.addEventListener("pointermove",x,{capture:!0,once:!0})},x=()=>d.current=!1;return document.addEventListener("keydown",v,{capture:!0}),()=>{document.removeEventListener("keydown",v,{capture:!0}),document.removeEventListener("pointerdown",x,{capture:!0}),document.removeEventListener("pointermove",x,{capture:!0})}},[]),(0,R.createElement)(lx,u,(0,R.createElement)(FI,{scope:t,open:a,onOpenChange:f,content:s,onContentChange:i},(0,R.createElement)(BI,{scope:t,onClose:(0,R.useCallback)(()=>f(!1),[f]),isUsingKeyboardRef:d,dir:p,modal:l},r)))},gx=(0,R.forwardRef)((e,t)=>{let n=e,{__scopeMenu:a}=n,r=A(n,["__scopeMenu"]),o=Uf(a);return(0,R.createElement)(ux,E({},o,r,{ref:t}))}),zI="MenuPortal",[f2,vx]=$r(zI,{forceMount:void 0});var na="MenuContent",[UI,qf]=$r(na),qI=(0,R.forwardRef)((e,t)=>{let a=vx(na,e.__scopeMenu),u=e,{forceMount:r=a.forceMount}=u,o=A(u,["forceMount"]),n=rn(na,e.__scopeMenu),l=xl(na,e.__scopeMenu);return(0,R.createElement)(gl.Provider,{scope:e.__scopeMenu},(0,R.createElement)(ga,{present:r||n.open},(0,R.createElement)(gl.Slot,{scope:e.__scopeMenu},l.modal?(0,R.createElement)(HI,E({},o,{ref:t})):(0,R.createElement)(VI,E({},o,{ref:t})))))}),HI=(0,R.forwardRef)((e,t)=>{let a=rn(na,e.__scopeMenu),r=(0,R.useRef)(null),o=se(t,r);return(0,R.useEffect)(()=>{let n=r.current;if(n)return vv(n)},[]),(0,R.createElement)(Hf,E({},e,{ref:o,trapFocus:a.open,disableOutsidePointerEvents:a.open,disableOutsideScroll:!0,onFocusOutside:q(e.onFocusOutside,n=>n.preventDefault(),{checkForDefaultPrevented:!1}),onDismiss:()=>a.onOpenChange(!1)}))}),VI=(0,R.forwardRef)((e,t)=>{let a=rn(na,e.__scopeMenu);return(0,R.createElement)(Hf,E({},e,{ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>a.onOpenChange(!1)}))}),Hf=(0,R.forwardRef)((e,t)=>{let Je=e,{__scopeMenu:a,loop:r=!1,trapFocus:o,onOpenAutoFocus:n,onCloseAutoFocus:l,disableOutsidePointerEvents:u,onEntryFocus:s,onEscapeKeyDown:i,onPointerDownOutside:d,onFocusOutside:f,onInteractOutside:p,onDismiss:v,disableOutsideScroll:x}=Je,g=A(Je,["__scopeMenu","loop","trapFocus","onOpenAutoFocus","onCloseAutoFocus","disableOutsidePointerEvents","onEntryFocus","onEscapeKeyDown","onPointerDownOutside","onFocusOutside","onInteractOutside","onDismiss","disableOutsideScroll"]),L=rn(na,a),m=xl(na,a),c=Uf(a),h=hx(a),y=AI(a),[C,I]=(0,R.useState)(null),k=(0,R.useRef)(null),S=se(t,k,L.onContentChange),M=(0,R.useRef)(0),P=(0,R.useRef)(""),U=(0,R.useRef)(0),j=(0,R.useRef)(null),ae=(0,R.useRef)("right"),Z=(0,R.useRef)(0),ue=x?Mf:R.Fragment,Q=x?{as:ha,allowPinchZoom:!0}:void 0,xe=W=>{var ye,yt;let Dt=P.current+W,sa=y().filter(_e=>!_e.disabled),jt=document.activeElement,Ve=(ye=sa.find(_e=>_e.ref.current===jt))===null||ye===void 0?void 0:ye.textValue,lt=function(_e,ut,Wt){let Cr=ut.length>1&&Array.from(ut).every(Wa=>Wa===ut[0]),Ir=Cr?ut[0]:ut,Sr=Wt?_e.indexOf(Wt):-1,ja=(Lt=_e,Rl=Math.max(Sr,0),Lt.map((Wa,Zs)=>Lt[(Rl+Zs)%Lt.length]));var Lt,Rl;Ir.length===1&&(ja=ja.filter(Wa=>Wa!==Wt));let Dl=ja.find(Wa=>Wa.toLowerCase().startsWith(Ir.toLowerCase()));return Dl!==Wt?Dl:void 0}(sa.map(_e=>_e.textValue),Dt,Ve),Sa=(yt=sa.find(_e=>_e.textValue===lt))===null||yt===void 0?void 0:yt.ref.current;(function _e(ut){P.current=ut,window.clearTimeout(M.current),ut!==""&&(M.current=window.setTimeout(()=>_e(""),1e3))})(Dt),Sa&&setTimeout(()=>Sa.focus())};(0,R.useEffect)(()=>()=>window.clearTimeout(M.current),[]),jg();let le=(0,R.useCallback)(W=>{var ye,yt;return ae.current===((ye=j.current)===null||ye===void 0?void 0:ye.side)&&function(Dt,sa){if(!sa)return!1;let jt={x:Dt.clientX,y:Dt.clientY};return function(Ve,lt){let{x:Sa,y:_e}=Ve,ut=!1;for(let Wt=0,Cr=lt.length-1;Wt_e!=Lt>_e&&Sa<(ja-Ir)*(_e-Sr)/(Lt-Sr)+Ir&&(ut=!ut)}return ut}(jt,sa)}(W,(yt=j.current)===null||yt===void 0?void 0:yt.area)},[]);return(0,R.createElement)(UI,{scope:a,searchRef:P,onItemEnter:(0,R.useCallback)(W=>{le(W)&&W.preventDefault()},[le]),onItemLeave:(0,R.useCallback)(W=>{var ye;le(W)||((ye=k.current)===null||ye===void 0||ye.focus(),I(null))},[le]),onTriggerLeave:(0,R.useCallback)(W=>{le(W)&&W.preventDefault()},[le]),pointerGraceTimerRef:U,onPointerGraceIntentChange:(0,R.useCallback)(W=>{j.current=W},[])},(0,R.createElement)(ue,Q,(0,R.createElement)(qg,{asChild:!0,trapped:o,onMountAutoFocus:q(n,W=>{var ye;W.preventDefault(),(ye=k.current)===null||ye===void 0||ye.focus()}),onUnmountAutoFocus:l},(0,R.createElement)(ds,{asChild:!0,disableOutsidePointerEvents:u,onEscapeKeyDown:i,onPointerDownOutside:d,onFocusOutside:f,onInteractOutside:p,onDismiss:v},(0,R.createElement)(fx,E({asChild:!0},h,{dir:m.dir,orientation:"vertical",loop:r,currentTabStopId:C,onCurrentTabStopIdChange:I,onEntryFocus:q(s,W=>{m.isUsingKeyboardRef.current||W.preventDefault()})}),(0,R.createElement)(sx,E({role:"menu","aria-orientation":"vertical","data-state":Ix(L.open),"data-radix-menu-content":"",dir:m.dir},c,g,{ref:S,style:w({outline:"none"},g.style),onKeyDown:q(g.onKeyDown,W=>{let ye=W.target.closest("[data-radix-menu-content]")===W.currentTarget,yt=W.ctrlKey||W.altKey||W.metaKey,Dt=W.key.length===1;ye&&(W.key==="Tab"&&W.preventDefault(),!yt&&Dt&&xe(W.key));let sa=k.current;if(W.target!==sa||!DI.includes(W.key))return;W.preventDefault();let jt=y().filter(Ve=>!Ve.disabled).map(Ve=>Ve.ref.current);mx.includes(W.key)&&jt.reverse(),function(Ve){let lt=document.activeElement;for(let Sa of Ve)if(Sa===lt||(Sa.focus(),document.activeElement!==lt))return}(jt)}),onBlur:q(e.onBlur,W=>{W.currentTarget.contains(W.target)||(window.clearTimeout(M.current),P.current="")}),onPointerMove:q(e.onPointerMove,vl(W=>{let ye=W.target,yt=Z.current!==W.clientX;if(W.currentTarget.contains(ye)&&yt){let Dt=W.clientX>Z.current?"right":"left";ae.current=Dt,Z.current=W.clientX}}))})))))))}),xx=(0,R.forwardRef)((e,t)=>{let o=e,{__scopeMenu:a}=o,r=A(o,["__scopeMenu"]);return(0,R.createElement)(oe.div,E({role:"group"},r,{ref:t}))}),jI=(0,R.forwardRef)((e,t)=>{let o=e,{__scopeMenu:a}=o,r=A(o,["__scopeMenu"]);return(0,R.createElement)(oe.div,E({},r,{ref:t}))}),Nf="MenuItem",px="menu.itemSelect",Vf=(0,R.forwardRef)((e,t)=>{let d=e,{disabled:a=!1,onSelect:r}=d,o=A(d,["disabled","onSelect"]),n=(0,R.useRef)(null),l=xl(Nf,e.__scopeMenu),u=qf(Nf,e.__scopeMenu),s=se(t,n),i=(0,R.useRef)(!1);return(0,R.createElement)(yx,E({},o,{ref:s,disabled:a,onClick:q(e.onClick,()=>{let f=n.current;if(!a&&f){let p=new CustomEvent(px,{bubbles:!0,cancelable:!0});f.addEventListener(px,v=>r==null?void 0:r(v),{once:!0}),Hr(f,p),p.defaultPrevented?i.current=!1:l.onClose()}}),onPointerDown:f=>{var p;(p=e.onPointerDown)===null||p===void 0||p.call(e,f),i.current=!0},onPointerUp:q(e.onPointerUp,f=>{var p;i.current||(p=f.currentTarget)===null||p===void 0||p.click()}),onKeyDown:q(e.onKeyDown,f=>{let p=u.searchRef.current!=="";a||p&&f.key===" "||Bf.includes(f.key)&&(f.currentTarget.click(),f.preventDefault())})}))}),yx=(0,R.forwardRef)((e,t)=>{let x=e,{__scopeMenu:a,disabled:r=!1,textValue:o}=x,n=A(x,["__scopeMenu","disabled","textValue"]),l=qf(Nf,a),u=hx(a),s=(0,R.useRef)(null),i=se(t,s),[d,f]=(0,R.useState)(!1),[p,v]=(0,R.useState)("");return(0,R.useEffect)(()=>{let g=s.current;var L;g&&v(((L=g.textContent)!==null&&L!==void 0?L:"").trim())},[n.children]),(0,R.createElement)(gl.ItemSlot,{scope:a,disabled:r,textValue:o!=null?o:p},(0,R.createElement)(cx,E({asChild:!0},u,{focusable:!r}),(0,R.createElement)(oe.div,E({role:"menuitem","data-highlighted":d?"":void 0,"aria-disabled":r||void 0,"data-disabled":r?"":void 0},n,{ref:i,onPointerMove:q(e.onPointerMove,vl(g=>{r?l.onItemLeave(g):(l.onItemEnter(g),!g.defaultPrevented&&g.currentTarget.focus())})),onPointerLeave:q(e.onPointerLeave,vl(g=>l.onItemLeave(g))),onFocus:q(e.onFocus,()=>f(!0)),onBlur:q(e.onBlur,()=>f(!1))}))))}),WI=(0,R.forwardRef)((e,t)=>{let n=e,{checked:a=!1,onCheckedChange:r}=n,o=A(n,["checked","onCheckedChange"]);return(0,R.createElement)(wx,{scope:e.__scopeMenu,checked:a},(0,R.createElement)(Vf,E({role:"menuitemcheckbox","aria-checked":_s(a)?"mixed":a},o,{ref:t,"data-state":jf(a),onSelect:q(o.onSelect,()=>r==null?void 0:r(!!_s(a)||!a),{checkForDefaultPrevented:!1})})))}),[GI,ZI]=$r("MenuRadioGroup",{value:void 0,onValueChange:()=>{}}),$I=(0,R.forwardRef)((e,t)=>{let l=e,{value:a,onValueChange:r}=l,o=A(l,["value","onValueChange"]),n=fe(r);return(0,R.createElement)(GI,{scope:e.__scopeMenu,value:a,onValueChange:n},(0,R.createElement)(xx,E({},o,{ref:t})))}),KI=(0,R.forwardRef)((e,t)=>{let l=e,{value:a}=l,r=A(l,["value"]),o=ZI("MenuRadioItem",e.__scopeMenu),n=a===o.value;return(0,R.createElement)(wx,{scope:e.__scopeMenu,checked:n},(0,R.createElement)(Vf,E({role:"menuitemradio","aria-checked":n},r,{ref:t,"data-state":jf(n),onSelect:q(r.onSelect,()=>{var u;return(u=o.onValueChange)===null||u===void 0?void 0:u.call(o,a)},{checkForDefaultPrevented:!1})})))}),Lx="MenuItemIndicator",[wx,XI]=$r(Lx,{checked:!1}),QI=(0,R.forwardRef)((e,t)=>{let l=e,{__scopeMenu:a,forceMount:r}=l,o=A(l,["__scopeMenu","forceMount"]),n=XI(Lx,a);return(0,R.createElement)(ga,{present:r||_s(n.checked)||n.checked===!0},(0,R.createElement)(oe.span,E({},o,{ref:t,"data-state":jf(n.checked)})))}),YI=(0,R.forwardRef)((e,t)=>{let o=e,{__scopeMenu:a}=o,r=A(o,["__scopeMenu"]);return(0,R.createElement)(oe.div,E({role:"separator","aria-orientation":"horizontal"},r,{ref:t}))}),JI="MenuSub",[c2,Cx]=$r(JI);var Ts="MenuSubTrigger",e0=(0,R.forwardRef)((e,t)=>{let a=rn(Ts,e.__scopeMenu),r=xl(Ts,e.__scopeMenu),o=Cx(Ts,e.__scopeMenu),n=qf(Ts,e.__scopeMenu),l=(0,R.useRef)(null),{pointerGraceTimerRef:u,onPointerGraceIntentChange:s}=n,i={__scopeMenu:e.__scopeMenu},d=(0,R.useCallback)(()=>{l.current&&window.clearTimeout(l.current),l.current=null},[]);return(0,R.useEffect)(()=>d,[d]),(0,R.useEffect)(()=>{let f=u.current;return()=>{window.clearTimeout(f),s(null)}},[u,s]),(0,R.createElement)(gx,E({asChild:!0},i),(0,R.createElement)(yx,E({id:o.triggerId,"aria-haspopup":"menu","aria-expanded":a.open,"aria-controls":o.contentId,"data-state":Ix(a.open)},e,{ref:Fa(t,o.onTriggerChange),onClick:f=>{var p;(p=e.onClick)===null||p===void 0||p.call(e,f),e.disabled||f.defaultPrevented||(f.currentTarget.focus(),a.open||a.onOpenChange(!0))},onPointerMove:q(e.onPointerMove,vl(f=>{n.onItemEnter(f),f.defaultPrevented||e.disabled||a.open||l.current||(n.onPointerGraceIntentChange(null),l.current=window.setTimeout(()=>{a.onOpenChange(!0),d()},100))})),onPointerLeave:q(e.onPointerLeave,vl(f=>{var p;d();let v=(p=a.content)===null||p===void 0?void 0:p.getBoundingClientRect();if(v){var x;let g=(x=a.content)===null||x===void 0?void 0:x.dataset.side,L=g==="right",m=L?-5:5,c=v[L?"left":"right"],h=v[L?"right":"left"];n.onPointerGraceIntentChange({area:[{x:f.clientX+m,y:f.clientY},{x:c,y:v.top},{x:h,y:v.top},{x:h,y:v.bottom},{x:c,y:v.bottom}],side:g}),window.clearTimeout(u.current),u.current=window.setTimeout(()=>n.onPointerGraceIntentChange(null),300)}else{if(n.onTriggerLeave(f),f.defaultPrevented)return;n.onPointerGraceIntentChange(null)}})),onKeyDown:q(e.onKeyDown,f=>{let p=n.searchRef.current!=="";var v;e.disabled||p&&f.key===" "||bI[r.dir].includes(f.key)&&(a.onOpenChange(!0),(v=a.content)===null||v===void 0||v.focus(),f.preventDefault())})})))}),t0=(0,R.forwardRef)((e,t)=>{let a=vx(na,e.__scopeMenu),d=e,{forceMount:r=a.forceMount}=d,o=A(d,["forceMount"]),n=rn(na,e.__scopeMenu),l=xl(na,e.__scopeMenu),u=Cx("MenuSubContent",e.__scopeMenu),s=(0,R.useRef)(null),i=se(t,s);return(0,R.createElement)(gl.Provider,{scope:e.__scopeMenu},(0,R.createElement)(ga,{present:r||n.open},(0,R.createElement)(gl.Slot,{scope:e.__scopeMenu},(0,R.createElement)(Hf,E({id:u.contentId,"aria-labelledby":u.triggerId},o,{ref:i,align:"start",side:l.dir==="rtl"?"left":"right",disableOutsidePointerEvents:!1,disableOutsideScroll:!1,trapFocus:!1,onOpenAutoFocus:f=>{var p;l.isUsingKeyboardRef.current&&((p=s.current)===null||p===void 0||p.focus()),f.preventDefault()},onCloseAutoFocus:f=>f.preventDefault(),onFocusOutside:q(e.onFocusOutside,f=>{f.target!==u.trigger&&n.onOpenChange(!1)}),onEscapeKeyDown:q(e.onEscapeKeyDown,f=>{l.onClose(),f.preventDefault()}),onKeyDown:q(e.onKeyDown,f=>{let p=f.currentTarget.contains(f.target),v=EI[l.dir].includes(f.key);var x;p&&v&&(n.onOpenChange(!1),(x=u.trigger)===null||x===void 0||x.focus(),f.preventDefault())})})))))});function Ix(e){return e?"open":"closed"}function _s(e){return e==="indeterminate"}function jf(e){return _s(e)?"indeterminate":e?"checked":"unchecked"}function vl(e){return t=>t.pointerType==="mouse"?e(t):void 0}var Sx=NI,kx=gx;var Px=qI,Mx=xx,Tx=jI,_x=Vf,Rx=WI,Dx=$I,bx=KI,Ex=QI,Ax=YI;var Ox=e0,Fx=t0;var ze=N(Ke(),1),za=N(G(),1);var ee=N(G(),1);var Bx="DropdownMenu",[a0,k2]=zt(Bx,[zf]),Tt=zf(),[r0,Nx]=a0(Bx),o0=e=>{let{__scopeDropdownMenu:t,children:a,dir:r,open:o,defaultOpen:n,onOpenChange:l,modal:u=!0}=e,s=Tt(t),i=(0,ee.useRef)(null),[d=!1,f]=Go({prop:o,defaultProp:n,onChange:l});return(0,ee.createElement)(r0,{scope:t,triggerId:gr(),triggerRef:i,contentId:gr(),open:d,onOpenChange:f,onOpenToggle:(0,ee.useCallback)(()=>f(p=>!p),[f]),modal:u},(0,ee.createElement)(Sx,E({},s,{open:d,onOpenChange:f,dir:r,modal:u}),a))},n0=(0,ee.forwardRef)((e,t)=>{let u=e,{__scopeDropdownMenu:a,disabled:r=!1}=u,o=A(u,["__scopeDropdownMenu","disabled"]),n=Nx("DropdownMenuTrigger",a),l=Tt(a);return(0,ee.createElement)(kx,E({asChild:!0},l),(0,ee.createElement)(oe.button,E({type:"button",id:n.triggerId,"aria-haspopup":"menu","aria-expanded":n.open,"aria-controls":n.open?n.contentId:void 0,"data-state":n.open?"open":"closed","data-disabled":r?"":void 0,disabled:r},o,{ref:Fa(t,n.triggerRef),onPointerDown:q(e.onPointerDown,s=>{r||s.button!==0||s.ctrlKey!==!1||(n.onOpenToggle(),n.open||s.preventDefault())}),onKeyDown:q(e.onKeyDown,s=>{r||(["Enter"," "].includes(s.key)&&n.onOpenToggle(),s.key==="ArrowDown"&&n.onOpenChange(!0),["Enter"," ","ArrowDown"].includes(s.key)&&s.preventDefault())})})))});var l0=(0,ee.forwardRef)((e,t)=>{let u=e,{__scopeDropdownMenu:a}=u,r=A(u,["__scopeDropdownMenu"]),o=Nx("DropdownMenuContent",a),n=Tt(a),l=(0,ee.useRef)(!1);return(0,ee.createElement)(Px,E({id:o.contentId,"aria-labelledby":o.triggerId},n,r,{ref:t,onCloseAutoFocus:q(e.onCloseAutoFocus,s=>{var i;l.current||(i=o.triggerRef.current)===null||i===void 0||i.focus(),l.current=!1,s.preventDefault()}),onInteractOutside:q(e.onInteractOutside,s=>{let i=s.detail.originalEvent,d=i.button===0&&i.ctrlKey===!0,f=i.button===2||d;o.modal&&!f||(l.current=!0)}),style:b(w({},e.style),{"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"})}))}),P2=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Mx,E({},o,r,{ref:t}))}),u0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Tx,E({},o,r,{ref:t}))}),s0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(_x,E({},o,r,{ref:t}))}),i0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Rx,E({},o,r,{ref:t}))}),M2=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Dx,E({},o,r,{ref:t}))}),d0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(bx,E({},o,r,{ref:t}))}),f0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Ex,E({},o,r,{ref:t}))}),c0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Ax,E({},o,r,{ref:t}))});var p0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Ox,E({},o,r,{ref:t}))}),m0=(0,ee.forwardRef)((e,t)=>{let n=e,{__scopeDropdownMenu:a}=n,r=A(n,["__scopeDropdownMenu"]),o=Tt(a);return(0,ee.createElement)(Fx,E({},o,r,{ref:t,style:b(w({},e.style),{"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"})}))}),zx=o0,Ux=n0;var Wf=l0;var Gf=u0,Zf=s0,$f=i0;var Kf=d0,Xf=f0,Qf=c0;var Yf=p0,Jf=m0;var ec=zx,tc=Ux;var qx=za.forwardRef((e,t)=>{var{className:a,inset:r,children:o}=e,n=Se(e,["className","inset","children"]);return(0,ze.jsxs)(Yf,Object.assign({ref:t,className:De("flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",r&&"pl-8",a)},n,{children:[o,(0,ze.jsx)(Tg,{className:"ml-auto h-4 w-4"})]}))});qx.displayName=Yf.displayName;var Hx=za.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,ze.jsx)(Jf,Object.assign({ref:t,className:De("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",a)},r))});Hx.displayName=Jf.displayName;var Ds=za.forwardRef((e,t)=>{var{className:a,sideOffset:r=4}=e,o=Se(e,["className","sideOffset"]);return(0,ze.jsx)(Wf,Object.assign({ref:t,sideOffset:r,className:De("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md","data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",a)},o))});Ds.displayName=Wf.displayName;var bs=za.forwardRef((e,t)=>{var{className:a,inset:r}=e,o=Se(e,["className","inset"]);return(0,ze.jsx)(Zf,Object.assign({ref:t,className:De("relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",r&&"pl-8",a)},o))});bs.displayName=Zf.displayName;var Vx=za.forwardRef((e,t)=>{var{className:a,children:r,checked:o}=e,n=Se(e,["className","children","checked"]);return(0,ze.jsxs)($f,Object.assign({ref:t,className:De("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",a),checked:o},n,{children:[(0,ze.jsx)("span",Object.assign({className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center"},{children:(0,ze.jsx)(Xf,{children:(0,ze.jsx)(Pg,{className:"h-4 w-4"})})})),r]}))});Vx.displayName=$f.displayName;var jx=za.forwardRef((e,t)=>{var{className:a,children:r}=e,o=Se(e,["className","children"]);return(0,ze.jsxs)(Kf,Object.assign({ref:t,className:De("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",a)},o,{children:[(0,ze.jsx)("span",Object.assign({className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center"},{children:(0,ze.jsx)(Xf,{children:(0,ze.jsx)(_g,{className:"h-4 w-4 fill-current"})})})),r]}))});jx.displayName=Kf.displayName;var Wx=za.forwardRef((e,t)=>{var{className:a,inset:r}=e,o=Se(e,["className","inset"]);return(0,ze.jsx)(Gf,Object.assign({ref:t,className:De("px-2 py-1.5 text-sm font-semibold",r&&"pl-8",a)},o))});Wx.displayName=Gf.displayName;var Gx=za.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,ze.jsx)(Qf,Object.assign({ref:t,className:De("-mx-1 my-1 h-px bg-muted",a)},r))});Gx.displayName=Qf.displayName;var Zx=e=>{var{className:t}=e,a=Se(e,["className"]);return(0,ze.jsx)("span",Object.assign({className:De("ml-auto text-xs tracking-widest opacity-60",t)},a))};Zx.displayName="DropdownMenuShortcut";var Es=N(G(),1);var h0=(0,Es.forwardRef)((e,t)=>(0,Es.createElement)(oe.span,E({},e,{ref:t,style:w({position:"absolute",border:0,width:1,height:1,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",wordWrap:"normal"},e.style)}))),$x=h0;var Ut=N(Ke(),1);var T=N(G(),1),Yx=N(Vo(),1);var Ll="NavigationMenu",[nc,Jx,g0]=qr(Ll),[ac,v0,x0]=qr(Ll),[lc,oM]=zt(Ll,[g0,x0]),[y0,la]=lc(Ll),[L0,w0]=lc(Ll),C0=(0,T.forwardRef)((e,t)=>{let M=e,{__scopeNavigationMenu:a,value:r,onValueChange:o,defaultValue:n,delayDuration:l=200,skipDelayDuration:u=300,orientation:s="horizontal",dir:i}=M,d=A(M,["__scopeNavigationMenu","value","onValueChange","defaultValue","delayDuration","skipDelayDuration","orientation","dir"]),[f,p]=(0,T.useState)(null),v=se(t,P=>p(P)),x=$o(i),g=(0,T.useRef)(0),L=(0,T.useRef)(0),m=(0,T.useRef)(0),[c,h]=(0,T.useState)(!0),[y="",C]=Go({prop:r,onChange:P=>{let U=u>0;P!==""?(window.clearTimeout(m.current),U&&h(!1)):(window.clearTimeout(m.current),m.current=window.setTimeout(()=>h(!0),u)),o==null||o(P)},defaultProp:n}),I=(0,T.useCallback)(()=>{window.clearTimeout(L.current),L.current=window.setTimeout(()=>C(""),150)},[C]),k=(0,T.useCallback)(P=>{window.clearTimeout(L.current),C(P)},[C]),S=(0,T.useCallback)(P=>{y===P?window.clearTimeout(L.current):g.current=window.setTimeout(()=>{window.clearTimeout(L.current),C(P)},l)},[y,C,l]);return(0,T.useEffect)(()=>()=>{window.clearTimeout(g.current),window.clearTimeout(L.current),window.clearTimeout(m.current)},[]),(0,T.createElement)(I0,{scope:a,isRootMenu:!0,value:y,dir:x,orientation:s,rootNavigationMenu:f,onTriggerEnter:P=>{window.clearTimeout(g.current),c?S(P):k(P)},onTriggerLeave:()=>{window.clearTimeout(g.current),I()},onContentEnter:()=>window.clearTimeout(L.current),onContentLeave:I,onItemSelect:P=>{C(U=>U===P?"":P)},onItemDismiss:()=>C("")},(0,T.createElement)(oe.nav,E({"aria-label":"Main","data-orientation":s,dir:x},d,{ref:v})))}),I0=e=>{let{scope:t,isRootMenu:a,rootNavigationMenu:r,dir:o,orientation:n,children:l,value:u,onItemSelect:s,onItemDismiss:i,onTriggerEnter:d,onTriggerLeave:f,onContentEnter:p,onContentLeave:v}=e,[x,g]=(0,T.useState)(null),[L,m]=(0,T.useState)(new Map),[c,h]=(0,T.useState)(null);return(0,T.createElement)(y0,{scope:t,isRootMenu:a,rootNavigationMenu:r,value:u,previousValue:xv(u),baseId:gr(),dir:o,orientation:n,viewport:x,onViewportChange:g,indicatorTrack:c,onIndicatorTrackChange:h,onTriggerEnter:fe(d),onTriggerLeave:fe(f),onContentEnter:fe(p),onContentLeave:fe(v),onItemSelect:fe(s),onItemDismiss:fe(i),onViewportContentChange:(0,T.useCallback)((y,C)=>{m(I=>(I.set(y,C),new Map(I)))},[]),onViewportContentRemove:(0,T.useCallback)(y=>{m(C=>C.has(y)?(C.delete(y),new Map(C)):C)},[])},(0,T.createElement)(nc.Provider,{scope:t},(0,T.createElement)(L0,{scope:t,items:L},l)))},S0=(0,T.forwardRef)((e,t)=>{let l=e,{__scopeNavigationMenu:a}=l,r=A(l,["__scopeNavigationMenu"]),o=la("NavigationMenuList",a),n=(0,T.createElement)(oe.ul,E({"data-orientation":o.orientation},r,{ref:t}));return(0,T.createElement)(oe.div,{style:{position:"relative"},ref:o.onIndicatorTrackChange},(0,T.createElement)(nc.Slot,{scope:a},o.isRootMenu?(0,T.createElement)(oy,{asChild:!0},n):n))}),[k0,ey]=lc("NavigationMenuItem"),P0=(0,T.forwardRef)((e,t)=>{let x=e,{__scopeNavigationMenu:a,value:r}=x,o=A(x,["__scopeNavigationMenu","value"]),n=gr(),l=r||n||"LEGACY_REACT_AUTO_VALUE",u=(0,T.useRef)(null),s=(0,T.useRef)(null),i=(0,T.useRef)(null),d=(0,T.useRef)(()=>{}),f=(0,T.useRef)(!1),p=(0,T.useCallback)((g="start")=>{if(u.current){d.current();let L=rc(u.current);L.length&&uc(g==="start"?L:L.reverse())}},[]),v=(0,T.useCallback)(()=>{if(u.current){let g=rc(u.current);g.length&&(d.current=function(L){return L.forEach(m=>{m.dataset.tabindex=m.getAttribute("tabindex")||"",m.setAttribute("tabindex","-1")}),()=>{L.forEach(m=>{let c=m.dataset.tabindex;m.setAttribute("tabindex",c)})}}(g))}},[]);return(0,T.createElement)(k0,{scope:a,value:l,triggerRef:s,contentRef:u,focusProxyRef:i,wasEscapeCloseRef:f,onEntryKeyDown:p,onFocusProxyEnter:p,onRootContentClose:v,onContentFocusOutside:v},(0,T.createElement)(oe.li,E({},o,{ref:t})))}),Kx="NavigationMenuTrigger",M0=(0,T.forwardRef)((e,t)=>{let x=e,{__scopeNavigationMenu:a,disabled:r}=x,o=A(x,["__scopeNavigationMenu","disabled"]),n=la(Kx,e.__scopeNavigationMenu),l=ey(Kx,e.__scopeNavigationMenu),u=(0,T.useRef)(null),s=se(u,l.triggerRef,t),i=ly(n.baseId,l.value),d=uy(n.baseId,l.value),f=(0,T.useRef)(!1),p=(0,T.useRef)(!1),v=l.value===n.value;return(0,T.createElement)(T.Fragment,null,(0,T.createElement)(nc.ItemSlot,{scope:a,value:l.value},(0,T.createElement)(ny,{asChild:!0},(0,T.createElement)(oe.button,E({id:i,disabled:r,"data-disabled":r?"":void 0,"data-state":sc(v),"aria-expanded":v,"aria-controls":d},o,{ref:s,onPointerEnter:q(e.onPointerEnter,()=>{p.current=!1,l.wasEscapeCloseRef.current=!1}),onPointerMove:q(e.onPointerMove,Os(()=>{r||p.current||l.wasEscapeCloseRef.current||f.current||(n.onTriggerEnter(l.value),f.current=!0)})),onPointerLeave:q(e.onPointerLeave,Os(()=>{r||(n.onTriggerLeave(),f.current=!1)})),onClick:q(e.onClick,()=>{n.onItemSelect(l.value),p.current=v}),onKeyDown:q(e.onKeyDown,g=>{let L={horizontal:"ArrowDown",vertical:n.dir==="rtl"?"ArrowLeft":"ArrowRight"}[n.orientation];v&&g.key===L&&(l.onEntryKeyDown(),g.preventDefault())})})))),v&&(0,T.createElement)(T.Fragment,null,(0,T.createElement)($x,{"aria-hidden":!0,tabIndex:0,ref:l.focusProxyRef,onFocus:g=>{let L=l.contentRef.current,m=g.relatedTarget,c=m===u.current,h=L==null?void 0:L.contains(m);!c&&h||l.onFocusProxyEnter(c?"start":"end")}}),n.viewport&&(0,T.createElement)("span",{"aria-owns":d})))}),Xx="navigationMenu.linkSelect",T0=(0,T.forwardRef)((e,t)=>{let l=e,{__scopeNavigationMenu:a,active:r,onSelect:o}=l,n=A(l,["__scopeNavigationMenu","active","onSelect"]);return(0,T.createElement)(ny,{asChild:!0},(0,T.createElement)(oe.a,E({"data-active":r?"":void 0,"aria-current":r?"page":void 0},n,{ref:t,onClick:q(e.onClick,u=>{let s=u.target,i=new CustomEvent(Xx,{bubbles:!0,cancelable:!0});if(s.addEventListener(Xx,d=>o==null?void 0:o(d),{once:!0}),Hr(s,i),!i.defaultPrevented&&!u.metaKey){let d=new CustomEvent(As,{bubbles:!0,cancelable:!0});Hr(s,d)}},{checkForDefaultPrevented:!1})})))}),ty="NavigationMenuIndicator",_0=(0,T.forwardRef)((e,t)=>{let l=e,{forceMount:a}=l,r=A(l,["forceMount"]),o=la(ty,e.__scopeNavigationMenu),n=!!o.value;return o.indicatorTrack?Yx.default.createPortal((0,T.createElement)(ga,{present:a||n},(0,T.createElement)(R0,E({},r,{ref:t}))),o.indicatorTrack):null}),R0=(0,T.forwardRef)((e,t)=>{let v=e,{__scopeNavigationMenu:a}=v,r=A(v,["__scopeNavigationMenu"]),o=la(ty,a),n=Jx(a),[l,u]=(0,T.useState)(null),[s,i]=(0,T.useState)(null),d=o.orientation==="horizontal",f=!!o.value;(0,T.useEffect)(()=>{var x;let g=(x=n().find(L=>L.value===o.value))===null||x===void 0?void 0:x.ref.current;g&&u(g)},[n,o.value]);let p=()=>{l&&i({size:d?l.offsetWidth:l.offsetHeight,offset:d?l.offsetLeft:l.offsetTop})};return oc(l,p),oc(o.indicatorTrack,p),s?(0,T.createElement)(oe.div,E({"aria-hidden":!0,"data-state":f?"visible":"hidden","data-orientation":o.orientation},r,{ref:t,style:w(w({position:"absolute"},d?{left:0,width:s.size+"px",transform:`translateX(${s.offset}px)`}:{top:0,height:s.size+"px",transform:`translateY(${s.offset}px)`}),r.style)})):null}),yl="NavigationMenuContent",D0=(0,T.forwardRef)((e,t)=>{let i=e,{forceMount:a}=i,r=A(i,["forceMount"]),o=la(yl,e.__scopeNavigationMenu),n=ey(yl,e.__scopeNavigationMenu),l=se(n.contentRef,t),u=n.value===o.value,s=w({value:n.value,triggerRef:n.triggerRef,focusProxyRef:n.focusProxyRef,wasEscapeCloseRef:n.wasEscapeCloseRef,onContentFocusOutside:n.onContentFocusOutside,onRootContentClose:n.onRootContentClose},r);return o.viewport?(0,T.createElement)(b0,E({forceMount:a},s,{ref:l})):(0,T.createElement)(ga,{present:a||u},(0,T.createElement)(ay,E({"data-state":sc(u)},s,{ref:l,onPointerEnter:q(e.onPointerEnter,o.onContentEnter),onPointerLeave:q(e.onPointerLeave,Os(o.onContentLeave)),style:w({pointerEvents:!u&&o.isRootMenu?"none":void 0},s.style)})))}),b0=(0,T.forwardRef)((e,t)=>{let a=la(yl,e.__scopeNavigationMenu),{onViewportContentChange:r,onViewportContentRemove:o}=a;return mt(()=>{r(e.value,w({ref:t},e))},[e,t,r]),mt(()=>()=>o(e.value),[e.value,o]),null}),As="navigationMenu.rootContentDismiss",ay=(0,T.forwardRef)((e,t)=>{let h=e,{__scopeNavigationMenu:a,value:r,triggerRef:o,focusProxyRef:n,wasEscapeCloseRef:l,onRootContentClose:u,onContentFocusOutside:s}=h,i=A(h,["__scopeNavigationMenu","value","triggerRef","focusProxyRef","wasEscapeCloseRef","onRootContentClose","onContentFocusOutside"]),d=la(yl,a),f=(0,T.useRef)(null),p=se(f,t),v=ly(d.baseId,r),x=uy(d.baseId,r),g=Jx(a),L=(0,T.useRef)(null),{onItemDismiss:m}=d;(0,T.useEffect)(()=>{let y=f.current;if(d.isRootMenu&&y){let C=()=>{var I;m(),u(),y.contains(document.activeElement)&&((I=o.current)===null||I===void 0||I.focus())};return y.addEventListener(As,C),()=>y.removeEventListener(As,C)}},[d.isRootMenu,e.value,o,m,u]);let c=(0,T.useMemo)(()=>{let y=g().map(P=>P.value);d.dir==="rtl"&&y.reverse();let C=y.indexOf(d.value),I=y.indexOf(d.previousValue),k=r===d.value,S=I===y.indexOf(r);if(!k&&!S)return L.current;let M=(()=>{if(C!==I){if(k&&I!==-1)return C>I?"from-end":"from-start";if(S&&C!==-1)return C>I?"to-start":"to-end"}return null})();return L.current=M,M},[d.previousValue,d.value,d.dir,g,r]);return(0,T.createElement)(oy,{asChild:!0},(0,T.createElement)(ds,E({id:x,"aria-labelledby":v,"data-motion":c,"data-orientation":d.orientation},i,{ref:p,onDismiss:()=>{var y;let C=new Event(As,{bubbles:!0,cancelable:!0});(y=f.current)===null||y===void 0||y.dispatchEvent(C)},onFocusOutside:q(e.onFocusOutside,y=>{var C;s();let I=y.target;(C=d.rootNavigationMenu)!==null&&C!==void 0&&C.contains(I)&&y.preventDefault()}),onPointerDownOutside:q(e.onPointerDownOutside,y=>{var C;let I=y.target,k=g().some(M=>{var P;return(P=M.ref.current)===null||P===void 0?void 0:P.contains(I)}),S=d.isRootMenu&&((C=d.viewport)===null||C===void 0?void 0:C.contains(I));(k||S||!d.isRootMenu)&&y.preventDefault()}),onKeyDown:q(e.onKeyDown,y=>{let C=y.altKey||y.ctrlKey||y.metaKey;if(y.key==="Tab"&&!C){let k=rc(y.currentTarget),S=document.activeElement,M=k.findIndex(P=>P===S);var I;uc(y.shiftKey?k.slice(0,M).reverse():k.slice(M+1,k.length))?y.preventDefault():(I=n.current)===null||I===void 0||I.focus()}}),onEscapeKeyDown:q(e.onEscapeKeyDown,y=>{l.current=!0})})))}),ry="NavigationMenuViewport",E0=(0,T.forwardRef)((e,t)=>{let l=e,{forceMount:a}=l,r=A(l,["forceMount"]),o=la(ry,e.__scopeNavigationMenu),n=!!o.value;return(0,T.createElement)(ga,{present:a||n},(0,T.createElement)(A0,E({},r,{ref:t})))}),A0=(0,T.forwardRef)((e,t)=>{let L=e,{__scopeNavigationMenu:a,children:r}=L,o=A(L,["__scopeNavigationMenu","children"]),n=la(ry,a),l=se(t,n.onViewportChange),u=w0(yl,e.__scopeNavigationMenu),[s,i]=(0,T.useState)(null),[d,f]=(0,T.useState)(null),p=s?(s==null?void 0:s.width)+"px":void 0,v=s?(s==null?void 0:s.height)+"px":void 0,x=!!n.value,g=x?n.value:n.previousValue;return oc(d,()=>{d&&i({width:d.offsetWidth,height:d.offsetHeight})}),(0,T.createElement)(oe.div,E({"data-state":sc(x),"data-orientation":n.orientation},o,{ref:l,style:w({pointerEvents:!x&&n.isRootMenu?"none":void 0,"--radix-navigation-menu-viewport-width":p,"--radix-navigation-menu-viewport-height":v},o.style),onPointerEnter:q(e.onPointerEnter,n.onContentEnter),onPointerLeave:q(e.onPointerLeave,Os(n.onContentLeave))}),Array.from(u.items).map(C=>{var[m,I]=C,k=I,{ref:c,forceMount:h}=k,y=A(k,["ref","forceMount"]);let S=g===m;return(0,T.createElement)(ga,{key:m,present:h||S},(0,T.createElement)(ay,E({},y,{ref:Fa(c,M=>{S&&M&&f(M)})})))}))}),oy=(0,T.forwardRef)((e,t)=>{let n=e,{__scopeNavigationMenu:a}=n,r=A(n,["__scopeNavigationMenu"]),o=la("FocusGroup",a);return(0,T.createElement)(ac.Provider,{scope:a},(0,T.createElement)(ac.Slot,{scope:a},(0,T.createElement)(oe.div,E({dir:o.dir},r,{ref:t}))))}),Qx=["ArrowRight","ArrowLeft","ArrowUp","ArrowDown"],ny=(0,T.forwardRef)((e,t)=>{let l=e,{__scopeNavigationMenu:a}=l,r=A(l,["__scopeNavigationMenu"]),o=v0(a),n=la("FocusGroupItem",a);return(0,T.createElement)(ac.ItemSlot,{scope:a},(0,T.createElement)(oe.button,E({},r,{ref:t,onKeyDown:q(e.onKeyDown,u=>{if(["Home","End",...Qx].includes(u.key)){let s=o().map(i=>i.ref.current);if([n.dir==="rtl"?"ArrowRight":"ArrowLeft","ArrowUp","End"].includes(u.key)&&s.reverse(),Qx.includes(u.key)){let i=s.indexOf(u.currentTarget);s=s.slice(i+1)}setTimeout(()=>uc(s)),u.preventDefault()}})})))});function rc(e){let t=[],a=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:r=>{let o=r.tagName==="INPUT"&&r.type==="hidden";return r.disabled||r.hidden||o?NodeFilter.FILTER_SKIP:r.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;a.nextNode();)t.push(a.currentNode);return t}function uc(e){let t=document.activeElement;return e.some(a=>a===t||(a.focus(),document.activeElement!==t))}function oc(e,t){let a=fe(t);mt(()=>{let r=0;if(e){let o=new ResizeObserver(()=>{cancelAnimationFrame(r),r=window.requestAnimationFrame(a)});return o.observe(e),()=>{window.cancelAnimationFrame(r),o.unobserve(e)}}},[e,a])}function sc(e){return e?"open":"closed"}function ly(e,t){return`${e}-trigger-${t}`}function uy(e,t){return`${e}-content-${t}`}function Os(e){return t=>t.pointerType==="mouse"?e(t):void 0}var ic=C0,dc=S0,sy=P0,fc=M0,iy=T0,cc=_0,pc=D0,mc=E0;var Kr=N(G(),1);var Fs=Kr.forwardRef((e,t)=>{var{className:a,children:r}=e,o=Se(e,["className","children"]);return(0,Ut.jsxs)(ic,Object.assign({ref:t,className:De("relative z-10 flex max-w-max flex-1 items-center justify-center",a)},o,{children:[r,(0,Ut.jsx)(vc,{})]}))});Fs.displayName=ic.displayName;var Bs=Kr.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,Ut.jsx)(dc,Object.assign({ref:t,className:De("group flex flex-1 list-none items-center justify-center space-x-1",a)},r))});Bs.displayName=dc.displayName;var hc=sy,Ns=os("group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"),dy=Kr.forwardRef((e,t)=>{var{className:a,children:r}=e,o=Se(e,["className","children"]);return(0,Ut.jsxs)(fc,Object.assign({ref:t,className:De(Ns(),"group",a),onPointerMove:n=>n.preventDefault(),onPointerLeave:n=>n.preventDefault()},o,{children:[r," ",(0,Ut.jsx)(Mg,{className:"relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180","aria-hidden":"true"})]}))});dy.displayName=fc.displayName;var fy=Kr.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,Ut.jsx)(pc,Object.assign({ref:t,className:De("left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",a),onPointerEnter:o=>o.preventDefault(),onPointerLeave:o=>o.preventDefault()},r))});fy.displayName=pc.displayName;var gc=iy,vc=Kr.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,Ut.jsx)("div",Object.assign({className:De("absolute left-0 top-full flex justify-center")},{children:(0,Ut.jsx)(mc,Object.assign({className:De("origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",a),ref:t},r))}))});vc.displayName=mc.displayName;var cy=Kr.forwardRef((e,t)=>{var{className:a}=e,r=Se(e,["className"]);return(0,Ut.jsx)(cc,Object.assign({ref:t,className:De("top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",a)},r,{children:(0,Ut.jsx)("div",{className:"relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md"})}))});cy.displayName=cc.displayName;var te;(function(e){e.assertEqual=o=>o;function t(o){}e.assertIs=t;function a(o){throw new Error}e.assertNever=a,e.arrayToEnum=o=>{let n={};for(let l of o)n[l]=l;return n},e.getValidEnumValues=o=>{let n=e.objectKeys(o).filter(u=>typeof o[o[u]]!="number"),l={};for(let u of n)l[u]=o[u];return e.objectValues(l)},e.objectValues=o=>e.objectKeys(o).map(function(n){return o[n]}),e.objectKeys=typeof Object.keys=="function"?o=>Object.keys(o):o=>{let n=[];for(let l in o)Object.prototype.hasOwnProperty.call(o,l)&&n.push(l);return n},e.find=(o,n)=>{for(let l of o)if(n(l))return l},e.isInteger=typeof Number.isInteger=="function"?o=>Number.isInteger(o):o=>typeof o=="number"&&isFinite(o)&&Math.floor(o)===o;function r(o,n=" | "){return o.map(l=>typeof l=="string"?`'${l}'`:l).join(n)}e.joinValues=r,e.jsonStringifyReplacer=(o,n)=>typeof n=="bigint"?n.toString():n})(te||(te={}));var yc;(function(e){e.mergeShapes=(t,a)=>w(w({},t),a)})(yc||(yc={}));var F=te.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),xr=e=>{switch(typeof e){case"undefined":return F.undefined;case"string":return F.string;case"number":return isNaN(e)?F.nan:F.number;case"boolean":return F.boolean;case"function":return F.function;case"bigint":return F.bigint;case"symbol":return F.symbol;case"object":return Array.isArray(e)?F.array:e===null?F.null:e.then&&typeof e.then=="function"&&e.catch&&typeof e.catch=="function"?F.promise:typeof Map!="undefined"&&e instanceof Map?F.map:typeof Set!="undefined"&&e instanceof Set?F.set:typeof Date!="undefined"&&e instanceof Date?F.date:F.object;default:return F.unknown}},D=te.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of","not_finite"]),O0=e=>JSON.stringify(e,null,2).replace(/"([^"]+)":/g,"$1:"),_t=class e extends Error{constructor(t){super(),this.issues=[],this.addIssue=r=>{this.issues=[...this.issues,r]},this.addIssues=(r=[])=>{this.issues=[...this.issues,...r]};let a=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,a):this.__proto__=a,this.name="ZodError",this.issues=t}get errors(){return this.issues}format(t){let a=t||function(n){return n.message},r={_errors:[]},o=n=>{for(let l of n.issues)if(l.code==="invalid_union")l.unionErrors.map(o);else if(l.code==="invalid_return_type")o(l.returnTypeError);else if(l.code==="invalid_arguments")o(l.argumentsError);else if(l.path.length===0)r._errors.push(a(l));else{let u=r,s=0;for(;sa.message){let a={},r=[];for(let o of this.issues)o.path.length>0?(a[o.path[0]]=a[o.path[0]]||[],a[o.path[0]].push(t(o))):r.push(t(o));return{formErrors:r,fieldErrors:a}}get formErrors(){return this.flatten()}};_t.create=e=>new _t(e);var ln=(e,t)=>{let a;switch(e.code){case D.invalid_type:e.received===F.undefined?a="Required":a=`Expected ${e.expected}, received ${e.received}`;break;case D.invalid_literal:a=`Invalid literal value, expected ${JSON.stringify(e.expected,te.jsonStringifyReplacer)}`;break;case D.unrecognized_keys:a=`Unrecognized key(s) in object: ${te.joinValues(e.keys,", ")}`;break;case D.invalid_union:a="Invalid input";break;case D.invalid_union_discriminator:a=`Invalid discriminator value. Expected ${te.joinValues(e.options)}`;break;case D.invalid_enum_value:a=`Invalid enum value. Expected ${te.joinValues(e.options)}, received '${e.received}'`;break;case D.invalid_arguments:a="Invalid function arguments";break;case D.invalid_return_type:a="Invalid function return type";break;case D.invalid_date:a="Invalid date";break;case D.invalid_string:typeof e.validation=="object"?"includes"in e.validation?(a=`Invalid input: must include "${e.validation.includes}"`,typeof e.validation.position=="number"&&(a=`${a} at one or more positions greater than or equal to ${e.validation.position}`)):"startsWith"in e.validation?a=`Invalid input: must start with "${e.validation.startsWith}"`:"endsWith"in e.validation?a=`Invalid input: must end with "${e.validation.endsWith}"`:te.assertNever(e.validation):e.validation!=="regex"?a=`Invalid ${e.validation}`:a="Invalid";break;case D.too_small:e.type==="array"?a=`Array must contain ${e.exact?"exactly":e.inclusive?"at least":"more than"} ${e.minimum} element(s)`:e.type==="string"?a=`String must contain ${e.exact?"exactly":e.inclusive?"at least":"over"} ${e.minimum} character(s)`:e.type==="number"?a=`Number must be ${e.exact?"exactly equal to ":e.inclusive?"greater than or equal to ":"greater than "}${e.minimum}`:e.type==="date"?a=`Date must be ${e.exact?"exactly equal to ":e.inclusive?"greater than or equal to ":"greater than "}${new Date(Number(e.minimum))}`:a="Invalid input";break;case D.too_big:e.type==="array"?a=`Array must contain ${e.exact?"exactly":e.inclusive?"at most":"less than"} ${e.maximum} element(s)`:e.type==="string"?a=`String must contain ${e.exact?"exactly":e.inclusive?"at most":"under"} ${e.maximum} character(s)`:e.type==="number"?a=`Number must be ${e.exact?"exactly":e.inclusive?"less than or equal to":"less than"} ${e.maximum}`:e.type==="bigint"?a=`BigInt must be ${e.exact?"exactly":e.inclusive?"less than or equal to":"less than"} ${e.maximum}`:e.type==="date"?a=`Date must be ${e.exact?"exactly":e.inclusive?"smaller than or equal to":"smaller than"} ${new Date(Number(e.maximum))}`:a="Invalid input";break;case D.custom:a="Invalid input";break;case D.invalid_intersection_types:a="Intersection results could not be merged";break;case D.not_multiple_of:a=`Number must be a multiple of ${e.multipleOf}`;break;case D.not_finite:a="Number must be finite";break;default:a=t.defaultError,te.assertNever(e)}return{message:a}},hy=ln;function F0(e){hy=e}function zs(){return hy}var Us=e=>{let{data:t,path:a,errorMaps:r,issueData:o}=e,n=[...a,...o.path||[]],l=b(w({},o),{path:n});if(o.message!==void 0)return b(w({},o),{path:n,message:o.message});let u="",s=r.filter(i=>!!i).slice().reverse();for(let i of s)u=i(l,{data:t,defaultError:u}).message;return b(w({},o),{path:n,message:u})},B0=[];function O(e,t){let a=zs(),r=Us({issueData:t,data:e.data,path:e.path,errorMaps:[e.common.contextualErrorMap,e.schemaErrorMap,a,a===ln?void 0:ln].filter(o=>!!o)});e.common.issues.push(r)}var Ye=class e{constructor(){this.value="valid"}dirty(){this.value==="valid"&&(this.value="dirty")}abort(){this.value!=="aborted"&&(this.value="aborted")}static mergeArray(t,a){let r=[];for(let o of a){if(o.status==="aborted")return V;o.status==="dirty"&&t.dirty(),r.push(o.value)}return{status:t.value,value:r}}static mergeObjectAsync(t,a){return Pe(this,null,function*(){let r=[];for(let o of a){let n=yield o.key,l=yield o.value;r.push({key:n,value:l})}return e.mergeObjectSync(t,r)})}static mergeObjectSync(t,a){let r={};for(let o of a){let{key:n,value:l}=o;if(n.status==="aborted"||l.status==="aborted")return V;n.status==="dirty"&&t.dirty(),l.status==="dirty"&&t.dirty(),n.value!=="__proto__"&&(typeof l.value!="undefined"||o.alwaysSet)&&(r[n.value]=l.value)}return{status:t.value,value:r}}},V=Object.freeze({status:"aborted"}),nn=e=>({status:"dirty",value:e}),nt=e=>({status:"valid",value:e}),Lc=e=>e.status==="aborted",wc=e=>e.status==="dirty",Il=e=>e.status==="valid",Sl=e=>typeof Promise!="undefined"&&e instanceof Promise;function qs(e,t,a,r){if(a==="a"&&!r)throw new TypeError("Private accessor was defined without a getter");if(typeof t=="function"?e!==t||!r:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return a==="m"?r:a==="a"?r.call(e):r?r.value:t.get(e)}function gy(e,t,a,r,o){if(r==="m")throw new TypeError("Private method is not writable");if(r==="a"&&!o)throw new TypeError("Private accessor was defined without a setter");if(typeof t=="function"?e!==t||!o:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return r==="a"?o.call(e,a):o?o.value=a:t.set(e,a),a}var z;(function(e){e.errToObj=t=>typeof t=="string"?{message:t}:t||{},e.toString=t=>typeof t=="string"?t:t==null?void 0:t.message})(z||(z={}));var wl,Cl,Ht=class{constructor(t,a,r,o){this._cachedPath=[],this.parent=t,this.data=a,this._path=r,this._key=o}get path(){return this._cachedPath.length||(this._key instanceof Array?this._cachedPath.push(...this._path,...this._key):this._cachedPath.push(...this._path,this._key)),this._cachedPath}},py=(e,t)=>{if(Il(t))return{success:!0,data:t.value};if(!e.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,get error(){if(this._error)return this._error;let a=new _t(e.common.issues);return this._error=a,this._error}}};function $(e){if(!e)return{};let{errorMap:t,invalid_type_error:a,required_error:r,description:o}=e;if(t&&(a||r))throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);return t?{errorMap:t,description:o}:{errorMap:(l,u)=>{var s,i;let{message:d}=e;return l.code==="invalid_enum_value"?{message:d!=null?d:u.defaultError}:typeof u.data=="undefined"?{message:(s=d!=null?d:r)!==null&&s!==void 0?s:u.defaultError}:l.code!=="invalid_type"?{message:u.defaultError}:{message:(i=d!=null?d:a)!==null&&i!==void 0?i:u.defaultError}},description:o}}var K=class{constructor(t){this.spa=this.safeParseAsync,this._def=t,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.brand=this.brand.bind(this),this.default=this.default.bind(this),this.catch=this.catch.bind(this),this.describe=this.describe.bind(this),this.pipe=this.pipe.bind(this),this.readonly=this.readonly.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this)}get description(){return this._def.description}_getType(t){return xr(t.data)}_getOrReturnCtx(t,a){return a||{common:t.parent.common,data:t.data,parsedType:xr(t.data),schemaErrorMap:this._def.errorMap,path:t.path,parent:t.parent}}_processInputParams(t){return{status:new Ye,ctx:{common:t.parent.common,data:t.data,parsedType:xr(t.data),schemaErrorMap:this._def.errorMap,path:t.path,parent:t.parent}}}_parseSync(t){let a=this._parse(t);if(Sl(a))throw new Error("Synchronous parse encountered promise.");return a}_parseAsync(t){let a=this._parse(t);return Promise.resolve(a)}parse(t,a){let r=this.safeParse(t,a);if(r.success)return r.data;throw r.error}safeParse(t,a){var r;let o={common:{issues:[],async:(r=a==null?void 0:a.async)!==null&&r!==void 0?r:!1,contextualErrorMap:a==null?void 0:a.errorMap},path:(a==null?void 0:a.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:t,parsedType:xr(t)},n=this._parseSync({data:t,path:o.path,parent:o});return py(o,n)}parseAsync(t,a){return Pe(this,null,function*(){let r=yield this.safeParseAsync(t,a);if(r.success)return r.data;throw r.error})}safeParseAsync(t,a){return Pe(this,null,function*(){let r={common:{issues:[],contextualErrorMap:a==null?void 0:a.errorMap,async:!0},path:(a==null?void 0:a.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:t,parsedType:xr(t)},o=this._parse({data:t,path:r.path,parent:r}),n=yield Sl(o)?o:Promise.resolve(o);return py(r,n)})}refine(t,a){let r=o=>typeof a=="string"||typeof a=="undefined"?{message:a}:typeof a=="function"?a(o):a;return this._refinement((o,n)=>{let l=t(o),u=()=>n.addIssue(w({code:D.custom},r(o)));return typeof Promise!="undefined"&&l instanceof Promise?l.then(s=>s?!0:(u(),!1)):l?!0:(u(),!1)})}refinement(t,a){return this._refinement((r,o)=>t(r)?!0:(o.addIssue(typeof a=="function"?a(r,o):a),!1))}_refinement(t){return new Rt({schema:this,typeName:H.ZodEffects,effect:{type:"refinement",refinement:t}})}superRefine(t){return this._refinement(t)}optional(){return qt.create(this,this._def)}nullable(){return Ia.create(this,this._def)}nullish(){return this.nullable().optional()}array(){return Ha.create(this,this._def)}promise(){return wr.create(this,this._def)}or(t){return ao.create([this,t],this._def)}and(t){return ro.create(this,t,this._def)}transform(t){return new Rt(b(w({},$(this._def)),{schema:this,typeName:H.ZodEffects,effect:{type:"transform",transform:t}}))}default(t){let a=typeof t=="function"?t:()=>t;return new so(b(w({},$(this._def)),{innerType:this,defaultValue:a,typeName:H.ZodDefault}))}brand(){return new kl(w({typeName:H.ZodBranded,type:this},$(this._def)))}catch(t){let a=typeof t=="function"?t:()=>t;return new io(b(w({},$(this._def)),{innerType:this,catchValue:a,typeName:H.ZodCatch}))}describe(t){let a=this.constructor;return new a(b(w({},this._def),{description:t}))}pipe(t){return Pl.create(this,t)}readonly(){return fo.create(this)}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}},N0=/^c[^\s-]{8,}$/i,z0=/^[0-9a-z]+$/,U0=/^[0-9A-HJKMNP-TV-Z]{26}$/,q0=/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i,H0=/^[a-z0-9_-]{21}$/i,V0=/^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/,j0=/^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i,W0="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$",xc,G0=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,Z0=/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/,$0=/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,vy="((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))",K0=new RegExp(`^${vy}$`);function xy(e){let t="([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d";return e.precision?t=`${t}\\.\\d{${e.precision}}`:e.precision==null&&(t=`${t}(\\.\\d+)?`),t}function X0(e){return new RegExp(`^${xy(e)}$`)}function yy(e){let t=`${vy}T${xy(e)}`,a=[];return a.push(e.local?"Z?":"Z"),e.offset&&a.push("([+-]\\d{2}:?\\d{2})"),t=`${t}(${a.join("|")})`,new RegExp(`^${t}$`)}function Q0(e,t){return!!((t==="v4"||!t)&&G0.test(e)||(t==="v6"||!t)&&Z0.test(e))}var yr=class e extends K{_parse(t){if(this._def.coerce&&(t.data=String(t.data)),this._getType(t)!==F.string){let n=this._getOrReturnCtx(t);return O(n,{code:D.invalid_type,expected:F.string,received:n.parsedType}),V}let r=new Ye,o;for(let n of this._def.checks)if(n.kind==="min")t.data.lengthn.value&&(o=this._getOrReturnCtx(t,o),O(o,{code:D.too_big,maximum:n.value,type:"string",inclusive:!0,exact:!1,message:n.message}),r.dirty());else if(n.kind==="length"){let l=t.data.length>n.value,u=t.data.lengtht.test(o),w({validation:a,code:D.invalid_string},z.errToObj(r)))}_addCheck(t){return new e(b(w({},this._def),{checks:[...this._def.checks,t]}))}email(t){return this._addCheck(w({kind:"email"},z.errToObj(t)))}url(t){return this._addCheck(w({kind:"url"},z.errToObj(t)))}emoji(t){return this._addCheck(w({kind:"emoji"},z.errToObj(t)))}uuid(t){return this._addCheck(w({kind:"uuid"},z.errToObj(t)))}nanoid(t){return this._addCheck(w({kind:"nanoid"},z.errToObj(t)))}cuid(t){return this._addCheck(w({kind:"cuid"},z.errToObj(t)))}cuid2(t){return this._addCheck(w({kind:"cuid2"},z.errToObj(t)))}ulid(t){return this._addCheck(w({kind:"ulid"},z.errToObj(t)))}base64(t){return this._addCheck(w({kind:"base64"},z.errToObj(t)))}ip(t){return this._addCheck(w({kind:"ip"},z.errToObj(t)))}datetime(t){var a,r;return typeof t=="string"?this._addCheck({kind:"datetime",precision:null,offset:!1,local:!1,message:t}):this._addCheck(w({kind:"datetime",precision:typeof(t==null?void 0:t.precision)=="undefined"?null:t==null?void 0:t.precision,offset:(a=t==null?void 0:t.offset)!==null&&a!==void 0?a:!1,local:(r=t==null?void 0:t.local)!==null&&r!==void 0?r:!1},z.errToObj(t==null?void 0:t.message)))}date(t){return this._addCheck({kind:"date",message:t})}time(t){return typeof t=="string"?this._addCheck({kind:"time",precision:null,message:t}):this._addCheck(w({kind:"time",precision:typeof(t==null?void 0:t.precision)=="undefined"?null:t==null?void 0:t.precision},z.errToObj(t==null?void 0:t.message)))}duration(t){return this._addCheck(w({kind:"duration"},z.errToObj(t)))}regex(t,a){return this._addCheck(w({kind:"regex",regex:t},z.errToObj(a)))}includes(t,a){return this._addCheck(w({kind:"includes",value:t,position:a==null?void 0:a.position},z.errToObj(a==null?void 0:a.message)))}startsWith(t,a){return this._addCheck(w({kind:"startsWith",value:t},z.errToObj(a)))}endsWith(t,a){return this._addCheck(w({kind:"endsWith",value:t},z.errToObj(a)))}min(t,a){return this._addCheck(w({kind:"min",value:t},z.errToObj(a)))}max(t,a){return this._addCheck(w({kind:"max",value:t},z.errToObj(a)))}length(t,a){return this._addCheck(w({kind:"length",value:t},z.errToObj(a)))}nonempty(t){return this.min(1,z.errToObj(t))}trim(){return new e(b(w({},this._def),{checks:[...this._def.checks,{kind:"trim"}]}))}toLowerCase(){return new e(b(w({},this._def),{checks:[...this._def.checks,{kind:"toLowerCase"}]}))}toUpperCase(){return new e(b(w({},this._def),{checks:[...this._def.checks,{kind:"toUpperCase"}]}))}get isDatetime(){return!!this._def.checks.find(t=>t.kind==="datetime")}get isDate(){return!!this._def.checks.find(t=>t.kind==="date")}get isTime(){return!!this._def.checks.find(t=>t.kind==="time")}get isDuration(){return!!this._def.checks.find(t=>t.kind==="duration")}get isEmail(){return!!this._def.checks.find(t=>t.kind==="email")}get isURL(){return!!this._def.checks.find(t=>t.kind==="url")}get isEmoji(){return!!this._def.checks.find(t=>t.kind==="emoji")}get isUUID(){return!!this._def.checks.find(t=>t.kind==="uuid")}get isNANOID(){return!!this._def.checks.find(t=>t.kind==="nanoid")}get isCUID(){return!!this._def.checks.find(t=>t.kind==="cuid")}get isCUID2(){return!!this._def.checks.find(t=>t.kind==="cuid2")}get isULID(){return!!this._def.checks.find(t=>t.kind==="ulid")}get isIP(){return!!this._def.checks.find(t=>t.kind==="ip")}get isBase64(){return!!this._def.checks.find(t=>t.kind==="base64")}get minLength(){let t=null;for(let a of this._def.checks)a.kind==="min"&&(t===null||a.value>t)&&(t=a.value);return t}get maxLength(){let t=null;for(let a of this._def.checks)a.kind==="max"&&(t===null||a.value{var t;return new yr(w({checks:[],typeName:H.ZodString,coerce:(t=e==null?void 0:e.coerce)!==null&&t!==void 0?t:!1},$(e)))};function Y0(e,t){let a=(e.toString().split(".")[1]||"").length,r=(t.toString().split(".")[1]||"").length,o=a>r?a:r,n=parseInt(e.toFixed(o).replace(".","")),l=parseInt(t.toFixed(o).replace(".",""));return n%l/Math.pow(10,o)}var Xr=class e extends K{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(t){if(this._def.coerce&&(t.data=Number(t.data)),this._getType(t)!==F.number){let n=this._getOrReturnCtx(t);return O(n,{code:D.invalid_type,expected:F.number,received:n.parsedType}),V}let r,o=new Ye;for(let n of this._def.checks)n.kind==="int"?te.isInteger(t.data)||(r=this._getOrReturnCtx(t,r),O(r,{code:D.invalid_type,expected:"integer",received:"float",message:n.message}),o.dirty()):n.kind==="min"?(n.inclusive?t.datan.value:t.data>=n.value)&&(r=this._getOrReturnCtx(t,r),O(r,{code:D.too_big,maximum:n.value,type:"number",inclusive:n.inclusive,exact:!1,message:n.message}),o.dirty()):n.kind==="multipleOf"?Y0(t.data,n.value)!==0&&(r=this._getOrReturnCtx(t,r),O(r,{code:D.not_multiple_of,multipleOf:n.value,message:n.message}),o.dirty()):n.kind==="finite"?Number.isFinite(t.data)||(r=this._getOrReturnCtx(t,r),O(r,{code:D.not_finite,message:n.message}),o.dirty()):te.assertNever(n);return{status:o.value,value:t.data}}gte(t,a){return this.setLimit("min",t,!0,z.toString(a))}gt(t,a){return this.setLimit("min",t,!1,z.toString(a))}lte(t,a){return this.setLimit("max",t,!0,z.toString(a))}lt(t,a){return this.setLimit("max",t,!1,z.toString(a))}setLimit(t,a,r,o){return new e(b(w({},this._def),{checks:[...this._def.checks,{kind:t,value:a,inclusive:r,message:z.toString(o)}]}))}_addCheck(t){return new e(b(w({},this._def),{checks:[...this._def.checks,t]}))}int(t){return this._addCheck({kind:"int",message:z.toString(t)})}positive(t){return this._addCheck({kind:"min",value:0,inclusive:!1,message:z.toString(t)})}negative(t){return this._addCheck({kind:"max",value:0,inclusive:!1,message:z.toString(t)})}nonpositive(t){return this._addCheck({kind:"max",value:0,inclusive:!0,message:z.toString(t)})}nonnegative(t){return this._addCheck({kind:"min",value:0,inclusive:!0,message:z.toString(t)})}multipleOf(t,a){return this._addCheck({kind:"multipleOf",value:t,message:z.toString(a)})}finite(t){return this._addCheck({kind:"finite",message:z.toString(t)})}safe(t){return this._addCheck({kind:"min",inclusive:!0,value:Number.MIN_SAFE_INTEGER,message:z.toString(t)})._addCheck({kind:"max",inclusive:!0,value:Number.MAX_SAFE_INTEGER,message:z.toString(t)})}get minValue(){let t=null;for(let a of this._def.checks)a.kind==="min"&&(t===null||a.value>t)&&(t=a.value);return t}get maxValue(){let t=null;for(let a of this._def.checks)a.kind==="max"&&(t===null||a.valuet.kind==="int"||t.kind==="multipleOf"&&te.isInteger(t.value))}get isFinite(){let t=null,a=null;for(let r of this._def.checks){if(r.kind==="finite"||r.kind==="int"||r.kind==="multipleOf")return!0;r.kind==="min"?(a===null||r.value>a)&&(a=r.value):r.kind==="max"&&(t===null||r.valuenew Xr(w({checks:[],typeName:H.ZodNumber,coerce:(e==null?void 0:e.coerce)||!1},$(e)));var Qr=class e extends K{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte}_parse(t){if(this._def.coerce&&(t.data=BigInt(t.data)),this._getType(t)!==F.bigint){let n=this._getOrReturnCtx(t);return O(n,{code:D.invalid_type,expected:F.bigint,received:n.parsedType}),V}let r,o=new Ye;for(let n of this._def.checks)n.kind==="min"?(n.inclusive?t.datan.value:t.data>=n.value)&&(r=this._getOrReturnCtx(t,r),O(r,{code:D.too_big,type:"bigint",maximum:n.value,inclusive:n.inclusive,message:n.message}),o.dirty()):n.kind==="multipleOf"?t.data%n.value!==BigInt(0)&&(r=this._getOrReturnCtx(t,r),O(r,{code:D.not_multiple_of,multipleOf:n.value,message:n.message}),o.dirty()):te.assertNever(n);return{status:o.value,value:t.data}}gte(t,a){return this.setLimit("min",t,!0,z.toString(a))}gt(t,a){return this.setLimit("min",t,!1,z.toString(a))}lte(t,a){return this.setLimit("max",t,!0,z.toString(a))}lt(t,a){return this.setLimit("max",t,!1,z.toString(a))}setLimit(t,a,r,o){return new e(b(w({},this._def),{checks:[...this._def.checks,{kind:t,value:a,inclusive:r,message:z.toString(o)}]}))}_addCheck(t){return new e(b(w({},this._def),{checks:[...this._def.checks,t]}))}positive(t){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!1,message:z.toString(t)})}negative(t){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!1,message:z.toString(t)})}nonpositive(t){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!0,message:z.toString(t)})}nonnegative(t){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!0,message:z.toString(t)})}multipleOf(t,a){return this._addCheck({kind:"multipleOf",value:t,message:z.toString(a)})}get minValue(){let t=null;for(let a of this._def.checks)a.kind==="min"&&(t===null||a.value>t)&&(t=a.value);return t}get maxValue(){let t=null;for(let a of this._def.checks)a.kind==="max"&&(t===null||a.value{var t;return new Qr(w({checks:[],typeName:H.ZodBigInt,coerce:(t=e==null?void 0:e.coerce)!==null&&t!==void 0?t:!1},$(e)))};var Yr=class extends K{_parse(t){if(this._def.coerce&&(t.data=!!t.data),this._getType(t)!==F.boolean){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.boolean,received:r.parsedType}),V}return nt(t.data)}};Yr.create=e=>new Yr(w({typeName:H.ZodBoolean,coerce:(e==null?void 0:e.coerce)||!1},$(e)));var Jr=class e extends K{_parse(t){if(this._def.coerce&&(t.data=new Date(t.data)),this._getType(t)!==F.date){let n=this._getOrReturnCtx(t);return O(n,{code:D.invalid_type,expected:F.date,received:n.parsedType}),V}if(isNaN(t.data.getTime())){let n=this._getOrReturnCtx(t);return O(n,{code:D.invalid_date}),V}let r=new Ye,o;for(let n of this._def.checks)n.kind==="min"?t.data.getTime()n.value&&(o=this._getOrReturnCtx(t,o),O(o,{code:D.too_big,message:n.message,inclusive:!0,exact:!1,maximum:n.value,type:"date"}),r.dirty()):te.assertNever(n);return{status:r.value,value:new Date(t.data.getTime())}}_addCheck(t){return new e(b(w({},this._def),{checks:[...this._def.checks,t]}))}min(t,a){return this._addCheck({kind:"min",value:t.getTime(),message:z.toString(a)})}max(t,a){return this._addCheck({kind:"max",value:t.getTime(),message:z.toString(a)})}get minDate(){let t=null;for(let a of this._def.checks)a.kind==="min"&&(t===null||a.value>t)&&(t=a.value);return t!=null?new Date(t):null}get maxDate(){let t=null;for(let a of this._def.checks)a.kind==="max"&&(t===null||a.valuenew Jr(w({checks:[],coerce:(e==null?void 0:e.coerce)||!1,typeName:H.ZodDate},$(e)));var un=class extends K{_parse(t){if(this._getType(t)!==F.symbol){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.symbol,received:r.parsedType}),V}return nt(t.data)}};un.create=e=>new un(w({typeName:H.ZodSymbol},$(e)));var eo=class extends K{_parse(t){if(this._getType(t)!==F.undefined){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.undefined,received:r.parsedType}),V}return nt(t.data)}};eo.create=e=>new eo(w({typeName:H.ZodUndefined},$(e)));var to=class extends K{_parse(t){if(this._getType(t)!==F.null){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.null,received:r.parsedType}),V}return nt(t.data)}};to.create=e=>new to(w({typeName:H.ZodNull},$(e)));var Lr=class extends K{constructor(){super(...arguments),this._any=!0}_parse(t){return nt(t.data)}};Lr.create=e=>new Lr(w({typeName:H.ZodAny},$(e)));var qa=class extends K{constructor(){super(...arguments),this._unknown=!0}_parse(t){return nt(t.data)}};qa.create=e=>new qa(w({typeName:H.ZodUnknown},$(e)));var ua=class extends K{_parse(t){let a=this._getOrReturnCtx(t);return O(a,{code:D.invalid_type,expected:F.never,received:a.parsedType}),V}};ua.create=e=>new ua(w({typeName:H.ZodNever},$(e)));var sn=class extends K{_parse(t){if(this._getType(t)!==F.undefined){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.void,received:r.parsedType}),V}return nt(t.data)}};sn.create=e=>new sn(w({typeName:H.ZodVoid},$(e)));var Ha=class e extends K{_parse(t){let{ctx:a,status:r}=this._processInputParams(t),o=this._def;if(a.parsedType!==F.array)return O(a,{code:D.invalid_type,expected:F.array,received:a.parsedType}),V;if(o.exactLength!==null){let l=a.data.length>o.exactLength.value,u=a.data.lengtho.maxLength.value&&(O(a,{code:D.too_big,maximum:o.maxLength.value,type:"array",inclusive:!0,exact:!1,message:o.maxLength.message}),r.dirty()),a.common.async)return Promise.all([...a.data].map((l,u)=>o.type._parseAsync(new Ht(a,l,a.path,u)))).then(l=>Ye.mergeArray(r,l));let n=[...a.data].map((l,u)=>o.type._parseSync(new Ht(a,l,a.path,u)));return Ye.mergeArray(r,n)}get element(){return this._def.type}min(t,a){return new e(b(w({},this._def),{minLength:{value:t,message:z.toString(a)}}))}max(t,a){return new e(b(w({},this._def),{maxLength:{value:t,message:z.toString(a)}}))}length(t,a){return new e(b(w({},this._def),{exactLength:{value:t,message:z.toString(a)}}))}nonempty(t){return this.min(1,t)}};Ha.create=(e,t)=>new Ha(w({type:e,minLength:null,maxLength:null,exactLength:null,typeName:H.ZodArray},$(t)));function on(e){if(e instanceof vt){let t={};for(let a in e.shape){let r=e.shape[a];t[a]=qt.create(on(r))}return new vt(b(w({},e._def),{shape:()=>t}))}else return e instanceof Ha?new Ha(b(w({},e._def),{type:on(e.element)})):e instanceof qt?qt.create(on(e.unwrap())):e instanceof Ia?Ia.create(on(e.unwrap())):e instanceof Ca?Ca.create(e.items.map(t=>on(t))):e}var vt=class e extends K{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=this.extend}_getCached(){if(this._cached!==null)return this._cached;let t=this._def.shape(),a=te.objectKeys(t);return this._cached={shape:t,keys:a}}_parse(t){if(this._getType(t)!==F.object){let i=this._getOrReturnCtx(t);return O(i,{code:D.invalid_type,expected:F.object,received:i.parsedType}),V}let{status:r,ctx:o}=this._processInputParams(t),{shape:n,keys:l}=this._getCached(),u=[];if(!(this._def.catchall instanceof ua&&this._def.unknownKeys==="strip"))for(let i in o.data)l.includes(i)||u.push(i);let s=[];for(let i of l){let d=n[i],f=o.data[i];s.push({key:{status:"valid",value:i},value:d._parse(new Ht(o,f,o.path,i)),alwaysSet:i in o.data})}if(this._def.catchall instanceof ua){let i=this._def.unknownKeys;if(i==="passthrough")for(let d of u)s.push({key:{status:"valid",value:d},value:{status:"valid",value:o.data[d]}});else if(i==="strict")u.length>0&&(O(o,{code:D.unrecognized_keys,keys:u}),r.dirty());else if(i!=="strip")throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{let i=this._def.catchall;for(let d of u){let f=o.data[d];s.push({key:{status:"valid",value:d},value:i._parse(new Ht(o,f,o.path,d)),alwaysSet:d in o.data})}}return o.common.async?Promise.resolve().then(()=>Pe(this,null,function*(){let i=[];for(let d of s){let f=yield d.key,p=yield d.value;i.push({key:f,value:p,alwaysSet:d.alwaysSet})}return i})).then(i=>Ye.mergeObjectSync(r,i)):Ye.mergeObjectSync(r,s)}get shape(){return this._def.shape()}strict(t){return z.errToObj,new e(w(b(w({},this._def),{unknownKeys:"strict"}),t!==void 0?{errorMap:(a,r)=>{var o,n,l,u;let s=(l=(n=(o=this._def).errorMap)===null||n===void 0?void 0:n.call(o,a,r).message)!==null&&l!==void 0?l:r.defaultError;return a.code==="unrecognized_keys"?{message:(u=z.errToObj(t).message)!==null&&u!==void 0?u:s}:{message:s}}}:{}))}strip(){return new e(b(w({},this._def),{unknownKeys:"strip"}))}passthrough(){return new e(b(w({},this._def),{unknownKeys:"passthrough"}))}extend(t){return new e(b(w({},this._def),{shape:()=>w(w({},this._def.shape()),t)}))}merge(t){return new e({unknownKeys:t._def.unknownKeys,catchall:t._def.catchall,shape:()=>w(w({},this._def.shape()),t._def.shape()),typeName:H.ZodObject})}setKey(t,a){return this.augment({[t]:a})}catchall(t){return new e(b(w({},this._def),{catchall:t}))}pick(t){let a={};return te.objectKeys(t).forEach(r=>{t[r]&&this.shape[r]&&(a[r]=this.shape[r])}),new e(b(w({},this._def),{shape:()=>a}))}omit(t){let a={};return te.objectKeys(this.shape).forEach(r=>{t[r]||(a[r]=this.shape[r])}),new e(b(w({},this._def),{shape:()=>a}))}deepPartial(){return on(this)}partial(t){let a={};return te.objectKeys(this.shape).forEach(r=>{let o=this.shape[r];t&&!t[r]?a[r]=o:a[r]=o.optional()}),new e(b(w({},this._def),{shape:()=>a}))}required(t){let a={};return te.objectKeys(this.shape).forEach(r=>{if(t&&!t[r])a[r]=this.shape[r];else{let n=this.shape[r];for(;n instanceof qt;)n=n._def.innerType;a[r]=n}}),new e(b(w({},this._def),{shape:()=>a}))}keyof(){return Ly(te.objectKeys(this.shape))}};vt.create=(e,t)=>new vt(w({shape:()=>e,unknownKeys:"strip",catchall:ua.create(),typeName:H.ZodObject},$(t)));vt.strictCreate=(e,t)=>new vt(w({shape:()=>e,unknownKeys:"strict",catchall:ua.create(),typeName:H.ZodObject},$(t)));vt.lazycreate=(e,t)=>new vt(w({shape:e,unknownKeys:"strip",catchall:ua.create(),typeName:H.ZodObject},$(t)));var ao=class extends K{_parse(t){let{ctx:a}=this._processInputParams(t),r=this._def.options;function o(n){for(let u of n)if(u.result.status==="valid")return u.result;for(let u of n)if(u.result.status==="dirty")return a.common.issues.push(...u.ctx.common.issues),u.result;let l=n.map(u=>new _t(u.ctx.common.issues));return O(a,{code:D.invalid_union,unionErrors:l}),V}if(a.common.async)return Promise.all(r.map(n=>Pe(this,null,function*(){let l=b(w({},a),{common:b(w({},a.common),{issues:[]}),parent:null});return{result:yield n._parseAsync({data:a.data,path:a.path,parent:l}),ctx:l}}))).then(o);{let n,l=[];for(let s of r){let i=b(w({},a),{common:b(w({},a.common),{issues:[]}),parent:null}),d=s._parseSync({data:a.data,path:a.path,parent:i});if(d.status==="valid")return d;d.status==="dirty"&&!n&&(n={result:d,ctx:i}),i.common.issues.length&&l.push(i.common.issues)}if(n)return a.common.issues.push(...n.ctx.common.issues),n.result;let u=l.map(s=>new _t(s));return O(a,{code:D.invalid_union,unionErrors:u}),V}}get options(){return this._def.options}};ao.create=(e,t)=>new ao(w({options:e,typeName:H.ZodUnion},$(t)));var Ua=e=>e instanceof oo?Ua(e.schema):e instanceof Rt?Ua(e.innerType()):e instanceof no?[e.value]:e instanceof lo?e.options:e instanceof uo?te.objectValues(e.enum):e instanceof so?Ua(e._def.innerType):e instanceof eo?[void 0]:e instanceof to?[null]:e instanceof qt?[void 0,...Ua(e.unwrap())]:e instanceof Ia?[null,...Ua(e.unwrap())]:e instanceof kl||e instanceof fo?Ua(e.unwrap()):e instanceof io?Ua(e._def.innerType):[],Hs=class e extends K{_parse(t){let{ctx:a}=this._processInputParams(t);if(a.parsedType!==F.object)return O(a,{code:D.invalid_type,expected:F.object,received:a.parsedType}),V;let r=this.discriminator,o=a.data[r],n=this.optionsMap.get(o);return n?a.common.async?n._parseAsync({data:a.data,path:a.path,parent:a}):n._parseSync({data:a.data,path:a.path,parent:a}):(O(a,{code:D.invalid_union_discriminator,options:Array.from(this.optionsMap.keys()),path:[r]}),V)}get discriminator(){return this._def.discriminator}get options(){return this._def.options}get optionsMap(){return this._def.optionsMap}static create(t,a,r){let o=new Map;for(let n of a){let l=Ua(n.shape[t]);if(!l.length)throw new Error(`A discriminator value for key \`${t}\` could not be extracted from all schema options`);for(let u of l){if(o.has(u))throw new Error(`Discriminator property ${String(t)} has duplicate value ${String(u)}`);o.set(u,n)}}return new e(w({typeName:H.ZodDiscriminatedUnion,discriminator:t,options:a,optionsMap:o},$(r)))}};function Cc(e,t){let a=xr(e),r=xr(t);if(e===t)return{valid:!0,data:e};if(a===F.object&&r===F.object){let o=te.objectKeys(t),n=te.objectKeys(e).filter(u=>o.indexOf(u)!==-1),l=w(w({},e),t);for(let u of n){let s=Cc(e[u],t[u]);if(!s.valid)return{valid:!1};l[u]=s.data}return{valid:!0,data:l}}else if(a===F.array&&r===F.array){if(e.length!==t.length)return{valid:!1};let o=[];for(let n=0;n{if(Lc(n)||Lc(l))return V;let u=Cc(n.value,l.value);return u.valid?((wc(n)||wc(l))&&a.dirty(),{status:a.value,value:u.data}):(O(r,{code:D.invalid_intersection_types}),V)};return r.common.async?Promise.all([this._def.left._parseAsync({data:r.data,path:r.path,parent:r}),this._def.right._parseAsync({data:r.data,path:r.path,parent:r})]).then(([n,l])=>o(n,l)):o(this._def.left._parseSync({data:r.data,path:r.path,parent:r}),this._def.right._parseSync({data:r.data,path:r.path,parent:r}))}};ro.create=(e,t,a)=>new ro(w({left:e,right:t,typeName:H.ZodIntersection},$(a)));var Ca=class e extends K{_parse(t){let{status:a,ctx:r}=this._processInputParams(t);if(r.parsedType!==F.array)return O(r,{code:D.invalid_type,expected:F.array,received:r.parsedType}),V;if(r.data.lengththis._def.items.length&&(O(r,{code:D.too_big,maximum:this._def.items.length,inclusive:!0,exact:!1,type:"array"}),a.dirty());let n=[...r.data].map((l,u)=>{let s=this._def.items[u]||this._def.rest;return s?s._parse(new Ht(r,l,r.path,u)):null}).filter(l=>!!l);return r.common.async?Promise.all(n).then(l=>Ye.mergeArray(a,l)):Ye.mergeArray(a,n)}get items(){return this._def.items}rest(t){return new e(b(w({},this._def),{rest:t}))}};Ca.create=(e,t)=>{if(!Array.isArray(e))throw new Error("You must pass an array of schemas to z.tuple([ ... ])");return new Ca(w({items:e,typeName:H.ZodTuple,rest:null},$(t)))};var Vs=class e extends K{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(t){let{status:a,ctx:r}=this._processInputParams(t);if(r.parsedType!==F.object)return O(r,{code:D.invalid_type,expected:F.object,received:r.parsedType}),V;let o=[],n=this._def.keyType,l=this._def.valueType;for(let u in r.data)o.push({key:n._parse(new Ht(r,u,r.path,u)),value:l._parse(new Ht(r,r.data[u],r.path,u)),alwaysSet:u in r.data});return r.common.async?Ye.mergeObjectAsync(a,o):Ye.mergeObjectSync(a,o)}get element(){return this._def.valueType}static create(t,a,r){return a instanceof K?new e(w({keyType:t,valueType:a,typeName:H.ZodRecord},$(r))):new e(w({keyType:yr.create(),valueType:t,typeName:H.ZodRecord},$(a)))}},dn=class extends K{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(t){let{status:a,ctx:r}=this._processInputParams(t);if(r.parsedType!==F.map)return O(r,{code:D.invalid_type,expected:F.map,received:r.parsedType}),V;let o=this._def.keyType,n=this._def.valueType,l=[...r.data.entries()].map(([u,s],i)=>({key:o._parse(new Ht(r,u,r.path,[i,"key"])),value:n._parse(new Ht(r,s,r.path,[i,"value"]))}));if(r.common.async){let u=new Map;return Promise.resolve().then(()=>Pe(this,null,function*(){for(let s of l){let i=yield s.key,d=yield s.value;if(i.status==="aborted"||d.status==="aborted")return V;(i.status==="dirty"||d.status==="dirty")&&a.dirty(),u.set(i.value,d.value)}return{status:a.value,value:u}}))}else{let u=new Map;for(let s of l){let i=s.key,d=s.value;if(i.status==="aborted"||d.status==="aborted")return V;(i.status==="dirty"||d.status==="dirty")&&a.dirty(),u.set(i.value,d.value)}return{status:a.value,value:u}}}};dn.create=(e,t,a)=>new dn(w({valueType:t,keyType:e,typeName:H.ZodMap},$(a)));var fn=class e extends K{_parse(t){let{status:a,ctx:r}=this._processInputParams(t);if(r.parsedType!==F.set)return O(r,{code:D.invalid_type,expected:F.set,received:r.parsedType}),V;let o=this._def;o.minSize!==null&&r.data.sizeo.maxSize.value&&(O(r,{code:D.too_big,maximum:o.maxSize.value,type:"set",inclusive:!0,exact:!1,message:o.maxSize.message}),a.dirty());let n=this._def.valueType;function l(s){let i=new Set;for(let d of s){if(d.status==="aborted")return V;d.status==="dirty"&&a.dirty(),i.add(d.value)}return{status:a.value,value:i}}let u=[...r.data.values()].map((s,i)=>n._parse(new Ht(r,s,r.path,i)));return r.common.async?Promise.all(u).then(s=>l(s)):l(u)}min(t,a){return new e(b(w({},this._def),{minSize:{value:t,message:z.toString(a)}}))}max(t,a){return new e(b(w({},this._def),{maxSize:{value:t,message:z.toString(a)}}))}size(t,a){return this.min(t,a).max(t,a)}nonempty(t){return this.min(1,t)}};fn.create=(e,t)=>new fn(w({valueType:e,minSize:null,maxSize:null,typeName:H.ZodSet},$(t)));var js=class e extends K{constructor(){super(...arguments),this.validate=this.implement}_parse(t){let{ctx:a}=this._processInputParams(t);if(a.parsedType!==F.function)return O(a,{code:D.invalid_type,expected:F.function,received:a.parsedType}),V;function r(u,s){return Us({data:u,path:a.path,errorMaps:[a.common.contextualErrorMap,a.schemaErrorMap,zs(),ln].filter(i=>!!i),issueData:{code:D.invalid_arguments,argumentsError:s}})}function o(u,s){return Us({data:u,path:a.path,errorMaps:[a.common.contextualErrorMap,a.schemaErrorMap,zs(),ln].filter(i=>!!i),issueData:{code:D.invalid_return_type,returnTypeError:s}})}let n={errorMap:a.common.contextualErrorMap},l=a.data;if(this._def.returns instanceof wr){let u=this;return nt(function(...s){return Pe(this,null,function*(){let i=new _t([]),d=yield u._def.args.parseAsync(s,n).catch(v=>{throw i.addIssue(r(s,v)),i}),f=yield Reflect.apply(l,this,d);return yield u._def.returns._def.type.parseAsync(f,n).catch(v=>{throw i.addIssue(o(f,v)),i})})})}else{let u=this;return nt(function(...s){let i=u._def.args.safeParse(s,n);if(!i.success)throw new _t([r(s,i.error)]);let d=Reflect.apply(l,this,i.data),f=u._def.returns.safeParse(d,n);if(!f.success)throw new _t([o(d,f.error)]);return f.data})}}parameters(){return this._def.args}returnType(){return this._def.returns}args(...t){return new e(b(w({},this._def),{args:Ca.create(t).rest(qa.create())}))}returns(t){return new e(b(w({},this._def),{returns:t}))}implement(t){return this.parse(t)}strictImplement(t){return this.parse(t)}static create(t,a,r){return new e(w({args:t||Ca.create([]).rest(qa.create()),returns:a||qa.create(),typeName:H.ZodFunction},$(r)))}},oo=class extends K{get schema(){return this._def.getter()}_parse(t){let{ctx:a}=this._processInputParams(t);return this._def.getter()._parse({data:a.data,path:a.path,parent:a})}};oo.create=(e,t)=>new oo(w({getter:e,typeName:H.ZodLazy},$(t)));var no=class extends K{_parse(t){if(t.data!==this._def.value){let a=this._getOrReturnCtx(t);return O(a,{received:a.data,code:D.invalid_literal,expected:this._def.value}),V}return{status:"valid",value:t.data}}get value(){return this._def.value}};no.create=(e,t)=>new no(w({value:e,typeName:H.ZodLiteral},$(t)));function Ly(e,t){return new lo(w({values:e,typeName:H.ZodEnum},$(t)))}var lo=class e extends K{constructor(){super(...arguments),wl.set(this,void 0)}_parse(t){if(typeof t.data!="string"){let a=this._getOrReturnCtx(t),r=this._def.values;return O(a,{expected:te.joinValues(r),received:a.parsedType,code:D.invalid_type}),V}if(qs(this,wl,"f")||gy(this,wl,new Set(this._def.values),"f"),!qs(this,wl,"f").has(t.data)){let a=this._getOrReturnCtx(t),r=this._def.values;return O(a,{received:a.data,code:D.invalid_enum_value,options:r}),V}return nt(t.data)}get options(){return this._def.values}get enum(){let t={};for(let a of this._def.values)t[a]=a;return t}get Values(){let t={};for(let a of this._def.values)t[a]=a;return t}get Enum(){let t={};for(let a of this._def.values)t[a]=a;return t}extract(t,a=this._def){return e.create(t,w(w({},this._def),a))}exclude(t,a=this._def){return e.create(this.options.filter(r=>!t.includes(r)),w(w({},this._def),a))}};wl=new WeakMap;lo.create=Ly;var uo=class extends K{constructor(){super(...arguments),Cl.set(this,void 0)}_parse(t){let a=te.getValidEnumValues(this._def.values),r=this._getOrReturnCtx(t);if(r.parsedType!==F.string&&r.parsedType!==F.number){let o=te.objectValues(a);return O(r,{expected:te.joinValues(o),received:r.parsedType,code:D.invalid_type}),V}if(qs(this,Cl,"f")||gy(this,Cl,new Set(te.getValidEnumValues(this._def.values)),"f"),!qs(this,Cl,"f").has(t.data)){let o=te.objectValues(a);return O(r,{received:r.data,code:D.invalid_enum_value,options:o}),V}return nt(t.data)}get enum(){return this._def.values}};Cl=new WeakMap;uo.create=(e,t)=>new uo(w({values:e,typeName:H.ZodNativeEnum},$(t)));var wr=class extends K{unwrap(){return this._def.type}_parse(t){let{ctx:a}=this._processInputParams(t);if(a.parsedType!==F.promise&&a.common.async===!1)return O(a,{code:D.invalid_type,expected:F.promise,received:a.parsedType}),V;let r=a.parsedType===F.promise?a.data:Promise.resolve(a.data);return nt(r.then(o=>this._def.type.parseAsync(o,{path:a.path,errorMap:a.common.contextualErrorMap})))}};wr.create=(e,t)=>new wr(w({type:e,typeName:H.ZodPromise},$(t)));var Rt=class extends K{innerType(){return this._def.schema}sourceType(){return this._def.schema._def.typeName===H.ZodEffects?this._def.schema.sourceType():this._def.schema}_parse(t){let{status:a,ctx:r}=this._processInputParams(t),o=this._def.effect||null,n={addIssue:l=>{O(r,l),l.fatal?a.abort():a.dirty()},get path(){return r.path}};if(n.addIssue=n.addIssue.bind(n),o.type==="preprocess"){let l=o.transform(r.data,n);if(r.common.async)return Promise.resolve(l).then(u=>Pe(this,null,function*(){if(a.value==="aborted")return V;let s=yield this._def.schema._parseAsync({data:u,path:r.path,parent:r});return s.status==="aborted"?V:s.status==="dirty"||a.value==="dirty"?nn(s.value):s}));{if(a.value==="aborted")return V;let u=this._def.schema._parseSync({data:l,path:r.path,parent:r});return u.status==="aborted"?V:u.status==="dirty"||a.value==="dirty"?nn(u.value):u}}if(o.type==="refinement"){let l=u=>{let s=o.refinement(u,n);if(r.common.async)return Promise.resolve(s);if(s instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return u};if(r.common.async===!1){let u=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});return u.status==="aborted"?V:(u.status==="dirty"&&a.dirty(),l(u.value),{status:a.value,value:u.value})}else return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then(u=>u.status==="aborted"?V:(u.status==="dirty"&&a.dirty(),l(u.value).then(()=>({status:a.value,value:u.value}))))}if(o.type==="transform")if(r.common.async===!1){let l=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});if(!Il(l))return l;let u=o.transform(l.value,n);if(u instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:a.value,value:u}}else return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then(l=>Il(l)?Promise.resolve(o.transform(l.value,n)).then(u=>({status:a.value,value:u})):l);te.assertNever(o)}};Rt.create=(e,t,a)=>new Rt(w({schema:e,typeName:H.ZodEffects,effect:t},$(a)));Rt.createWithPreprocess=(e,t,a)=>new Rt(w({schema:t,effect:{type:"preprocess",transform:e},typeName:H.ZodEffects},$(a)));var qt=class extends K{_parse(t){return this._getType(t)===F.undefined?nt(void 0):this._def.innerType._parse(t)}unwrap(){return this._def.innerType}};qt.create=(e,t)=>new qt(w({innerType:e,typeName:H.ZodOptional},$(t)));var Ia=class extends K{_parse(t){return this._getType(t)===F.null?nt(null):this._def.innerType._parse(t)}unwrap(){return this._def.innerType}};Ia.create=(e,t)=>new Ia(w({innerType:e,typeName:H.ZodNullable},$(t)));var so=class extends K{_parse(t){let{ctx:a}=this._processInputParams(t),r=a.data;return a.parsedType===F.undefined&&(r=this._def.defaultValue()),this._def.innerType._parse({data:r,path:a.path,parent:a})}removeDefault(){return this._def.innerType}};so.create=(e,t)=>new so(w({innerType:e,typeName:H.ZodDefault,defaultValue:typeof t.default=="function"?t.default:()=>t.default},$(t)));var io=class extends K{_parse(t){let{ctx:a}=this._processInputParams(t),r=b(w({},a),{common:b(w({},a.common),{issues:[]})}),o=this._def.innerType._parse({data:r.data,path:r.path,parent:w({},r)});return Sl(o)?o.then(n=>({status:"valid",value:n.status==="valid"?n.value:this._def.catchValue({get error(){return new _t(r.common.issues)},input:r.data})})):{status:"valid",value:o.status==="valid"?o.value:this._def.catchValue({get error(){return new _t(r.common.issues)},input:r.data})}}removeCatch(){return this._def.innerType}};io.create=(e,t)=>new io(w({innerType:e,typeName:H.ZodCatch,catchValue:typeof t.catch=="function"?t.catch:()=>t.catch},$(t)));var cn=class extends K{_parse(t){if(this._getType(t)!==F.nan){let r=this._getOrReturnCtx(t);return O(r,{code:D.invalid_type,expected:F.nan,received:r.parsedType}),V}return{status:"valid",value:t.data}}};cn.create=e=>new cn(w({typeName:H.ZodNaN},$(e)));var J0=Symbol("zod_brand"),kl=class extends K{_parse(t){let{ctx:a}=this._processInputParams(t),r=a.data;return this._def.type._parse({data:r,path:a.path,parent:a})}unwrap(){return this._def.type}},Pl=class e extends K{_parse(t){let{status:a,ctx:r}=this._processInputParams(t);if(r.common.async)return Pe(this,null,function*(){let n=yield this._def.in._parseAsync({data:r.data,path:r.path,parent:r});return n.status==="aborted"?V:n.status==="dirty"?(a.dirty(),nn(n.value)):this._def.out._parseAsync({data:n.value,path:r.path,parent:r})});{let o=this._def.in._parseSync({data:r.data,path:r.path,parent:r});return o.status==="aborted"?V:o.status==="dirty"?(a.dirty(),{status:"dirty",value:o.value}):this._def.out._parseSync({data:o.value,path:r.path,parent:r})}}static create(t,a){return new e({in:t,out:a,typeName:H.ZodPipeline})}},fo=class extends K{_parse(t){let a=this._def.innerType._parse(t),r=o=>(Il(o)&&(o.value=Object.freeze(o.value)),o);return Sl(a)?a.then(o=>r(o)):r(a)}unwrap(){return this._def.innerType}};fo.create=(e,t)=>new fo(w({innerType:e,typeName:H.ZodReadonly},$(t)));function wy(e,t={},a){return e?Lr.create().superRefine((r,o)=>{var n,l;if(!e(r)){let u=typeof t=="function"?t(r):typeof t=="string"?{message:t}:t,s=(l=(n=u.fatal)!==null&&n!==void 0?n:a)!==null&&l!==void 0?l:!0,i=typeof u=="string"?{message:u}:u;o.addIssue(b(w({code:"custom"},i),{fatal:s}))}}):Lr.create()}var eS={object:vt.lazycreate},H;(function(e){e.ZodString="ZodString",e.ZodNumber="ZodNumber",e.ZodNaN="ZodNaN",e.ZodBigInt="ZodBigInt",e.ZodBoolean="ZodBoolean",e.ZodDate="ZodDate",e.ZodSymbol="ZodSymbol",e.ZodUndefined="ZodUndefined",e.ZodNull="ZodNull",e.ZodAny="ZodAny",e.ZodUnknown="ZodUnknown",e.ZodNever="ZodNever",e.ZodVoid="ZodVoid",e.ZodArray="ZodArray",e.ZodObject="ZodObject",e.ZodUnion="ZodUnion",e.ZodDiscriminatedUnion="ZodDiscriminatedUnion",e.ZodIntersection="ZodIntersection",e.ZodTuple="ZodTuple",e.ZodRecord="ZodRecord",e.ZodMap="ZodMap",e.ZodSet="ZodSet",e.ZodFunction="ZodFunction",e.ZodLazy="ZodLazy",e.ZodLiteral="ZodLiteral",e.ZodEnum="ZodEnum",e.ZodEffects="ZodEffects",e.ZodNativeEnum="ZodNativeEnum",e.ZodOptional="ZodOptional",e.ZodNullable="ZodNullable",e.ZodDefault="ZodDefault",e.ZodCatch="ZodCatch",e.ZodPromise="ZodPromise",e.ZodBranded="ZodBranded",e.ZodPipeline="ZodPipeline",e.ZodReadonly="ZodReadonly"})(H||(H={}));var tS=(e,t={message:`Input not instance of ${e.name}`})=>wy(a=>a instanceof e,t),Cy=yr.create,Iy=Xr.create,aS=cn.create,rS=Qr.create,Sy=Yr.create,oS=Jr.create,nS=un.create,lS=eo.create,uS=to.create,sS=Lr.create,iS=qa.create,dS=ua.create,fS=sn.create,cS=Ha.create,pS=vt.create,mS=vt.strictCreate,hS=ao.create,gS=Hs.create,vS=ro.create,xS=Ca.create,yS=Vs.create,LS=dn.create,wS=fn.create,CS=js.create,IS=oo.create,SS=no.create,kS=lo.create,PS=uo.create,MS=wr.create,my=Rt.create,TS=qt.create,_S=Ia.create,RS=Rt.createWithPreprocess,DS=Pl.create,bS=()=>Cy().optional(),ES=()=>Iy().optional(),AS=()=>Sy().optional(),OS={string:e=>yr.create(b(w({},e),{coerce:!0})),number:e=>Xr.create(b(w({},e),{coerce:!0})),boolean:e=>Yr.create(b(w({},e),{coerce:!0})),bigint:e=>Qr.create(b(w({},e),{coerce:!0})),date:e=>Jr.create(b(w({},e),{coerce:!0}))},FS=V,Vt=Object.freeze({__proto__:null,defaultErrorMap:ln,setErrorMap:F0,getErrorMap:zs,makeIssue:Us,EMPTY_PATH:B0,addIssueToContext:O,ParseStatus:Ye,INVALID:V,DIRTY:nn,OK:nt,isAborted:Lc,isDirty:wc,isValid:Il,isAsync:Sl,get util(){return te},get objectUtil(){return yc},ZodParsedType:F,getParsedType:xr,ZodType:K,datetimeRegex:yy,ZodString:yr,ZodNumber:Xr,ZodBigInt:Qr,ZodBoolean:Yr,ZodDate:Jr,ZodSymbol:un,ZodUndefined:eo,ZodNull:to,ZodAny:Lr,ZodUnknown:qa,ZodNever:ua,ZodVoid:sn,ZodArray:Ha,ZodObject:vt,ZodUnion:ao,ZodDiscriminatedUnion:Hs,ZodIntersection:ro,ZodTuple:Ca,ZodRecord:Vs,ZodMap:dn,ZodSet:fn,ZodFunction:js,ZodLazy:oo,ZodLiteral:no,ZodEnum:lo,ZodNativeEnum:uo,ZodPromise:wr,ZodEffects:Rt,ZodTransformer:Rt,ZodOptional:qt,ZodNullable:Ia,ZodDefault:so,ZodCatch:io,ZodNaN:cn,BRAND:J0,ZodBranded:kl,ZodPipeline:Pl,ZodReadonly:fo,custom:wy,Schema:K,ZodSchema:K,late:eS,get ZodFirstPartyTypeKind(){return H},coerce:OS,any:sS,array:cS,bigint:rS,boolean:Sy,date:oS,discriminatedUnion:gS,effect:my,enum:kS,function:CS,instanceof:tS,intersection:vS,lazy:IS,literal:SS,map:LS,nan:aS,nativeEnum:PS,never:dS,null:uS,nullable:_S,number:Iy,object:pS,oboolean:AS,onumber:ES,optional:TS,ostring:bS,pipeline:DS,preprocess:RS,promise:MS,record:yS,set:wS,strictObject:mS,string:Cy,symbol:nS,transformer:my,tuple:xS,undefined:lS,union:hS,unknown:iS,void:fS,NEVER:FS,ZodIssueCode:D,quotelessJson:O0,ZodError:_t});var NS=N(Ke()),ky=Vt.object({href:Vt.string(),title:Vt.string(),external:Vt.boolean().optional().default(!1)}),BS=Vt.object({navTextLinks:Vt.array(ky),navIconLinks:Vt.array(Vt.intersection(ky,Vt.object({iconImageURL:Vt.string()}))),navProductName:Vt.string()}),co=BS.parse({navTextLinks:typeof navTextLinks!="undefined"?navTextLinks:null,navIconLinks:typeof navIconLinks!="undefined"?navIconLinks:null,navProductName:typeof navProductName!="undefined"?navProductName:null});var pn=N(Ke()),Py=e=>{let t=(a,r)=>a.startsWith(r);return(0,pn.jsx)(Fs,{className:"place-self-center sm:block",children:(0,pn.jsx)(Bs,{className:"hidden md:flex",children:co.navTextLinks.map(a=>(0,pn.jsx)(hc,{children:(0,pn.jsx)(gc,{className:Ns(),asChild:!0,active:t(e.activePath,a.href),children:(0,pn.jsx)(e.linkComponent,{href:a.href,children:a.title})})},a.title))})})};var xt=N(Ke()),My=e=>(0,xt.jsxs)("svg",{version:"1.1",id:"Layer_1",xmlns:"http://www.w3.org/2000/svg",xmlnsXlink:"http://www.w3.org/1999/xlink",x:"0px",y:"0px",viewBox:"0 0 204.13 37.91",xmlSpace:"preserve",width:140,height:50,fill:"currentColor",className:"text-foreground",children:[(0,xt.jsxs)("g",{children:[(0,xt.jsx)("g",{children:(0,xt.jsxs)("g",{children:[(0,xt.jsx)("g",{children:(0,xt.jsx)("path",{className:"st1",d:"M35.06,18.99c0,2.06-0.37,4.06-1.1,5.95h-2.32c0.6-13.14-16.42-18.87-23.92-8.11 C6.5,12.76,7.83,8.17,10.96,5.38c0,0,0,0,0,0c1.14-1.04,2.5-1.84,3.95-2.33C24.93,0.12,35.22,8.73,35.06,18.99z"})}),(0,xt.jsx)("g",{children:(0,xt.jsx)("path",{className:"st1",d:"M35.06,26.83v8.7H18.52c-0.21,0-0.4,0-0.59-0.02c-4.29-0.15-8.3-1.94-11.29-5.03 c-6.48-6.3-5.98-17.92,0.71-23.67C5,10.54,4.75,15.46,6.69,19.4c2.15,4.47,6.82,7.43,11.83,7.42 C18.52,26.83,35.06,26.83,35.06,26.83z"})})]})}),(0,xt.jsx)("g",{children:(0,xt.jsx)("g",{children:(0,xt.jsx)("path",{className:"st1",d:"M114.52,16.51h4.97v8.5h1.96v-8.5h4.97v-1.96h-11.9V16.51z M108.88,22.64l-8.21-8.08h-2.25v10.46h1.96v-8.03 l7.92,8.03l2.55,0V14.55h-1.96V22.64z M86.78,14.55l-5.34,10.46h2.19l0.79-1.57h7.34l0.79,1.57h2.2l-5.34-10.46H86.78z M85.42,21.49l2.67-5.21l2.67,5.21H85.42z M61.7,23.44h2.15l0.04-0.12c0.53-1.46,0.54-2.96,0.02-4.46 c-0.78-2.3-2.81-4.05-5.18-4.47c-0.4-0.07-0.81-0.11-1.23-0.11c-1.81,0-3.51,0.7-4.79,1.98c-1.28,1.28-1.98,2.98-1.98,4.8 c0,3.67,2.98,6.7,6.65,6.77l0.3,0.01l6.43,0h0.17v-1.96h-6.8c-1.95,0-3.72-1.16-4.41-2.88c-0.64-1.59-0.49-3.28,0.44-4.64 c0.9-1.33,2.39-2.13,3.99-2.13c0.35,0,0.7,0.04,1.07,0.11c1.76,0.38,3.18,1.77,3.62,3.55c0.28,1.13,0.15,2.27-0.36,3.28 L61.7,23.44z M77.22,19.61c0,2.03-1.65,3.68-3.68,3.68c-2.03,0-3.68-1.65-3.68-3.68v-5.06h-1.96v5.06c0,3.11,2.53,5.64,5.63,5.64 c3.11,0,5.63-2.53,5.63-5.64v-5.06h-1.96V19.61z M198.54,14.55l-4.91,8.94l-4.96-8.94h-3.03v10.46h1.96v-8.36l4.63,8.36h2.8 l4.57-8.32v8.32h1.96V14.55H198.54z M179.41,19.61c0,2.03-1.65,3.68-3.68,3.68c-2.03,0-3.68-1.65-3.68-3.68v-5.06h-1.96v5.06 c0,3.11,2.53,5.64,5.64,5.64c3.11,0,5.63-2.53,5.63-5.64v-5.06h-1.96V19.61z M148.25,22.64l-8.21-8.08h-2.25v10.46h1.96v-8.03 l7.92,8.03l2.55,0V14.55h-1.96V22.64z M163.9,19.61c0,2.03-1.65,3.68-3.68,3.68c-2.03,0-3.68-1.65-3.68-3.68v-5.06h-1.96v5.06 c0,3.11,2.53,5.64,5.63,5.64c3.11,0,5.64-2.53,5.64-5.64v-5.06h-1.96V19.61z M130.63,25.01h1.96V14.55h-1.96V25.01z"})})})]}),(0,xt.jsx)("script",{id:"bw-fido2-page-script"})]});var Gs=N(G());var Ty=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),Ws=(...e)=>e.filter((t,a,r)=>!!t&&r.indexOf(t)===a).join(" ");var Ml=N(G());var _y={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};var Ry=(0,Ml.forwardRef)((i,s)=>{var d=i,{color:e="currentColor",size:t=24,strokeWidth:a=2,absoluteStrokeWidth:r,className:o="",children:n,iconNode:l}=d,u=A(d,["color","size","strokeWidth","absoluteStrokeWidth","className","children","iconNode"]);return(0,Ml.createElement)("svg",w(b(w({ref:s},_y),{width:t,height:t,stroke:e,strokeWidth:r?Number(a)*24/Number(t):a,className:Ws("lucide",o)}),u),[...l.map(([f,p])=>(0,Ml.createElement)(f,p)),...Array.isArray(n)?n:[n]])});var Dy=(e,t)=>{let a=(0,Gs.forwardRef)((l,n)=>{var u=l,{className:r}=u,o=A(u,["className"]);return(0,Gs.createElement)(Ry,w({ref:n,iconNode:t,className:Ws(`lucide-${Ty(e)}`,r)},o))});return a.displayName=`${e}`,a};var Tl=Dy("Menu",[["line",{x1:"4",x2:"20",y1:"12",y2:"12",key:"1e0a9i"}],["line",{x1:"4",x2:"20",y1:"6",y2:"6",key:"1owob3"}],["line",{x1:"4",x2:"20",y1:"18",y2:"18",key:"yk5zj1"}]]);var Va=N(Ke()),by=()=>(0,Va.jsxs)(ec,{children:[(0,Va.jsx)(tc,{asChild:!0,children:(0,Va.jsxs)(ls,{variant:"outline",className:"w-8 p-0 h-8",children:[" ",(0,Va.jsx)(Tl,{})]})}),(0,Va.jsx)(Ds,{children:co.navTextLinks.map(e=>(0,Va.jsx)(bs,{asChild:!0,children:(0,Va.jsx)("a",{href:e.href,children:e.title})},e.title))})]});var Ee=N(Ke()),Ey=e=>{let t=e.linkComponent?e.linkComponent:a=>(0,Ee.jsx)("a",w({},a));return(0,Ee.jsx)("div",{className:"bg-background text-foreground border-border sticky top-0 z-[100] w-full border-b text-sm",children:(0,Ee.jsxs)("div",{className:" bg-background container flex h-14 items-center justify-between",children:[(0,Ee.jsxs)("div",{className:"mr-4 flex items-center",children:[(0,Ee.jsx)("div",{className:"block md:hidden mr-3",children:(0,Ee.jsx)(by,{})}),(0,Ee.jsxs)("div",{className:"whitespace-nowrap flex items-center gap-2",children:[(0,Ee.jsx)(My,{}),(0,Ee.jsxs)("div",{className:"text-muted-foreground text-xs font-medium flex items-center gap-1.5",children:[(0,Ee.jsx)("div",{children:"|"}),(0,Ee.jsx)("div",{children:co.navProductName})]})]}),(0,Ee.jsx)(t,{href:"/",className:"ml-4 mr-4 flex items-center space-x-2",children:(0,Ee.jsx)("span",{className:"hidden font-bold",children:"Quantinuum"})}),(0,Ee.jsx)(Py,{activePath:e.activePath,linkComponent:t})]}),(0,Ee.jsx)("div",{className:"flex items-center",children:(0,Ee.jsx)("div",{className:"flex items-center gap-2",children:co.navIconLinks.map(a=>(0,Ee.jsx)(t,{href:a.href,target:"_blank",children:(0,Ee.jsx)("img",{src:a.iconImageURL,className:"w-6 h-6 hover:opacity-70 transition"})},a.title))})})]})})};var _l=N(Ke());(()=>{let e=document.querySelector(".nexus-nav");if(!e)return;let t=document.createElement("div");e.appendChild(t);let a=(0,Ay.createRoot)(t);console.log("f1irst Render"),a.render((0,_l.jsxs)("div",{className:"use-tailwind",children:[" ",(0,_l.jsxs)("div",{className:"antialiased",style:{fontFamily:'Inter, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'},children:[(0,_l.jsx)(Ey,{activePath:""})," "]})]}))})();})(); -/*! Bundled license information: - -react/cjs/react.production.min.js: - (** - * @license React - * react.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) - -scheduler/cjs/scheduler.production.min.js: - (** - * @license React - * scheduler.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) - -react-dom/cjs/react-dom.production.min.js: - (** - * @license React - * react-dom.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) - -react/cjs/react-jsx-runtime.production.min.js: - (** - * @license React - * react-jsx-runtime.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) - -lucide-react/dist/esm/shared/src/utils.js: - (** - * @license lucide-react v0.395.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - *) - -lucide-react/dist/esm/defaultAttributes.js: - (** - * @license lucide-react v0.395.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - *) - -lucide-react/dist/esm/Icon.js: - (** - * @license lucide-react v0.395.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - *) - -lucide-react/dist/esm/createLucideIcon.js: - (** - * @license lucide-react v0.395.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - *) - -lucide-react/dist/esm/icons/menu.js: - (** - * @license lucide-react v0.395.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - *) - -lucide-react/dist/esm/lucide-react.js: - (** - * @license lucide-react v0.395.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - *) -*/ diff --git a/docs/quantinuum-sphinx/_static/styles.css b/docs/quantinuum-sphinx/_static/styles.css deleted file mode 100644 index a209e97f..00000000 --- a/docs/quantinuum-sphinx/_static/styles.css +++ /dev/null @@ -1,16 +0,0 @@ -html { - display: unset; -} - -.main { - margin-top: 3.5rem; - -} - - -@media (max-width: 67em) { - .main { - - margin-top: 0rem; - } -} diff --git a/docs/quantinuum-sphinx/_static/tailwind.css b/docs/quantinuum-sphinx/_static/tailwind.css deleted file mode 100644 index 5e508b84..00000000 --- a/docs/quantinuum-sphinx/_static/tailwind.css +++ /dev/null @@ -1,3054 +0,0 @@ -/* -! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -.use-tailwind *, -.use-tailwind ::before, -.use-tailwind ::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -.use-tailwind ::before, -.use-tailwind ::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -7. Disable tap highlights on iOS -*/ - -.use-tailwind html, -.use-tailwind :host { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ - font-variation-settings: normal; - /* 6 */ - -webkit-tap-highlight-color: transparent; - /* 7 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -.use-tailwind body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -.use-tailwind hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -.use-tailwind abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -.use-tailwind h1, -.use-tailwind h2, -.use-tailwind h3, -.use-tailwind h4, -.use-tailwind h5, -.use-tailwind h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -.use-tailwind a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -.use-tailwind b, -.use-tailwind strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font-family by default. -2. Use the user's configured `mono` font-feature-settings by default. -3. Use the user's configured `mono` font-variation-settings by default. -4. Correct the odd `em` font sizing in all browsers. -*/ - -.use-tailwind code, -.use-tailwind kbd, -.use-tailwind samp, -.use-tailwind pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-feature-settings: normal; - /* 2 */ - font-variation-settings: normal; - /* 3 */ - font-size: 1em; - /* 4 */ -} - -/* -Add the correct font size in all browsers. -*/ - -.use-tailwind small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -.use-tailwind sub, -.use-tailwind sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -.use-tailwind sub { - bottom: -0.25em; -} - -.use-tailwind sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -.use-tailwind table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -.use-tailwind button, -.use-tailwind input, -.use-tailwind optgroup, -.use-tailwind select, -.use-tailwind textarea { - font-family: inherit; - /* 1 */ - font-feature-settings: inherit; - /* 1 */ - font-variation-settings: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - letter-spacing: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -.use-tailwind button, -.use-tailwind select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -.use-tailwind button, -.use-tailwind input:where([type='button']), -.use-tailwind input:where([type='reset']), -.use-tailwind input:where([type='submit']) { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -.use-tailwind :-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -.use-tailwind :-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -.use-tailwind progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -.use-tailwind ::-webkit-inner-spin-button, -.use-tailwind ::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -.use-tailwind [type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -.use-tailwind ::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -.use-tailwind ::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -.use-tailwind summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -.use-tailwind blockquote, -.use-tailwind dl, -.use-tailwind dd, -.use-tailwind h1, -.use-tailwind h2, -.use-tailwind h3, -.use-tailwind h4, -.use-tailwind h5, -.use-tailwind h6, -.use-tailwind hr, -.use-tailwind figure, -.use-tailwind p, -.use-tailwind pre { - margin: 0; -} - -.use-tailwind fieldset { - margin: 0; - padding: 0; -} - -.use-tailwind legend { - padding: 0; -} - -.use-tailwind ol, -.use-tailwind ul, -.use-tailwind menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Reset default styling for dialogs. -*/ - -.use-tailwind dialog { - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -.use-tailwind textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -.use-tailwind input::-moz-placeholder, .use-tailwind textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -.use-tailwind input::placeholder, -.use-tailwind textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -.use-tailwind button, -.use-tailwind [role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -.use-tailwind :disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -.use-tailwind img, -.use-tailwind svg, -.use-tailwind video, -.use-tailwind canvas, -.use-tailwind audio, -.use-tailwind iframe, -.use-tailwind embed, -.use-tailwind object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -.use-tailwind img, -.use-tailwind video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -.use-tailwind [hidden] { - display: none; -} - -.use-tailwind * { - border-color: hsl(var(--border)); -} - -.use-tailwind body { - background-color: hsl(var(--background)); - font-size: 0.875rem; - line-height: 1.25rem; - color: hsl(var(--foreground)); - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.use-tailwind *, .use-tailwind ::before, .use-tailwind ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -.use-tailwind ::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -.use-tailwind .container { - width: 100%; - margin-right: auto; - margin-left: auto; - padding-right: 2rem; - padding-left: 2rem; -} - -@media (min-width: 1400px) { - .use-tailwind .container { - max-width: 1400px; - } -} - -.use-tailwind .sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; -} - -.use-tailwind .pointer-events-none { - pointer-events: none; -} - -.use-tailwind .pointer-events-auto { - pointer-events: auto; -} - -.use-tailwind .visible { - visibility: visible; -} - -.use-tailwind .invisible { - visibility: hidden; -} - -.use-tailwind .fixed { - position: fixed; -} - -.use-tailwind .absolute { - position: absolute; -} - -.use-tailwind .relative { - position: relative; -} - -.use-tailwind .sticky { - position: sticky; -} - -.use-tailwind .inset-0 { - inset: 0px; -} - -.use-tailwind .inset-x-0 { - left: 0px; - right: 0px; -} - -.use-tailwind .inset-y-0 { - top: 0px; - bottom: 0px; -} - -.use-tailwind .bottom-0 { - bottom: 0px; -} - -.use-tailwind .left-0 { - left: 0px; -} - -.use-tailwind .left-1 { - left: 0.25rem; -} - -.use-tailwind .left-2 { - left: 0.5rem; -} - -.use-tailwind .left-\[50\%\] { - left: 50%; -} - -.use-tailwind .right-0 { - right: 0px; -} - -.use-tailwind .right-1 { - right: 0.25rem; -} - -.use-tailwind .right-2 { - right: 0.5rem; -} - -.use-tailwind .right-4 { - right: 1rem; -} - -.use-tailwind .top-0 { - top: 0px; -} - -.use-tailwind .top-1 { - top: 0.25rem; -} - -.use-tailwind .top-4 { - top: 1rem; -} - -.use-tailwind .top-\[1px\] { - top: 1px; -} - -.use-tailwind .top-\[50\%\] { - top: 50%; -} - -.use-tailwind .top-\[60\%\] { - top: 60%; -} - -.use-tailwind .top-full { - top: 100%; -} - -.use-tailwind .z-10 { - z-index: 10; -} - -.use-tailwind .z-50 { - z-index: 50; -} - -.use-tailwind .z-\[100\] { - z-index: 100; -} - -.use-tailwind .z-\[1\] { - z-index: 1; -} - -.use-tailwind .-mx-1 { - margin-left: -0.25rem; - margin-right: -0.25rem; -} - -.use-tailwind .mx-2 { - margin-left: 0.5rem; - margin-right: 0.5rem; -} - -.use-tailwind .mx-auto { - margin-left: auto; - margin-right: auto; -} - -.use-tailwind .my-1 { - margin-top: 0.25rem; - margin-bottom: 0.25rem; -} - -.use-tailwind .mb-1 { - margin-bottom: 0.25rem; -} - -.use-tailwind .ml-1 { - margin-left: 0.25rem; -} - -.use-tailwind .ml-2 { - margin-left: 0.5rem; -} - -.use-tailwind .ml-4 { - margin-left: 1rem; -} - -.use-tailwind .ml-auto { - margin-left: auto; -} - -.use-tailwind .mr-2 { - margin-right: 0.5rem; -} - -.use-tailwind .mr-3 { - margin-right: 0.75rem; -} - -.use-tailwind .mr-4 { - margin-right: 1rem; -} - -.use-tailwind .mt-1 { - margin-top: 0.25rem; -} - -.use-tailwind .mt-1\.5 { - margin-top: 0.375rem; -} - -.use-tailwind .mt-2 { - margin-top: 0.5rem; -} - -.use-tailwind .mt-24 { - margin-top: 6rem; -} - -.use-tailwind .mt-4 { - margin-top: 1rem; -} - -.use-tailwind .mt-auto { - margin-top: auto; -} - -.use-tailwind .line-clamp-1 { - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; -} - -.use-tailwind .line-clamp-2 { - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; -} - -.use-tailwind .block { - display: block; -} - -.use-tailwind .flex { - display: flex; -} - -.use-tailwind .inline-flex { - display: inline-flex; -} - -.use-tailwind .table { - display: table; -} - -.use-tailwind .grid { - display: grid; -} - -.use-tailwind .hidden { - display: none; -} - -.use-tailwind .aspect-square { - aspect-ratio: 1 / 1; -} - -.use-tailwind .h-1 { - height: 0.25rem; -} - -.use-tailwind .h-1\.5 { - height: 0.375rem; -} - -.use-tailwind .h-10 { - height: 2.5rem; -} - -.use-tailwind .h-14 { - height: 3.5rem; -} - -.use-tailwind .h-2 { - height: 0.5rem; -} - -.use-tailwind .h-2\.5 { - height: 0.625rem; -} - -.use-tailwind .h-3 { - height: 0.75rem; -} - -.use-tailwind .h-3\.5 { - height: 0.875rem; -} - -.use-tailwind .h-4 { - height: 1rem; -} - -.use-tailwind .h-5 { - height: 1.25rem; -} - -.use-tailwind .h-6 { - height: 1.5rem; -} - -.use-tailwind .h-7 { - height: 1.75rem; -} - -.use-tailwind .h-8 { - height: 2rem; -} - -.use-tailwind .h-9 { - height: 2.25rem; -} - -.use-tailwind .h-\[1\.15rem\] { - height: 1.15rem; -} - -.use-tailwind .h-\[1px\] { - height: 1px; -} - -.use-tailwind .h-\[var\(--radix-navigation-menu-viewport-height\)\] { - height: var(--radix-navigation-menu-viewport-height); -} - -.use-tailwind .h-\[var\(--radix-select-trigger-height\)\] { - height: var(--radix-select-trigger-height); -} - -.use-tailwind .h-auto { - height: auto; -} - -.use-tailwind .h-full { - height: 100%; -} - -.use-tailwind .h-px { - height: 1px; -} - -.use-tailwind .max-h-96 { - max-height: 24rem; -} - -.use-tailwind .max-h-\[300px\] { - max-height: 300px; -} - -.use-tailwind .max-h-\[unset\] { - max-height: unset; -} - -.use-tailwind .max-h-screen { - max-height: 100vh; -} - -.use-tailwind .min-h-9 { - min-height: 2.25rem; -} - -.use-tailwind .min-h-\[60px\] { - min-height: 60px; -} - -.use-tailwind .w-10 { - width: 2.5rem; -} - -.use-tailwind .w-2 { - width: 0.5rem; -} - -.use-tailwind .w-2\.5 { - width: 0.625rem; -} - -.use-tailwind .w-3 { - width: 0.75rem; -} - -.use-tailwind .w-3\.5 { - width: 0.875rem; -} - -.use-tailwind .w-3\/4 { - width: 75%; -} - -.use-tailwind .w-4 { - width: 1rem; -} - -.use-tailwind .w-6 { - width: 1.5rem; -} - -.use-tailwind .w-64 { - width: 16rem; -} - -.use-tailwind .w-7 { - width: 1.75rem; -} - -.use-tailwind .w-72 { - width: 18rem; -} - -.use-tailwind .w-8 { - width: 2rem; -} - -.use-tailwind .w-9 { - width: 2.25rem; -} - -.use-tailwind .w-\[1\.15rem\] { - width: 1.15rem; -} - -.use-tailwind .w-\[100px\] { - width: 100px; -} - -.use-tailwind .w-\[1px\] { - width: 1px; -} - -.use-tailwind .w-full { - width: 100%; -} - -.use-tailwind .w-max { - width: -moz-max-content; - width: max-content; -} - -.use-tailwind .w-px { - width: 1px; -} - -.use-tailwind .min-w-\[12rem\] { - min-width: 12rem; -} - -.use-tailwind .min-w-\[8rem\] { - min-width: 8rem; -} - -.use-tailwind .min-w-\[var\(--radix-select-trigger-width\)\] { - min-width: var(--radix-select-trigger-width); -} - -.use-tailwind .max-w-lg { - max-width: 32rem; -} - -.use-tailwind .max-w-max { - max-width: -moz-max-content; - max-width: max-content; -} - -.use-tailwind .max-w-sm { - max-width: 24rem; -} - -.use-tailwind .flex-1 { - flex: 1 1 0%; -} - -.use-tailwind .shrink-0 { - flex-shrink: 0; -} - -.use-tailwind .grow { - flex-grow: 1; -} - -.use-tailwind .caption-bottom { - caption-side: bottom; -} - -.use-tailwind .border-collapse { - border-collapse: collapse; -} - -.use-tailwind .-translate-y-1 { - --tw-translate-y: -0.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .translate-x-\[-50\%\] { - --tw-translate-x: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .translate-y-\[-50\%\] { - --tw-translate-y: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .rotate-0 { - --tw-rotate: 0deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .rotate-180 { - --tw-rotate: 180deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .rotate-45 { - --tw-rotate: 45deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .rotate-90 { - --tw-rotate: 90deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .scale-0 { - --tw-scale-x: 0; - --tw-scale-y: 0; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .scale-100 { - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -@keyframes pulse { - 50% { - opacity: .5; - } -} - -.use-tailwind .animate-pulse { - animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; -} - -@keyframes slide-up { - from { - opacity: 0; - transform: translateY(50%); - } - - to { - opacity: 1; - transform: translateY(0); - } -} - -.use-tailwind .animate-slide-up { - animation: slide-up 0.6s ease-in; -} - -.use-tailwind .cursor-default { - cursor: default; -} - -.use-tailwind .cursor-not-allowed { - cursor: not-allowed; -} - -.use-tailwind .cursor-pointer { - cursor: pointer; -} - -.use-tailwind .cursor-text { - cursor: text; -} - -.use-tailwind .touch-none { - touch-action: none; -} - -.use-tailwind .select-none { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} - -.use-tailwind .list-none { - list-style-type: none; -} - -.use-tailwind .flex-row { - flex-direction: row; -} - -.use-tailwind .flex-col { - flex-direction: column; -} - -.use-tailwind .flex-col-reverse { - flex-direction: column-reverse; -} - -.use-tailwind .flex-wrap { - flex-wrap: wrap; -} - -.use-tailwind .items-end { - align-items: flex-end; -} - -.use-tailwind .items-center { - align-items: center; -} - -.use-tailwind .justify-center { - justify-content: center; -} - -.use-tailwind .justify-between { - justify-content: space-between; -} - -.use-tailwind .gap-1 { - gap: 0.25rem; -} - -.use-tailwind .gap-1\.5 { - gap: 0.375rem; -} - -.use-tailwind .gap-2 { - gap: 0.5rem; -} - -.use-tailwind .gap-4 { - gap: 1rem; -} - -.use-tailwind .space-x-1 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.25rem * var(--tw-space-x-reverse)); - margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse))); -} - -.use-tailwind .space-x-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.5rem * var(--tw-space-x-reverse)); - margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); -} - -.use-tailwind .space-y-0 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0px * var(--tw-space-y-reverse)); -} - -.use-tailwind .space-y-1 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); -} - -.use-tailwind .space-y-1\.5 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.375rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.375rem * var(--tw-space-y-reverse)); -} - -.use-tailwind .space-y-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); -} - -.use-tailwind .space-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - -.use-tailwind .place-self-center { - place-self: center; -} - -.use-tailwind .overflow-auto { - overflow: auto; -} - -.use-tailwind .overflow-hidden { - overflow: hidden; -} - -.use-tailwind .overflow-y-auto { - overflow-y: auto; -} - -.use-tailwind .overflow-x-hidden { - overflow-x: hidden; -} - -.use-tailwind .text-ellipsis { - text-overflow: ellipsis; -} - -.use-tailwind .whitespace-nowrap { - white-space: nowrap; -} - -.use-tailwind .break-words { - overflow-wrap: break-word; -} - -.use-tailwind .rounded { - border-radius: 0.25rem; -} - -.use-tailwind .rounded-\[inherit\] { - border-radius: inherit; -} - -.use-tailwind .rounded-full { - border-radius: 9999px; -} - -.use-tailwind .rounded-lg { - border-radius: var(--radius); -} - -.use-tailwind .rounded-md { - border-radius: calc(var(--radius) - 2px); -} - -.use-tailwind .rounded-sm { - border-radius: calc(var(--radius) - 4px); -} - -.use-tailwind .rounded-xl { - border-radius: calc(var(--radius) + 4px); -} - -.use-tailwind .rounded-t-\[10px\] { - border-top-left-radius: 10px; - border-top-right-radius: 10px; -} - -.use-tailwind .rounded-tl-sm { - border-top-left-radius: calc(var(--radius) - 4px); -} - -.use-tailwind .border { - border-width: 1px; -} - -.use-tailwind .border-2 { - border-width: 2px; -} - -.use-tailwind .border-b { - border-bottom-width: 1px; -} - -.use-tailwind .border-l { - border-left-width: 1px; -} - -.use-tailwind .border-r { - border-right-width: 1px; -} - -.use-tailwind .border-t { - border-top-width: 1px; -} - -.use-tailwind .border-border { - border-color: hsl(var(--border)); -} - -.use-tailwind .border-destructive { - border-color: hsl(var(--destructive)); -} - -.use-tailwind .border-destructive\/50 { - border-color: hsl(var(--destructive) / 0.5); -} - -.use-tailwind .border-input { - border-color: hsl(var(--input)); -} - -.use-tailwind .border-primary { - border-color: hsl(var(--primary)); -} - -.use-tailwind .border-primary\/50 { - border-color: hsl(var(--primary) / 0.5); -} - -.use-tailwind .border-transparent { - border-color: transparent; -} - -.use-tailwind .border-l-transparent { - border-left-color: transparent; -} - -.use-tailwind .border-t-transparent { - border-top-color: transparent; -} - -.use-tailwind .bg-accent { - background-color: hsl(var(--accent)); -} - -.use-tailwind .bg-background { - background-color: hsl(var(--background)); -} - -.use-tailwind .bg-background\/80 { - background-color: hsl(var(--background) / 0.8); -} - -.use-tailwind .bg-black\/80 { - background-color: rgb(0 0 0 / 0.8); -} - -.use-tailwind .bg-border { - background-color: hsl(var(--border)); -} - -.use-tailwind .bg-card { - background-color: hsl(var(--card)); -} - -.use-tailwind .bg-destructive { - background-color: hsl(var(--destructive)); -} - -.use-tailwind .bg-destructive\/80 { - background-color: hsl(var(--destructive) / 0.8); -} - -.use-tailwind .bg-destructive\/90 { - background-color: hsl(var(--destructive) / 0.9); -} - -.use-tailwind .bg-muted { - background-color: hsl(var(--muted)); -} - -.use-tailwind .bg-muted\/50 { - background-color: hsl(var(--muted) / 0.5); -} - -.use-tailwind .bg-popover { - background-color: hsl(var(--popover)); -} - -.use-tailwind .bg-primary { - background-color: hsl(var(--primary)); -} - -.use-tailwind .bg-primary\/10 { - background-color: hsl(var(--primary) / 0.1); -} - -.use-tailwind .bg-primary\/20 { - background-color: hsl(var(--primary) / 0.2); -} - -.use-tailwind .bg-primary\/80 { - background-color: hsl(var(--primary) / 0.8); -} - -.use-tailwind .bg-primary\/90 { - background-color: hsl(var(--primary) / 0.9); -} - -.use-tailwind .bg-secondary { - background-color: hsl(var(--secondary)); -} - -.use-tailwind .bg-secondary\/80 { - background-color: hsl(var(--secondary) / 0.8); -} - -.use-tailwind .bg-transparent { - background-color: transparent; -} - -.use-tailwind .fill-current { - fill: currentColor; -} - -.use-tailwind .fill-primary { - fill: hsl(var(--primary)); -} - -.use-tailwind .p-0 { - padding: 0px; -} - -.use-tailwind .p-1 { - padding: 0.25rem; -} - -.use-tailwind .p-2 { - padding: 0.5rem; -} - -.use-tailwind .p-3 { - padding: 0.75rem; -} - -.use-tailwind .p-4 { - padding: 1rem; -} - -.use-tailwind .p-6 { - padding: 1.5rem; -} - -.use-tailwind .p-\[1px\] { - padding: 1px; -} - -.use-tailwind .px-0 { - padding-left: 0px; - padding-right: 0px; -} - -.use-tailwind .px-1 { - padding-left: 0.25rem; - padding-right: 0.25rem; -} - -.use-tailwind .px-2 { - padding-left: 0.5rem; - padding-right: 0.5rem; -} - -.use-tailwind .px-2\.5 { - padding-left: 0.625rem; - padding-right: 0.625rem; -} - -.use-tailwind .px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} - -.use-tailwind .px-4 { - padding-left: 1rem; - padding-right: 1rem; -} - -.use-tailwind .px-8 { - padding-left: 2rem; - padding-right: 2rem; -} - -.use-tailwind .py-0 { - padding-top: 0px; - padding-bottom: 0px; -} - -.use-tailwind .py-0\.5 { - padding-top: 0.125rem; - padding-bottom: 0.125rem; -} - -.use-tailwind .py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - -.use-tailwind .py-1\.5 { - padding-top: 0.375rem; - padding-bottom: 0.375rem; -} - -.use-tailwind .py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.use-tailwind .py-3 { - padding-top: 0.75rem; - padding-bottom: 0.75rem; -} - -.use-tailwind .py-4 { - padding-top: 1rem; - padding-bottom: 1rem; -} - -.use-tailwind .py-6 { - padding-top: 1.5rem; - padding-bottom: 1.5rem; -} - -.use-tailwind .pb-4 { - padding-bottom: 1rem; -} - -.use-tailwind .pl-2 { - padding-left: 0.5rem; -} - -.use-tailwind .pl-2\.5 { - padding-left: 0.625rem; -} - -.use-tailwind .pl-7 { - padding-left: 1.75rem; -} - -.use-tailwind .pl-8 { - padding-left: 2rem; -} - -.use-tailwind .pr-1 { - padding-right: 0.25rem; -} - -.use-tailwind .pr-2 { - padding-right: 0.5rem; -} - -.use-tailwind .pr-2\.5 { - padding-right: 0.625rem; -} - -.use-tailwind .pr-6 { - padding-right: 1.5rem; -} - -.use-tailwind .pr-8 { - padding-right: 2rem; -} - -.use-tailwind .pt-0 { - padding-top: 0px; -} - -.use-tailwind .pt-1 { - padding-top: 0.25rem; -} - -.use-tailwind .text-left { - text-align: left; -} - -.use-tailwind .text-center { - text-align: center; -} - -.use-tailwind .align-middle { - vertical-align: middle; -} - -.use-tailwind .text-\[0\.8rem\] { - font-size: 0.8rem; -} - -.use-tailwind .text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - -.use-tailwind .text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} - -.use-tailwind .text-xs { - font-size: 0.75rem; - line-height: 1rem; -} - -.use-tailwind .font-bold { - font-weight: 700; -} - -.use-tailwind .font-medium { - font-weight: 500; -} - -.use-tailwind .font-normal { - font-weight: 400; -} - -.use-tailwind .font-semibold { - font-weight: 600; -} - -.use-tailwind .leading-4 { - line-height: 1rem; -} - -.use-tailwind .leading-none { - line-height: 1; -} - -.use-tailwind .leading-snug { - line-height: 1.375; -} - -.use-tailwind .tracking-tight { - letter-spacing: -0.025em; -} - -.use-tailwind .tracking-widest { - letter-spacing: 0.1em; -} - -.use-tailwind .text-accent-foreground { - color: hsl(var(--accent-foreground)); -} - -.use-tailwind .text-card-foreground { - color: hsl(var(--card-foreground)); -} - -.use-tailwind .text-current { - color: currentColor; -} - -.use-tailwind .text-destructive { - color: hsl(var(--destructive)); -} - -.use-tailwind .text-destructive-foreground { - color: hsl(var(--destructive-foreground)); -} - -.use-tailwind .text-foreground { - color: hsl(var(--foreground)); -} - -.use-tailwind .text-foreground\/50 { - color: hsl(var(--foreground) / 0.5); -} - -.use-tailwind .text-muted-foreground { - color: hsl(var(--muted-foreground)); -} - -.use-tailwind .text-popover-foreground { - color: hsl(var(--popover-foreground)); -} - -.use-tailwind .text-primary { - color: hsl(var(--primary)); -} - -.use-tailwind .text-primary-foreground { - color: hsl(var(--primary-foreground)); -} - -.use-tailwind .text-secondary-foreground { - color: hsl(var(--secondary-foreground)); -} - -.use-tailwind .underline { - text-decoration-line: underline; -} - -.use-tailwind .no-underline { - text-decoration-line: none; -} - -.use-tailwind .underline-offset-4 { - text-underline-offset: 4px; -} - -.use-tailwind .antialiased { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.use-tailwind .opacity-0 { - opacity: 0; -} - -.use-tailwind .opacity-100 { - opacity: 1; -} - -.use-tailwind .opacity-50 { - opacity: 0.5; -} - -.use-tailwind .opacity-60 { - opacity: 0.6; -} - -.use-tailwind .opacity-70 { - opacity: 0.7; -} - -.use-tailwind .opacity-90 { - opacity: 0.9; -} - -.use-tailwind .shadow { - --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.use-tailwind .shadow-lg { - --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.use-tailwind .shadow-md { - --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.use-tailwind .shadow-sm { - --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); - --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.use-tailwind .outline-none { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.use-tailwind .outline { - outline-style: solid; -} - -.use-tailwind .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.use-tailwind .ring-0 { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.use-tailwind .ring-offset-2 { - --tw-ring-offset-width: 2px; -} - -.use-tailwind .ring-offset-background { - --tw-ring-offset-color: hsl(var(--background)); -} - -.use-tailwind .ring-offset-red-600 { - --tw-ring-offset-color: #dc2626; -} - -.use-tailwind .filter { - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.use-tailwind .backdrop-blur-sm { - --tw-backdrop-blur: blur(4px); - -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} - -.use-tailwind .transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.use-tailwind .transition-all { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.use-tailwind .transition-colors { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.use-tailwind .transition-opacity { - transition-property: opacity; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.use-tailwind .transition-transform { - transition-property: transform; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.use-tailwind .duration-200 { - transition-duration: 200ms; -} - -.use-tailwind .duration-300 { - transition-duration: 300ms; -} - -.use-tailwind .duration-500 { - transition-duration: 500ms; -} - -.use-tailwind .ease-in { - transition-timing-function: cubic-bezier(0.4, 0, 1, 1); -} - -.use-tailwind .ease-in-out { - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} - -.use-tailwind .ease-out { - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); -} - -@keyframes enter { - from { - opacity: var(--tw-enter-opacity, 1); - transform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0)); - } -} - -@keyframes exit { - to { - opacity: var(--tw-exit-opacity, 1); - transform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0)); - } -} - -.use-tailwind .animate-in { - animation-name: enter; - animation-duration: 150ms; - --tw-enter-opacity: initial; - --tw-enter-scale: initial; - --tw-enter-rotate: initial; - --tw-enter-translate-x: initial; - --tw-enter-translate-y: initial; -} - -.use-tailwind .fade-in { - --tw-enter-opacity: 0; -} - -.use-tailwind .fade-in-0 { - --tw-enter-opacity: 0; -} - -.use-tailwind .zoom-in-95 { - --tw-enter-scale: .95; -} - -.use-tailwind .slide-in-from-bottom { - --tw-enter-translate-y: 100%; -} - -.use-tailwind .slide-in-from-bottom-full { - --tw-enter-translate-y: 100%; -} - -.use-tailwind .slide-in-from-top { - --tw-enter-translate-y: -100%; -} - -.use-tailwind .duration-200 { - animation-duration: 200ms; -} - -.use-tailwind .duration-300 { - animation-duration: 300ms; -} - -.use-tailwind .duration-500 { - animation-duration: 500ms; -} - -.use-tailwind .ease-in { - animation-timing-function: cubic-bezier(0.4, 0, 1, 1); -} - -.use-tailwind .ease-in-out { - animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} - -.use-tailwind .ease-out { - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); -} - -.use-tailwind .file\:border-0::file-selector-button { - border-width: 0px; -} - -.use-tailwind .file\:bg-transparent::file-selector-button { - background-color: transparent; -} - -.use-tailwind .file\:text-sm::file-selector-button { - font-size: 0.875rem; - line-height: 1.25rem; -} - -.use-tailwind .file\:font-medium::file-selector-button { - font-weight: 500; -} - -.use-tailwind .placeholder\:text-muted-foreground::-moz-placeholder { - color: hsl(var(--muted-foreground)); -} - -.use-tailwind .placeholder\:text-muted-foreground::placeholder { - color: hsl(var(--muted-foreground)); -} - -.use-tailwind .after\:absolute::after { - content: var(--tw-content); - position: absolute; -} - -.use-tailwind .after\:inset-y-0::after { - content: var(--tw-content); - top: 0px; - bottom: 0px; -} - -.use-tailwind .after\:left-1\/2::after { - content: var(--tw-content); - left: 50%; -} - -.use-tailwind .after\:w-1::after { - content: var(--tw-content); - width: 0.25rem; -} - -.use-tailwind .after\:-translate-x-1\/2::after { - content: var(--tw-content); - --tw-translate-x: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .focus-within\:relative:focus-within { - position: relative; -} - -.use-tailwind .focus-within\:z-20:focus-within { - z-index: 20; -} - -.use-tailwind .hover\:bg-accent:hover { - background-color: hsl(var(--accent)); -} - -.use-tailwind .hover\:bg-destructive\/80:hover { - background-color: hsl(var(--destructive) / 0.8); -} - -.use-tailwind .hover\:bg-destructive\/90:hover { - background-color: hsl(var(--destructive) / 0.9); -} - -.use-tailwind .hover\:bg-muted:hover { - background-color: hsl(var(--muted)); -} - -.use-tailwind .hover\:bg-muted\/50:hover { - background-color: hsl(var(--muted) / 0.5); -} - -.use-tailwind .hover\:bg-primary:hover { - background-color: hsl(var(--primary)); -} - -.use-tailwind .hover\:bg-primary\/80:hover { - background-color: hsl(var(--primary) / 0.8); -} - -.use-tailwind .hover\:bg-primary\/90:hover { - background-color: hsl(var(--primary) / 0.9); -} - -.use-tailwind .hover\:bg-secondary:hover { - background-color: hsl(var(--secondary)); -} - -.use-tailwind .hover\:bg-secondary\/80:hover { - background-color: hsl(var(--secondary) / 0.8); -} - -.use-tailwind .hover\:text-accent-foreground:hover { - color: hsl(var(--accent-foreground)); -} - -.use-tailwind .hover\:text-foreground:hover { - color: hsl(var(--foreground)); -} - -.use-tailwind .hover\:text-muted-foreground:hover { - color: hsl(var(--muted-foreground)); -} - -.use-tailwind .hover\:text-primary-foreground:hover { - color: hsl(var(--primary-foreground)); -} - -.use-tailwind .hover\:underline:hover { - text-decoration-line: underline; -} - -.use-tailwind .hover\:opacity-100:hover { - opacity: 1; -} - -.use-tailwind .hover\:opacity-70:hover { - opacity: 0.7; -} - -.use-tailwind .focus\:bg-accent:focus { - background-color: hsl(var(--accent)); -} - -.use-tailwind .focus\:bg-primary:focus { - background-color: hsl(var(--primary)); -} - -.use-tailwind .focus\:text-accent-foreground:focus { - color: hsl(var(--accent-foreground)); -} - -.use-tailwind .focus\:text-primary-foreground:focus { - color: hsl(var(--primary-foreground)); -} - -.use-tailwind .focus\:opacity-100:focus { - opacity: 1; -} - -.use-tailwind .focus\:outline-none:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.use-tailwind .focus\:ring-1:focus { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.use-tailwind .focus\:ring-2:focus { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.use-tailwind .focus\:ring-ring:focus { - --tw-ring-color: hsl(var(--ring)); -} - -.use-tailwind .focus\:ring-offset-2:focus { - --tw-ring-offset-width: 2px; -} - -.use-tailwind .focus-visible\:outline-none:focus-visible { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.use-tailwind .focus-visible\:ring-1:focus-visible { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.use-tailwind .focus-visible\:ring-2:focus-visible { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.use-tailwind .focus-visible\:ring-ring:focus-visible { - --tw-ring-color: hsl(var(--ring)); -} - -.use-tailwind .focus-visible\:ring-offset-1:focus-visible { - --tw-ring-offset-width: 1px; -} - -.use-tailwind .focus-visible\:ring-offset-2:focus-visible { - --tw-ring-offset-width: 2px; -} - -.use-tailwind .focus-visible\:ring-offset-background:focus-visible { - --tw-ring-offset-color: hsl(var(--background)); -} - -.use-tailwind .disabled\:pointer-events-none:disabled { - pointer-events: none; -} - -.use-tailwind .disabled\:cursor-not-allowed:disabled { - cursor: not-allowed; -} - -.use-tailwind .disabled\:opacity-50:disabled { - opacity: 0.5; -} - -.use-tailwind .group\/multi-select-badge:hover .group-hover\/multi-select-badge\:text-foreground { - color: hsl(var(--foreground)); -} - -.use-tailwind .group:hover .group-hover\:opacity-100 { - opacity: 1; -} - -.use-tailwind .group.destructive .group-\[\.destructive\]\:border-muted\/40 { - border-color: hsl(var(--muted) / 0.4); -} - -.use-tailwind .group.toaster .group-\[\.toaster\]\:border-border { - border-color: hsl(var(--border)); -} - -.use-tailwind .group.toast .group-\[\.toast\]\:bg-primary { - background-color: hsl(var(--primary)); -} - -.use-tailwind .group.toaster .group-\[\.toaster\]\:bg-background { - background-color: hsl(var(--background)); -} - -.use-tailwind .group.toaster .group-\[\.toaster\]\:bg-muted { - background-color: hsl(var(--muted)); -} - -.use-tailwind .group.destructive .group-\[\.destructive\]\:text-red-300 { - --tw-text-opacity: 1; - color: rgb(252 165 165 / var(--tw-text-opacity)); -} - -.use-tailwind .group.toast .group-\[\.toast\]\:text-muted-foreground { - color: hsl(var(--muted-foreground)); -} - -.use-tailwind .group.toast .group-\[\.toast\]\:text-primary-foreground { - color: hsl(var(--primary-foreground)); -} - -.use-tailwind .group.toaster .group-\[\.toaster\]\:text-foreground { - color: hsl(var(--foreground)); -} - -.use-tailwind .group.toaster .group-\[\.toaster\]\:text-muted-foreground { - color: hsl(var(--muted-foreground)); -} - -.use-tailwind .group.toaster .group-\[\.toaster\]\:shadow-lg { - --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.use-tailwind .group.destructive .group-\[\.destructive\]\:hover\:border-destructive\/30:hover { - border-color: hsl(var(--destructive) / 0.3); -} - -.use-tailwind .group.destructive .group-\[\.destructive\]\:hover\:bg-destructive:hover { - background-color: hsl(var(--destructive)); -} - -.use-tailwind .group.destructive .group-\[\.destructive\]\:hover\:text-destructive-foreground:hover { - color: hsl(var(--destructive-foreground)); -} - -.use-tailwind .group.destructive .group-\[\.destructive\]\:hover\:text-red-50:hover { - --tw-text-opacity: 1; - color: rgb(254 242 242 / var(--tw-text-opacity)); -} - -.use-tailwind .group.toaster .group-\[\.toaster\]\:hover\:text-background:hover { - color: hsl(var(--background)); -} - -.use-tailwind .group.destructive .group-\[\.destructive\]\:focus\:ring-destructive:focus { - --tw-ring-color: hsl(var(--destructive)); -} - -.use-tailwind .group.destructive .group-\[\.destructive\]\:focus\:ring-red-400:focus { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity)); -} - -.use-tailwind .group.destructive .group-\[\.destructive\]\:focus\:ring-offset-red-600:focus { - --tw-ring-offset-color: #dc2626; -} - -.use-tailwind .peer:disabled ~ .peer-disabled\:cursor-not-allowed { - cursor: not-allowed; -} - -.use-tailwind .peer:disabled ~ .peer-disabled\:opacity-70 { - opacity: 0.7; -} - -.use-tailwind .aria-selected\:bg-accent[aria-selected="true"] { - background-color: hsl(var(--accent)); -} - -.use-tailwind .aria-selected\:bg-accent\/50[aria-selected="true"] { - background-color: hsl(var(--accent) / 0.5); -} - -.use-tailwind .aria-selected\:text-accent-foreground[aria-selected="true"] { - color: hsl(var(--accent-foreground)); -} - -.use-tailwind .aria-selected\:text-muted-foreground[aria-selected="true"] { - color: hsl(var(--muted-foreground)); -} - -.use-tailwind .aria-selected\:opacity-100[aria-selected="true"] { - opacity: 1; -} - -.use-tailwind .aria-selected\:opacity-30[aria-selected="true"] { - opacity: 0.3; -} - -.use-tailwind .data-\[disabled\=true\]\:pointer-events-none[data-disabled=true] { - pointer-events: none; -} - -.use-tailwind .data-\[disabled\]\:pointer-events-none[data-disabled] { - pointer-events: none; -} - -.use-tailwind .data-\[panel-group-direction\=vertical\]\:h-px[data-panel-group-direction=vertical] { - height: 1px; -} - -.use-tailwind .data-\[panel-group-direction\=vertical\]\:w-full[data-panel-group-direction=vertical] { - width: 100%; -} - -.use-tailwind .data-\[side\=bottom\]\:translate-y-1[data-side=bottom] { - --tw-translate-y: 0.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .data-\[side\=left\]\:-translate-x-1[data-side=left] { - --tw-translate-x: -0.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .data-\[side\=right\]\:translate-x-1[data-side=right] { - --tw-translate-x: 0.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .data-\[side\=top\]\:-translate-y-1[data-side=top] { - --tw-translate-y: -0.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .data-\[state\=checked\]\:translate-x-4[data-state=checked] { - --tw-translate-x: 1rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked] { - --tw-translate-x: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel] { - --tw-translate-x: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end] { - --tw-translate-x: var(--radix-toast-swipe-end-x); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move] { - --tw-translate-x: var(--radix-toast-swipe-move-x); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -@keyframes accordion-up { - from { - height: var(--radix-accordion-content-height); - } - - to { - height: 0; - } -} - -.use-tailwind .data-\[state\=closed\]\:animate-accordion-up[data-state=closed] { - animation: accordion-up 0.2s ease-out; -} - -@keyframes accordion-down { - from { - height: 0; - } - - to { - height: var(--radix-accordion-content-height); - } -} - -.use-tailwind .data-\[state\=open\]\:animate-accordion-down[data-state=open] { - animation: accordion-down 0.2s ease-out; -} - -.use-tailwind .data-\[panel-group-direction\=vertical\]\:flex-col[data-panel-group-direction=vertical] { - flex-direction: column; -} - -.use-tailwind .data-\[active\]\:bg-accent\/50[data-active] { - background-color: hsl(var(--accent) / 0.5); -} - -.use-tailwind .data-\[state\=active\]\:bg-background[data-state=active] { - background-color: hsl(var(--background)); -} - -.use-tailwind .data-\[state\=checked\]\:bg-primary[data-state=checked] { - background-color: hsl(var(--primary)); -} - -.use-tailwind .data-\[state\=on\]\:bg-accent[data-state=on] { - background-color: hsl(var(--accent)); -} - -.use-tailwind .data-\[state\=open\]\:bg-accent[data-state=open] { - background-color: hsl(var(--accent)); -} - -.use-tailwind .data-\[state\=open\]\:bg-accent\/50[data-state=open] { - background-color: hsl(var(--accent) / 0.5); -} - -.use-tailwind .data-\[state\=open\]\:bg-secondary[data-state=open] { - background-color: hsl(var(--secondary)); -} - -.use-tailwind .data-\[state\=selected\]\:bg-muted[data-state=selected] { - background-color: hsl(var(--muted)); -} - -.use-tailwind .data-\[state\=unchecked\]\:bg-input[data-state=unchecked] { - background-color: hsl(var(--input)); -} - -.use-tailwind .data-\[state\=active\]\:text-foreground[data-state=active] { - color: hsl(var(--foreground)); -} - -.use-tailwind .data-\[state\=checked\]\:text-primary-foreground[data-state=checked] { - color: hsl(var(--primary-foreground)); -} - -.use-tailwind .data-\[state\=on\]\:text-accent-foreground[data-state=on] { - color: hsl(var(--accent-foreground)); -} - -.use-tailwind .data-\[state\=open\]\:text-accent-foreground[data-state=open] { - color: hsl(var(--accent-foreground)); -} - -.use-tailwind .data-\[state\=open\]\:text-muted-foreground[data-state=open] { - color: hsl(var(--muted-foreground)); -} - -.use-tailwind .data-\[disabled\=true\]\:opacity-50[data-disabled=true] { - opacity: 0.5; -} - -.use-tailwind .data-\[disabled\]\:opacity-50[data-disabled] { - opacity: 0.5; -} - -.use-tailwind .data-\[state\=active\]\:shadow[data-state=active] { - --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.use-tailwind .data-\[swipe\=move\]\:transition-none[data-swipe=move] { - transition-property: none; -} - -.use-tailwind .data-\[state\=closed\]\:duration-300[data-state=closed] { - transition-duration: 300ms; -} - -.use-tailwind .data-\[state\=open\]\:duration-500[data-state=open] { - transition-duration: 500ms; -} - -.use-tailwind .data-\[motion\^\=from-\]\:animate-in[data-motion^=from-] { - animation-name: enter; - animation-duration: 150ms; - --tw-enter-opacity: initial; - --tw-enter-scale: initial; - --tw-enter-rotate: initial; - --tw-enter-translate-x: initial; - --tw-enter-translate-y: initial; -} - -.use-tailwind .data-\[state\=open\]\:animate-in[data-state=open] { - animation-name: enter; - animation-duration: 150ms; - --tw-enter-opacity: initial; - --tw-enter-scale: initial; - --tw-enter-rotate: initial; - --tw-enter-translate-x: initial; - --tw-enter-translate-y: initial; -} - -.use-tailwind .data-\[state\=visible\]\:animate-in[data-state=visible] { - animation-name: enter; - animation-duration: 150ms; - --tw-enter-opacity: initial; - --tw-enter-scale: initial; - --tw-enter-rotate: initial; - --tw-enter-translate-x: initial; - --tw-enter-translate-y: initial; -} - -.use-tailwind .data-\[motion\^\=to-\]\:animate-out[data-motion^=to-] { - animation-name: exit; - animation-duration: 150ms; - --tw-exit-opacity: initial; - --tw-exit-scale: initial; - --tw-exit-rotate: initial; - --tw-exit-translate-x: initial; - --tw-exit-translate-y: initial; -} - -.use-tailwind .data-\[state\=closed\]\:animate-out[data-state=closed] { - animation-name: exit; - animation-duration: 150ms; - --tw-exit-opacity: initial; - --tw-exit-scale: initial; - --tw-exit-rotate: initial; - --tw-exit-translate-x: initial; - --tw-exit-translate-y: initial; -} - -.use-tailwind .data-\[state\=hidden\]\:animate-out[data-state=hidden] { - animation-name: exit; - animation-duration: 150ms; - --tw-exit-opacity: initial; - --tw-exit-scale: initial; - --tw-exit-rotate: initial; - --tw-exit-translate-x: initial; - --tw-exit-translate-y: initial; -} - -.use-tailwind .data-\[swipe\=end\]\:animate-out[data-swipe=end] { - animation-name: exit; - animation-duration: 150ms; - --tw-exit-opacity: initial; - --tw-exit-scale: initial; - --tw-exit-rotate: initial; - --tw-exit-translate-x: initial; - --tw-exit-translate-y: initial; -} - -.use-tailwind .data-\[motion\^\=from-\]\:fade-in[data-motion^=from-] { - --tw-enter-opacity: 0; -} - -.use-tailwind .data-\[motion\^\=to-\]\:fade-out[data-motion^=to-] { - --tw-exit-opacity: 0; -} - -.use-tailwind .data-\[state\=closed\]\:fade-out-0[data-state=closed] { - --tw-exit-opacity: 0; -} - -.use-tailwind .data-\[state\=closed\]\:fade-out-80[data-state=closed] { - --tw-exit-opacity: 0.8; -} - -.use-tailwind .data-\[state\=hidden\]\:fade-out[data-state=hidden] { - --tw-exit-opacity: 0; -} - -.use-tailwind .data-\[state\=open\]\:fade-in-0[data-state=open] { - --tw-enter-opacity: 0; -} - -.use-tailwind .data-\[state\=visible\]\:fade-in[data-state=visible] { - --tw-enter-opacity: 0; -} - -.use-tailwind .data-\[state\=closed\]\:zoom-out-95[data-state=closed] { - --tw-exit-scale: .95; -} - -.use-tailwind .data-\[state\=open\]\:zoom-in-90[data-state=open] { - --tw-enter-scale: .9; -} - -.use-tailwind .data-\[state\=open\]\:zoom-in-95[data-state=open] { - --tw-enter-scale: .95; -} - -.use-tailwind .data-\[motion\=from-end\]\:slide-in-from-right-52[data-motion=from-end] { - --tw-enter-translate-x: 13rem; -} - -.use-tailwind .data-\[motion\=from-start\]\:slide-in-from-left-52[data-motion=from-start] { - --tw-enter-translate-x: -13rem; -} - -.use-tailwind .data-\[motion\=to-end\]\:slide-out-to-right-52[data-motion=to-end] { - --tw-exit-translate-x: 13rem; -} - -.use-tailwind .data-\[motion\=to-start\]\:slide-out-to-left-52[data-motion=to-start] { - --tw-exit-translate-x: -13rem; -} - -.use-tailwind .data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom] { - --tw-enter-translate-y: -0.5rem; -} - -.use-tailwind .data-\[side\=left\]\:slide-in-from-right-2[data-side=left] { - --tw-enter-translate-x: 0.5rem; -} - -.use-tailwind .data-\[side\=right\]\:slide-in-from-left-2[data-side=right] { - --tw-enter-translate-x: -0.5rem; -} - -.use-tailwind .data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top] { - --tw-enter-translate-y: 0.5rem; -} - -.use-tailwind .data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed] { - --tw-exit-translate-y: 100%; -} - -.use-tailwind .data-\[state\=closed\]\:slide-out-to-left[data-state=closed] { - --tw-exit-translate-x: -100%; -} - -.use-tailwind .data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed] { - --tw-exit-translate-x: -50%; -} - -.use-tailwind .data-\[state\=closed\]\:slide-out-to-right[data-state=closed] { - --tw-exit-translate-x: 100%; -} - -.use-tailwind .data-\[state\=closed\]\:slide-out-to-right-full[data-state=closed] { - --tw-exit-translate-x: 100%; -} - -.use-tailwind .data-\[state\=closed\]\:slide-out-to-top[data-state=closed] { - --tw-exit-translate-y: -100%; -} - -.use-tailwind .data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed] { - --tw-exit-translate-y: -48%; -} - -.use-tailwind .data-\[state\=open\]\:slide-in-from-bottom[data-state=open] { - --tw-enter-translate-y: 100%; -} - -.use-tailwind .data-\[state\=open\]\:slide-in-from-left[data-state=open] { - --tw-enter-translate-x: -100%; -} - -.use-tailwind .data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open] { - --tw-enter-translate-x: -50%; -} - -.use-tailwind .data-\[state\=open\]\:slide-in-from-right[data-state=open] { - --tw-enter-translate-x: 100%; -} - -.use-tailwind .data-\[state\=open\]\:slide-in-from-top[data-state=open] { - --tw-enter-translate-y: -100%; -} - -.use-tailwind .data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open] { - --tw-enter-translate-y: -48%; -} - -.use-tailwind .data-\[state\=open\]\:slide-in-from-top-full[data-state=open] { - --tw-enter-translate-y: -100%; -} - -.use-tailwind .data-\[state\=closed\]\:duration-300[data-state=closed] { - animation-duration: 300ms; -} - -.use-tailwind .data-\[state\=open\]\:duration-500[data-state=open] { - animation-duration: 500ms; -} - -.use-tailwind .data-\[panel-group-direction\=vertical\]\:after\:left-0[data-panel-group-direction=vertical]::after { - content: var(--tw-content); - left: 0px; -} - -.use-tailwind .data-\[panel-group-direction\=vertical\]\:after\:h-1[data-panel-group-direction=vertical]::after { - content: var(--tw-content); - height: 0.25rem; -} - -.use-tailwind .data-\[panel-group-direction\=vertical\]\:after\:w-full[data-panel-group-direction=vertical]::after { - content: var(--tw-content); - width: 100%; -} - -.use-tailwind .data-\[panel-group-direction\=vertical\]\:after\:-translate-y-1\/2[data-panel-group-direction=vertical]::after { - content: var(--tw-content); - --tw-translate-y: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .data-\[panel-group-direction\=vertical\]\:after\:translate-x-0[data-panel-group-direction=vertical]::after { - content: var(--tw-content); - --tw-translate-x: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .data-\[state\=inactive\]\:hover\:brightness-50:hover[data-state=inactive] { - --tw-brightness: brightness(.5); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.use-tailwind .group[data-state=open] .group-data-\[state\=open\]\:rotate-180 { - --tw-rotate: 180deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .dark\:-rotate-90:is(.theme-mode-dark *) { - --tw-rotate: -90deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .dark\:rotate-0:is(.theme-mode-dark *) { - --tw-rotate: 0deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .dark\:scale-0:is(.theme-mode-dark *) { - --tw-scale-x: 0; - --tw-scale-y: 0; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .dark\:scale-100:is(.theme-mode-dark *) { - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .dark\:border-destructive:is(.theme-mode-dark *) { - border-color: hsl(var(--destructive)); -} - -.use-tailwind .data-\[state\=inactive\]\:dark\:hover\:brightness-150:hover:is(.theme-mode-dark *)[data-state=inactive] { - --tw-brightness: brightness(1.5); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -@media (min-width: 640px) { - .use-tailwind .sm\:bottom-0 { - bottom: 0px; - } - - .use-tailwind .sm\:right-0 { - right: 0px; - } - - .use-tailwind .sm\:top-auto { - top: auto; - } - - .use-tailwind .sm\:mt-0 { - margin-top: 0px; - } - - .use-tailwind .sm\:block { - display: block; - } - - .use-tailwind .sm\:max-w-sm { - max-width: 24rem; - } - - .use-tailwind .sm\:flex-row { - flex-direction: row; - } - - .use-tailwind .sm\:flex-col { - flex-direction: column; - } - - .use-tailwind .sm\:justify-end { - justify-content: flex-end; - } - - .use-tailwind .sm\:gap-2 { - gap: 0.5rem; - } - - .use-tailwind .sm\:gap-2\.5 { - gap: 0.625rem; - } - - .use-tailwind .sm\:space-x-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.5rem * var(--tw-space-x-reverse)); - margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); - } - - .use-tailwind .sm\:space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); - } - - .use-tailwind .sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0px * var(--tw-space-y-reverse)); - } - - .use-tailwind .sm\:rounded-lg { - border-radius: var(--radius); - } - - .use-tailwind .sm\:text-left { - text-align: left; - } - - .use-tailwind .data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open] { - --tw-enter-translate-y: 100%; - } -} - -@media (min-width: 768px) { - .use-tailwind .md\:absolute { - position: absolute; - } - - .use-tailwind .md\:flex { - display: flex; - } - - .use-tailwind .md\:hidden { - display: none; - } - - .use-tailwind .md\:w-\[var\(--radix-navigation-menu-viewport-width\)\] { - width: var(--radix-navigation-menu-viewport-width); - } - - .use-tailwind .md\:w-auto { - width: auto; - } - - .use-tailwind .md\:max-w-\[420px\] { - max-width: 420px; - } -} - -.use-tailwind .\[\&\+div\]\:text-xs+div { - font-size: 0.75rem; - line-height: 1rem; -} - -.use-tailwind .\[\&\:has\(\>\.day-range-end\)\]\:rounded-r-md:has(>.day-range-end) { - border-top-right-radius: calc(var(--radius) - 2px); - border-bottom-right-radius: calc(var(--radius) - 2px); -} - -.use-tailwind .\[\&\:has\(\>\.day-range-start\)\]\:rounded-l-md:has(>.day-range-start) { - border-top-left-radius: calc(var(--radius) - 2px); - border-bottom-left-radius: calc(var(--radius) - 2px); -} - -.use-tailwind .\[\&\:has\(\[aria-selected\]\)\]\:rounded-md:has([aria-selected]) { - border-radius: calc(var(--radius) - 2px); -} - -.use-tailwind .\[\&\:has\(\[aria-selected\]\)\]\:bg-accent:has([aria-selected]) { - background-color: hsl(var(--accent)); -} - -.use-tailwind .first\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-l-md:has([aria-selected]):first-child { - border-top-left-radius: calc(var(--radius) - 2px); - border-bottom-left-radius: calc(var(--radius) - 2px); -} - -.use-tailwind .last\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-r-md:has([aria-selected]):last-child { - border-top-right-radius: calc(var(--radius) - 2px); - border-bottom-right-radius: calc(var(--radius) - 2px); -} - -.use-tailwind .\[\&\:has\(\[aria-selected\]\.day-outside\)\]\:bg-accent\/50:has([aria-selected].day-outside) { - background-color: hsl(var(--accent) / 0.5); -} - -.use-tailwind .\[\&\:has\(\[aria-selected\]\.day-range-end\)\]\:rounded-r-md:has([aria-selected].day-range-end) { - border-top-right-radius: calc(var(--radius) - 2px); - border-bottom-right-radius: calc(var(--radius) - 2px); -} - -.use-tailwind .\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]) { - padding-right: 0px; -} - -.use-tailwind .\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\]>[role=checkbox] { - --tw-translate-y: 2px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .\[\&\>span\]\:line-clamp-1>span { - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; -} - -.use-tailwind .\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div { - --tw-translate-y: -3px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .\[\&\>svg\]\:absolute>svg { - position: absolute; -} - -.use-tailwind .\[\&\>svg\]\:left-4>svg { - left: 1rem; -} - -.use-tailwind .\[\&\>svg\]\:top-4>svg { - top: 1rem; -} - -.use-tailwind .\[\&\>svg\]\:size-3\.5>svg { - width: 0.875rem; - height: 0.875rem; -} - -.use-tailwind .\[\&\>svg\]\:text-destructive>svg { - color: hsl(var(--destructive)); -} - -.use-tailwind .\[\&\>svg\]\:text-foreground>svg { - color: hsl(var(--foreground)); -} - -.use-tailwind .\[\&\>svg\~\*\]\:pl-7>svg~* { - padding-left: 1.75rem; -} - -.use-tailwind .\[\&\>tr\]\:last\:border-b-0:last-child>tr { - border-bottom-width: 0px; -} - -.use-tailwind .\[\&\[data-panel-group-direction\=vertical\]\>div\]\:rotate-90[data-panel-group-direction=vertical]>div { - --tw-rotate: 90deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg { - --tw-rotate: 180deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.use-tailwind .\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading] { - padding-left: 0.5rem; - padding-right: 0.5rem; -} - -.use-tailwind .\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading] { - padding-top: 0.375rem; - padding-bottom: 0.375rem; -} - -.use-tailwind .\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading] { - font-size: 0.75rem; - line-height: 1rem; -} - -.use-tailwind .\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading] { - font-weight: 500; -} - -.use-tailwind .\[\&_\[cmdk-group-heading\]\]\:text-muted-foreground [cmdk-group-heading] { - color: hsl(var(--muted-foreground)); -} - -.use-tailwind .\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 [cmdk-group]:not([hidden]) ~[cmdk-group] { - padding-top: 0px; -} - -.use-tailwind .\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group] { - padding-left: 0.5rem; - padding-right: 0.5rem; -} - -.use-tailwind .\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg { - height: 1.25rem; -} - -.use-tailwind .\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg { - width: 1.25rem; -} - -.use-tailwind .\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input] { - height: 3rem; -} - -.use-tailwind .\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item] { - padding-left: 0.5rem; - padding-right: 0.5rem; -} - -.use-tailwind .\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item] { - padding-top: 0.75rem; - padding-bottom: 0.75rem; -} - -.use-tailwind .\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg { - height: 1.25rem; -} - -.use-tailwind .\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg { - width: 1.25rem; -} - -.use-tailwind .\[\&_p\]\:leading-relaxed p { - line-height: 1.625; -} - -.use-tailwind .\[\&_tr\:last-child\]\:border-0 tr:last-child { - border-width: 0px; -} - -.use-tailwind .\[\&_tr\]\:border-b tr { - border-bottom-width: 1px; -} diff --git a/docs/quantinuum-sphinx/_static/tokens.css b/docs/quantinuum-sphinx/_static/tokens.css deleted file mode 100644 index 6fbfa246..00000000 --- a/docs/quantinuum-sphinx/_static/tokens.css +++ /dev/null @@ -1,63 +0,0 @@ -:root { - color-scheme: light; - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; - - --card: 0 0% 100%; - --card-foreground: 240 10% 3.9%; - - --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; - - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; - - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; - - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; - - --accent: 240 4.8% 95.9%; - --accent-foreground: 240 5.9% 10%; - - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - - --border: 240 5.9% 90%; - --input: 240 5.9% 90%; - --ring: 240 10% 3.9%; - - --radius: 0.5rem; -} - -:root.theme-mode-dark { - color-scheme: dark; - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; - - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; - - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; - - --destructive: 0 84% 60%; - --destructive-foreground: 240 10% 3.9%; - - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 240 4.9% 83.9%; -} diff --git a/docs/quantinuum-sphinx/_templates/page.html b/docs/quantinuum-sphinx/_templates/page.html deleted file mode 100644 index 2bebea6c..00000000 --- a/docs/quantinuum-sphinx/_templates/page.html +++ /dev/null @@ -1,313 +0,0 @@ -{% extends "furo/base.html" %} - -{% block extrahead %} - - - - - - - - -{% endblock %} - -{% block body -%} - -{{ super() }} -{% include "partials/icons.html" %} - - - - - - - - - - - - - - - {%- trans -%} - Skip to content - {%- endtrans -%} - - -{% if theme_announcement -%} -
- -
-{%- endif %} - -
-
-
- -
- -
-
- -
- -
-
- -
-
-
- - - - - {% trans %}Back to top{% endtrans %} - -
- {% if theme_top_of_page_button != "edit" -%} - {{ warning("Got configuration for 'top_of_page_button': this is deprecated.") }} - {%- endif -%} - - {%- if theme_top_of_page_buttons == "" -%} - {% if theme_top_of_page_button == None -%} - {#- We respect the old configuration of disabling all the buttons -#} - {%- set theme_top_of_page_buttons = [] -%} - {% else %} - {%- set theme_top_of_page_buttons = ["view", "edit"] -%} - {%- endif -%} - {% else -%} - {% if theme_top_of_page_button != "edit" -%} - {%- set theme_top_of_page_buttons = [] -%} - {{ warning("Got configuration for both 'top_of_page_button' and 'top_of_page_buttons', ignoring both and removing all top of page buttons.") }} - {%- endif -%} - {%- endif -%} - {% for button in theme_top_of_page_buttons -%} - {% if button == "view" %} - {%- include "components/view-this-page.html" with context -%} - {% elif button == "edit" %} - {%- include "components/edit-this-page.html" with context -%} - {% else %} - {{ warning("Got an unsupported value in 'top_of_page_buttons' for theme configuration") }} - {% endif %} - {%- endfor -%} - {#- Theme toggle -#} -
- -
- -
-
- {% block content %}{{ body }}{% endblock %} -
-
-
- {% block footer %} - -
-
- {%- if show_copyright %} - - {%- endif %} - {% trans %}Made with {% endtrans -%} - {%- if show_sphinx -%} - {% trans %}Sphinx and {% endtrans -%} - @pradyunsg's - {% endif -%} - {% trans %} - Furo - {% endtrans %} - {%- if last_updated -%} -
- {% trans last_updated=last_updated|e -%} - Last updated on {{ last_updated }} - {%- endtrans -%} -
- {%- endif %} -
-
- {% if theme_footer_icons or READTHEDOCS -%} -
- {% if theme_footer_icons -%} - {% for icon_dict in theme_footer_icons -%} - - {{- icon_dict.html -}} - - {% endfor %} - {%- else -%} - {#- Show Read the Docs project -#} - {%- if READTHEDOCS and slug -%} - - - - - - {%- endif -%} - {#- Show GitHub repository home -#} - {%- if READTHEDOCS and display_github and github_user != "None" and github_repo != "None" -%} - - - - - - {%- endif -%} - {%- endif %} -
- {%- endif %} -
-
- {% endblock footer %} -
-
- -
-
-{%- endblock %} - - - - From 0ca986dcf870329dbf5fd31b82040a26e80e8bc5 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:34:22 +0100 Subject: [PATCH 61/76] use submodules in build --- .github/workflows/check-docs-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/check-docs-build.yml b/.github/workflows/check-docs-build.yml index ea8ba033..2bb99148 100644 --- a/.github/workflows/check-docs-build.yml +++ b/.github/workflows/check-docs-build.yml @@ -35,6 +35,7 @@ jobs: steps: - uses: actions/checkout@v4 with: + submodules: true fetch-depth: '0' - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/* - name: Set up Python 3.11 From e40cc4e7a7ae280f119d5d0cdae205b625dd6268 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:16:09 +0100 Subject: [PATCH 62/76] update submodule to latest --- docs/quantinuum-sphinx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quantinuum-sphinx b/docs/quantinuum-sphinx index 30f30c39..5b1ba8c4 160000 --- a/docs/quantinuum-sphinx +++ b/docs/quantinuum-sphinx @@ -1 +1 @@ -Subproject commit 30f30c390e9f29e00e8f8ae5d4096e244b7b117c +Subproject commit 5b1ba8c4da45651d1039b96610f917ed86fd873c From d053f9a177dafb531ef8a97230a003beff599649 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:27:25 +0100 Subject: [PATCH 63/76] update to latest quantinuum-sphinx --- docs/quantinuum-sphinx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quantinuum-sphinx b/docs/quantinuum-sphinx index 5b1ba8c4..160b2977 160000 --- a/docs/quantinuum-sphinx +++ b/docs/quantinuum-sphinx @@ -1 +1 @@ -Subproject commit 5b1ba8c4da45651d1039b96610f917ed86fd873c +Subproject commit 160b297794d5af8832b7d7085707a9bcc43c93d3 From 73f38a3eabf1402f2f5aaf718c0cefc050abcc8d Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:36:31 +0100 Subject: [PATCH 64/76] add 404.html file back in --- docs/404.html | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docs/404.html diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 00000000..c57b14c0 --- /dev/null +++ b/docs/404.html @@ -0,0 +1,20 @@ + + + + + + + + TKET docs have moved to tket.quantinuum.com + + + +

+ If you are not redirected automatically, follow + this link to the user manual on tket.quantinuum.com. +

+ + + \ No newline at end of file From cabf99b59ea246a4097e0386728c8055082291e7 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 1 Aug 2024 13:58:25 +0100 Subject: [PATCH 65/76] add more intersphinx mappings for extensions --- docs/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 57d6d111..6696f29b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,6 +35,8 @@ "https://tket.quantinuum.com/extensions/pytket-quantinuum/", None, ), + "pytket-qujax": ("https://tket.quantinuum.com/extensions/pytket-qujax/", None), + "pytket-cirq": ("https://tket.quantinuum.com/extensions/pytket-cirq/", None), "sympy": ("https://docs.sympy.org/latest/", None), } From 31953efa2943905644fb8902ba66059a46b47d47 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 1 Aug 2024 13:58:43 +0100 Subject: [PATCH 66/76] update pytket-cirq ref --- docs/manual/manual_backend.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/manual_backend.rst b/docs/manual/manual_backend.rst index 57b0ec1d..a202b479 100644 --- a/docs/manual/manual_backend.rst +++ b/docs/manual/manual_backend.rst @@ -530,7 +530,7 @@ Embedding into Qiskit Not only is the goal of tket to be a device-agnostic platform, but also interface-agnostic, so users are not obliged to have to work entirely in tket to benefit from the wide range of devices supported. For example, Qiskit is currently the most widely adopted quantum software development platform, providing its own modules for building and compiling circuits, submitting to backends, applying error mitigation techniques and combining these into higher-level algorithms. Each :py:class:`~pytket.backends.Backend` in ``pytket`` can be wrapped up to imitate a Qiskit backend, allowing the benefits of tket to be felt in existing Qiskit projects with minimal work. -Below we show how the :py:class:`CirqStateSampleBackend` from the ``pytket-cirq`` extension can be used with its :py:meth:`default_compilation_pass` directly in qiskit. +Below we show how the :py:class:`~pytket.extensions.cirq.CirqStateSampleBackend` from the ``pytket-cirq`` extension can be used with its :py:meth:`default_compilation_pass` directly in qiskit. .. jupyter-execute:: From 2e9ce327385bcef8e3a4f0102ea2685e401d9dce Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 1 Aug 2024 14:09:56 +0100 Subject: [PATCH 67/76] remove all content for archived pytket-qsharp --- docs/examples/backends/comparing_simulators.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/backends/comparing_simulators.ipynb b/docs/examples/backends/comparing_simulators.ipynb index 34aa07fe..b75faa46 100644 --- a/docs/examples/backends/comparing_simulators.ipynb +++ b/docs/examples/backends/comparing_simulators.ipynb @@ -1 +1 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Comparison of the simulators available through TKET\n","\n","**Download this notebook - {nb-download}`comparing_simulators.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:
\n","- exploring the wide array of simulators available through the extension modules for `pytket`;
\n","- comparing their unique features and capabilities."]},{"cell_type":"markdown","metadata":{},"source":["This example assumes the reader is familiar with the basics of circuit construction and evaluation.
\n","
\n","To run every option in this example, you will need `pytket`, `pytket-qiskit`, `pytket-pyquil`, `pytket-qsharp`, `pytket-qulacs`, and `pytket-projectq`.
\n","
\n","With the number of simulator `Backend`s available across the `pytket` extension modules, we are often asked why to use one over another. Surely, any two simulators are equivalent if they are able to sample the circuits in the same way, right? Not quite. In this notebook we go through each of the simulators in turn and describe what sets them apart from others and how to make use of any unique features.
\n","
\n","But first, to demonstrate the significant overlap in functionality, we'll just give some examples of common usage for different types of backends."]},{"cell_type":"markdown","metadata":{},"source":["## Sampling simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3, 3)\n","c.Ry(0.7, 0)\n","c.CX(0, 1)\n","c.X(2)\n","c.measure_all()"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","counts = backend.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## Statevector simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerStateBackend"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the statevector:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerStateBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c)\n","state = backend.get_result(handle).get_state()\n","print(state)"]},{"cell_type":"markdown","metadata":{},"source":["## Expectation value usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit, Qubit\n","from pytket.extensions.qiskit import AerBackend, AerStateBackend\n","from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Define the measurement operator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["xxi = QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X})\n","zzz = QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z, Qubit(2): Pauli.Z})\n","op = QubitPauliOperator({xxi: -1.8, zzz: 0.7})"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","exp = backend.get_operator_expectation_value(c, op)\n","print(exp)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerBackend` wraps up the `qasm_simulator` from the Qiskit Aer package. It supports an extremely flexible set of circuits and uses many effective simulation methods making it a great all-purpose sampling simulator.
\n","
\n","Unique features:
\n","- supports mid-circuit measurement and OpenQASM-style conditional gates;
\n","- encompasses a variety of underlying simulation methods and automatically selects the best one for each circuit (including statevector, density matrix, (extended) stabilizer and matrix product state);
\n","- can be provided with a `qiskit.providers.Aer.noise.NoiseModel` on instantiation to perform a noisy simulation."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend\n","from itertools import combinations\n","from qiskit_aer.noise import NoiseModel, depolarizing_error"]},{"cell_type":"markdown","metadata":{},"source":["Quantum teleportation circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","alice = c.add_q_register(\"a\", 2)\n","bob = c.add_q_register(\"b\", 1)\n","data = c.add_c_register(\"d\", 2)\n","final = c.add_c_register(\"f\", 1)"]},{"cell_type":"markdown","metadata":{},"source":["Start in an interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Rx(0.3, alice[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a Bell state between Alice and Bob:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.H(alice[1]).CX(alice[1], bob[0])"]},{"cell_type":"markdown","metadata":{},"source":["Measure Alice's qubits in the Bell basis:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.CX(alice[0], alice[1]).H(alice[0])\n","c.Measure(alice[0], data[0])\n","c.Measure(alice[1], data[1])"]},{"cell_type":"markdown","metadata":{},"source":["Correct Bob's qubit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=1)\n","c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=2)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3)"]},{"cell_type":"markdown","metadata":{},"source":["Measure Bob's qubit to observe the interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Measure(bob[0], final[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a noisy simulator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["model = NoiseModel()\n","dep_err = depolarizing_error(0.04, 2)\n","for i, j in combinations(range(3), r=2):\n"," model.add_quantum_error(dep_err, [\"cx\"], [i, j])\n"," model.add_quantum_error(dep_err, [\"cx\"], [j, i])\n","backend = AerBackend(noise_model=model)"]},{"cell_type":"markdown","metadata":{},"source":["Run circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","result = backend.get_result(handle)\n","counts = result.get_counts([final[0]])\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerStateBackend` provides access to Qiskit Aer's `statevector_simulator`. It supports a similarly large gate set and has competitive speed for statevector simulations.
\n","
\n","Useful features:
\n","- no dependency on external executables, making it easy to install and run on any computer;
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `AerUnitaryBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Finishing the set of simulators from Qiskit Aer, `AerUnitaryBackend` captures the `unitary_simulator`, allowing for the entire unitary of a pure quantum process to be calculated. This is especially useful for testing small subcircuits that will be used many times in a larger computation.
\n","
\n","Unique features:
\n","- provides the full unitary matrix for a pure quantum circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerUnitaryBackend\n","from pytket.predicates import NoClassicalControlPredicate"]},{"cell_type":"markdown","metadata":{},"source":["Define a simple quantum incrementer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the unitary:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerUnitaryBackend()\n","c = backend.get_compiled_circuit(c)\n","result = backend.run_circuit(c)\n","unitary = result.get_unitary()\n","print(unitary.round(1).real)"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Whilst it can, with suitable credentials, be used to access the Rigetti QPUs, the `ForestBackend` also features a simulator mode which turns it into a noiseless sampling simulator that matches the constraints of the simulated device (e.g. the same gate set, restricted connectivity, measurement model, etc.). This is useful when playing around with custom compilation strategies to ensure that your final circuits are suitable to run on the device and for checking that your overall program works fine before you invest in reserving a QPU.
\n","
\n","Unique features:
\n","- faithful recreation of the circuit constraints of Rigetti QPUs."]},{"cell_type":"markdown","metadata":{},"source":["If trying to use the `ForestBackend` locally (i.e. not on a Rigetti QMI), you will need to have `quilc` and `qvm` running as separate processes in server mode. One easy way of doing this is with `docker` (see the `quilc` and `qvm` documentation for alternative methods of running them):
\n","`docker run --rm -it -p 5555:5555 rigetti/quilc -R`
\n","`docker run --rm -it -p 5000:5000 rigetti/qvm -S`"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The Rigetti `pyquil` package also provides the `WavefunctionSimulator`, which we present as the `ForestStateBackend`. Functionally, it is very similar to the `AerStateBackend` so can be used interchangeably. It does require that `quilc` and `qvm` are running as separate processes when not running on a Rigetti QMI.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpSimulatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QsharpSimulatorBackend` is another basic sampling simulator that is interchangeable with others, using the Microsoft QDK simulator. Note that the `pytket-qsharp` package is dependent on the `dotnet` SDK and `iqsharp` tool. Please consult the `pytket-qsharp` installation instructions for recommendations."]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpToffoliSimulatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Toffoli circuits form a strict fragment of quantum circuits and can be efficiently simulated. The `QsharpToffoliSimulatorBackend` can only operate on these circuits, but scales much better with system size than regular simulators.
\n","
\n","Unique features:
\n","- efficient simulation of Toffoli circuits."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qsharp import QsharpToffoliSimulatorBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit - start in a basis state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.X(0).X(2)\n","# Define a circuit - incrementer\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = QsharpToffoliSimulatorBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=10)\n","counts = backend.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpEstimatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QsharpEstimatorBackend` is not strictly a simulator, as it doesn't model the state of the quantum system and try to identify the final state, but instead analyses the circuit to estimate the required resources to run it. It does not support any of the regular outcome types (e.g. shots, counts, statevector), just the summary of the estimated resources.
\n","
\n","Unique features:
\n","- estimates resources to perform the circuit, without actually simulating/running it."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit"]},{"cell_type":"markdown","metadata":{},"source":["from pytket.extensions.qsharp import QsharpEstimatorBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit - start in a basis state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.X(0).X(2)\n","# Define a circuit - incrementer\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"markdown","metadata":{},"source":["(disabled because of https://github.com/CQCL/pytket-qsharp/issues/37)
\n","backend = QsharpEstimatorBackend()
\n","c = backend.get_compiled_circuit(c)
\n","handle = backend.process_circuit(c, n_shots=10)
\n","resources = backend.get_resources(handle)
\n","print(resources)"]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QulacsBackend` is an all-purpose simulator with both sampling and statevector modes, using the basic CPU simulator from Qulacs.
\n","
\n","Unique features:
\n","- supports both sampling (shots/counts) and complete statevector outputs."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsGPUBackend`"]},{"cell_type":"markdown","metadata":{},"source":["If the GPU version of Qulacs is installed, the `QulacsGPUBackend` will use that to benefit from even faster speeds. It is very easy to get started with using a GPU, as it only requires a CUDA installation and the `qulacs-gpu` package from `pip`. Functionally, it is identical to the `QulacsBackend`, but potentially faster if you have GPU resources available.
\n","
\n","Unique features:
\n","- GPU support for very fast simulation."]},{"cell_type":"markdown","metadata":{},"source":["## `ProjectQBackend`"]},{"cell_type":"markdown","metadata":{},"source":["ProjectQ is a popular quantum circuit simulator, thanks to its availability and ease of use. It provides a similar level of performance and features to `AerStateBackend`.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Comparison of the simulators available through TKET\n","\n","**Download this notebook - {nb-download}`comparing_simulators.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:
\n","- exploring the wide array of simulators available through the extension modules for `pytket`;
\n","- comparing their unique features and capabilities."]},{"cell_type":"markdown","metadata":{},"source":["This example assumes the reader is familiar with the basics of circuit construction and evaluation.
\n","
\n","To run every option in this example, you will need `pytket`, `pytket-qiskit`, `pytket-pyquil`, `pytket-qulacs`, and `pytket-projectq`.
\n","
\n","With the number of simulator `Backend`s available across the `pytket` extension modules, we are often asked why to use one over another. Surely, any two simulators are equivalent if they are able to sample the circuits in the same way, right? Not quite. In this notebook we go through each of the simulators in turn and describe what sets them apart from others and how to make use of any unique features.
\n","
\n","But first, to demonstrate the significant overlap in functionality, we'll just give some examples of common usage for different types of backends."]},{"cell_type":"markdown","metadata":{},"source":["## Sampling simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3, 3)\n","c.Ry(0.7, 0)\n","c.CX(0, 1)\n","c.X(2)\n","c.measure_all()"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","counts = backend.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## Statevector simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerStateBackend"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the statevector:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerStateBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c)\n","state = backend.get_result(handle).get_state()\n","print(state)"]},{"cell_type":"markdown","metadata":{},"source":["## Expectation value usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit, Qubit\n","from pytket.extensions.qiskit import AerBackend, AerStateBackend\n","from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Define the measurement operator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["xxi = QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X})\n","zzz = QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z, Qubit(2): Pauli.Z})\n","op = QubitPauliOperator({xxi: -1.8, zzz: 0.7})"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","exp = backend.get_operator_expectation_value(c, op)\n","print(exp)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerBackend` wraps up the `qasm_simulator` from the Qiskit Aer package. It supports an extremely flexible set of circuits and uses many effective simulation methods making it a great all-purpose sampling simulator.
\n","
\n","Unique features:
\n","- supports mid-circuit measurement and OpenQASM-style conditional gates;
\n","- encompasses a variety of underlying simulation methods and automatically selects the best one for each circuit (including statevector, density matrix, (extended) stabilizer and matrix product state);
\n","- can be provided with a `qiskit.providers.Aer.noise.NoiseModel` on instantiation to perform a noisy simulation."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend\n","from itertools import combinations\n","from qiskit_aer.noise import NoiseModel, depolarizing_error"]},{"cell_type":"markdown","metadata":{},"source":["Quantum teleportation circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","alice = c.add_q_register(\"a\", 2)\n","bob = c.add_q_register(\"b\", 1)\n","data = c.add_c_register(\"d\", 2)\n","final = c.add_c_register(\"f\", 1)"]},{"cell_type":"markdown","metadata":{},"source":["Start in an interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Rx(0.3, alice[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a Bell state between Alice and Bob:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.H(alice[1]).CX(alice[1], bob[0])"]},{"cell_type":"markdown","metadata":{},"source":["Measure Alice's qubits in the Bell basis:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.CX(alice[0], alice[1]).H(alice[0])\n","c.Measure(alice[0], data[0])\n","c.Measure(alice[1], data[1])"]},{"cell_type":"markdown","metadata":{},"source":["Correct Bob's qubit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=1)\n","c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=2)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3)"]},{"cell_type":"markdown","metadata":{},"source":["Measure Bob's qubit to observe the interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Measure(bob[0], final[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a noisy simulator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["model = NoiseModel()\n","dep_err = depolarizing_error(0.04, 2)\n","for i, j in combinations(range(3), r=2):\n"," model.add_quantum_error(dep_err, [\"cx\"], [i, j])\n"," model.add_quantum_error(dep_err, [\"cx\"], [j, i])\n","backend = AerBackend(noise_model=model)"]},{"cell_type":"markdown","metadata":{},"source":["Run circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","result = backend.get_result(handle)\n","counts = result.get_counts([final[0]])\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerStateBackend` provides access to Qiskit Aer's `statevector_simulator`. It supports a similarly large gate set and has competitive speed for statevector simulations.
\n","
\n","Useful features:
\n","- no dependency on external executables, making it easy to install and run on any computer;
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `AerUnitaryBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Finishing the set of simulators from Qiskit Aer, `AerUnitaryBackend` captures the `unitary_simulator`, allowing for the entire unitary of a pure quantum process to be calculated. This is especially useful for testing small subcircuits that will be used many times in a larger computation.
\n","
\n","Unique features:
\n","- provides the full unitary matrix for a pure quantum circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerUnitaryBackend\n","from pytket.predicates import NoClassicalControlPredicate"]},{"cell_type":"markdown","metadata":{},"source":["Define a simple quantum incrementer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the unitary:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerUnitaryBackend()\n","c = backend.get_compiled_circuit(c)\n","result = backend.run_circuit(c)\n","unitary = result.get_unitary()\n","print(unitary.round(1).real)"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Whilst it can, with suitable credentials, be used to access the Rigetti QPUs, the `ForestBackend` also features a simulator mode which turns it into a noiseless sampling simulator that matches the constraints of the simulated device (e.g. the same gate set, restricted connectivity, measurement model, etc.). This is useful when playing around with custom compilation strategies to ensure that your final circuits are suitable to run on the device and for checking that your overall program works fine before you invest in reserving a QPU.
\n","
\n","Unique features:
\n","- faithful recreation of the circuit constraints of Rigetti QPUs."]},{"cell_type":"markdown","metadata":{},"source":["If trying to use the `ForestBackend` locally (i.e. not on a Rigetti QMI), you will need to have `quilc` and `qvm` running as separate processes in server mode. One easy way of doing this is with `docker` (see the `quilc` and `qvm` documentation for alternative methods of running them):
\n","`docker run --rm -it -p 5555:5555 rigetti/quilc -R`
\n","`docker run --rm -it -p 5000:5000 rigetti/qvm -S`"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The Rigetti `pyquil` package also provides the `WavefunctionSimulator`, which we present as the `ForestStateBackend`. Functionally, it is very similar to the `AerStateBackend` so can be used interchangeably. It does require that `quilc` and `qvm` are running as separate processes when not running on a Rigetti QMI.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpSimulatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QsharpSimulatorBackend` is another basic sampling simulator that is interchangeable with others, using the Microsoft QDK simulator. Note that the `pytket-qsharp` package is dependent on the `dotnet` SDK and `iqsharp` tool. Please consult the `pytket-qsharp` installation instructions for recommendations."]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpToffoliSimulatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Toffoli circuits form a strict fragment of quantum circuits and can be efficiently simulated. The `QsharpToffoliSimulatorBackend` can only operate on these circuits, but scales much better with system size than regular simulators.
\n","
\n","Unique features:
\n","- efficient simulation of Toffoli circuits."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qsharp import QsharpToffoliSimulatorBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit - start in a basis state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.X(0).X(2)\n","# Define a circuit - incrementer\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = QsharpToffoliSimulatorBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=10)\n","counts = backend.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpEstimatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QsharpEstimatorBackend` is not strictly a simulator, as it doesn't model the state of the quantum system and try to identify the final state, but instead analyses the circuit to estimate the required resources to run it. It does not support any of the regular outcome types (e.g. shots, counts, statevector), just the summary of the estimated resources.
\n","
\n","Unique features:
\n","- estimates resources to perform the circuit, without actually simulating/running it."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit"]},{"cell_type":"markdown","metadata":{},"source":["from pytket.extensions.qsharp import QsharpEstimatorBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit - start in a basis state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.X(0).X(2)\n","# Define a circuit - incrementer\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"markdown","metadata":{},"source":["(disabled because of https://github.com/CQCL/pytket-qsharp/issues/37)
\n","backend = QsharpEstimatorBackend()
\n","c = backend.get_compiled_circuit(c)
\n","handle = backend.process_circuit(c, n_shots=10)
\n","resources = backend.get_resources(handle)
\n","print(resources)"]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QulacsBackend` is an all-purpose simulator with both sampling and statevector modes, using the basic CPU simulator from Qulacs.
\n","
\n","Unique features:
\n","- supports both sampling (shots/counts) and complete statevector outputs."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsGPUBackend`"]},{"cell_type":"markdown","metadata":{},"source":["If the GPU version of Qulacs is installed, the `QulacsGPUBackend` will use that to benefit from even faster speeds. It is very easy to get started with using a GPU, as it only requires a CUDA installation and the `qulacs-gpu` package from `pip`. Functionally, it is identical to the `QulacsBackend`, but potentially faster if you have GPU resources available.
\n","
\n","Unique features:
\n","- GPU support for very fast simulation."]},{"cell_type":"markdown","metadata":{},"source":["## `ProjectQBackend`"]},{"cell_type":"markdown","metadata":{},"source":["ProjectQ is a popular quantum circuit simulator, thanks to its availability and ease of use. It provides a similar level of performance and features to `AerStateBackend`.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} From 8dd5008cc91b39b5546ff4bcc09109824802e4b2 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 1 Aug 2024 14:20:45 +0100 Subject: [PATCH 68/76] add some html to redirect from old pages --- examples/index.html | 20 ++++++++++++++++++++ user-manual/index.html | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 examples/index.html create mode 100644 user-manual/index.html diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 00000000..62e7e173 --- /dev/null +++ b/examples/index.html @@ -0,0 +1,20 @@ + + + + + + + + TKET docs have moved to tket.quantinuum.com + + + +

+ If you are not redirected automatically, follow + this link to the user guide on tket.quantinuum.com. +

+ + + \ No newline at end of file diff --git a/user-manual/index.html b/user-manual/index.html new file mode 100644 index 00000000..62e7e173 --- /dev/null +++ b/user-manual/index.html @@ -0,0 +1,20 @@ + + + + + + + + TKET docs have moved to tket.quantinuum.com + + + +

+ If you are not redirected automatically, follow + this link to the user guide on tket.quantinuum.com. +

+ + + \ No newline at end of file From 0540dbe272ebdc166c44facdf63e38cdb3542395 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 1 Aug 2024 14:26:04 +0100 Subject: [PATCH 69/76] add index.html file back into docs --- docs/index.html | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docs/index.html diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..c57b14c0 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,20 @@ + + + + + + + + TKET docs have moved to tket.quantinuum.com + + + +

+ If you are not redirected automatically, follow + this link to the user manual on tket.quantinuum.com. +

+ + + \ No newline at end of file From 189c4f80850cd573f61563b23a38f5d755122094 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:47:49 +0100 Subject: [PATCH 70/76] remove qsharp stuff --- docs/examples/backends/comparing_simulators.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/backends/comparing_simulators.ipynb b/docs/examples/backends/comparing_simulators.ipynb index b75faa46..44e380f1 100644 --- a/docs/examples/backends/comparing_simulators.ipynb +++ b/docs/examples/backends/comparing_simulators.ipynb @@ -1 +1 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Comparison of the simulators available through TKET\n","\n","**Download this notebook - {nb-download}`comparing_simulators.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:
\n","- exploring the wide array of simulators available through the extension modules for `pytket`;
\n","- comparing their unique features and capabilities."]},{"cell_type":"markdown","metadata":{},"source":["This example assumes the reader is familiar with the basics of circuit construction and evaluation.
\n","
\n","To run every option in this example, you will need `pytket`, `pytket-qiskit`, `pytket-pyquil`, `pytket-qulacs`, and `pytket-projectq`.
\n","
\n","With the number of simulator `Backend`s available across the `pytket` extension modules, we are often asked why to use one over another. Surely, any two simulators are equivalent if they are able to sample the circuits in the same way, right? Not quite. In this notebook we go through each of the simulators in turn and describe what sets them apart from others and how to make use of any unique features.
\n","
\n","But first, to demonstrate the significant overlap in functionality, we'll just give some examples of common usage for different types of backends."]},{"cell_type":"markdown","metadata":{},"source":["## Sampling simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3, 3)\n","c.Ry(0.7, 0)\n","c.CX(0, 1)\n","c.X(2)\n","c.measure_all()"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","counts = backend.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## Statevector simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerStateBackend"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the statevector:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerStateBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c)\n","state = backend.get_result(handle).get_state()\n","print(state)"]},{"cell_type":"markdown","metadata":{},"source":["## Expectation value usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit, Qubit\n","from pytket.extensions.qiskit import AerBackend, AerStateBackend\n","from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Define the measurement operator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["xxi = QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X})\n","zzz = QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z, Qubit(2): Pauli.Z})\n","op = QubitPauliOperator({xxi: -1.8, zzz: 0.7})"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","exp = backend.get_operator_expectation_value(c, op)\n","print(exp)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerBackend` wraps up the `qasm_simulator` from the Qiskit Aer package. It supports an extremely flexible set of circuits and uses many effective simulation methods making it a great all-purpose sampling simulator.
\n","
\n","Unique features:
\n","- supports mid-circuit measurement and OpenQASM-style conditional gates;
\n","- encompasses a variety of underlying simulation methods and automatically selects the best one for each circuit (including statevector, density matrix, (extended) stabilizer and matrix product state);
\n","- can be provided with a `qiskit.providers.Aer.noise.NoiseModel` on instantiation to perform a noisy simulation."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend\n","from itertools import combinations\n","from qiskit_aer.noise import NoiseModel, depolarizing_error"]},{"cell_type":"markdown","metadata":{},"source":["Quantum teleportation circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","alice = c.add_q_register(\"a\", 2)\n","bob = c.add_q_register(\"b\", 1)\n","data = c.add_c_register(\"d\", 2)\n","final = c.add_c_register(\"f\", 1)"]},{"cell_type":"markdown","metadata":{},"source":["Start in an interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Rx(0.3, alice[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a Bell state between Alice and Bob:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.H(alice[1]).CX(alice[1], bob[0])"]},{"cell_type":"markdown","metadata":{},"source":["Measure Alice's qubits in the Bell basis:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.CX(alice[0], alice[1]).H(alice[0])\n","c.Measure(alice[0], data[0])\n","c.Measure(alice[1], data[1])"]},{"cell_type":"markdown","metadata":{},"source":["Correct Bob's qubit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=1)\n","c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=2)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3)"]},{"cell_type":"markdown","metadata":{},"source":["Measure Bob's qubit to observe the interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Measure(bob[0], final[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a noisy simulator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["model = NoiseModel()\n","dep_err = depolarizing_error(0.04, 2)\n","for i, j in combinations(range(3), r=2):\n"," model.add_quantum_error(dep_err, [\"cx\"], [i, j])\n"," model.add_quantum_error(dep_err, [\"cx\"], [j, i])\n","backend = AerBackend(noise_model=model)"]},{"cell_type":"markdown","metadata":{},"source":["Run circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","result = backend.get_result(handle)\n","counts = result.get_counts([final[0]])\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerStateBackend` provides access to Qiskit Aer's `statevector_simulator`. It supports a similarly large gate set and has competitive speed for statevector simulations.
\n","
\n","Useful features:
\n","- no dependency on external executables, making it easy to install and run on any computer;
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `AerUnitaryBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Finishing the set of simulators from Qiskit Aer, `AerUnitaryBackend` captures the `unitary_simulator`, allowing for the entire unitary of a pure quantum process to be calculated. This is especially useful for testing small subcircuits that will be used many times in a larger computation.
\n","
\n","Unique features:
\n","- provides the full unitary matrix for a pure quantum circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerUnitaryBackend\n","from pytket.predicates import NoClassicalControlPredicate"]},{"cell_type":"markdown","metadata":{},"source":["Define a simple quantum incrementer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the unitary:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerUnitaryBackend()\n","c = backend.get_compiled_circuit(c)\n","result = backend.run_circuit(c)\n","unitary = result.get_unitary()\n","print(unitary.round(1).real)"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Whilst it can, with suitable credentials, be used to access the Rigetti QPUs, the `ForestBackend` also features a simulator mode which turns it into a noiseless sampling simulator that matches the constraints of the simulated device (e.g. the same gate set, restricted connectivity, measurement model, etc.). This is useful when playing around with custom compilation strategies to ensure that your final circuits are suitable to run on the device and for checking that your overall program works fine before you invest in reserving a QPU.
\n","
\n","Unique features:
\n","- faithful recreation of the circuit constraints of Rigetti QPUs."]},{"cell_type":"markdown","metadata":{},"source":["If trying to use the `ForestBackend` locally (i.e. not on a Rigetti QMI), you will need to have `quilc` and `qvm` running as separate processes in server mode. One easy way of doing this is with `docker` (see the `quilc` and `qvm` documentation for alternative methods of running them):
\n","`docker run --rm -it -p 5555:5555 rigetti/quilc -R`
\n","`docker run --rm -it -p 5000:5000 rigetti/qvm -S`"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The Rigetti `pyquil` package also provides the `WavefunctionSimulator`, which we present as the `ForestStateBackend`. Functionally, it is very similar to the `AerStateBackend` so can be used interchangeably. It does require that `quilc` and `qvm` are running as separate processes when not running on a Rigetti QMI.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpSimulatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QsharpSimulatorBackend` is another basic sampling simulator that is interchangeable with others, using the Microsoft QDK simulator. Note that the `pytket-qsharp` package is dependent on the `dotnet` SDK and `iqsharp` tool. Please consult the `pytket-qsharp` installation instructions for recommendations."]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpToffoliSimulatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Toffoli circuits form a strict fragment of quantum circuits and can be efficiently simulated. The `QsharpToffoliSimulatorBackend` can only operate on these circuits, but scales much better with system size than regular simulators.
\n","
\n","Unique features:
\n","- efficient simulation of Toffoli circuits."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qsharp import QsharpToffoliSimulatorBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit - start in a basis state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.X(0).X(2)\n","# Define a circuit - incrementer\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = QsharpToffoliSimulatorBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=10)\n","counts = backend.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## `QsharpEstimatorBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QsharpEstimatorBackend` is not strictly a simulator, as it doesn't model the state of the quantum system and try to identify the final state, but instead analyses the circuit to estimate the required resources to run it. It does not support any of the regular outcome types (e.g. shots, counts, statevector), just the summary of the estimated resources.
\n","
\n","Unique features:
\n","- estimates resources to perform the circuit, without actually simulating/running it."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit"]},{"cell_type":"markdown","metadata":{},"source":["from pytket.extensions.qsharp import QsharpEstimatorBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit - start in a basis state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.X(0).X(2)\n","# Define a circuit - incrementer\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"markdown","metadata":{},"source":["(disabled because of https://github.com/CQCL/pytket-qsharp/issues/37)
\n","backend = QsharpEstimatorBackend()
\n","c = backend.get_compiled_circuit(c)
\n","handle = backend.process_circuit(c, n_shots=10)
\n","resources = backend.get_resources(handle)
\n","print(resources)"]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QulacsBackend` is an all-purpose simulator with both sampling and statevector modes, using the basic CPU simulator from Qulacs.
\n","
\n","Unique features:
\n","- supports both sampling (shots/counts) and complete statevector outputs."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsGPUBackend`"]},{"cell_type":"markdown","metadata":{},"source":["If the GPU version of Qulacs is installed, the `QulacsGPUBackend` will use that to benefit from even faster speeds. It is very easy to get started with using a GPU, as it only requires a CUDA installation and the `qulacs-gpu` package from `pip`. Functionally, it is identical to the `QulacsBackend`, but potentially faster if you have GPU resources available.
\n","
\n","Unique features:
\n","- GPU support for very fast simulation."]},{"cell_type":"markdown","metadata":{},"source":["## `ProjectQBackend`"]},{"cell_type":"markdown","metadata":{},"source":["ProjectQ is a popular quantum circuit simulator, thanks to its availability and ease of use. It provides a similar level of performance and features to `AerStateBackend`.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Comparison of the simulators available through TKET\n","\n","**Download this notebook - {nb-download}`comparing_simulators.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:
\n","- exploring the wide array of simulators available through the extension modules for `pytket`;
\n","- comparing their unique features and capabilities."]},{"cell_type":"markdown","metadata":{},"source":["This example assumes the reader is familiar with the basics of circuit construction and evaluation.
\n","
\n","To run every option in this example, you will need `pytket`, `pytket-qiskit`, `pytket-pyquil`, `pytket-qulacs`, and `pytket-projectq`.
\n","
\n","With the number of simulator `Backend`s available across the `pytket` extension modules, we are often asked why to use one over another. Surely, any two simulators are equivalent if they are able to sample the circuits in the same way, right? Not quite. In this notebook we go through each of the simulators in turn and describe what sets them apart from others and how to make use of any unique features.
\n","
\n","But first, to demonstrate the significant overlap in functionality, we'll just give some examples of common usage for different types of backends."]},{"cell_type":"markdown","metadata":{},"source":["## Sampling simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3, 3)\n","c.Ry(0.7, 0)\n","c.CX(0, 1)\n","c.X(2)\n","c.measure_all()"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","counts = backend.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## Statevector simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerStateBackend"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the statevector:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerStateBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c)\n","state = backend.get_result(handle).get_state()\n","print(state)"]},{"cell_type":"markdown","metadata":{},"source":["## Expectation value usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit, Qubit\n","from pytket.extensions.qiskit import AerBackend, AerStateBackend\n","from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Define the measurement operator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["xxi = QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X})\n","zzz = QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z, Qubit(2): Pauli.Z})\n","op = QubitPauliOperator({xxi: -1.8, zzz: 0.7})"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","exp = backend.get_operator_expectation_value(c, op)\n","print(exp)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerBackend` wraps up the `qasm_simulator` from the Qiskit Aer package. It supports an extremely flexible set of circuits and uses many effective simulation methods making it a great all-purpose sampling simulator.
\n","
\n","Unique features:
\n","- supports mid-circuit measurement and OpenQASM-style conditional gates;
\n","- encompasses a variety of underlying simulation methods and automatically selects the best one for each circuit (including statevector, density matrix, (extended) stabilizer and matrix product state);
\n","- can be provided with a `qiskit.providers.Aer.noise.NoiseModel` on instantiation to perform a noisy simulation."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend\n","from itertools import combinations\n","from qiskit_aer.noise import NoiseModel, depolarizing_error"]},{"cell_type":"markdown","metadata":{},"source":["Quantum teleportation circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","alice = c.add_q_register(\"a\", 2)\n","bob = c.add_q_register(\"b\", 1)\n","data = c.add_c_register(\"d\", 2)\n","final = c.add_c_register(\"f\", 1)"]},{"cell_type":"markdown","metadata":{},"source":["Start in an interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Rx(0.3, alice[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a Bell state between Alice and Bob:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.H(alice[1]).CX(alice[1], bob[0])"]},{"cell_type":"markdown","metadata":{},"source":["Measure Alice's qubits in the Bell basis:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.CX(alice[0], alice[1]).H(alice[0])\n","c.Measure(alice[0], data[0])\n","c.Measure(alice[1], data[1])"]},{"cell_type":"markdown","metadata":{},"source":["Correct Bob's qubit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=1)\n","c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=2)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3)"]},{"cell_type":"markdown","metadata":{},"source":["Measure Bob's qubit to observe the interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Measure(bob[0], final[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a noisy simulator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["model = NoiseModel()\n","dep_err = depolarizing_error(0.04, 2)\n","for i, j in combinations(range(3), r=2):\n"," model.add_quantum_error(dep_err, [\"cx\"], [i, j])\n"," model.add_quantum_error(dep_err, [\"cx\"], [j, i])\n","backend = AerBackend(noise_model=model)"]},{"cell_type":"markdown","metadata":{},"source":["Run circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","result = backend.get_result(handle)\n","counts = result.get_counts([final[0]])\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerStateBackend` provides access to Qiskit Aer's `statevector_simulator`. It supports a similarly large gate set and has competitive speed for statevector simulations.
\n","
\n","Useful features:
\n","- no dependency on external executables, making it easy to install and run on any computer;
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `AerUnitaryBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Finishing the set of simulators from Qiskit Aer, `AerUnitaryBackend` captures the `unitary_simulator`, allowing for the entire unitary of a pure quantum process to be calculated. This is especially useful for testing small subcircuits that will be used many times in a larger computation.
\n","
\n","Unique features:
\n","- provides the full unitary matrix for a pure quantum circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerUnitaryBackend\n","from pytket.predicates import NoClassicalControlPredicate"]},{"cell_type":"markdown","metadata":{},"source":["Define a simple quantum incrementer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the unitary:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerUnitaryBackend()\n","c = backend.get_compiled_circuit(c)\n","result = backend.run_circuit(c)\n","unitary = result.get_unitary()\n","print(unitary.round(1).real)"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Whilst it can, with suitable credentials, be used to access the Rigetti QPUs, the `ForestBackend` also features a simulator mode which turns it into a noiseless sampling simulator that matches the constraints of the simulated device (e.g. the same gate set, restricted connectivity, measurement model, etc.). This is useful when playing around with custom compilation strategies to ensure that your final circuits are suitable to run on the device and for checking that your overall program works fine before you invest in reserving a QPU.
\n","
\n","Unique features:
\n","- faithful recreation of the circuit constraints of Rigetti QPUs."]},{"cell_type":"markdown","metadata":{},"source":["If trying to use the `ForestBackend` locally (i.e. not on a Rigetti QMI), you will need to have `quilc` and `qvm` running as separate processes in server mode. One easy way of doing this is with `docker` (see the `quilc` and `qvm` documentation for alternative methods of running them):
\n","`docker run --rm -it -p 5555:5555 rigetti/quilc -R`
\n","`docker run --rm -it -p 5000:5000 rigetti/qvm -S`"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The Rigetti `pyquil` package also provides the `WavefunctionSimulator`, which we present as the `ForestStateBackend`. Functionally, it is very similar to the `AerStateBackend` so can be used interchangeably. It does require that `quilc` and `qvm` are running as separate processes when not running on a Rigetti QMI.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QulacsBackend` is an all-purpose simulator with both sampling and statevector modes, using the basic CPU simulator from Qulacs.
\n","
\n","Unique features:
\n","- supports both sampling (shots/counts) and complete statevector outputs."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsGPUBackend`"]},{"cell_type":"markdown","metadata":{},"source":["If the GPU version of Qulacs is installed, the `QulacsGPUBackend` will use that to benefit from even faster speeds. It is very easy to get started with using a GPU, as it only requires a CUDA installation and the `qulacs-gpu` package from `pip`. Functionally, it is identical to the `QulacsBackend`, but potentially faster if you have GPU resources available.
\n","
\n","Unique features:
\n","- GPU support for very fast simulation."]},{"cell_type":"markdown","metadata":{},"source":["## `ProjectQBackend`"]},{"cell_type":"markdown","metadata":{},"source":["ProjectQ is a popular quantum circuit simulator, thanks to its availability and ease of use. It provides a similar level of performance and features to `AerStateBackend`.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2} From abcfe33bd15e18e9ea0f79eb28543d1e48afbf5b Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:23:35 +0100 Subject: [PATCH 71/76] don't excute flaky qujax example --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 6696f29b..90dd8ccc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,6 +50,7 @@ "examples/backends/comparing_simulators.ipynb", "examples/algorithms_and_protocols/expectation_value_example.ipynb", "examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb", + "examples/algorithms_and_protocols/pytket-qujax-classification.ipynb", "examples/algorithms_and_protocols/spam_example.ipynb", "examples/algorithms_and_protocols/entanglement_swapping.ipynb", ] From 4579038d515457b264e6fcd6c4ebf528692bb596 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 5 Aug 2024 13:26:09 +0100 Subject: [PATCH 72/76] don't execute qujax qaoa --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 90dd8ccc..44601413 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,6 +51,7 @@ "examples/algorithms_and_protocols/expectation_value_example.ipynb", "examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb", "examples/algorithms_and_protocols/pytket-qujax-classification.ipynb", + "examples/algorithms_and_protocols/pytket-qujax_qaoa.ipynb", "examples/algorithms_and_protocols/spam_example.ipynb", "examples/algorithms_and_protocols/entanglement_swapping.ipynb", ] From 141c4ae199fae6d4b8b4cc3a9788727599365f2b Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 5 Aug 2024 13:38:38 +0100 Subject: [PATCH 73/76] use latest pytket v1.31.0 --- poetry.lock | 46 +++++++++++++++++----------------------------- pyproject.toml | 1 - 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/poetry.lock b/poetry.lock index cbae9d23..5cd2ac61 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -3002,26 +3002,26 @@ six = ">=1.5" [[package]] name = "pytket" -version = "1.30.0" +version = "1.31.0" description = "Quantum computing toolkit and interface to the TKET compiler" optional = false python-versions = ">=3.10" files = [ - {file = "pytket-1.30.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d111e978f027c65363d019408d76f69ea584e8fc878d2302d5f867091e769327"}, - {file = "pytket-1.30.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:85d99f81b955573c13fe1e166400e9553549f6a38151a3097c4143d9883f9d5c"}, - {file = "pytket-1.30.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19bb8268dc7d28bc33482750330549f7f7ebe546abcfc22170fa6f299241d918"}, - {file = "pytket-1.30.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce598114ff9674503b61a15b2dd42dd74068ed74e662492256ad78a425f9a569"}, - {file = "pytket-1.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:977b17c573ab5f3f0771448e42415f16595331699d63eeabaf17b72e6ddc06c6"}, - {file = "pytket-1.30.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:87cc5f77f189f39f354f2df31061e1f58b949a514c6c90bad80044f4c0e37410"}, - {file = "pytket-1.30.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:c78076b66d4b8102ac7538a227415f75639b0445e629de82a5d451b52658ef6d"}, - {file = "pytket-1.30.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f845ab325f3747776a080a1addc3236c00f6099c1d0de1b0f21af98e4d059aed"}, - {file = "pytket-1.30.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:06d308ef53121afc89f7111748f2776bd6161e1a4f7b3febea9949d058f4e5da"}, - {file = "pytket-1.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:4267c45e8d21655d47dbf2e85f56bde03ff00e402bb97a356e208073cbc5b929"}, - {file = "pytket-1.30.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:4010feed278ec363c1a06d462c3e547fc991de85954af9a083ca0b925bc48054"}, - {file = "pytket-1.30.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:2eeff1016a667a69997af2904ae35c74a6954dea6b0e840f2087e806e3fd9801"}, - {file = "pytket-1.30.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97cdb52d1fb440da2452c8a295aa67d44e5e70e2ed9113d48f34b3c948dbcc69"}, - {file = "pytket-1.30.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eebdb1879bc8fd57d2267b704902d37ba7403119e3aa80cbe2e99897f8a91576"}, - {file = "pytket-1.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:95bc56d7e8570af43df2b86ac7501623dba9dbd725df353de02daef3c7d29f8d"}, + {file = "pytket-1.31.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:6a1981f2e8fcfa4bfa2bf7c2732f7062646415d2846a717d9542f44fb2c506a7"}, + {file = "pytket-1.31.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:625fa01d930cafb99538fe9d6134039d25feb27bba528f32a78e1679d4103d0f"}, + {file = "pytket-1.31.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33762b67a39fe0052afa39a21e6b0f9c045d7c5639a7571ece4cf1bcb0b68e3f"}, + {file = "pytket-1.31.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9878468c7fda0eb8ca3f0b9c349f811a1e7a7c896463892bfea9b66f88c99447"}, + {file = "pytket-1.31.0-cp310-cp310-win_amd64.whl", hash = "sha256:8cc42819d6ca0e4c2587235fc78ec6ae2b75610190f0a0ab369911043e6a7dc0"}, + {file = "pytket-1.31.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:be5fab98fcc14860ee4effe0a7657b39c2487a29c048e0ab228b7be3ec80e049"}, + {file = "pytket-1.31.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:258ee145704e040d352c164e11d794bf612d26cc37c1f585a5453397a86f7113"}, + {file = "pytket-1.31.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a2f7ff90a2d73447d1e45c2f56ce08116634a4c2986312fc88a7903a328b4b5"}, + {file = "pytket-1.31.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5069fd53997ae93826df24ff7e2b5309d4d0ae645b6c10d3ac62725688b2556f"}, + {file = "pytket-1.31.0-cp311-cp311-win_amd64.whl", hash = "sha256:e4e361269f611869495ac34aaa68a07994f76bd35d21962629d2aa54a62d5726"}, + {file = "pytket-1.31.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:848350011e8f04f59ec43deb79b1fd1ff00382f71fd208adfa972c0b74efa09d"}, + {file = "pytket-1.31.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:88957b28fec827a55eceac7d4c70e29ddf89531cb62c325585cab7600a4c55b9"}, + {file = "pytket-1.31.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f674664465c04336f7d85b6f0aafdd28f5437acd647c9083df1433a90d10a98d"}, + {file = "pytket-1.31.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fa61c7a629402fb260a71c6425466ad205ef148463959f1b29717fd66a79681"}, + {file = "pytket-1.31.0-cp312-cp312-win_amd64.whl", hash = "sha256:c414598ea4a66a8075943cdee067911af597ca9dc0a22e9ffa4596c8e995abe2"}, ] [package.dependencies] @@ -3036,7 +3036,6 @@ quimb = {version = ">=1.8.2", optional = true, markers = "extra == \"zx\""} qwasm = ">=1.0.1" scipy = ">=1.13.1" sympy = ">=1.12.1" -types-pkg-resources = ">=0.1.3" typing-extensions = ">=4.12.2" [package.extras] @@ -4297,17 +4296,6 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] -[[package]] -name = "types-pkg-resources" -version = "0.1.3" -description = "Typing stubs for pkg_resources" -optional = false -python-versions = "*" -files = [ - {file = "types-pkg_resources-0.1.3.tar.gz", hash = "sha256:834a9b8d3dbea343562fd99d5d3359a726f6bf9d3733bccd2b4f3096fbab9dae"}, - {file = "types_pkg_resources-0.1.3-py2.py3-none-any.whl", hash = "sha256:0cb9972cee992249f93fff1a491bf2dc3ce674e5a1926e27d4f0866f7d9b6d9c"}, -] - [[package]] name = "typing-extensions" version = "4.12.2" diff --git a/pyproject.toml b/pyproject.toml index 5cc1ca69..64f87e37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,5 @@ [tool.poetry] name = "pytket-docs" -package-mode = false version = "0.1.0" description = "" authors = ["CalMacCQ <93673602+CalMacCQ@users.noreply.github.com>"] From 9f2fb020063f02ee4a3cb9a01711b3a749bbb9e6 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 5 Aug 2024 13:43:32 +0100 Subject: [PATCH 74/76] set package mode to false --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 64f87e37..5cc1ca69 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,6 @@ [tool.poetry] name = "pytket-docs" +package-mode = false version = "0.1.0" description = "" authors = ["CalMacCQ <93673602+CalMacCQ@users.noreply.github.com>"] From b141e148e547525d861e72750a2d8e9aea82931c Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 5 Aug 2024 13:50:02 +0100 Subject: [PATCH 75/76] don't execute big vqe notebook --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 44601413..df8345ac 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -52,6 +52,7 @@ "examples/algorithms_and_protocols/pytket-qujax_heisenberg_vqe.ipynb", "examples/algorithms_and_protocols/pytket-qujax-classification.ipynb", "examples/algorithms_and_protocols/pytket-qujax_qaoa.ipynb", + "examples/algorithms_and_protocols/ucc_vqe.ipynb", "examples/algorithms_and_protocols/spam_example.ipynb", "examples/algorithms_and_protocols/entanglement_swapping.ipynb", ] From 1d9c80770a61a28f4a4062d2f8368854be858c7d Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:41:20 +0100 Subject: [PATCH 76/76] delete dummy directories with redirects --- examples/index.html | 20 -------------------- user-manual/index.html | 20 -------------------- 2 files changed, 40 deletions(-) delete mode 100644 examples/index.html delete mode 100644 user-manual/index.html diff --git a/examples/index.html b/examples/index.html deleted file mode 100644 index 62e7e173..00000000 --- a/examples/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - TKET docs have moved to tket.quantinuum.com - - - -

- If you are not redirected automatically, follow - this link to the user guide on tket.quantinuum.com. -

- - - \ No newline at end of file diff --git a/user-manual/index.html b/user-manual/index.html deleted file mode 100644 index 62e7e173..00000000 --- a/user-manual/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - TKET docs have moved to tket.quantinuum.com - - - -

- If you are not redirected automatically, follow - this link to the user guide on tket.quantinuum.com. -

- - - \ No newline at end of file