From 0bae034326b43bcafb5de26f00af43c4f9f5a1bf Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Wed, 8 Aug 2018 20:37:25 +0800 Subject: [PATCH 01/85] initial empty commit From 3b4b7ba0054640e2ae6faeca8d5ea400120389ab Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Wed, 8 Aug 2018 20:37:25 +0800 Subject: [PATCH 02/85] QuAlgorithmZoo.jl generated files. license: ASL authors: Roger-luo years: 2018 user: Julia Version 0.6.3 [d55cadc350] --- .codecov.yml | 1 + .gitignore | 3 + .travis.yml | 35 +++++++ LICENSE.md | 206 ++++++++++++++++++++++++++++++++++++++++++ README.md | 1 + REQUIRE | 1 + appveyor.yml | 47 ++++++++++ src/QuAlgorithmZoo.jl | 5 + test/runtests.jl | 9 ++ 9 files changed, 308 insertions(+) create mode 100644 .codecov.yml create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 REQUIRE create mode 100644 appveyor.yml create mode 100644 src/QuAlgorithmZoo.jl create mode 100644 test/runtests.jl diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 000000000..69cb76019 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1 @@ +comment: false diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8c960ec80 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.jl.cov +*.jl.*.cov +*.jl.mem diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..28a460fc1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,35 @@ +## Documentation: http://docs.travis-ci.com/user/languages/julia/ +language: julia +os: + - linux + - osx +julia: + - 0.6 + - nightly +notifications: + email: false +git: + depth: 99999999 + +## uncomment the following lines to allow failures on nightly julia +## (tests will run but not make your overall status red) +#matrix: +# allow_failures: +# - julia: nightly + +## uncomment and modify the following lines to manually install system packages +#addons: +# apt: # apt-get for linux +# packages: +# - gfortran +#before_script: # homebrew for mac +# - if [ $TRAVIS_OS_NAME = osx ]; then brew install gcc; fi + +## uncomment the following lines to override the default test script +#script: +# - julia -e 'Pkg.clone(pwd()); Pkg.build("QuAlgorithmZoo"); Pkg.test("QuAlgorithmZoo"; coverage=true)' +after_success: + # push coverage results to Coveralls + - julia -e 'cd(Pkg.dir("QuAlgorithmZoo")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())' + # push coverage results to Codecov + - julia -e 'cd(Pkg.dir("QuAlgorithmZoo")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..a0b4807ab --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,206 @@ +The QuAlgorithmZoo.jl package is licensed under the Apache License, Version 2.0: + +> Copyright (c) 2018: Roger-luo. +> +> Apache License +> Version 2.0, January 2004 +> http://www.apache.org/licenses/ +> +> TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +> +> 1. Definitions. +> +> "License" shall mean the terms and conditions for use, reproduction, +> and distribution as defined by Sections 1 through 9 of this document. +> +> "Licensor" shall mean the copyright owner or entity authorized by +> the copyright owner that is granting the License. +> +> "Legal Entity" shall mean the union of the acting entity and all +> other entities that control, are controlled by, or are under common +> control with that entity. For the purposes of this definition, +> "control" means (i) the power, direct or indirect, to cause the +> direction or management of such entity, whether by contract or +> otherwise, or (ii) ownership of fifty percent (50%) or more of the +> outstanding shares, or (iii) beneficial ownership of such entity. +> +> "You" (or "Your") shall mean an individual or Legal Entity +> exercising permissions granted by this License. +> +> "Source" form shall mean the preferred form for making modifications, +> including but not limited to software source code, documentation +> source, and configuration files. +> +> "Object" form shall mean any form resulting from mechanical +> transformation or translation of a Source form, including but +> not limited to compiled object code, generated documentation, +> and conversions to other media types. +> +> "Work" shall mean the work of authorship, whether in Source or +> Object form, made available under the License, as indicated by a +> copyright notice that is included in or attached to the work +> (an example is provided in the Appendix below). +> +> "Derivative Works" shall mean any work, whether in Source or Object +> form, that is based on (or derived from) the Work and for which the +> editorial revisions, annotations, elaborations, or other modifications +> represent, as a whole, an original work of authorship. For the purposes +> of this License, Derivative Works shall not include works that remain +> separable from, or merely link (or bind by name) to the interfaces of, +> the Work and Derivative Works thereof. +> +> "Contribution" shall mean any work of authorship, including +> the original version of the Work and any modifications or additions +> to that Work or Derivative Works thereof, that is intentionally +> submitted to Licensor for inclusion in the Work by the copyright owner +> or by an individual or Legal Entity authorized to submit on behalf of +> the copyright owner. For the purposes of this definition, "submitted" +> means any form of electronic, verbal, or written communication sent +> to the Licensor or its representatives, including but not limited to +> communication on electronic mailing lists, source code control systems, +> and issue tracking systems that are managed by, or on behalf of, the +> Licensor for the purpose of discussing and improving the Work, but +> excluding communication that is conspicuously marked or otherwise +> designated in writing by the copyright owner as "Not a Contribution." +> +> "Contributor" shall mean Licensor and any individual or Legal Entity +> on behalf of whom a Contribution has been received by Licensor and +> subsequently incorporated within the Work. +> +> 2. Grant of Copyright License. Subject to the terms and conditions of +> this License, each Contributor hereby grants to You a perpetual, +> worldwide, non-exclusive, no-charge, royalty-free, irrevocable +> copyright license to reproduce, prepare Derivative Works of, +> publicly display, publicly perform, sublicense, and distribute the +> Work and such Derivative Works in Source or Object form. +> +> 3. Grant of Patent License. Subject to the terms and conditions of +> this License, each Contributor hereby grants to You a perpetual, +> worldwide, non-exclusive, no-charge, royalty-free, irrevocable +> (except as stated in this section) patent license to make, have made, +> use, offer to sell, sell, import, and otherwise transfer the Work, +> where such license applies only to those patent claims licensable +> by such Contributor that are necessarily infringed by their +> Contribution(s) alone or by combination of their Contribution(s) +> with the Work to which such Contribution(s) was submitted. If You +> institute patent litigation against any entity (including a +> cross-claim or counterclaim in a lawsuit) alleging that the Work +> or a Contribution incorporated within the Work constitutes direct +> or contributory patent infringement, then any patent licenses +> granted to You under this License for that Work shall terminate +> as of the date such litigation is filed. +> +> 4. Redistribution. You may reproduce and distribute copies of the +> Work or Derivative Works thereof in any medium, with or without +> modifications, and in Source or Object form, provided that You +> meet the following conditions: +> +> (a) You must give any other recipients of the Work or +> Derivative Works a copy of this License; and +> +> (b) You must cause any modified files to carry prominent notices +> stating that You changed the files; and +> +> (c) You must retain, in the Source form of any Derivative Works +> that You distribute, all copyright, patent, trademark, and +> attribution notices from the Source form of the Work, +> excluding those notices that do not pertain to any part of +> the Derivative Works; and +> +> (d) If the Work includes a "NOTICE" text file as part of its +> distribution, then any Derivative Works that You distribute must +> include a readable copy of the attribution notices contained +> within such NOTICE file, excluding those notices that do not +> pertain to any part of the Derivative Works, in at least one +> of the following places: within a NOTICE text file distributed +> as part of the Derivative Works; within the Source form or +> documentation, if provided along with the Derivative Works; or, +> within a display generated by the Derivative Works, if and +> wherever such third-party notices normally appear. The contents +> of the NOTICE file are for informational purposes only and +> do not modify the License. You may add Your own attribution +> notices within Derivative Works that You distribute, alongside +> or as an addendum to the NOTICE text from the Work, provided +> that such additional attribution notices cannot be construed +> as modifying the License. +> +> You may add Your own copyright statement to Your modifications and +> may provide additional or different license terms and conditions +> for use, reproduction, or distribution of Your modifications, or +> for any such Derivative Works as a whole, provided Your use, +> reproduction, and distribution of the Work otherwise complies with +> the conditions stated in this License. +> +> 5. Submission of Contributions. Unless You explicitly state otherwise, +> any Contribution intentionally submitted for inclusion in the Work +> by You to the Licensor shall be under the terms and conditions of +> this License, without any additional terms or conditions. +> Notwithstanding the above, nothing herein shall supersede or modify +> the terms of any separate license agreement you may have executed +> with Licensor regarding such Contributions. +> +> 6. Trademarks. This License does not grant permission to use the trade +> names, trademarks, service marks, or product names of the Licensor, +> except as required for reasonable and customary use in describing the +> origin of the Work and reproducing the content of the NOTICE file. +> +> 7. Disclaimer of Warranty. Unless required by applicable law or +> agreed to in writing, Licensor provides the Work (and each +> Contributor provides its Contributions) on an "AS IS" BASIS, +> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +> implied, including, without limitation, any warranties or conditions +> of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +> PARTICULAR PURPOSE. You are solely responsible for determining the +> appropriateness of using or redistributing the Work and assume any +> risks associated with Your exercise of permissions under this License. +> +> 8. Limitation of Liability. In no event and under no legal theory, +> whether in tort (including negligence), contract, or otherwise, +> unless required by applicable law (such as deliberate and grossly +> negligent acts) or agreed to in writing, shall any Contributor be +> liable to You for damages, including any direct, indirect, special, +> incidental, or consequential damages of any character arising as a +> result of this License or out of the use or inability to use the +> Work (including but not limited to damages for loss of goodwill, +> work stoppage, computer failure or malfunction, or any and all +> other commercial damages or losses), even if such Contributor +> has been advised of the possibility of such damages. +> +> 9. Accepting Warranty or Additional Liability. While redistributing +> the Work or Derivative Works thereof, You may choose to offer, +> and charge a fee for, acceptance of support, warranty, indemnity, +> or other liability obligations and/or rights consistent with this +> License. However, in accepting such obligations, You may act only +> on Your own behalf and on Your sole responsibility, not on behalf +> of any other Contributor, and only if You agree to indemnify, +> defend, and hold each Contributor harmless for any liability +> incurred by, or claims asserted against, such Contributor by reason +> of your accepting any such warranty or additional liability. +> +> END OF TERMS AND CONDITIONS +> +> APPENDIX: How to apply the Apache License to your work. +> +> To apply the Apache License to your work, attach the following +> boilerplate notice, with the fields enclosed by brackets "{}" +> replaced with your own identifying information. (Don't include +> the brackets!) The text should be enclosed in the appropriate +> comment syntax for the file format. We also recommend that a +> file or class name and description of purpose be included on the +> same "printed page" as the copyright notice for easier +> identification within third-party archives. +> +> Copyright [year] [fullname] +> +> Licensed under the Apache License, Version 2.0 (the "License"); +> you may not use this file except in compliance with the License. +> You may obtain a copy of the License at +> +> http://www.apache.org/licenses/LICENSE-2.0 +> +> Unless required by applicable law or agreed to in writing, software +> distributed under the License is distributed on an "AS IS" BASIS, +> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +> See the License for the specific language governing permissions and +> limitations under the License. +> diff --git a/README.md b/README.md new file mode 100644 index 000000000..dd94c5de2 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# QuAlgorithmZoo diff --git a/REQUIRE b/REQUIRE new file mode 100644 index 000000000..137767a42 --- /dev/null +++ b/REQUIRE @@ -0,0 +1 @@ +julia 0.6 diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..f5f0921bb --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,47 @@ +environment: + matrix: + - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe" + - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe" + - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" + - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" + +## uncomment the following lines to allow failures on nightly julia +## (tests will run but not make your overall status red) +#matrix: +# allow_failures: +# - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" +# - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" + +branches: + only: + - master + - /release-.*/ + +notifications: + - provider: Email + on_build_success: false + on_build_failure: false + on_build_status_changed: false + +install: + - ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" +# If there's a newer build queued for the same PR, cancel this one + - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` + https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` + Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` + throw "There are newer queued builds for this pull request, failing early." } +# Download most recent Julia Windows binary + - ps: (new-object net.webclient).DownloadFile( + $env:JULIA_URL, + "C:\projects\julia-binary.exe") +# Run installer silently, output to C:\projects\julia + - C:\projects\julia-binary.exe /S /D=C:\projects\julia + +build_script: +# Need to convert from shallow to complete for Pkg.clone to work + - IF EXIST .git\shallow (git fetch --unshallow) + - C:\projects\julia\bin\julia -e "versioninfo(); + Pkg.clone(pwd(), \"QuAlgorithmZoo\"); Pkg.build(\"QuAlgorithmZoo\")" + +test_script: + - C:\projects\julia\bin\julia -e "Pkg.test(\"QuAlgorithmZoo\")" diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl new file mode 100644 index 000000000..d9563bec1 --- /dev/null +++ b/src/QuAlgorithmZoo.jl @@ -0,0 +1,5 @@ +module QuAlgorithmZoo + +# package code goes here + +end # module diff --git a/test/runtests.jl b/test/runtests.jl new file mode 100644 index 000000000..c4241aae3 --- /dev/null +++ b/test/runtests.jl @@ -0,0 +1,9 @@ +using QuAlgorithmZoo +@static if VERSION < v"0.7.0-DEV.2005" + using Base.Test +else + using Test +end + +# write your own tests here +@test 1 == 2 From e4cde2d2d9827391f1785418be65f3b874dec972 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Wed, 8 Aug 2018 21:00:08 +0800 Subject: [PATCH 03/85] copy from Yao --- Manifest.toml | 194 ++++++++++++++++++++++++++++++++++++++++++ Project.toml | 8 ++ README.md | 2 + REQUIRE | 1 - appveyor.yml | 4 +- src/Differential.jl | 95 +++++++++++++++++++++ src/Grover.jl | 86 +++++++++++++++++++ src/QFT.jl | 54 ++++++++++++ src/QuAlgorithmZoo.jl | 22 ++++- src/RotBasis.jl | 69 +++++++++++++++ test/Differential.jl | 21 +++++ test/Grover.jl | 71 ++++++++++++++++ test/QFT.jl | 47 ++++++++++ test/RotBasis.jl | 34 ++++++++ test/runtests.jl | 25 ++++-- 15 files changed, 723 insertions(+), 10 deletions(-) create mode 100644 Manifest.toml create mode 100644 Project.toml delete mode 100644 REQUIRE create mode 100644 src/Differential.jl create mode 100644 src/Grover.jl create mode 100644 src/QFT.jl create mode 100644 src/RotBasis.jl create mode 100644 test/Differential.jl create mode 100644 test/Grover.jl create mode 100644 test/QFT.jl create mode 100644 test/RotBasis.jl diff --git a/Manifest.toml b/Manifest.toml new file mode 100644 index 000000000..c92695fb1 --- /dev/null +++ b/Manifest.toml @@ -0,0 +1,194 @@ +[[AbstractFFTs]] +deps = ["Compat", "LinearAlgebra"] +git-tree-sha1 = "8d59c3b1463b5e0ad05a3698167f85fac90e184d" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "0.3.2" + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[BinaryProvider]] +deps = ["Libdl", "Pkg", "SHA", "Test"] +git-tree-sha1 = "2883bc1b389e3d57a134796c39c788db3f298cef" +uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" +version = "0.4.0" + +[[CacheServers]] +deps = ["Compat"] +git-tree-sha1 = "320b889acf772213547f3c80db5b351ca2e5efa5" +uuid = "a921213e-d44a-5460-ac04-5d720a99ba71" +version = "0.1.0" + +[[Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "277d3807440d9793421354b6680911fc95d91a84" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "1.0.1" + +[[Conda]] +deps = ["Compat", "JSON", "VersionParsing"] +git-tree-sha1 = "b76a441539d66a18ba34df28d34dc5b9202164a3" +uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d" +version = "1.0.0" + +[[DataStructures]] +deps = ["InteractiveUtils", "REPL", "Random", "Serialization", "Test"] +git-tree-sha1 = "6e72a9098f5774601c8c8d6a4511a68270594910" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.11.0" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["LinearAlgebra", "Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[FFTW]] +deps = ["AbstractFFTs", "BinaryProvider", "Compat", "Conda", "Libdl", "LinearAlgebra", "Reexport", "Test"] +git-tree-sha1 = "60346335ad3aa60bfbdc358c6d1468bffbcff071" +uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +version = "0.2.3" + +[[InteractiveUtils]] +deps = ["LinearAlgebra", "Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[JSON]] +deps = ["Dates", "Distributed", "Mmap", "Pkg", "Sockets", "Test", "Unicode"] +git-tree-sha1 = "fec8e4d433072731466d37ed0061b3ba7f70eeb9" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.19.0" + +[[Lazy]] +deps = ["Compat", "MacroTools"] +git-tree-sha1 = "1dea2ef59fab543a5b1ce066b678dcdd70d21abf" +uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" +version = "0.12.1" + +[[LibGit2]] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[LuxurySparse]] +deps = ["Compat", "LinearAlgebra", "Random", "SparseArrays", "StaticArrays"] +path = "../LuxurySparse" +uuid = "72cad168-9621-11e8-1b1e-73228c0c6ca7" +version = "0.1.0" + +[[MacroTools]] +deps = ["Compat"] +git-tree-sha1 = "c443e1c8d58a4e9f61b708ad0a88286c7042145b" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.4.4" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[Missings]] +deps = ["Compat"] +git-tree-sha1 = "866fb034899be6055dcb6cb1a70fbda379bb0b55" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "0.2.10" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[Pkg]] +deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[Reexport]] +deps = ["Pkg"] +git-tree-sha1 = "7b1d07f411bc8ddb7977ec7f377b97b158514fe0" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "0.2.0" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SortingAlgorithms]] +deps = ["DataStructures", "Random", "Test"] +git-tree-sha1 = "03f5898c9959f8115e30bc7226ada7d0df554ddd" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "0.3.1" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[StaticArrays]] +deps = ["InteractiveUtils", "LinearAlgebra", "Random", "Statistics", "Test"] +git-tree-sha1 = "d998a83bc2cd507e12b08203e7523f2e60ac9ee6" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "0.8.2" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[StatsBase]] +deps = ["DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "Test"] +git-tree-sha1 = "723193a13e8078cec6dcd0b8fe245c8bfd81690e" +uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +version = "0.25.0" + +[[Test]] +deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[UUIDs]] +deps = ["Random"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[VersionParsing]] +deps = ["Compat"] +git-tree-sha1 = "e8524bb636735227fd9d750dfa81f98db8bc5b0c" +uuid = "81def892-9a0e-5fdd-b105-ffc91e053289" +version = "1.1.1" + +[[Yao]] +deps = ["CacheServers", "DataStructures", "FFTW", "Lazy", "LinearAlgebra", "LuxurySparse", "MacroTools", "Random", "Reexport", "SparseArrays", "StaticArrays", "StatsBase"] +path = "../Yao" +uuid = "1bc31baa-972f-11e8-31dd-c182aaa973df" +version = "0.1.3" diff --git a/Project.toml b/Project.toml new file mode 100644 index 000000000..a25ffd655 --- /dev/null +++ b/Project.toml @@ -0,0 +1,8 @@ +name = "QuAlgorithmZoo" +uuid = "65c24e16-9b0a-11e8-1353-efc5bc5f6586" +authors = ["Roger-luo "] +version = "0.1.0" + +[deps] +LuxurySparse = "72cad168-9621-11e8-1b1e-73228c0c6ca7" +Yao = "1bc31baa-972f-11e8-31dd-c182aaa973df" diff --git a/README.md b/README.md index dd94c5de2..6a68a5dc9 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ # QuAlgorithmZoo + +A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) diff --git a/REQUIRE b/REQUIRE deleted file mode 100644 index 137767a42..000000000 --- a/REQUIRE +++ /dev/null @@ -1 +0,0 @@ -julia 0.6 diff --git a/appveyor.yml b/appveyor.yml index f5f0921bb..f5814299d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ environment: matrix: - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe" - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe" + - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.7/julia-0.7-latest-win32.exe" + - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.7/julia-0.7-latest-win64.exe" - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" diff --git a/src/Differential.jl b/src/Differential.jl new file mode 100644 index 000000000..0da286307 --- /dev/null +++ b/src/Differential.jl @@ -0,0 +1,95 @@ +export diff_circuit, num_gradient, rotter, cnot_entangler, opgrad, collect_rotblocks, perturb + +""" + rotter(noleading::Bool=false, notrailing::Bool=false) -> ChainBlock{1, ComplexF64} + +Arbitrary rotation unit, set parameters notrailing, noleading true to remove trailing and leading Z gates. +""" +rotter(noleading::Bool=false, notrailing::Bool=false) = noleading ? (notrailing ? Rx(0) : chain(Rx(0), Rz(0))) : (notrailing ? chain(Rz(0), Rx(0)) : chain(Rz(0), Rz(0), Rz(0))) + +""" + cnot_entangler([n::Int, ] pairs::Vector{Pair}) = ChainBlock + +Arbitrary rotation unit, support lazy construction. +""" +cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) +cnot_entangler(pairs) = n->cnot_entangler(n, pairs) + +""" + diff_circuit(n, nlayer, pairs) -> ChainBlock + +A kind of widely used differentiable quantum circuit, angles in the circuit is randomely initialized. + +ref: + 1. Kandala, A., Mezzacapo, A., Temme, K., Takita, M., Chow, J. M., & Gambetta, J. M. (2017). + Hardware-efficient Quantum Optimizer for Small Molecules and Quantum Magnets. Nature Publishing Group, 549(7671), 242–246. + https://doi.org/10.1038/nature23879. +""" +function diff_circuit(n, nlayer, pairs) + circuit = chain(n) + + for i = 1:(nlayer + 1) + if i!=1 push!(circuit, cnot_entangler(pairs) |> cache) end + push!(circuit, rollrepeat(n, rotter(i==1, i==nlayer+1))) + #for j = 1:n + # push!(circuit, put(n, j=>rotter(i==1, i==nlayer+1))) + #end + end + dispatch!(circuit, rand(nparameters(circuit))*2π) +end + +""" + collect_rotblocks(blk::AbstractBlock) -> Vector{RotationGate} + +filter out all rotation gates, which is differentiable. +""" +function collect_rotblocks(blk::AbstractBlock) + rots = blockfilter!(x->x isa RotationGate, Vector{RotationGate}([]), blk) + nparameters(blk)==length(rots) || warn("some parameters in this circuit are not differentiable!") + rots +end + +""" + perturb(func, gates::Vector{<:RotationGate}, diff::Real) -> Matrix + +perturb every rotation gates, and evaluate losses. +The i-th element of first column of resulting Matrix corresponds to Gi(θ+δ), and the second corresponds to Gi(θ-δ). +""" +function perturb(func, gates::Vector{<:RotationGate}, diff::Real) + ng = length(gates) + res = Matrix{Float64}(undef, ng, 2) + for i in 1:ng + gate = gates[i] + dispatch!(+, gate, diff) + res[i, 1] = func() + + dispatch!(+, gate, -2*diff) + res[i, 2] = func() + + dispatch!(+, gate, diff) # set back + end + res +end + +""" + num_gradient(lossfunc, rots::Vector{<:RotationGate}, δ::Float64=1e-2) -> Vector + +Compute gradient numerically. +""" +function num_gradient(lossfunc, rots::Vector{<:RotationGate}, δ::Float64=1e-2) + gperturb = perturb(lossfunc, rots, δ) + (gperturb[:,1] - gperturb[:,2])/(2δ) +end + +""" + opgrad(op_expect, rots::Vector{<:RotationGate}) -> Vector + +get the gradient of an operator expectation function. + +References: + Mitarai, K., Negoro, M., Kitagawa, M., & Fujii, K. (2018). Quantum Circuit Learning, 1–3. Retrieved from http://arxiv.org/abs/1803.00745 +""" +function opgrad(op_expect, rots::Vector{<:RotationGate}) + gperturb = perturb(op_expect, rots, π/2) + (gperturb[:,1] - gperturb[:,2])/2 +end diff --git a/src/Grover.jl b/src/Grover.jl new file mode 100644 index 000000000..3207680f0 --- /dev/null +++ b/src/Grover.jl @@ -0,0 +1,86 @@ +export num_grover_step, inference_oracle, GroverIter, groverblock, groveriter, prob_match_oracle + +""" + inference_oracle([nbit::Int,] locs::Vector{Int}) -> ControlBlock + +A simple inference oracle, e.g. inference([-1, -8, 5]) is a control block that flip the bit if values of bits on position [1, 8, 5] match [0, 0, 1]. +""" +inference_oracle(locs::Vector{Int}) = control(locs[1:end-1], abs(locs[end]) => (locs[end]>0 ? Z : chain(phase(π), Z))) +inference_oracle(nbit::Int, locs::Vector{Int}) = inference_oracle(locs)(nbit) + +""" + target_space(oracle) -> Vector{Bool} + +Return a mask, that disired subspace of an oracle are masked true. +""" +function target_space(nbit::Int, oracle) + r = register(ones(ComplexF64, 1< Float64 + +Return the probability that `psi` matches oracle. +""" +prob_match_oracle(psi::DefaultRegister, oracle) = prob_inspace(psi, target_space(nqubits(psi), oracle)) + +""" + num_grover_step(psi::DefaultRegister, oracle) -> Int + +Return number of grover steps needed to match the oracle. +""" +num_grover_step(psi::DefaultRegister, oracle) = _num_grover_step(prob_match_oracle(psi, oracle)) + +_num_grover_step(prob::Real) = Int(round(pi/4/sqrt(prob)))-1 + +""" + GroverIter{N, T} + + GroverIter(oracle, ref::ReflectBlock{N, T}, psi::DefaultRegister, niter::Int) + +an iterator that perform Grover operations step by step. +An Grover operation consists of applying oracle and Reflection. +""" +struct GroverIter{N, T} + psi::DefaultRegister + oracle + ref::ReflectBlock{N, T} + niter::Int +end + +groveriter(psi::DefaultRegister, oracle, ref::ReflectBlock{N, T}, niter::Int) where {N, T} = GroverIter{N, T}(psi, oracle, ref, niter) +groveriter(psi::DefaultRegister, oracle, niter::Int) = groveriter(psi, oracle, ReflectBlock(psi |> copy), niter) +groveriter(psi::DefaultRegister, oracle) = groveriter(psi, oracle, ReflectBlock(psi |> copy), num_grover_step(psi, oracle)) + +function Base.iterate(it::GroverIter, st=1) + if it.niter + 1 == st + nothing + else + apply!(it.psi, it.oracle) + apply!(it.psi, it.ref), st+1 + end +end + +Base.length(it::GroverIter) = it.niter + +""" + groverblock(oracle, ref::ReflectBlock{N, T}, niter::Int=-1) + groverblock(oracle, psi::DefaultRegister, niter::Int=-1) + +Return a ChainBlock/Sequential as Grover Iteration, the default `niter` will stop at the first optimal step. +""" +function groverblock(oracle::MatrixBlock{N, T}, ref::ReflectBlock{N, T}, niter::Int=-1) where {N, T} + if niter == -1 niter = num_grover_step(ref.psi, oracle) end + chain(N, chain(oracle, ref) for i = 1:niter) +end + +function groverblock(oracle, ref::ReflectBlock{N, T}, niter::Int=-1) where {N, T} + if niter == -1 niter = num_grover_step(ref.psi, oracle) end + sequence(sequence(oracle, ref) for i = 1:niter) +end + +groverblock(oracle, psi::DefaultRegister, niter::Int=-1) = groverblock(oracle, ReflectBlock(psi |> copy), niter) diff --git a/src/QFT.jl b/src/QFT.jl new file mode 100644 index 000000000..b05ebfd0e --- /dev/null +++ b/src/QFT.jl @@ -0,0 +1,54 @@ +@static if VERSION >= v"0.7-" + using FFTW +end + +export QFTCircuit, QFTBlock, invorder_firstdim + +CRk(i::Int, j::Int, k::Int) = control([i, ], j=>shift(2π/(1<H) : CRk(j, i, j-i+1) for j = i:n) +QFTCircuit(n::Int) = chain(n, CRot(n, i) for i = 1:n) + +struct QFTBlock{N} <: PrimitiveBlock{N,ComplexF64} end +mat(q::QFTBlock{N}) where N = applymatrix(q) + +apply!(reg::DefaultRegister{B}, ::QFTBlock) where B = (reg.state = ifft!(invorder_firstdim(reg |> state), 1)*sqrt(1<state, 1)/sqrt(1< log2i + n_2 = n ÷ 2 + mask = [bmask(i, n-i+1) for i in 1:n_2] + @simd for b in basis(n) + @inbounds w[breflect(n, b, mask)+1,:] = v[b+1,:] + end + w +end + +function invorder_firstdim(v::Vector) + n = length(v) |> log2i + n_2 = n ÷ 2 + w = similar(v) + #mask = SVector{n_2, Int}([bmask(i, n-i+1)::Int for i in 1:n_2]) + mask = [bmask(i, n-i+1)::Int for i in 1:n_2] + @simd for b in basis(n) + @inbounds w[breflect(n, b, mask)+1] = v[b+1] + end + w +end diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index d9563bec1..5bc86e505 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -1,5 +1,25 @@ module QuAlgorithmZoo -# package code goes here +using LuxurySparse, LinearAlgebra +using ..Yao +using ..Intrinsics +using ..Registers +using ..Blocks +import ..Blocks: mat, dispatch!, nparameters, parameters, cache_key, print_block, _make_rot_mat, apply! +import Base: ==, copy, hash +import ..Intrinsics: ishermitian, isreflexive, isunitary + +export openbox + +""" +For a black box, like QFTBlock, you can get its white box (loyal simulation) using this function. +""" +function openbox end + +include("QFT.jl") +include("Differential.jl") +include("RotBasis.jl") +include("Grover.jl") + end # module diff --git a/src/RotBasis.jl b/src/RotBasis.jl new file mode 100644 index 000000000..48f7b0df7 --- /dev/null +++ b/src/RotBasis.jl @@ -0,0 +1,69 @@ +export RotBasis, randpolar, polar2u, u2polar, rot_basis + +""" + RotBasis{T} <: PrimitiveBlock{1, Complex{T}} + +A special rotation block that transform basis to angle θ and ϕ in bloch sphere. +""" +mutable struct RotBasis{T} <: PrimitiveBlock{1, Complex{T}} + theta::T + phi::T +end + +# chain -> * +# mat(rb::RotBasis{T}) where T = mat(Ry(-rb.theta))*mat(Rz(-rb.phi)) +function mat(x::RotBasis{T}) where T + R1 = _make_rot_mat(IMatrix{2, Complex{T}}(), mat(Z), -x.phi) + R2 = _make_rot_mat(IMatrix{2, Complex{T}}(), mat(Y), -x.theta) + R2 * R1 +end + +==(rb1::RotBasis, rb2::RotBasis) = rb1.theta == rb2.theta && rb1.phi == rb2.phi + +copy(block::RotBasis{T}) where T = RotBasis{T}(block.theta, block.phi) +dispatch!(block::RotBasis, params) = ((block.theta, block.phi) = params; block) + +parameters(rb::RotBasis) = (rb.theta, rb.phi) +nparameters(::Type{<:RotBasis}) = 2 + +function print_block(io::IO, R::RotBasis) + print(io, "RotBasis($(R.theta), $(R.phi))") +end + +function hash(gate::RotBasis, h::UInt) + hash(hash(gate.theta, gate.phi, objectid(gate)), h) +end + +cache_key(gate::RotBasis) = (gate.theta, gate.phi) + +rot_basis(num_bit::Int) = dispatch!(chain(num_bit, put(i=>RotBasis(0.0, 0.0)) for i=1:num_bit), randpolar(num_bit) |> vec) + +""" + u2polar(vec::Array) -> Array + +transform su(2) state vector to polar angle, apply to the first dimension of size 2. +""" +function u2polar(vec::Vector) + ratio = vec[2]/vec[1] + [atan(abs(ratio))*2, angle(ratio)] +end + +""" + polar2u(vec::Array) -> Array + +transform polar angle to su(2) state vector, apply to the first dimension of size 2. +""" +function polar2u(polar::Vector) + theta, phi = polar + [cos(theta/2)*exp(-im*phi/2), sin(theta/2)*exp(im*phi/2)] +end + +u2polar(arr::Array) = mapslices(u2polar, arr, dims=[1]) +polar2u(arr::Array) = mapslices(polar2u, arr, dims=[1]) + +""" + randpolar(params::Int...) -> Array + +random polar basis, number of basis +""" +randpolar(params::Int...) = rand(2, params...)*pi diff --git a/test/Differential.jl b/test/Differential.jl new file mode 100644 index 000000000..cb130b4da --- /dev/null +++ b/test/Differential.jl @@ -0,0 +1,21 @@ +using Test, Random, LinearAlgebra, SparseArrays + + +using QuAlgorithmZoo, Yao.Blocks + +@testset "rotter, collect_rotblocks, num_gradient, opgrad" begin + c = diff_circuit(4, 3, [1=>3, 2=>4, 2=>3, 4=>1]) + rots = collect_rotblocks(c) + @test length(rots) == nparameters(c) == 40 + + obs = kron(nqubits(c), 2=>X) + #@test mean(opgrad(()->expect(obs, apply!(zero_state(4), c))|>real, rots) .|> abs) > 3e-4 + gradA = opgrad(()->expect(obs, apply!(zero_state(4), c)) |> real, rots) + gradB = num_gradient(()->expect(obs, apply!(zero_state(4),c)) |> real, rots) + @test isapprox(gradA, gradB, atol=1e-4) + + @test rotter(true, true) == Rx(0) + @test rotter(false, false) == rotter() == chain(Rz(0), Rx(0), Rz(0)) + @test rotter(false, true) == chain(Rz(0), Rx(0)) + @test rotter(true, false) == chain(Rx(0), Rz(0)) +end diff --git a/test/Grover.jl b/test/Grover.jl new file mode 100644 index 000000000..586fb4e74 --- /dev/null +++ b/test/Grover.jl @@ -0,0 +1,71 @@ +using Test, Random, LinearAlgebra, SparseArrays + +using QuAlgorithmZoo +import QuAlgorithmZoo: _num_grover_step +using Yao.Blocks +using Yao.Intrinsics + +function GroverSearch(oracle, num_bit::Int; psi::DefaultRegister = uniform_state(num_bit)) + it = groveriter(psi, oracle) + for l_psi in it psi = l_psi end + return (it.niter, psi) +end + +function inference(psi::DefaultRegister, evidense::Vector{Int}, num_iter::Int) + oracle = inference_oracle(evidense)(nqubits(psi)) + it = groveriter(psi, oracle) + for l_psi in it psi = l_psi end + it.niter, psi +end + +@testset "oracle" begin + oracle = inference_oracle([2,-1,3])(3) + # ≈ method for Identity/PermuteMultiply/Sparse + # add and mimus between sparse matrices. + # alway use sorted CSC format. + v = ones(1<<3) + v[Int(0b110)+1] *= -1 + @test mat(oracle) ≈ Diagonal(v) + @test mat(chain(phase(π), Z)) ≈ -mat(Z) +end + +@testset "Grover Search" begin + ####### Construct Grover Search Using Reflection Block + num_bit = 12 + oracle = inference_oracle(push!(collect(Int, 1:num_bit-1), num_bit))(num_bit) + + niter, psi = GroverSearch(oracle, 12) + target_state = zeros(1<apply!(reg, or)) + gb = groverblock(or, psi) + gb2 = groverblock(func_or, psi) + @test apply!(copy(psi), gb) == (for l_psi in groveriter(copy(psi), func_or) psi = l_psi end; psi) + @test apply!(copy(psi), gb) == apply!(copy(psi), gb2) +end + +@testset "test inference" begin + num_bit = 12 + psi0 = rand_state(num_bit) + #psi0 = uniform_state(num_bit) + evidense = [1, 2, 3, 4, 5, 6, 7, 8, 9] + #evidense = collect(1:num_bit) + + # the desired subspace + basis = collect(UInt, 0:1<0)) + + v_desired = statevec(psi0)[subinds .+ 1] + p = norm(v_desired)^2 + v_desired[:] ./= sqrt(p) + + # search the subspace + num_iter = _num_grover_step(p) + niter, psi = inference(psi0, evidense, num_iter) + @test isapprox((psi.state[subinds .+ 1]'*v_desired) |> abs2, 1, atol=1e-2) +end diff --git a/test/QFT.jl b/test/QFT.jl new file mode 100644 index 000000000..4082ecd6e --- /dev/null +++ b/test/QFT.jl @@ -0,0 +1,47 @@ +using Test, Random, LinearAlgebra, SparseArrays, LuxurySparse + +using Yao +using QuAlgorithmZoo +using FFTW + +@testset "QFT" begin + num_bit = 5 + fftblock = QFTCircuit(num_bit) + ifftblock = adjoint(fftblock) + reg = rand_state(num_bit) + rv = copy(statevec(reg)) + + @test Matrix(mat(chain(3, QFTCircuit(3) |> adjoint, QFTCircuit(3)))) ≈ IMatrix(1<<3) + + # test ifft + reg1 = apply!(copy(reg), ifftblock) + + # permute lines (Manually) + kv = fft(statevec(reg))/sqrt(length(rv)) + @test statevec(reg1) ≈ invorder(kv) + + # test fft + reg2 = apply!(invorder!(copy(reg)), fftblock) + kv = ifft(rv) * sqrt(length(rv)) + @test statevec(reg2) ≈ kv +end + + +@testset "QFTBlock" begin + num_bit = 5 + qft = QFTCircuit(num_bit) + iqft = adjoint(qft) + qftblock = QFTBlock{num_bit}() + iqftblock = QFTBlock{num_bit}() |> adjoint + @test openbox(qftblock) == qft + @test openbox(iqftblock) == iqft + reg = rand_state(num_bit) + + @test Matrix(mat(chain(3, QFTBlock{3}() |> adjoint, QFTBlock{3}()))) ≈ IMatrix(1<<3) + + # permute lines (Manually) + @test apply!(copy(reg), iqft) ≈ apply!(copy(reg), QFTBlock{num_bit}() |> adjoint) + + # test fft + @test apply!(copy(reg), qft) ≈ apply!(copy(reg), qftblock) +end diff --git a/test/RotBasis.jl b/test/RotBasis.jl new file mode 100644 index 000000000..8b3cdbc41 --- /dev/null +++ b/test/RotBasis.jl @@ -0,0 +1,34 @@ +using Test, Random, LinearAlgebra, SparseArrays + +using Yao +using QuAlgorithmZoo + +@testset "RotBasis" begin + rt = RotBasis(0.5, 0.4) + crt = chain(rt) + + dispatch!(crt, [2., 3.]) + @test nparameters(crt) == 2 + + for (t1, t2, t3) in zip(parameters(rt), (2, 3), parameters(crt)) + @test t1 == t2 == t3 + end + + # check consistency + rb = roll(1, RotBasis(0.1, 0.3))#rot_basis(1) + angles = randpolar(1) + # prepair a state in the angles direction. + psi = angles |> polar2u |> register + + # rotate to the same direction for measurements. + dispatch!(rb, vec(angles)) + @test state(apply!(psi, rb)) ≈ [1, 0] + + @test nparameters(rot_basis(3)) == 6 +end + +@testset "polar and u" begin + polar = randpolar(10) + @test size(polar) == (2, 10) + @test polar |> polar2u |> u2polar ≈ polar +end diff --git a/test/runtests.jl b/test/runtests.jl index c4241aae3..ed04c17f0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,9 +1,22 @@ +using Test, Random, LinearAlgebra, SparseArrays + + +using Yao using QuAlgorithmZoo -@static if VERSION < v"0.7.0-DEV.2005" - using Base.Test -else - using Test + + +@testset "QFT" begin + include("QFT.jl") end -# write your own tests here -@test 1 == 2 +@testset "Differential" begin + include("Differential.jl") +end + +@testset "RotBasis" begin + include("RotBasis.jl") +end + +@testset "Grover" begin + include("Grover.jl") +end From 59269a07470262134e8d10446b6ff5a477b2d02c Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Wed, 8 Aug 2018 21:00:38 +0800 Subject: [PATCH 04/85] update ignore and travis --- .gitignore | 11 +++++++++++ .travis.yml | 10 +++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 8c960ec80..2068af813 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ *.jl.cov *.jl.*.cov *.jl.mem + +docs/build/ +docs/site/ + +*.ipynb_checkpoints +**/*.ipynb_checkpoints +**/**/*.ipynb_checkpoints + +_*.dat +*.swp +__pycache__/ diff --git a/.travis.yml b/.travis.yml index 28a460fc1..a6ce0b3f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: - linux - osx julia: - - 0.6 + - 0.7 - nightly notifications: email: false @@ -30,6 +30,10 @@ git: # - julia -e 'Pkg.clone(pwd()); Pkg.build("QuAlgorithmZoo"); Pkg.test("QuAlgorithmZoo"; coverage=true)' after_success: # push coverage results to Coveralls - - julia -e 'cd(Pkg.dir("QuAlgorithmZoo")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())' + - julia -e 'using Pkg; cd(Pkg.dir("QuAlgorithmZoo")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())' # push coverage results to Codecov - - julia -e 'cd(Pkg.dir("QuAlgorithmZoo")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' + - julia -e 'using Pkg; cd(Pkg.dir("QuAlgorithmZoo")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' + # add Documenter + # - julia -e 'using Pkg; Pkg.add("Documenter")' + # # push documents to gh-pages + # - julia -e 'using Pkg; cd(Pkg.dir("QuAlgorithmZoo")); include(joinpath("docs", "make.jl"))' From 512c3aae165f2d9b56c46f2b3cfcae7a59db7343 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Thu, 16 Aug 2018 21:38:18 +0800 Subject: [PATCH 05/85] update project --- Project.toml | 1 - examples/README.md | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 examples/README.md diff --git a/Project.toml b/Project.toml index a25ffd655..89ff8c936 100644 --- a/Project.toml +++ b/Project.toml @@ -4,5 +4,4 @@ authors = ["Roger-luo "] version = "0.1.0" [deps] -LuxurySparse = "72cad168-9621-11e8-1b1e-73228c0c6ca7" Yao = "1bc31baa-972f-11e8-31dd-c182aaa973df" diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..2a27f3403 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,20 @@ +# Quantum Algorithm Zoo + +This folder contains the examples of the quantum algorithms implemented in the Zoo +in jupyter notebook form. + +## Usage + +1. Install IJulia for jupyter notebook kernels + +```julia +pkg> add IJulia +``` + +If you have IJulia installed. + +```julia +julia> using IJulia + +julia> notebook() +``` From 5c6ae9e63044c79448d3432f13088e42dfe10f0e Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sat, 18 Aug 2018 20:55:54 +0800 Subject: [PATCH 06/85] add Literate --- .gitignore | 2 + Manifest.toml | 48 +++--- Project.toml | 2 + docs/README.md | 7 + docs/docs/Project.toml | 3 + docs/make.jl | 18 ++ examples/QCBM.jl | 370 +++++++++++++++++++++++++++++++++++++++++ examples/make.jl | 3 + 8 files changed, 430 insertions(+), 23 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/docs/Project.toml create mode 100644 docs/make.jl create mode 100644 examples/QCBM.jl create mode 100644 examples/make.jl diff --git a/.gitignore b/.gitignore index 2068af813..78fd49431 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ docs/site/ _*.dat *.swp __pycache__/ + +Manifest.toml diff --git a/Manifest.toml b/Manifest.toml index c92695fb1..86160e6cd 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -9,9 +9,9 @@ uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[BinaryProvider]] deps = ["Libdl", "Pkg", "SHA", "Test"] -git-tree-sha1 = "2883bc1b389e3d57a134796c39c788db3f298cef" +git-tree-sha1 = "ffbf89cc49b073cffdb91381b7ee60557a03ec96" uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" -version = "0.4.0" +version = "0.4.1" [[CacheServers]] deps = ["Compat"] @@ -27,9 +27,9 @@ version = "1.0.1" [[Conda]] deps = ["Compat", "JSON", "VersionParsing"] -git-tree-sha1 = "b76a441539d66a18ba34df28d34dc5b9202164a3" +git-tree-sha1 = "a47f9a2c7b80095e6a935536795635522fe27f5d" uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d" -version = "1.0.0" +version = "1.0.1" [[DataStructures]] deps = ["InteractiveUtils", "REPL", "Random", "Serialization", "Test"] @@ -50,10 +50,10 @@ deps = ["LinearAlgebra", "Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" [[FFTW]] -deps = ["AbstractFFTs", "BinaryProvider", "Compat", "Conda", "Libdl", "LinearAlgebra", "Reexport", "Test"] -git-tree-sha1 = "60346335ad3aa60bfbdc358c6d1468bffbcff071" +deps = ["AbstractFFTs", "BinaryProvider", "Compat", "Conda", "Libdl", "LinearAlgebra", "Pkg", "Reexport", "Test"] +git-tree-sha1 = "29cda58afbf62f35b1a094882ad6c745a47b2eaa" uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "0.2.3" +version = "0.2.4" [[InteractiveUtils]] deps = ["LinearAlgebra", "Markdown"] @@ -66,10 +66,10 @@ uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" version = "0.19.0" [[Lazy]] -deps = ["Compat", "MacroTools"] -git-tree-sha1 = "1dea2ef59fab543a5b1ce066b678dcdd70d21abf" +deps = ["Compat", "MacroTools", "Test"] +git-tree-sha1 = "6b8b57bed0c4fc6198d81301cce7b2f09ebd55af" uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" -version = "0.12.1" +version = "0.13.0" [[LibGit2]] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" @@ -85,10 +85,10 @@ uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" [[LuxurySparse]] -deps = ["Compat", "LinearAlgebra", "Random", "SparseArrays", "StaticArrays"] -path = "../LuxurySparse" -uuid = "72cad168-9621-11e8-1b1e-73228c0c6ca7" -version = "0.1.0" +deps = ["Compat", "LinearAlgebra", "Pkg", "Random", "SparseArrays", "StaticArrays", "Test"] +git-tree-sha1 = "e3fa3d15813a01de9f41e839f9c4acdb1dd310a2" +uuid = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" +version = "0.1.2" [[MacroTools]] deps = ["Compat"] @@ -101,10 +101,10 @@ deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[Missings]] -deps = ["Compat"] -git-tree-sha1 = "866fb034899be6055dcb6cb1a70fbda379bb0b55" +deps = ["Dates", "InteractiveUtils", "SparseArrays", "Test"] +git-tree-sha1 = "196528fa1df03df435025f52f9c1ff8356f94738" uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "0.2.10" +version = "0.3.0" [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" @@ -156,9 +156,9 @@ uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [[StaticArrays]] deps = ["InteractiveUtils", "LinearAlgebra", "Random", "Statistics", "Test"] -git-tree-sha1 = "d998a83bc2cd507e12b08203e7523f2e60ac9ee6" +git-tree-sha1 = "d432c79bef174a830304f8601427a4357dfdbfb7" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "0.8.2" +version = "0.8.3" [[Statistics]] deps = ["LinearAlgebra", "SparseArrays"] @@ -183,12 +183,14 @@ uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [[VersionParsing]] deps = ["Compat"] -git-tree-sha1 = "e8524bb636735227fd9d750dfa81f98db8bc5b0c" +git-tree-sha1 = "c9d5aa108588b978bd859554660c8a5c4f2f7669" uuid = "81def892-9a0e-5fdd-b105-ffc91e053289" -version = "1.1.1" +version = "1.1.2" [[Yao]] deps = ["CacheServers", "DataStructures", "FFTW", "Lazy", "LinearAlgebra", "LuxurySparse", "MacroTools", "Random", "Reexport", "SparseArrays", "StaticArrays", "StatsBase"] -path = "../Yao" +git-tree-sha1 = "cf978e1b0027fc3637dfc9aa257d0cec02b9e1af" +repo-rev = "8e117ded97d3a0d384c673b9d6ff62f424d97478" +repo-url = "https://github.com/QuantumBFS/Yao.jl.git" uuid = "1bc31baa-972f-11e8-31dd-c182aaa973df" -version = "0.1.3" +version = "0.2.0" diff --git a/Project.toml b/Project.toml index 89ff8c936..fed9027f6 100644 --- a/Project.toml +++ b/Project.toml @@ -4,4 +4,6 @@ authors = ["Roger-luo "] version = "0.1.0" [deps] +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" Yao = "1bc31baa-972f-11e8-31dd-c182aaa973df" diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..259000068 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,7 @@ +# QuAlgorithmZoo Documentation + +To build the documentation locally: + +```julia +julia make.jl local +``` diff --git a/docs/docs/Project.toml b/docs/docs/Project.toml new file mode 100644 index 000000000..cf645b524 --- /dev/null +++ b/docs/docs/Project.toml @@ -0,0 +1,3 @@ +[deps] +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 000000000..34cbdbc4a --- /dev/null +++ b/docs/make.jl @@ -0,0 +1,18 @@ +using Pkg + +pkg""" +add Documenter Literate +""" + +using Documenter, Literate, QuAlgorithmZoo + +const TUTORIAL = joinpath(@__DIR__, "src/tutorial") +const EXAMPLES = joinpath(@__DIR__, "..", "examples") + +for (root, dirs, files) in walkdir(EXAMPLES) + for file in files + println(joinpath(root, file)) + end +end + +# Literate.markdown(EXAMPLES, TUTORIAL) diff --git a/examples/QCBM.jl b/examples/QCBM.jl new file mode 100644 index 000000000..56f98a669 --- /dev/null +++ b/examples/QCBM.jl @@ -0,0 +1,370 @@ +# # Quantum Circuit Born Machine +# +# Quantum circuit born machine is a fresh approach to quantum machine learning. +# It use a parameterized quantum circuit to learning machine learning tasks with +# gradient based optimization. In this tutorial, we will show how to implement it +# with **Yao** (幺) framework. + +using Yao, Plots + +#---------------- + +@doc 幺 + +# ## Training target +# a gaussian distribution + +function gaussian_pdf(n, μ, σ) + x = collect(1:1<U for i = 1:n}) \iff kron(n, \text{i=>U for i=1:n}) \iff U \otimes \dots \otimes U +# ``` + +#---------------- + +@doc Val + +#---------------- + +roll(4, i=>X for i = 1:4) + +#---------------- + +rollrepeat(4, X) + +#---------------- + +kron(4, i=>X for i = 1:4) + +# However, `kron` is calculated differently comparing to `roll`. +# In principal, **Roller** will be able to calculate small blocks +# with same size with higher efficiency. But for large blocks **Roller** +# may be slower. In **幺**, we offer you this freedom to choose the most suitable solution. +# +# +# all factory methods will **lazy** evaluate **the first arguements**, +# which is the number of qubits. It will return a lambda function that +# requires a single interger input. The instance of desired block will +# only be constructed until all the information is filled. + +rollrepeat(X) + +#--------------- + +rollrepeat(X)(4) + +# When you filled all the information in somewhere of the declaration, +# 幺 will be able to infer the others. + +chain(4, rollrepeat(X), rollrepeat(Y)) + +#--------------- + +# We will now define the rest of rotation layers + +layer(::Val{:last}) = rollrepeat(chain(Rz(0), Rx(0))) +layer(::Val{:mid}) = rollrepeat(chain(Rz(0), Rx(0), Rz(0))) + +# ### CNOT Entangler + +# Another component of quantum circuit born machine is several **CNOT** +# operators applied on different qubits. + +entangler(pairs) = chain(control([ctrl, ], target=>X) for (ctrl, target) in pairs) + +# We can then define such a born machine + +function QCBM(n, nlayer, pairs) + circuit = chain(n) + push!(circuit, layer(:first)) + + for i = 1:(nlayer - 1) + push!(circuit, cache(entangler(pairs))) + push!(circuit, layer(:mid)) + end + + push!(circuit, cache(entangler(pairs))) + push!(circuit, layer(:last)) + + circuit +end + +# We use the method `cache` here to tag the entangler block that it should be +# cached after its first run, because it is actually a constant oracle. +# Let's see what will be constructed + +QCBM(4, 1, [1=>2, 2=>3, 3=>4, 4=>1]) + +# ## MMD Loss & Gradients +# +# The MMD loss is describe below: +# +# ```math +# \begin{aligned} +# \mathcal{L} &= \left| \sum_{x} p \theta(x) \phi(x) - \sum_{x} \pi(x) \phi(x) \right|^2\\ +# &= \langle K(x, y) \rangle_{x \sim p_{\theta}, y\sim p_{\theta}} - 2 \langle K(x, y) +# \rangle_{x\sim p_{\theta}, y\sim \pi} + \langle K(x, y) \rangle_{x\sim\pi, y\sim\pi} +# \end{aligned} +# ``` +# +# +# We will use a squared exponential kernel here. + +struct Kernel + sigma::Float64 + matrix::Matrix{Float64} +end + +function Kernel(nqubits, sigma) + basis = collect(0:(1<2, 3=>4, 5=>6, 2=>3, 4=>5, 6=>1]) + +# Now, we define its shorthand + +get_prob(qcbm) = apply!(register(bit"0"^nqubits(qcbm)), qcbm) |> statevec .|> abs2 + +# We will first iterate through each layer contains rotation gates and allocate +# an array to store our gradient + +function gradient(n, nlayers, qcbm, kernel, ptrain) + prob = get_prob(qcbm) + grad = zeros(real(datatype(qcbm)), nparameters(qcbm)) + idx = 0 + for ilayer = 1:2:(2 * nlayers + 1) + idx = grad_layer!(grad, idx, prob, qcbm, qcbm[ilayer], kernel, ptrain) + end + grad +end + +# Then we iterate through each rotation gate. + +function grad_layer!(grad, idx, prob, qcbm, layer, kernel, ptrain) + count = idx + for each_line in blocks(layer) + for each in blocks(each_line) + gradient!(grad, count+1, prob, qcbm, each, kernel, ptrain) + count += 1 + end + end + count +end + +# We update each parameter by rotate it $-\pi/2$ and $\pi/2$ + +function gradient!(grad, idx, prob, qcbm, gate, kernel, ptrain) + dispatch!(+, gate, pi / 2) + prob_pos = get_prob(qcbm) + + dispatch!(-, gate, pi) + prob_neg = get_prob(qcbm) + + dispatch!(+, gate, pi / 2) # set back + + grad_pos = expect(kernel, prob, prob_pos) - expect(kernel, prob, prob_neg) + grad_neg = expect(kernel, ptrain, prob_pos) - expect(kernel, ptrain, prob_neg) + grad[idx] = grad_pos - grad_neg + grad +end + +# ## Optimizer +# +# We will use the Adam optimizer. Since we don't want you to install another package for this, +# the following code for this optimizer is copied from [Knet.jl](https://github.com/denizyuret/Knet.jl) +# +# Reference: [Kingma, D. P., & Ba, +# J. L. (2015)](https://arxiv.org/abs/1412.6980). Adam: a Method for +# Stochastic Optimization. International Conference on Learning +# Representations, 1–13. + +using LinearAlgebra + +mutable struct Adam + lr::AbstractFloat + gclip::AbstractFloat + beta1::AbstractFloat + beta2::AbstractFloat + eps::AbstractFloat + t::Int + fstm + scndm +end + +Adam(; lr=0.001, gclip=0, beta1=0.9, beta2=0.999, eps=1e-8)=Adam(lr, gclip, beta1, beta2, eps, 0, nothing, nothing) + +function update!(w, g, p::Adam) + gclip!(g, p.gclip) + if p.fstm===nothing; p.fstm=zero(w); p.scndm=zero(w); end + p.t += 1 + lmul!(p.beta1, p.fstm) + BLAS.axpy!(1-p.beta1, g, p.fstm) + lmul!(p.beta2, p.scndm) + BLAS.axpy!(1-p.beta2, g .* g, p.scndm) + fstm_corrected = p.fstm / (1 - p.beta1 ^ p.t) + scndm_corrected = p.scndm / (1 - p.beta2 ^ p.t) + BLAS.axpy!(-p.lr, @.(fstm_corrected / (sqrt(scndm_corrected) + p.eps)), w) +end + +function gclip!(g, gclip) + if gclip == 0 + g + else + gnorm = vecnorm(g) + if gnorm <= gclip + g + else + BLAS.scale!(gclip/gnorm, g) + end + end +end + +# The training of the quantum circuit is simple, just iterate through the steps. + +function train!(qcbm, ptrain, optim; learning_rate=0.1, niter=50) + # initialize the parameters + params = 2pi * rand(nparameters(qcbm)) + dispatch!(qcbm, params) + kernel = Kernel(nqubits(qcbm), 0.25) + + n, nlayers = nqubits(qcbm), (length(qcbm)-1)÷2 + history = Float64[] + + for i = 1:niter + grad = gradient(n, nlayers, qcbm, kernel, ptrain) + curr_loss = loss(qcbm, kernel, ptrain) + push!(history, curr_loss) + params = collect(parameters(qcbm)) + update!(params, grad, optim) + dispatch!(qcbm, params) + end + history +end + +#--------------------- + +optim = Adam(lr=0.1) +his = train!(circuit, pg, optim, niter=50, learning_rate=0.1) +plot(1:50, his, xlabel="iteration", ylabel="loss") + +#---------------------- + +p = get_prob(circuit) +plot(0:1< Date: Sat, 18 Aug 2018 20:56:51 +0800 Subject: [PATCH 07/85] update Project --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index fed9027f6..ae056339e 100644 --- a/Project.toml +++ b/Project.toml @@ -6,4 +6,4 @@ version = "0.1.0" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" -Yao = "1bc31baa-972f-11e8-31dd-c182aaa973df" +Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" From bbd0e5ae452e2f6e7d65b56634f7fcff6e277ea9 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 01:25:33 +0800 Subject: [PATCH 08/85] add ignore for docs --- Manifest.toml | 196 ------------------------------------------------ docs/.gitignore | 2 + 2 files changed, 2 insertions(+), 196 deletions(-) delete mode 100644 Manifest.toml create mode 100644 docs/.gitignore diff --git a/Manifest.toml b/Manifest.toml deleted file mode 100644 index 86160e6cd..000000000 --- a/Manifest.toml +++ /dev/null @@ -1,196 +0,0 @@ -[[AbstractFFTs]] -deps = ["Compat", "LinearAlgebra"] -git-tree-sha1 = "8d59c3b1463b5e0ad05a3698167f85fac90e184d" -uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "0.3.2" - -[[Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[BinaryProvider]] -deps = ["Libdl", "Pkg", "SHA", "Test"] -git-tree-sha1 = "ffbf89cc49b073cffdb91381b7ee60557a03ec96" -uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" -version = "0.4.1" - -[[CacheServers]] -deps = ["Compat"] -git-tree-sha1 = "320b889acf772213547f3c80db5b351ca2e5efa5" -uuid = "a921213e-d44a-5460-ac04-5d720a99ba71" -version = "0.1.0" - -[[Compat]] -deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "277d3807440d9793421354b6680911fc95d91a84" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "1.0.1" - -[[Conda]] -deps = ["Compat", "JSON", "VersionParsing"] -git-tree-sha1 = "a47f9a2c7b80095e6a935536795635522fe27f5d" -uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d" -version = "1.0.1" - -[[DataStructures]] -deps = ["InteractiveUtils", "REPL", "Random", "Serialization", "Test"] -git-tree-sha1 = "6e72a9098f5774601c8c8d6a4511a68270594910" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.11.0" - -[[Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[DelimitedFiles]] -deps = ["Mmap"] -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" - -[[Distributed]] -deps = ["LinearAlgebra", "Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" - -[[FFTW]] -deps = ["AbstractFFTs", "BinaryProvider", "Compat", "Conda", "Libdl", "LinearAlgebra", "Pkg", "Reexport", "Test"] -git-tree-sha1 = "29cda58afbf62f35b1a094882ad6c745a47b2eaa" -uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "0.2.4" - -[[InteractiveUtils]] -deps = ["LinearAlgebra", "Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[JSON]] -deps = ["Dates", "Distributed", "Mmap", "Pkg", "Sockets", "Test", "Unicode"] -git-tree-sha1 = "fec8e4d433072731466d37ed0061b3ba7f70eeb9" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.19.0" - -[[Lazy]] -deps = ["Compat", "MacroTools", "Test"] -git-tree-sha1 = "6b8b57bed0c4fc6198d81301cce7b2f09ebd55af" -uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" -version = "0.13.0" - -[[LibGit2]] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[LinearAlgebra]] -deps = ["Libdl"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[LuxurySparse]] -deps = ["Compat", "LinearAlgebra", "Pkg", "Random", "SparseArrays", "StaticArrays", "Test"] -git-tree-sha1 = "e3fa3d15813a01de9f41e839f9c4acdb1dd310a2" -uuid = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" -version = "0.1.2" - -[[MacroTools]] -deps = ["Compat"] -git-tree-sha1 = "c443e1c8d58a4e9f61b708ad0a88286c7042145b" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.4.4" - -[[Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[Missings]] -deps = ["Dates", "InteractiveUtils", "SparseArrays", "Test"] -git-tree-sha1 = "196528fa1df03df435025f52f9c1ff8356f94738" -uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "0.3.0" - -[[Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[Pkg]] -deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" - -[[Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[Random]] -deps = ["Serialization"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[Reexport]] -deps = ["Pkg"] -git-tree-sha1 = "7b1d07f411bc8ddb7977ec7f377b97b158514fe0" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "0.2.0" - -[[SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" - -[[Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" - -[[Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[SortingAlgorithms]] -deps = ["DataStructures", "Random", "Test"] -git-tree-sha1 = "03f5898c9959f8115e30bc7226ada7d0df554ddd" -uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "0.3.1" - -[[SparseArrays]] -deps = ["LinearAlgebra", "Random"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - -[[StaticArrays]] -deps = ["InteractiveUtils", "LinearAlgebra", "Random", "Statistics", "Test"] -git-tree-sha1 = "d432c79bef174a830304f8601427a4357dfdbfb7" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "0.8.3" - -[[Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[StatsBase]] -deps = ["DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "Test"] -git-tree-sha1 = "723193a13e8078cec6dcd0b8fe245c8bfd81690e" -uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.25.0" - -[[Test]] -deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[UUIDs]] -deps = ["Random"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - -[[VersionParsing]] -deps = ["Compat"] -git-tree-sha1 = "c9d5aa108588b978bd859554660c8a5c4f2f7669" -uuid = "81def892-9a0e-5fdd-b105-ffc91e053289" -version = "1.1.2" - -[[Yao]] -deps = ["CacheServers", "DataStructures", "FFTW", "Lazy", "LinearAlgebra", "LuxurySparse", "MacroTools", "Random", "Reexport", "SparseArrays", "StaticArrays", "StatsBase"] -git-tree-sha1 = "cf978e1b0027fc3637dfc9aa257d0cec02b9e1af" -repo-rev = "8e117ded97d3a0d384c673b9d6ff62f424d97478" -repo-url = "https://github.com/QuantumBFS/Yao.jl.git" -uuid = "1bc31baa-972f-11e8-31dd-c182aaa973df" -version = "0.2.0" diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..8eed45923 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +src/tutorial +src/tutorial/* From 4906e030457cc3fc36bd3241c96dc3655c701781 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 01:49:59 +0800 Subject: [PATCH 09/85] update ignore --- .gitignore | 1 + docs/.gitignore | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 docs/.gitignore diff --git a/.gitignore b/.gitignore index 78fd49431..8e512f4e4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ docs/build/ docs/site/ +docs/src/tutorial/ *.ipynb_checkpoints **/*.ipynb_checkpoints diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 8eed45923..000000000 --- a/docs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -src/tutorial -src/tutorial/* From 2afd927695882358195e2e3e0858f936b5687758 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 01:50:09 +0800 Subject: [PATCH 10/85] update project --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index ae056339e..5bc2e83ae 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,7 @@ authors = ["Roger-luo "] version = "0.1.0" [deps] +FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" From f5b1f2d9a9d13f764cd34f2ba19d4d45bcfe7e27 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 01:50:21 +0800 Subject: [PATCH 11/85] add docs --- docs/make.jl | 56 +++++++++++++++++++++++++++++++++++++++-------- docs/src/index.md | 7 ++++++ 2 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 docs/src/index.md diff --git a/docs/make.jl b/docs/make.jl index 34cbdbc4a..b7bbd8ff8 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,18 +1,56 @@ using Pkg -pkg""" -add Documenter Literate -""" +# pkg""" +# add Documenter Literate +# """ using Documenter, Literate, QuAlgorithmZoo -const TUTORIAL = joinpath(@__DIR__, "src/tutorial") -const EXAMPLES = joinpath(@__DIR__, "..", "examples") +const PATH = ( + tutorial = joinpath(@__DIR__, "src/tutorial"), + examples = joinpath(@__DIR__, "..", "examples") +) -for (root, dirs, files) in walkdir(EXAMPLES) - for file in files - println(joinpath(root, file)) +function process_literate_scripts(;excludes=["make.jl", "README.md"]) + TUTORIALS = [] + for (root, dirs, files) in walkdir(PATH.examples) + for file in files + file in excludes && continue + filepath = joinpath(root, file) + Literate.markdown(filepath, PATH.tutorial) + + filename, _ = splitext(file) + mdfile = join([filename, ".md"]) + # TODO: use PATH.tutorial rather then manual path + push!(TUTORIALS, relpath(joinpath("tutorial", mdfile))) + end end + TUTORIALS end -# Literate.markdown(EXAMPLES, TUTORIAL) +const TUTORIALS = process_literate_scripts() + +#----------------------------------------------- + +makedocs( + modules = [QuAlgorithmZoo], + clean = false, + format = :html, + sitename = "Quantum Algorithm Zoo", + linkcheck = !("skiplinks" in ARGS), + analytics = "UA-89508993-1", + pages = [ + "Home" => "index.md", + "Tutorial" => TUTORIALS, + ], + html_prettyurls = !("local" in ARGS), + html_canonical = "https://quantumbfs.github.io/QuAlgorithmZoo.jl/latest/", +) + +deploydocs( + repo = "github.com/QuantumBFS/QuAlgorithmZoo.jl.git", + target = "build", + julia = "1.0", + deps = nothing, + make = nothing, +) diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 000000000..93c5c7397 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,7 @@ +```@meta +CurrentModule = QuAlgorithmZoo +``` + +# Quantum Algorithm Zoo + +A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) From 98cafe3a9ca5a81fd40b8aa783e3acfba0ca9a0a Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 01:50:44 +0800 Subject: [PATCH 12/85] add Literate scripts --- examples/QCBM.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/QCBM.jl b/examples/QCBM.jl index 56f98a669..a532b616f 100644 --- a/examples/QCBM.jl +++ b/examples/QCBM.jl @@ -236,7 +236,7 @@ r = register(bit"0000") #---------------------- -circuit = QCBM(6, 10, [1=>2, 3=>4, 5=>6, 2=>3, 4=>5, 6=>1]) +circuit = QCBM(6, 10, [1=>2, 3=>4, 5=>6, 2=>3, 4=>5, 6=>1]); # Now, we define its shorthand @@ -367,4 +367,5 @@ plot(1:50, his, xlabel="iteration", ylabel="loss") #---------------------- p = get_prob(circuit) -plot(0:1< Date: Sun, 19 Aug 2018 01:51:10 +0800 Subject: [PATCH 13/85] fix module path --- src/QuAlgorithmZoo.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index 5bc86e505..a8d0ed936 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -1,13 +1,13 @@ module QuAlgorithmZoo using LuxurySparse, LinearAlgebra -using ..Yao -using ..Intrinsics -using ..Registers -using ..Blocks -import ..Blocks: mat, dispatch!, nparameters, parameters, cache_key, print_block, _make_rot_mat, apply! +using Yao +using Yao.Intrinsics +using Yao.Registers +using Yao.Blocks +import Yao.Blocks: mat, dispatch!, nparameters, parameters, cache_key, print_block, _make_rot_mat, apply! import Base: ==, copy, hash -import ..Intrinsics: ishermitian, isreflexive, isunitary +import Yao.Intrinsics: ishermitian, isreflexive, isunitary export openbox From 64c333a8280f40d2770b8803fc98597180a34faa Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 01:51:30 +0800 Subject: [PATCH 14/85] rm env --- docs/docs/Project.toml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 docs/docs/Project.toml diff --git a/docs/docs/Project.toml b/docs/docs/Project.toml deleted file mode 100644 index cf645b524..000000000 --- a/docs/docs/Project.toml +++ /dev/null @@ -1,3 +0,0 @@ -[deps] -Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" From f92c10ca3ed00560f13d4d97b1013958f9f86484 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 01:56:29 +0800 Subject: [PATCH 15/85] set up Documenter --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a6ce0b3f5..9bfa00ecf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,4 @@ after_success: # push coverage results to Codecov - julia -e 'using Pkg; cd(Pkg.dir("QuAlgorithmZoo")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' # add Documenter - # - julia -e 'using Pkg; Pkg.add("Documenter")' - # # push documents to gh-pages - # - julia -e 'using Pkg; cd(Pkg.dir("QuAlgorithmZoo")); include(joinpath("docs", "make.jl"))' + - julia -e 'using Pkg; cd(Pkg.dir("QuAlgorithmZoo")); include(joinpath("docs", "make.jl"))' From 8fd28877b5c750d1ebbad7a25790e7a11ff838f4 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 01:56:43 +0800 Subject: [PATCH 16/85] add pkgs --- docs/make.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index b7bbd8ff8..43af1100c 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,8 +1,8 @@ using Pkg -# pkg""" -# add Documenter Literate -# """ +pkg""" +add Documenter Literate +""" using Documenter, Literate, QuAlgorithmZoo From c102ce5a0963059bbfa4670288a40113f6a3b808 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 01:58:34 +0800 Subject: [PATCH 17/85] add 1.0 in CI --- .travis.yml | 1 + appveyor.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9bfa00ecf..7b90c2620 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ os: - osx julia: - 0.7 + - 1.0 - nightly notifications: email: false diff --git a/appveyor.yml b/appveyor.yml index f5814299d..862691efb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,8 @@ environment: matrix: - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.7/julia-0.7-latest-win32.exe" - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.7/julia-0.7-latest-win64.exe" + - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/1.0/julia-1.0-latest-win32.exe" + - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/1.0/julia-1.0-latest-win64.exe" - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" From 5c83f2d9ebf93c37f8aeeac86b17adad471cc9b7 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 02:01:10 +0800 Subject: [PATCH 18/85] fix test --- Project.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Project.toml b/Project.toml index 5bc2e83ae..5fb921d28 100644 --- a/Project.toml +++ b/Project.toml @@ -8,3 +8,9 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] From 2041fa2e968c4b75918ccdc8f7aa80444dd692d4 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 02:04:40 +0800 Subject: [PATCH 19/85] update deps --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 5fb921d28..0c08caf76 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ version = "0.1.0" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" [extras] From de2330bc712661f700f95c9e3f1c575016e72365 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 02:09:18 +0800 Subject: [PATCH 20/85] fix plots --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 43af1100c..7c570c82f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,7 +1,7 @@ using Pkg pkg""" -add Documenter Literate +add Documenter Literate Plots """ using Documenter, Literate, QuAlgorithmZoo From cba5c4e0796f1e45f9daaf466bbdd230195a49a3 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 02:10:12 +0800 Subject: [PATCH 21/85] add another dep --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 7c570c82f..1805d5159 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,7 +1,7 @@ using Pkg pkg""" -add Documenter Literate Plots +add Documenter Literate Plots Yao """ using Documenter, Literate, QuAlgorithmZoo From 43fcf1cb0d9e4887016792c0fa31b051fc1a04db Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 02:25:43 +0800 Subject: [PATCH 22/85] update README --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index 6a68a5dc9..7eee77977 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,32 @@ # QuAlgorithmZoo +[![Build Status](https://travis-ci.org/QuantumBFS/QuAlgorithmZoo.jl.svg?branch=master)](https://travis-ci.org/QuantumBFS/QuAlgorithmZoo.jl) +[![Build status](https://ci.appveyor.com/api/projects/status/wdbroxclvf1nhsen/branch/master?svg=true)](https://ci.appveyor.com/project/Roger-luo/qualgorithmzoo-jl/branch/master) + +[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://quantumbfs.github.io/QuAlgorithmZoo.jl/stable/) +[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://quantumbfs.github.io/QuAlgorithmZoo.jl/latest/) + A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) + +## Installation + +QuAlgorithmZoo.jl is not registered yet, please use the following command: + +```julia +pkg> add https://github.com/QuantumBFS/QuAlgorithmZoo.jl.git +``` + +Please notice, this package is still under development and needs further polish. A crazy refactor is coming! + +## Contents + +- [x] QCBM tutorial +- [ ] differentiable circuit +- [ ] grover search +- [ ] HHL +- [ ] QFT +- [ ] QAOA + +## License + +QuAlgorithmZoo.jl is released under Apache License 2.0. From 0d05392305a08c3b287330c79b6d77e1b806e747 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 02:26:27 +0800 Subject: [PATCH 23/85] adjust badges --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 7eee77977..2a0dee345 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![Build Status](https://travis-ci.org/QuantumBFS/QuAlgorithmZoo.jl.svg?branch=master)](https://travis-ci.org/QuantumBFS/QuAlgorithmZoo.jl) [![Build status](https://ci.appveyor.com/api/projects/status/wdbroxclvf1nhsen/branch/master?svg=true)](https://ci.appveyor.com/project/Roger-luo/qualgorithmzoo-jl/branch/master) - [![](https://img.shields.io/badge/docs-stable-blue.svg)](https://quantumbfs.github.io/QuAlgorithmZoo.jl/stable/) [![](https://img.shields.io/badge/docs-latest-blue.svg)](https://quantumbfs.github.io/QuAlgorithmZoo.jl/latest/) From 753de5d3d2e914f9c8dbf9d7fd1d37252e15d2b9 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Sun, 19 Aug 2018 02:38:37 +0800 Subject: [PATCH 24/85] fix windows CI --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 862691efb..eb4e82b24 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -42,7 +42,7 @@ install: build_script: # Need to convert from shallow to complete for Pkg.clone to work - IF EXIST .git\shallow (git fetch --unshallow) - - C:\projects\julia\bin\julia -e "versioninfo(); + - C:\projects\julia\bin\julia -e "using InteractiveUtils; versioninfo(); Pkg.clone(pwd(), \"QuAlgorithmZoo\"); Pkg.build(\"QuAlgorithmZoo\")" test_script: From 9a8a1ddb68d26c6fc442695e86773feff1e4a047 Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 23 Aug 2018 17:02:18 +0800 Subject: [PATCH 25/85] quickfix of QFT --- src/QFT.jl | 4 ++-- src/QuAlgorithmZoo.jl | 2 +- test/QFT.jl | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/QFT.jl b/src/QFT.jl index b05ebfd0e..7f611c38e 100644 --- a/src/QFT.jl +++ b/src/QFT.jl @@ -11,8 +11,8 @@ QFTCircuit(n::Int) = chain(n, CRot(n, i) for i = 1:n) struct QFTBlock{N} <: PrimitiveBlock{N,ComplexF64} end mat(q::QFTBlock{N}) where N = applymatrix(q) -apply!(reg::DefaultRegister{B}, ::QFTBlock) where B = (reg.state = ifft!(invorder_firstdim(reg |> state), 1)*sqrt(1<state, 1)/sqrt(1< state), 1)*sqrt(1<state, 1)/sqrt(1< isnormalized end From 9e0dfef091e2c132888cd7b052d877e1658ac9b2 Mon Sep 17 00:00:00 2001 From: Leo Date: Sat, 29 Sep 2018 21:48:42 +0800 Subject: [PATCH 26/85] new HHL --- Project.toml | 2 +- src/HHL.jl | 84 +++++++++++++++++++++++++++++++++++++++ src/Math.jl | 34 ++++++++++++++++ src/Miscellaneous.jl | 19 +++++++++ src/PhaseEstimation.jl | 47 ++++++++++++++++++++++ src/QuAlgorithmZoo.jl | 3 ++ test/HHL.jl | 87 +++++++++++++++++++++++++++++++++++++++++ test/PhaseEstimation.jl | 68 ++++++++++++++++++++++++++++++++ test/runtests.jl | 8 ++++ 9 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 src/HHL.jl create mode 100644 src/Math.jl create mode 100644 src/Miscellaneous.jl create mode 100644 src/PhaseEstimation.jl create mode 100644 test/HHL.jl create mode 100644 test/PhaseEstimation.jl diff --git a/Project.toml b/Project.toml index 0c08caf76..7532c2c2c 100644 --- a/Project.toml +++ b/Project.toml @@ -8,7 +8,7 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" +Yao = "1bc31baa-972f-11e8-31dd-c182aaa973df" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/src/HHL.jl b/src/HHL.jl new file mode 100644 index 000000000..d9ed5aae5 --- /dev/null +++ b/src/HHL.jl @@ -0,0 +1,84 @@ +export hhlcircuit, hhlproject!, hhlsolve, HHLCRot + +""" + HHLCRot{N, NC, T} <: PrimitiveBlock{N, Complex{T}} + +Controlled rotation gate used in HHL algorithm, applied on N qubits. + + * cbits: control bits. + * ibit:: the ancilla bit. + * C_value:: the value of constant "C", should be smaller than the spectrum "gap". +""" +struct HHLCRot{N, NC, T} <: PrimitiveBlock{N, Complex{T}} + cbits::Vector{Int} + ibit::Int + C_value::T + HHLCRot{N}(cbits::Vector{Int}, ibit::Int, C_value::T) where {N, T} = new{N, length(cbits), T}(cbits, ibit, C_value) +end + +@inline function hhlrotmat(λ::Real, C_value::Real) + b = C_value/λ + a = sqrt(1-b^2) + a, -b, b, a +end + +function apply!(reg::DefaultRegister, hr::HHLCRot{N, NC, T}) where {N, NC, T} + mask = bmask(hr.ibit) + step = 1<<(hr.ibit-1) + step_2 = step*2 + nbit = nqubits(reg) + for j = 0:step_2:size(reg.state, 1)-step + for i = j+1:j+step + λ = bfloat(takebit(i-1, hr.cbits...), nbit=nbit-1) + if λ >= hr.C_value + u = hhlrotmat(λ, hr.C_value) + u1rows!(state(reg), i, i+step, u...) + end + end + end + reg +end + +""" + hhlproject!(all_bit::DefaultRegister, n_reg::Int) -> Vector + +project to aiming state |1>|00>|u>, and return |u> vector. +""" +function hhlproject!(all_bit::DefaultRegister, n_reg::Int) + all_bit |> focus!(1:(n_reg+1)...) |> select!(1) |> relaxedvec +end + +""" +Function to build up a HHL circuit. +""" +function hhlcircuit(UG, n_reg::Int, C_value::Real) + n_b = nqubits(UG) + n_all = 1 + n_reg + n_b + pe = PEBlock(UG, n_reg, n_b) + cr = HHLCRot{n_reg+1}([2:n_reg+1...], 1, C_value) + chain(n_all, concentrate(n_all, pe, [2:n_all...,]), concentrate(n_all, cr, [1:(n_reg+1)...,]), concentrate(n_all, pe', [2:n_all...,])) +end + +""" + hhlsolve(A::Matrix, b::Vector) -> Vector + +solving linear system using HHL algorithm. Here, A must be hermitian. +""" +function hhlsolve(A::Matrix, b::Vector, n_reg::Int, C_value::Real) + if !ishermitian(A) + throw(ArgumentError("Input matrix not hermitian!")) + end + UG = matrixgate(exp(2π*im.*A)) + + # Generating input bits + all_bit = zero_state(1) ⊗ zero_state(n_reg) ⊗ register(b) + + # Construct HHL circuit. + circuit = hhlcircuit(UG, n_reg, C_value) + + # Apply bits to the circuit. + apply!(all_bit, circuit) + + # Get state of aiming state |1>|00>|u>. + hhlproject!(all_bit, n_reg) ./ C_value +end diff --git a/src/Math.jl b/src/Math.jl new file mode 100644 index 000000000..2c9307b57 --- /dev/null +++ b/src/Math.jl @@ -0,0 +1,34 @@ +""" + rand_unitary(N::Int) -> Matrix + +Random unitary matrix. +""" +function rand_unitary(N::Int) + qr(randn(ComplexF64, N, N)).Q |> Matrix +end + +""" + rand_hermitian(N::Int) -> Matrix + +Random hermitian matrix. +""" +function rand_hermitian(N::Int) + A = randn(ComplexF64, N, N) + A + A' +end + +""" +Function to make a unify matrixgate. + +TODO: +deprecated this function. +""" +function matrix_unify(A) + U = exp(2π*im.*A) + UG = matrixgate(U) + UG +end + +############ Leo's Magic Tricks ################ +Base.:(|>)(reg::AbstractRegister, circuit::AbstractBlock) = apply!(reg, circuit) +⊗(reg::AbstractRegister, reg2::AbstractRegister) = join(reg, reg2) diff --git a/src/Miscellaneous.jl b/src/Miscellaneous.jl new file mode 100644 index 000000000..e077d5cdd --- /dev/null +++ b/src/Miscellaneous.jl @@ -0,0 +1,19 @@ +export inverselines + +""" + inverselines(nbit::Int; n_reg::Int=nbit) -> ChainBlock + +inverse first `n_reg` lines + +TODO: +deprecate this function, it is not used. +""" +function inverselines(nbit::Int; n_reg::Int=nbit) + c = chain(nbit) + for i = 1:(n_reg ÷ 2) + push!(c, swap(i,(n_reg-i+1))) + end + c +end + + diff --git a/src/PhaseEstimation.jl b/src/PhaseEstimation.jl new file mode 100644 index 000000000..95d1ba134 --- /dev/null +++ b/src/PhaseEstimation.jl @@ -0,0 +1,47 @@ +export PEBlock, projection_analysis +""" + PEBlock(UG, n_reg, n_b) -> ChainBlock + +phase estimation circuit. + + * `UG`: the input unitary matrix. + * `n_reg`: the number of bits to store phases, + * `n_b`: the number of bits to store vector. +""" +function PEBlock(UG::GeneralMatrixGate, n_reg::Int, n_b::Int) + nbit = n_b + n_reg + # Apply Hadamard Gate. + hs = repeat(nbit, H, 1:n_reg) + + # Construct a control circuit. + control_circuit = chain(nbit) + for i = 1:n_reg + push!(control_circuit, control(nbit, (i,), (n_reg+1:nbit...,)=>UG)) + if i != n_reg + UG = matrixgate(mat(UG) * mat(UG)) + end + end + + # Inverse QFT Block. + iqft = concentrate(nbit, QFTBlock{n_reg}() |> adjoint,[1:n_reg...,]) + chain(hs, control_circuit, iqft) +end + +""" + projection_analysis(evec::Matrix, reg::DefaultRegister) -> Tuple + +Analyse using state projection. +It returns a tuple of (most probable configuration, the overlap matrix, the relative probability for this configuration) +""" +function projection_analysis(evec::Matrix, reg::DefaultRegister) + overlap = register(evec)'*reg + amp_relative = Float64[] + bs = Int[] + + for b in basis(overlap) + mc = argmax(view(overlap, b+1, :) .|> abs)-1 + push!(amp_relative, abs2(overlap[b+1, mc+1])/sum(overlap[b+1, :] .|> abs2)) + push!(bs, mc) + end + bs, overlap, amp_relative +end diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index e389a4298..bb605a1e5 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -16,10 +16,13 @@ For a black box, like QFTBlock, you can get its white box (loyal simulation) usi """ function openbox end +include("Miscellaneous.jl") include("QFT.jl") include("Differential.jl") include("RotBasis.jl") include("Grover.jl") +include("PhaseEstimation.jl") +include("HHL.jl") end # module diff --git a/test/HHL.jl b/test/HHL.jl new file mode 100644 index 000000000..391a4ddd3 --- /dev/null +++ b/test/HHL.jl @@ -0,0 +1,87 @@ +using Yao +using Yao.Intrinsics +using QuAlgorithmZoo +using Test, LinearAlgebra + +function crot(n_reg::Int, C_value::Real) + n_rot = n_reg + 1 + rot = chain(n_rot) + θ = zeros(1< 1 + return println("C_value = $C_value, λ = $λ, sinθ = $sin_value > 1, please lower C_value.\n") + end + θ[(i)] = 2.0*asin(C_value / λ) + push!(rot, control(c_bit, 1=>Ry(θ[(i)]))) + end + rot +end + +@testset "HHLCRot" begin + hr = HHLCRot{4}([4,3,2], 1, 0.01) + reg = rand_state(4) + @test reg |> copy |> hr |> isnormalized + + hr2 = crot(3, 0.01) + reg1 = reg |> copy |> hr + reg2 = reg |> copy |> hr2 + @test fidelity(reg1, reg2)[] ≈ 1 +end + +""" + hhl_problem(nbit::Int) -> Tuple + +Returns (A, b), where + * `A` is positive definite, hermitian, with its maximum eigenvalue λ_max < 1. + * `b` is normalized. +""" +function hhl_problem(nbit::Int) + siz = 1< = |b>. + ## signs: Diagonal Matrix of eigen values of A. + ## base_space: the eigen space of A. + ## x: |x>. + using Random + Random.seed!(2) + N = 3 + A, b = hhl_problem(N) + x = A^(-1)*b # base_i = base_space[:,i] ϕ1 = (A*base_i./base_i)[1] + + ## n_b : number of bits for |b>. + ## n_reg: number of PE register. + ## n_all: number of all bits. + n_reg = 12 + + ## C_value: value of constant C in control rotation. + ## It should be samller than the minimum eigen value of A. + C_value = minimum(eigvals(A) .|> abs)*0.25 + #C_value = 1.0/(1<|00>|u>. + @test isapprox.(x, res, atol=0.5) |> all +end diff --git a/test/PhaseEstimation.jl b/test/PhaseEstimation.jl new file mode 100644 index 000000000..54360ced7 --- /dev/null +++ b/test/PhaseEstimation.jl @@ -0,0 +1,68 @@ +using Yao +using Yao.Intrinsics +using Test, LinearAlgebra +using QuAlgorithmZoo + +""" +random phase estimation problem setup. +""" +function rand_phaseest_setup(N::Int) + U = rand_unitary(1< adjoint) ≈ join(reg1, reg2) +end + +@testset "phaseest, non-eigen" begin + # Generate a random matrix. + N = 3 + U, b, ϕs, evec = rand_phaseest_setup(N) + + # Define registers and U operator. + M = 6 + reg1 = zero_state(M) + reg2 = register(b) + UG = matrixgate(U); + + # run circuit + reg= join(reg1, reg2) + pe = PEBlock(UG, M, N) + apply!(reg, pe) + + # measure + bs, proj, amp_relative = projection_analysis(evec, focus!(reg, M+1:M+N)) + @test isapprox(ϕs, bfloat.(bs, nbit=M), atol=0.05) +end diff --git a/test/runtests.jl b/test/runtests.jl index ed04c17f0..294101484 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -20,3 +20,11 @@ end @testset "Grover" begin include("Grover.jl") end + +@testset "PhaseEstimation" begin + include("PhaseEstimation.jl") +end + +@testset "HHL" begin + include("HHL.jl") +end From 38fab116425154a52378f68329897acabaeee7db Mon Sep 17 00:00:00 2001 From: Leo Date: Sat, 29 Sep 2018 21:57:33 +0800 Subject: [PATCH 27/85] fix --- src/HHL.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HHL.jl b/src/HHL.jl index d9ed5aae5..60b709852 100644 --- a/src/HHL.jl +++ b/src/HHL.jl @@ -45,7 +45,7 @@ end project to aiming state |1>|00>|u>, and return |u> vector. """ function hhlproject!(all_bit::DefaultRegister, n_reg::Int) - all_bit |> focus!(1:(n_reg+1)...) |> select!(1) |> relaxedvec + all_bit |> focus!(1:(n_reg+1)...) |> select!(1) |> state |> vec end """ From 8d0fb2aec0bd40320904e1da8ab0defa654c0a24 Mon Sep 17 00:00:00 2001 From: Leo Date: Sat, 29 Sep 2018 22:01:37 +0800 Subject: [PATCH 28/85] fix deps --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 7532c2c2c..0c08caf76 100644 --- a/Project.toml +++ b/Project.toml @@ -8,7 +8,7 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -Yao = "1bc31baa-972f-11e8-31dd-c182aaa973df" +Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From c548adb091d32e5ca6b8a2eb7fb46e40f64b07f1 Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 4 Oct 2018 23:45:33 +0800 Subject: [PATCH 29/85] fix example for v0.3 compacity --- src/Differential.jl | 10 +++++----- src/QuAlgorithmZoo.jl | 2 +- src/RotBasis.jl | 8 ++++++-- test/Differential.jl | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Differential.jl b/src/Differential.jl index 0da286307..493f17c24 100644 --- a/src/Differential.jl +++ b/src/Differential.jl @@ -30,7 +30,7 @@ function diff_circuit(n, nlayer, pairs) for i = 1:(nlayer + 1) if i!=1 push!(circuit, cnot_entangler(pairs) |> cache) end - push!(circuit, rollrepeat(n, rotter(i==1, i==nlayer+1))) + push!(circuit, chain(n, [put(n, j=>rotter(i==1, i==nlayer+1)) for j=1:n])) #for j = 1:n # push!(circuit, put(n, j=>rotter(i==1, i==nlayer+1))) #end @@ -45,7 +45,7 @@ filter out all rotation gates, which is differentiable. """ function collect_rotblocks(blk::AbstractBlock) rots = blockfilter!(x->x isa RotationGate, Vector{RotationGate}([]), blk) - nparameters(blk)==length(rots) || warn("some parameters in this circuit are not differentiable!") + nparameters(blk)==length(rots) || @warn "some parameters in this circuit are not differentiable!" rots end @@ -60,13 +60,13 @@ function perturb(func, gates::Vector{<:RotationGate}, diff::Real) res = Matrix{Float64}(undef, ng, 2) for i in 1:ng gate = gates[i] - dispatch!(+, gate, diff) + setiparameters!(+, gate, diff) res[i, 1] = func() - dispatch!(+, gate, -2*diff) + setiparameters!(+, gate, -2*diff) res[i, 2] = func() - dispatch!(+, gate, diff) # set back + setiparameters!(+, gate, diff) # set back end res end diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index bb605a1e5..b1a7c8cbf 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -5,7 +5,7 @@ using Yao using Yao.Intrinsics using Yao.Registers using Yao.Blocks -import Yao.Blocks: mat, dispatch!, nparameters, parameters, cache_key, print_block, _make_rot_mat, apply!, PrimitiveBlock +import Yao.Blocks: mat, dispatch!, niparameters, iparameters, setiparameters!, cache_key, print_block, _make_rot_mat, apply!, PrimitiveBlock import Base: ==, copy, hash import Yao.Intrinsics: ishermitian, isreflexive, isunitary diff --git a/src/RotBasis.jl b/src/RotBasis.jl index 48f7b0df7..00d23fe71 100644 --- a/src/RotBasis.jl +++ b/src/RotBasis.jl @@ -23,8 +23,12 @@ end copy(block::RotBasis{T}) where T = RotBasis{T}(block.theta, block.phi) dispatch!(block::RotBasis, params) = ((block.theta, block.phi) = params; block) -parameters(rb::RotBasis) = (rb.theta, rb.phi) -nparameters(::Type{<:RotBasis}) = 2 +iparameters(rb::RotBasis) = (rb.theta, rb.phi) +function setiparameters!(rb::RotBasis, params) + rb.theta, rb.phi = params + rb +end +niparameters(::Type{<:RotBasis}) = 2 function print_block(io::IO, R::RotBasis) print(io, "RotBasis($(R.theta), $(R.phi))") diff --git a/test/Differential.jl b/test/Differential.jl index cb130b4da..ff5533fe2 100644 --- a/test/Differential.jl +++ b/test/Differential.jl @@ -1,7 +1,7 @@ using Test, Random, LinearAlgebra, SparseArrays -using QuAlgorithmZoo, Yao.Blocks +using Yao, QuAlgorithmZoo, Yao.Blocks @testset "rotter, collect_rotblocks, num_gradient, opgrad" begin c = diff_circuit(4, 3, [1=>3, 2=>4, 2=>3, 4=>1]) From 736f9a7c6ce08636188d93f93e8a6d86ecd10050 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 8 Oct 2018 16:08:25 +0800 Subject: [PATCH 30/85] fix differential --- Project.toml | 2 +- examples/Grover.jl | 30 ++++++++++++++++++++++++++++++ src/Differential.jl | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 examples/Grover.jl diff --git a/Project.toml b/Project.toml index 0c08caf76..19e7f38cc 100644 --- a/Project.toml +++ b/Project.toml @@ -8,7 +8,7 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" +Yao = "5a1af4f6-c801-11e8-08ea-1bad16a356b2" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/examples/Grover.jl b/examples/Grover.jl new file mode 100644 index 000000000..70846cf8a --- /dev/null +++ b/examples/Grover.jl @@ -0,0 +1,30 @@ +using Yao +using QuAlgorithmZoo: groveriter!, inference_oracle, prob_match_oracle + +num_bit = 12 +oracle(reg::DefaultRegister) = (reg.state[100:101,:]*=-1; reg) +target_state = zeros(1< Date: Mon, 8 Oct 2018 16:25:43 +0800 Subject: [PATCH 31/85] emergent bug gix --- src/Differential.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Differential.jl b/src/Differential.jl index 0da286307..d27c9e442 100644 --- a/src/Differential.jl +++ b/src/Differential.jl @@ -5,7 +5,7 @@ export diff_circuit, num_gradient, rotter, cnot_entangler, opgrad, collect_rotbl Arbitrary rotation unit, set parameters notrailing, noleading true to remove trailing and leading Z gates. """ -rotter(noleading::Bool=false, notrailing::Bool=false) = noleading ? (notrailing ? Rx(0) : chain(Rx(0), Rz(0))) : (notrailing ? chain(Rz(0), Rx(0)) : chain(Rz(0), Rz(0), Rz(0))) +rotter(noleading::Bool=false, notrailing::Bool=false) = noleading ? (notrailing ? Rx(0) : chain(Rx(0), Rz(0))) : (notrailing ? chain(Rz(0), Rx(0)) : chain(Rz(0), Rx(0), Rz(0))) """ cnot_entangler([n::Int, ] pairs::Vector{Pair}) = ChainBlock From 0e0ea4ee8e9703f107a3180af537a51f9a7e5f51 Mon Sep 17 00:00:00 2001 From: Leo Date: Sat, 20 Oct 2018 23:51:15 +0800 Subject: [PATCH 32/85] update QCGAN, new QuGAN --- Project.toml | 1 + docs/make.jl | 3 + docs/src/index.md | 18 ++ docs/src/man/zoo.md | 6 + examples/Grover.jl | 21 ++- examples/QCBM.jl | 391 +++------------------------------------- examples/QuGAN.jl | 29 +++ src/Adam.jl | 40 ++++ src/CircuitBuild.jl | 137 ++++++++++++++ src/Differential.jl | 95 ---------- src/HHL.jl | 2 +- src/Kernels.jl | 38 ++++ src/Math.jl | 34 ---- src/PhaseEstimation.jl | 2 +- src/QCBM.jl | 72 ++++++++ src/QCOptProblem.jl | 57 ++++++ src/QFT.jl | 2 +- src/QuAlgorithmZoo.jl | 6 +- src/QuGAN.jl | 103 +++++++++++ src/RotBasis.jl | 7 +- test/CircuitBuild.jl | 65 +++++++ test/Differential.jl | 21 --- test/Grover.jl | 2 +- test/PhaseEstimation.jl | 6 +- test/QCBM.jl | 32 ++++ test/QCOptProblem.jl | 7 + test/QuGAN.jl | 22 +++ test/RotBasis.jl | 4 + test/runtests.jl | 8 +- 29 files changed, 697 insertions(+), 534 deletions(-) create mode 100644 docs/src/man/zoo.md create mode 100644 examples/QuGAN.jl create mode 100644 src/Adam.jl create mode 100644 src/CircuitBuild.jl delete mode 100644 src/Differential.jl create mode 100644 src/Kernels.jl delete mode 100644 src/Math.jl create mode 100644 src/QCBM.jl create mode 100644 src/QCOptProblem.jl create mode 100644 src/QuGAN.jl create mode 100644 test/CircuitBuild.jl delete mode 100644 test/Differential.jl create mode 100644 test/QCBM.jl create mode 100644 test/QCOptProblem.jl create mode 100644 test/QuGAN.jl diff --git a/Project.toml b/Project.toml index 19e7f38cc..8e9891d4d 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Yao = "5a1af4f6-c801-11e8-08ea-1bad16a356b2" [extras] diff --git a/docs/make.jl b/docs/make.jl index 1805d5159..56e95dad8 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -42,6 +42,9 @@ makedocs( pages = [ "Home" => "index.md", "Tutorial" => TUTORIALS, + "Manual" => Any[ + "man/zoo.md", + ], ], html_prettyurls = !("local" in ARGS), html_canonical = "https://quantumbfs.github.io/QuAlgorithmZoo.jl/latest/", diff --git a/docs/src/index.md b/docs/src/index.md index 93c5c7397..5d4d05b07 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -5,3 +5,21 @@ CurrentModule = QuAlgorithmZoo # Quantum Algorithm Zoo A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) + +## Tutorial +```@contents +Pages = [ + "tutorial/Grover.md", + "tutorial/QCBM.md", + "tutorial/QuGAN.md", +] +Depth = 1 +``` + +## Manual +```@contents +Pages = [ + "man/zoo.md", +] +Depth = 1 +``` diff --git a/docs/src/man/zoo.md b/docs/src/man/zoo.md new file mode 100644 index 000000000..ff1ede4ae --- /dev/null +++ b/docs/src/man/zoo.md @@ -0,0 +1,6 @@ +# QuAlgorithmZoo + +```@autodocs +Modules = [QuAlgorithmZoo] +Order = [:module, :constant, :type, :macro, :function] +``` diff --git a/examples/Grover.jl b/examples/Grover.jl index 70846cf8a..48d2006f3 100644 --- a/examples/Grover.jl +++ b/examples/Grover.jl @@ -1,29 +1,34 @@ +# # Grover Search using Yao -using QuAlgorithmZoo: groveriter!, inference_oracle, prob_match_oracle +using LinearAlgebra +using QuAlgorithmZoo: groveriter, inference_oracle, prob_match_oracle +# ## Target Space and Evidense num_bit = 12 -oracle(reg::DefaultRegister) = (reg.state[100:101,:]*=-1; reg) -target_state = zeros(1<U for i = 1:n}) \iff kron(n, \text{i=>U for i=1:n}) \iff U \otimes \dots \otimes U -# ``` - -#---------------- - -@doc Val - -#---------------- - -roll(4, i=>X for i = 1:4) - -#---------------- - -rollrepeat(4, X) - -#---------------- - -kron(4, i=>X for i = 1:4) - -# However, `kron` is calculated differently comparing to `roll`. -# In principal, **Roller** will be able to calculate small blocks -# with same size with higher efficiency. But for large blocks **Roller** -# may be slower. In **幺**, we offer you this freedom to choose the most suitable solution. -# -# -# all factory methods will **lazy** evaluate **the first arguements**, -# which is the number of qubits. It will return a lambda function that -# requires a single interger input. The instance of desired block will -# only be constructed until all the information is filled. - -rollrepeat(X) - -#--------------- - -rollrepeat(X)(4) - -# When you filled all the information in somewhere of the declaration, -# 幺 will be able to infer the others. - -chain(4, rollrepeat(X), rollrepeat(Y)) - -#--------------- - -# We will now define the rest of rotation layers - -layer(::Val{:last}) = rollrepeat(chain(Rz(0), Rx(0))) -layer(::Val{:mid}) = rollrepeat(chain(Rz(0), Rx(0), Rz(0))) - -# ### CNOT Entangler - -# Another component of quantum circuit born machine is several **CNOT** -# operators applied on different qubits. - -entangler(pairs) = chain(control([ctrl, ], target=>X) for (ctrl, target) in pairs) - -# We can then define such a born machine - -function QCBM(n, nlayer, pairs) - circuit = chain(n) - push!(circuit, layer(:first)) - - for i = 1:(nlayer - 1) - push!(circuit, cache(entangler(pairs))) - push!(circuit, layer(:mid)) - end - - push!(circuit, cache(entangler(pairs))) - push!(circuit, layer(:last)) - - circuit -end - -# We use the method `cache` here to tag the entangler block that it should be -# cached after its first run, because it is actually a constant oracle. -# Let's see what will be constructed - -QCBM(4, 1, [1=>2, 2=>3, 3=>4, 4=>1]) - -# ## MMD Loss & Gradients -# -# The MMD loss is describe below: -# -# ```math -# \begin{aligned} -# \mathcal{L} &= \left| \sum_{x} p \theta(x) \phi(x) - \sum_{x} \pi(x) \phi(x) \right|^2\\ -# &= \langle K(x, y) \rangle_{x \sim p_{\theta}, y\sim p_{\theta}} - 2 \langle K(x, y) -# \rangle_{x\sim p_{\theta}, y\sim \pi} + \langle K(x, y) \rangle_{x\sim\pi, y\sim\pi} -# \end{aligned} -# ``` -# -# -# We will use a squared exponential kernel here. - -struct Kernel - sigma::Float64 - matrix::Matrix{Float64} -end - -function Kernel(nqubits, sigma) - basis = collect(0:(1<2, 3=>4, 5=>6, 2=>3, 4=>5, 6=>1]); - -# Now, we define its shorthand - -get_prob(qcbm) = apply!(register(bit"0"^nqubits(qcbm)), qcbm) |> statevec .|> abs2 - -# We will first iterate through each layer contains rotation gates and allocate -# an array to store our gradient - -function gradient(n, nlayers, qcbm, kernel, ptrain) - prob = get_prob(qcbm) - grad = zeros(real(datatype(qcbm)), nparameters(qcbm)) - idx = 0 - for ilayer = 1:2:(2 * nlayers + 1) - idx = grad_layer!(grad, idx, prob, qcbm, qcbm[ilayer], kernel, ptrain) - end - grad -end - -# Then we iterate through each rotation gate. - -function grad_layer!(grad, idx, prob, qcbm, layer, kernel, ptrain) - count = idx - for each_line in blocks(layer) - for each in blocks(each_line) - gradient!(grad, count+1, prob, qcbm, each, kernel, ptrain) - count += 1 - end - end - count -end - -# We update each parameter by rotate it $-\pi/2$ and $\pi/2$ - -function gradient!(grad, idx, prob, qcbm, gate, kernel, ptrain) - dispatch!(+, gate, pi / 2) - prob_pos = get_prob(qcbm) - - dispatch!(-, gate, pi) - prob_neg = get_prob(qcbm) - - dispatch!(+, gate, pi / 2) # set back - - grad_pos = expect(kernel, prob, prob_pos) - expect(kernel, prob, prob_neg) - grad_neg = expect(kernel, ptrain, prob_pos) - expect(kernel, ptrain, prob_neg) - grad[idx] = grad_pos - grad_neg - grad -end - -# ## Optimizer -# -# We will use the Adam optimizer. Since we don't want you to install another package for this, -# the following code for this optimizer is copied from [Knet.jl](https://github.com/denizyuret/Knet.jl) -# -# Reference: [Kingma, D. P., & Ba, -# J. L. (2015)](https://arxiv.org/abs/1412.6980). Adam: a Method for -# Stochastic Optimization. International Conference on Learning -# Representations, 1–13. - -using LinearAlgebra - -mutable struct Adam - lr::AbstractFloat - gclip::AbstractFloat - beta1::AbstractFloat - beta2::AbstractFloat - eps::AbstractFloat - t::Int - fstm - scndm -end - -Adam(; lr=0.001, gclip=0, beta1=0.9, beta2=0.999, eps=1e-8)=Adam(lr, gclip, beta1, beta2, eps, 0, nothing, nothing) - -function update!(w, g, p::Adam) - gclip!(g, p.gclip) - if p.fstm===nothing; p.fstm=zero(w); p.scndm=zero(w); end - p.t += 1 - lmul!(p.beta1, p.fstm) - BLAS.axpy!(1-p.beta1, g, p.fstm) - lmul!(p.beta2, p.scndm) - BLAS.axpy!(1-p.beta2, g .* g, p.scndm) - fstm_corrected = p.fstm / (1 - p.beta1 ^ p.t) - scndm_corrected = p.scndm / (1 - p.beta2 ^ p.t) - BLAS.axpy!(-p.lr, @.(fstm_corrected / (sqrt(scndm_corrected) + p.eps)), w) -end - -function gclip!(g, gclip) - if gclip == 0 - g - else - gnorm = vecnorm(g) - if gnorm <= gclip - g - else - BLAS.scale!(gclip/gnorm, g) - end - end -end - -# The training of the quantum circuit is simple, just iterate through the steps. - -function train!(qcbm, ptrain, optim; learning_rate=0.1, niter=50) - # initialize the parameters - params = 2pi * rand(nparameters(qcbm)) - dispatch!(qcbm, params) - kernel = Kernel(nqubits(qcbm), 0.25) - - n, nlayers = nqubits(qcbm), (length(qcbm)-1)÷2 - history = Float64[] - - for i = 1:niter - grad = gradient(n, nlayers, qcbm, kernel, ptrain) - curr_loss = loss(qcbm, kernel, ptrain) - push!(history, curr_loss) - params = collect(parameters(qcbm)) - update!(params, grad, optim) - dispatch!(qcbm, params) - end - history -end - -#--------------------- - +nbit = 6 +N = 1< autodiff(:QC); +dispatch!(circuit, :random) +qcbm = QCBM(circuit, kernel, pg); + +# ## TRAINING: Adam Optimizer +# We probide the QCBMGo! iterative interface for training +niter = 100 optim = Adam(lr=0.1) -his = train!(circuit, pg, optim, niter=50, learning_rate=0.1) -plot(1:50, his, xlabel="iteration", ylabel="loss") - -#---------------------- - -p = get_prob(circuit) -p = plot(0:1< loss)") +end diff --git a/src/Adam.jl b/src/Adam.jl new file mode 100644 index 000000000..3183e70a6 --- /dev/null +++ b/src/Adam.jl @@ -0,0 +1,40 @@ +export Adam + +mutable struct Adam + lr::AbstractFloat + gclip::AbstractFloat + beta1::AbstractFloat + beta2::AbstractFloat + eps::AbstractFloat + t::Int + fstm + scndm +end + +Adam(; lr=0.001, gclip=0, beta1=0.9, beta2=0.999, eps=1e-8)=Adam(lr, gclip, beta1, beta2, eps, 0, nothing, nothing) + +function update!(w, g, p::Adam) + gclip!(g, p.gclip) + if p.fstm===nothing; p.fstm=zero(w); p.scndm=zero(w); end + p.t += 1 + lmul!(p.beta1, p.fstm) + BLAS.axpy!(1-p.beta1, g, p.fstm) + lmul!(p.beta2, p.scndm) + BLAS.axpy!(1-p.beta2, g .* g, p.scndm) + fstm_corrected = p.fstm / (1 - p.beta1 ^ p.t) + scndm_corrected = p.scndm / (1 - p.beta2 ^ p.t) + BLAS.axpy!(-p.lr, @.(fstm_corrected / (sqrt(scndm_corrected) + p.eps)), w) +end + +function gclip!(g, gclip) + if gclip == 0 + g + else + gnorm = vecnorm(g) + if gnorm <= gclip + g + else + BLAS.scale!(gclip/gnorm, g) + end + end +end diff --git a/src/CircuitBuild.jl b/src/CircuitBuild.jl new file mode 100644 index 000000000..3b77bd5ee --- /dev/null +++ b/src/CircuitBuild.jl @@ -0,0 +1,137 @@ +using StatsBase: sample + +export pair_ring, pair_square, cnot_entangler +export rotor, merged_rotor, rotorset +export random_diff_circuit +export rand_single_gate, rand_gate, rand_circuit + +################## Entangler ################### +""" + pair_ring(n::Int) -> Vector + +Pair ring. +""" +pair_ring(n::Int) = [i=>mod(i, n)+1 for i=1:n] + +""" + pair_square(m::Int, n::Int) -> Vector + +Pair square. +""" +function pair_square(m::Int, n::Int) + nsite = m*n + res = Vector{Pair{Int, Int}}(undef, 2*nsite) + li = LinearIndices((m, n)) + k = 1 + for i = 1:2:m, j=1:n + res[k] = li[i, j] => li[i%m+1, j] + k+=1 + end + for i = 2:2:m, j=1:n + res[k] = li[i, j] => li[i%m+1, j] + k+=1 + end + for i = 1:m, j=1:2:n + res[k] = li[i, j] => li[i, j%n+1] + k+=1 + end + for i = 1:m, j=2:2:n + res[k] = li[i, j] => li[i, j%n+1] + k+=1 + end + res +end + +""" + cnot_entangler([n::Int, ] pairs::Vector{Pair}) = ChainBlock + +Arbitrary rotation unit, support lazy construction. +""" +cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) +cnot_entangler(pairs) = n->cnot_entangler(n, pairs) + +###################### rotor and rotorset ##################### +""" + merged_rotor(noleading::Bool=false, notrailing::Bool=false) -> ChainBlock{1, ComplexF64} + +Single qubit arbitrary rotation unit, set parameters notrailing, noleading true to remove trailing and leading Z gates. + +!!! note + + Here, `merged` means `Rz(η)⋅Rx(θ)⋅Rz(ξ)` are multiplied first, this kind of operation if now allowed in differentiable + circuit with back-propagation (`:BP`) mode (just because we are lazy to implement it!). + But is a welcoming component in quantum differentiation. +""" +merged_rotor(noleading::Bool=false, notrailing::Bool=false) = noleading ? (notrailing ? Rx(0) : chain(Rx(0), Rz(0))) : (notrailing ? chain(Rz(0), Rx(0)) : chain(Rz(0), Rx(0), Rz(0))) + +""" + rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) -> ChainBlock{nbit, ComplexF64} + +Arbitrary rotation unit (put in `nbit` space), set parameters notrailing, noleading true to remove trailing and leading Z gates. +""" +function rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) + rt = chain(nbit, [put(nbit, ibit=>Rz(0.0)), put(nbit, ibit=>Rx(0.0)), put(nbit, ibit=>Rz(0.0))]) + noleading && popfirst!(rt) + notrailing && pop!(rt) + rt +end + +rotorset(::Val{:Merged}, nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [put(nbit, j=>merged_rotor(noleading, notrailing)) for j=1:nbit]) +rotorset(::Val{:Split}, nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [rotor(nbit, j, noleading, notrailing) for j=1:nbit]) +rotorset(mode::Symbol, nbit::Int, noleading::Bool=false, notrailing::Bool=false) = rotorset(Val(mode), nbit, noleading, notrailing) + +""" + qdiff_circuit(n, nlayer, pairs) -> ChainBlock + +A kind of widely used differentiable quantum circuit, angles in the circuit is randomely initialized. + +ref: + 1. Kandala, A., Mezzacapo, A., Temme, K., Takita, M., Chow, J. M., & Gambetta, J. M. (2017). + Hardware-efficient Quantum Optimizer for Small Molecules and Quantum Magnets. Nature Publishing Group, 549(7671), 242–246. + https://doi.org/10.1038/nature23879. +""" +function random_diff_circuit(nbit, nlayer, pairs; mode=:Split) + circuit = chain(nbit) + + ent = cnot_entangler(pairs) |> cache + for i = 1:(nlayer + 1) + i!=1 && push!(circuit, ent) + push!(circuit, rotorset(mode, nbit, i==1, i==nlayer+1)) + end + circuit +end + +############### Completely random circuits (for testing and demo) ################ +randlocs(nbit::Int, mbit::Int) = sample(1:nbit, mbit, replace=false) +const SINGLE_GATES = [X, Y, Z, H, Rx, Ry, Rz, shift, phase] + +rand_single_gate(ngate::Int) = [rand_single_gates() for i=1:ngate] +function rand_single_gate() + gate = rand(SINGLE_GATES) + gate isa AbstractBlock ? gate : gate(rand()*2π) +end + +""" + rand_gate(nbit::Int, mbit::Int, [ngate::Int]) -> MatrixBlock + +random nbit gate. +""" +rand_gate(nbit::Int, mbit::Int) = rand_gate(nbit, Val(mbit)) +rand_gate(nbit::Int, mbit::Int, ngate::Int) = [rand_gate(nbit, mbit) for i=1:ngate] +rand_gate(nbit::Int, ::Val{1}) = put(nbit, rand(1:nbit)=>rand_single_gate()) +function rand_gate(nbit::Int, ::Val{M}) where M + locs = randlocs(nbit, M) + control(nbit, locs[1:M-1], last(locs)=>rand_single_gate()) +end + +function rand_circuit(nbit::Int; p1 = (nbit==1 ? 1.0 : 0.66), ngate=5*nbit) + c = chain(nbit) + for i=1:ngate + if rand() < p1 + push!(c, rand_gate(nbit, 1)) + else + push!(c, rand_gate(nbit, 2)) + end + end + c +end diff --git a/src/Differential.jl b/src/Differential.jl deleted file mode 100644 index 9ae85161e..000000000 --- a/src/Differential.jl +++ /dev/null @@ -1,95 +0,0 @@ -export diff_circuit, num_gradient, rotter, cnot_entangler, opgrad, collect_rotblocks, perturb - -""" - rotter(noleading::Bool=false, notrailing::Bool=false) -> ChainBlock{1, ComplexF64} - -Arbitrary rotation unit, set parameters notrailing, noleading true to remove trailing and leading Z gates. -""" -rotter(noleading::Bool=false, notrailing::Bool=false) = noleading ? (notrailing ? Rx(0) : chain(Rx(0), Rz(0))) : (notrailing ? chain(Rz(0), Rx(0)) : chain(Rz(0), Rx(0), Rz(0))) - -""" - cnot_entangler([n::Int, ] pairs::Vector{Pair}) = ChainBlock - -Arbitrary rotation unit, support lazy construction. -""" -cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) -cnot_entangler(pairs) = n->cnot_entangler(n, pairs) - -""" - diff_circuit(n, nlayer, pairs) -> ChainBlock - -A kind of widely used differentiable quantum circuit, angles in the circuit is randomely initialized. - -ref: - 1. Kandala, A., Mezzacapo, A., Temme, K., Takita, M., Chow, J. M., & Gambetta, J. M. (2017). - Hardware-efficient Quantum Optimizer for Small Molecules and Quantum Magnets. Nature Publishing Group, 549(7671), 242–246. - https://doi.org/10.1038/nature23879. -""" -function diff_circuit(n, nlayer, pairs) - circuit = chain(n) - - for i = 1:(nlayer + 1) - if i!=1 push!(circuit, cnot_entangler(pairs) |> cache) end - push!(circuit, chain(n, [put(n, j=>rotter(i==1, i==nlayer+1)) for j=1:n])) - #for j = 1:n - # push!(circuit, put(n, j=>rotter(i==1, i==nlayer+1))) - #end - end - dispatch!(circuit, rand(nparameters(circuit))*2π) -end - -""" - collect_rotblocks(blk::AbstractBlock) -> Vector{RotationGate} - -filter out all rotation gates, which is differentiable. -""" -function collect_rotblocks(blk::AbstractBlock) - rots = blockfilter!(x->x isa RotationGate, Vector{RotationGate}([]), blk) - nparameters(blk)==length(rots) || @warn "some parameters in this circuit are not differentiable!" - rots -end - -""" - perturb(func, gates::Vector{<:RotationGate}, diff::Real) -> Matrix - -perturb every rotation gates, and evaluate losses. -The i-th element of first column of resulting Matrix corresponds to Gi(θ+δ), and the second corresponds to Gi(θ-δ). -""" -function perturb(func, gates::Vector{<:RotationGate}, diff::Real) - ng = length(gates) - res = Matrix{Float64}(undef, ng, 2) - for i in 1:ng - gate = gates[i] - setiparameters!(+, gate, diff) - res[i, 1] = func() - - setiparameters!(+, gate, -2*diff) - res[i, 2] = func() - - setiparameters!(+, gate, diff) # set back - end - res -end - -""" - num_gradient(lossfunc, rots::Vector{<:RotationGate}, δ::Float64=1e-2) -> Vector - -Compute gradient numerically. -""" -function num_gradient(lossfunc, rots::Vector{<:RotationGate}, δ::Float64=1e-2) - gperturb = perturb(lossfunc, rots, δ) - (gperturb[:,1] - gperturb[:,2])/(2δ) -end - -""" - opgrad(op_expect, rots::Vector{<:RotationGate}) -> Vector - -get the gradient of an operator expectation function. - -References: - Mitarai, K., Negoro, M., Kitagawa, M., & Fujii, K. (2018). Quantum Circuit Learning, 1–3. Retrieved from http://arxiv.org/abs/1803.00745 -""" -function opgrad(op_expect, rots::Vector{<:RotationGate}) - gperturb = perturb(op_expect, rots, π/2) - (gperturb[:,1] - gperturb[:,2])/2 -end diff --git a/src/HHL.jl b/src/HHL.jl index 60b709852..d1c883629 100644 --- a/src/HHL.jl +++ b/src/HHL.jl @@ -71,7 +71,7 @@ function hhlsolve(A::Matrix, b::Vector, n_reg::Int, C_value::Real) UG = matrixgate(exp(2π*im.*A)) # Generating input bits - all_bit = zero_state(1) ⊗ zero_state(n_reg) ⊗ register(b) + all_bit = register(b) ⊗ zero_state(n_reg) ⊗ zero_state(1) # Construct HHL circuit. circuit = hhlcircuit(UG, n_reg, C_value) diff --git a/src/Kernels.jl b/src/Kernels.jl new file mode 100644 index 000000000..9abad2d2d --- /dev/null +++ b/src/Kernels.jl @@ -0,0 +1,38 @@ +export AbstractKernel, RBFKernel, kmat, rbf_kernel, kernel_expect + +abstract type AbstractKernel end + +""" + RBFKernel(σ, matrix) + +RBF Kernel with dense array as kernel matrix. +""" +struct RBFKernel <: AbstractKernel + sigma::Float64 + matrix::Matrix{Float64} +end + +""" + kmat(k::AbstractKernel) -> Matrix + +Returns Kernel Matrix. +""" +function kmat end +kmat(mbf::RBFKernel) = mbf.matrix + +""" + rbf_kernel(basis, σ::Real) -> RBFKernel + +Returns RBF Kernel Matrix. +""" +function rbf_kernel(basis, σ::Real) + dx2 = (basis .- basis').^2 + RBFKernel(σ, exp.(-1/2σ * dx2)) +end + +""" + kernel_expect(kernel::AbstractKernel, px::Vector{Float64}, py::Vector{Float64}) -> Float64 + +Returns the expectation value of kernel on specific distributions. +""" +kernel_expect(kernel::AbstractKernel, px::Vector, py::Vector=px) = px' * kmat(kernel) * py diff --git a/src/Math.jl b/src/Math.jl deleted file mode 100644 index 2c9307b57..000000000 --- a/src/Math.jl +++ /dev/null @@ -1,34 +0,0 @@ -""" - rand_unitary(N::Int) -> Matrix - -Random unitary matrix. -""" -function rand_unitary(N::Int) - qr(randn(ComplexF64, N, N)).Q |> Matrix -end - -""" - rand_hermitian(N::Int) -> Matrix - -Random hermitian matrix. -""" -function rand_hermitian(N::Int) - A = randn(ComplexF64, N, N) - A + A' -end - -""" -Function to make a unify matrixgate. - -TODO: -deprecated this function. -""" -function matrix_unify(A) - U = exp(2π*im.*A) - UG = matrixgate(U) - UG -end - -############ Leo's Magic Tricks ################ -Base.:(|>)(reg::AbstractRegister, circuit::AbstractBlock) = apply!(reg, circuit) -⊗(reg::AbstractRegister, reg2::AbstractRegister) = join(reg, reg2) diff --git a/src/PhaseEstimation.jl b/src/PhaseEstimation.jl index 95d1ba134..ef8b148f1 100644 --- a/src/PhaseEstimation.jl +++ b/src/PhaseEstimation.jl @@ -34,7 +34,7 @@ Analyse using state projection. It returns a tuple of (most probable configuration, the overlap matrix, the relative probability for this configuration) """ function projection_analysis(evec::Matrix, reg::DefaultRegister) - overlap = register(evec)'*reg + overlap = evec'*state(reg) amp_relative = Float64[] bs = Int[] diff --git a/src/QCBM.jl b/src/QCBM.jl new file mode 100644 index 000000000..b38ea53d7 --- /dev/null +++ b/src/QCBM.jl @@ -0,0 +1,72 @@ +import Yao.Registers: probs +export QCBM, QCBMGo!, psi, mmdgrad + +include("Kernels.jl") + +##### QCBM methods ##### +struct QCBM{BT<:AbstractBlock, KT<:AbstractKernel} <: QCOptProblem + circuit::BT + kernel::KT + ptrain::Vector{Float64} + + dbs +end +function QCBM(circuit::AbstractBlock, kernel::AbstractKernel, ptrain::Vector) + QCBM(circuit, kernel, ptrain, collect(circuit, AbstractDiff)) +end + +# INTERFACES +circuit(qcbm::QCBM) = qcbm.circuit +diff_blocks(qcbm::QCBM) = qcbm.dbs +""" + loss(qcbm::QCBM, [p=qcbm|>probs]) -> Float64 + +loss function, optional parameter `p` is the probability distribution. +""" +function loss(qcbm::QCBM, p=qcbm|>probs) + p -= qcbm.ptrain + kernel_expect(qcbm.kernel, p, p) +end +""" + gradient(qcbm::QCBM, p0=qcbm |> probs) -> Vector + +gradient of MMD two sample test loss, `db` must be contained in qcbm. +optional parameter `p0` is current probability distribution. +""" +gradient(qcbm::QCBM, p0=qcbm |> probs) = mmdgrad.(Ref(qcbm), qcbm.dbs, p0=p0) + +"""generated wave function""" +psi(qcbm) = zero_state(qcbm.circuit |> nqubits) |> qcbm.circuit +"""generated probability distribution""" +probs(qcbm::QCBM) = qcbm |> psi |> probs +""" + mmdgrad(qcbm::QCBM, db::AbstractDiff; p0::Vector) -> Float64 + +gradient of MMD two sample test loss, `db` must be contained in qcbm. +`p0` is current probability distribution. +""" +function mmdgrad(qcbm::QCBM, db::AbstractDiff; p0::Vector) + vstatdiff(()->psi(qcbm), db, Vstat(kmat(qcbm.kernel)), p0=p0) - 2*vstatdiff(()->psi(qcbm), db, Vstat(kmat(qcbm.kernel)*qcbm.ptrain)) +end + +""" +quantum circuit born machine trainer. +""" +struct QCBMGo!{QT<:QCBM, OT} <: QCOptGo!{QT} + qcbm::QT + optimizer::OT + niter::Int +end +QCBMGo!(qcbm::QCBM, optimizer) = QCBMGo!(qcbm, optimizer, typemax(Int)) +Base.length(qo::QCBMGo!) = qo.niter + +function Base.iterate(qo::QCBMGo!, state=(1, parameters(qo.qcbm.circuit))) + state[1] > qo.niter && return nothing + + # initialize the parameters + p0 = qo.qcbm |> probs + grad = gradient(qo.qcbm, p0) + update!(state[2], grad, qo.optimizer) + dispatch!(qo.qcbm.circuit, state[2]) + Dict("probs"=>p0, "step"=>state[1], "gradient"=>grad), (state[1]+1, state[2]) +end diff --git a/src/QCOptProblem.jl b/src/QCOptProblem.jl new file mode 100644 index 000000000..b96b696e1 --- /dev/null +++ b/src/QCOptProblem.jl @@ -0,0 +1,57 @@ +import Yao: gradient +export num_gradient, diff_blocks, loss, circuit, QCOptProblem, QCOptGo! + +""" + QCOptProblem + +General quantum circuit optimization problem interface +""" +abstract type QCOptProblem end + +""" + circuit(qop::QCOptProblem) -> AbstractBlock + +circuit to optimize +""" +function circuit end + +""" + loss(qop::QCOptProblem) -> Real + +Return the loss. +""" +function loss end + +##################################################### + +""" + gradient(qop::QCOptProblem) -> Vector + +the gradients with respect to `diff_blocks`. +""" +function gradient end + +""" + diff_blocks(qop::QCOptProblem) -> iterable + +collection of all differentiable units. +""" +diff_blocks(qop::QCOptProblem) = collect(qop |> circuit, AbstractDiff) + +""" + num_gradient(qop::QCOptProblem) -> Vector + +obtain the gradient numerically +""" +num_gradient(qop::QCOptProblem) = numdiff.(()->loss(qop), qop |> diff_blocks) + +################# Optimization ################### +""" + QCOptGo!{QT} + +quantum circuit optimization problem optimizer. +""" +abstract type QCOptGo!{QT} end + +include("QuGAN.jl") +include("QCBM.jl") diff --git a/src/QFT.jl b/src/QFT.jl index 7f611c38e..37123bef3 100644 --- a/src/QFT.jl +++ b/src/QFT.jl @@ -20,7 +20,7 @@ isreflexive(q::QFTBlock{N}) where N = N==1 isunitary(q::QFTBlock{N}) where N = true openbox(q::QFTBlock{N}) where N = QFTCircuit(N) -openbox(q::Daggered{N,T,<:QFTBlock}) where {N, T} = adjoint(QFTCircuit(N)) +openbox(q::Daggered{<:QFTBlock, N}) where {N} = adjoint(QFTCircuit(N)) function print_block(io::IO, pb::QFTBlock{N}) where N printstyled(io, "QFT(1-$N)"; bold=true, color=:blue) diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index b1a7c8cbf..2592ecb9e 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -12,13 +12,17 @@ import Yao.Intrinsics: ishermitian, isreflexive, isunitary export openbox """ + openbox(block::AbstractBlock) -> AbstractBlock + For a black box, like QFTBlock, you can get its white box (loyal simulation) using this function. """ function openbox end include("Miscellaneous.jl") +include("Adam.jl") include("QFT.jl") -include("Differential.jl") +include("CircuitBuild.jl") +include("QCOptProblem.jl") include("RotBasis.jl") include("Grover.jl") include("PhaseEstimation.jl") diff --git a/src/QuGAN.jl b/src/QuGAN.jl new file mode 100644 index 000000000..7db37b6f3 --- /dev/null +++ b/src/QuGAN.jl @@ -0,0 +1,103 @@ +import Yao.Registers: tracedist + +export QuGAN, psi, toy_qugan, QuGANGo! +""" +Quantum GAN. + +Reference: + Benedetti, M., Grant, E., Wossnig, L., & Severini, S. (2018). Adversarial quantum circuit learning for pure state approximation, 1–14. +""" +struct QuGAN{N} <: QCOptProblem + target::DefaultRegister + generator::MatrixBlock{N} + discriminator::MatrixBlock + reg0::DefaultRegister + witness_op::MatrixBlock + circuit::AbstractBlock + gdiffs + ddiffs + + function QuGAN(target::DefaultRegister, gen::MatrixBlock, dis::MatrixBlock) + N = nqubits(target) + c = sequence(gen, addbit(1), dis) + witness_op = put(N+1, (N+1)=>P0) + gdiffs = collect(gen, AbstractDiff) + ddiffs = collect(dis, AbstractDiff) + new{N}(target, gen, dis, zero_state(N), witness_op, c, gdiffs, ddiffs) + end +end + +# INTERFACES +circuit(qg::QuGAN) = qg.circuit +diff_blocks(qg::QuGAN) = [qg.gdiffs...; qg.ddiffs...] +loss(qg::QuGAN) = p0t(qg) - p0g(qg) +function gradient(qg::QuGAN) + ggrad_g = opdiff.(()->psi_discgen(qg), qg.gdiffs, Ref(qg.witness_op)) + dgrad_g = opdiff.(()->psi_discgen(qg), qg.ddiffs, Ref(qg.witness_op)) + dgrad_t = opdiff.(()->psi_disctarget(qg), qg.ddiffs, Ref(qg.witness_op)) + [-ggrad_g; dgrad_t - dgrad_g] +end + +"""probability to get evidense qubit 0 on generation set.""" +p0g(qg::QuGAN) = expect(qg.witness_op, psi_discgen(qg)) |> real +"""probability to get evidense qubit 0 on target set.""" +p0t(qg::QuGAN) = expect(qg.witness_op, psi_disctarget(qg)) |> real +"""generated wave function""" +psi(qg::QuGAN) = copy(qg.reg0) |> qg.generator +"""input |> generator |> discriminator""" +psi_discgen(qg::QuGAN) = copy(qg.reg0) |> qg.circuit +"""target |> discriminator""" +psi_disctarget(qg::QuGAN) = copy(qg.target) |> qg.circuit[2:end] +"""tracedistance between target and generated wave function""" +tracedist(qg::QuGAN) = tracedist(qg.target, psi(qg))[] + +""" + toy_qugan(target::DefaultRegister, depth_gen::Int, depth_disc::Int) -> QuGAN + +Construct a toy qugan. +""" +function toy_qugan(target::DefaultRegister, depth_gen::Int, depth_disc::Int) + n = nqubits(target) + generator = dispatch!(random_diff_circuit(n, depth_gen, pair_ring(n)), :random) |> autodiff(:QC) + discriminator = dispatch!(random_diff_circuit(n+1, depth_disc, pair_ring(n+1)), :random) |> autodiff(:QC) + return QuGAN(target, generator, discriminator) +end + +# Optimization +""" + QuGANGo!{QT<:QuGAN, OT} <: QCOptGo!{QT} + +Iterative training of quantum generative optimization problem, +QT is the type of quantum optimization problem, +OT is the optimizer/learning_rate parameter type. +""" +struct QuGANGo!{QT<:QuGAN, OT} <: QCOptGo!{QT} + qop::QT + goptim::OT + doptim::OT + niter::Int +end +QuGANGo!(qg::QuGAN, glr, dlr) = QuGANGo!(qg, glr, dlr, typemax(Int)) +Base.length(qgg::QuGANGo!) = qgg.niter + +function Base.iterate(qgg::QuGANGo!{<:Any, <:Real}, state=1) + state > qgg.niter && return nothing + + qg = qgg.qop + ng = length(qg.gdiffs) + grad = gradient(qg) + dispatch!(+, qg.generator, -grad[1:ng]*qgg.goptim) + dispatch!(-, qg.discriminator, -grad[ng+1:end]*qgg.doptim) + Dict("step"=>state,"gradient"=>grad), state+1 +end + +function Base.iterate(qgg::QuGANGo!{<:Any, <:Adam}, state=(1, parameters(qgg.qop.generator), parameters(qgg.qop.discriminator))) + state[1] > qgg.niter && return nothing + + qg = qgg.qop + ng = length(qg.gdiffs) + grad = gradient(qg) + dispatch!(qg.generator, update!(state[2], grad[1:ng], qgg.goptim)) + dispatch!(qg.discriminator, update!(state[3], -grad[ng+1:end], qgg.doptim)) + Dict("step"=>state[1], "gradient"=>grad), (state[1]+1, state[2], state[3]) +end diff --git a/src/RotBasis.jl b/src/RotBasis.jl index 00d23fe71..bdde812da 100644 --- a/src/RotBasis.jl +++ b/src/RotBasis.jl @@ -21,14 +21,15 @@ end ==(rb1::RotBasis, rb2::RotBasis) = rb1.theta == rb2.theta && rb1.phi == rb2.phi copy(block::RotBasis{T}) where T = RotBasis{T}(block.theta, block.phi) -dispatch!(block::RotBasis, params) = ((block.theta, block.phi) = params; block) +dispatch!(block::RotBasis, params::Vector) = ((block.theta, block.phi) = params; block) iparameters(rb::RotBasis) = (rb.theta, rb.phi) -function setiparameters!(rb::RotBasis, params) - rb.theta, rb.phi = params +function setiparameters!(rb::RotBasis, theta::Real, phi::Real) + rb.theta, rb.phi = theta, phi rb end niparameters(::Type{<:RotBasis}) = 2 +render_params(r::RotBasis, ::Val{:random}) = rand()*π, rand()*2π function print_block(io::IO, R::RotBasis) print(io, "RotBasis($(R.theta), $(R.phi))") diff --git a/test/CircuitBuild.jl b/test/CircuitBuild.jl new file mode 100644 index 000000000..b87e6c2d7 --- /dev/null +++ b/test/CircuitBuild.jl @@ -0,0 +1,65 @@ +using Test +using Yao, Yao.Blocks, QuAlgorithmZoo + +@testset "pairs geometries" begin + @test pair_ring(3) == [1=>2,2=>3,3=>1] + ps = pair_square(2, 2) + @test length(ps) == 8 + for item in [1=>2, 3=>4, 2=>1, 4=>3, 1=>3, 2=>4, 3=>1, 4=>2] + @test item in ps + end + @test cnot_entangler(4, ps) isa ChainBlock + @test cnot_entangler(4, ps) |> length == 8 +end + +@testset "random circuit" begin + c = rand_circuit(1) + @test c isa ChainBlock + @test length(c) == 5 + c = rand_circuit(9) + @test c isa ChainBlock + @test length(c) == 45 +end + +@testset "rotter, collect_rotblocks, num_gradient, opgrad" begin + @test merged_rotor(true, true) == Rx(0) + @test merged_rotor(false, false) == merged_rotor() == chain(Rz(0), Rx(0), Rz(0)) + @test merged_rotor(false, true) == chain(Rz(0), Rx(0)) + @test merged_rotor(true, false) == chain(Rx(0), Rz(0)) + @test collect(rotorset(:Merged, 5, true, false), RotationGate) |> length == 10 + + @test rotor(5, 2, true, true) isa ChainBlock + @test rotor(5, 2, true, true) |> length == 1 + @test rotor(5, 2, true, true) |> nqubits == 5 + @test collect(rotorset(:Split, 5, true, false), PutBlock{<:Any, <:Any, <:RotationGate}) |> length == 10 +end + +@testset "random diff circuit" begin + c = random_diff_circuit(4, 3, [1=>3, 2=>4, 2=>3, 4=>1]) + rots = collect(c, RotationGate) + @test length(rots) == nparameters(c) == 40 + @test dispatch!(+, c, ones(40)*0.1) |> parameters == ones(40)*0.1 + @test dispatch!(+, c, :random) |> parameters != ones(40)*0.1 + + nbit = 4 + c = random_diff_circuit(nbit, 1, pair_ring(nbit), mode=:Split) |> autodiff(:BP) + reg = rand_state(4) + dispatch!(c, randn(nparameters(c))) + + dbs = collect(c, BPDiff) + op = kron(4, 1=>Z, 2=>X) + loss1z() = expect(op, copy(reg) |> c) # return loss please + + # back propagation + ψ = copy(reg) |> c + δ = copy(ψ) |> op + backward!(δ, c) + bd = gradient(c) + + # get num gradient + nd = numdiff.(loss1z, dbs) + ed = opdiff.(()->copy(reg)|>c, dbs, Ref(op)) + + @test isapprox.(nd, ed, atol=1e-4) |> all + @test isapprox.(nd, bd, atol=1e-4) |> all +end diff --git a/test/Differential.jl b/test/Differential.jl deleted file mode 100644 index ff5533fe2..000000000 --- a/test/Differential.jl +++ /dev/null @@ -1,21 +0,0 @@ -using Test, Random, LinearAlgebra, SparseArrays - - -using Yao, QuAlgorithmZoo, Yao.Blocks - -@testset "rotter, collect_rotblocks, num_gradient, opgrad" begin - c = diff_circuit(4, 3, [1=>3, 2=>4, 2=>3, 4=>1]) - rots = collect_rotblocks(c) - @test length(rots) == nparameters(c) == 40 - - obs = kron(nqubits(c), 2=>X) - #@test mean(opgrad(()->expect(obs, apply!(zero_state(4), c))|>real, rots) .|> abs) > 3e-4 - gradA = opgrad(()->expect(obs, apply!(zero_state(4), c)) |> real, rots) - gradB = num_gradient(()->expect(obs, apply!(zero_state(4),c)) |> real, rots) - @test isapprox(gradA, gradB, atol=1e-4) - - @test rotter(true, true) == Rx(0) - @test rotter(false, false) == rotter() == chain(Rz(0), Rx(0), Rz(0)) - @test rotter(false, true) == chain(Rz(0), Rx(0)) - @test rotter(true, false) == chain(Rx(0), Rz(0)) -end diff --git a/test/Grover.jl b/test/Grover.jl index 586fb4e74..f8e06a1ec 100644 --- a/test/Grover.jl +++ b/test/Grover.jl @@ -2,7 +2,7 @@ using Test, Random, LinearAlgebra, SparseArrays using QuAlgorithmZoo import QuAlgorithmZoo: _num_grover_step -using Yao.Blocks +using Yao, Yao.Blocks using Yao.Intrinsics function GroverSearch(oracle, num_bit::Int; psi::DefaultRegister = uniform_state(num_bit)) diff --git a/test/PhaseEstimation.jl b/test/PhaseEstimation.jl index 54360ced7..07c80a9c8 100644 --- a/test/PhaseEstimation.jl +++ b/test/PhaseEstimation.jl @@ -37,13 +37,13 @@ end circuit = PEBlock(UG, M, N) # run - reg = apply!(join(reg1, reg2), circuit) + reg = apply!(join(reg2, reg1), circuit) # measure res = breflect(M, measure(focus!(copy(reg), 1:M), 10)[1]) / (1< adjoint) ≈ join(reg1, reg2) + @test apply!(reg, circuit |> adjoint) ≈ join(reg2, reg1) end @testset "phaseest, non-eigen" begin @@ -58,7 +58,7 @@ end UG = matrixgate(U); # run circuit - reg= join(reg1, reg2) + reg= join(reg2, reg1) pe = PEBlock(UG, M, N) apply!(reg, pe) diff --git a/test/QCBM.jl b/test/QCBM.jl new file mode 100644 index 000000000..0f523ecb8 --- /dev/null +++ b/test/QCBM.jl @@ -0,0 +1,32 @@ +using Test +using Yao +using QuAlgorithmZoo + +""" + gaussian_pdf(x, μ::Real, σ::Real) + +gaussian probability density function. +""" +function gaussian_pdf(x, μ::Real, σ::Real) + pl = @. 1 / sqrt(2pi * σ^2) * exp(-(x - μ)^2 / (2 * σ^2)) + pl / sum(pl) +end + +@testset "qcbm" begin + # problem setup + n = 6 + depth = 6 + + N = 1< autodiff(:QC) + dispatch!(circuit, :random) + qcbm = QCBM(circuit, kernel, pg) + + # training + niter = 100 + optim = Adam(lr=0.1) + for info in QCBMGo!(qcbm, optim, niter) end + @test qcbm |> loss < 1e-4 +end diff --git a/test/QCOptProblem.jl b/test/QCOptProblem.jl new file mode 100644 index 000000000..153ba97f5 --- /dev/null +++ b/test/QCOptProblem.jl @@ -0,0 +1,7 @@ +@testset "QuGAN" begin + include("QuGAN.jl") +end + +@testset "QCBM" begin + include("QCBM.jl") +end diff --git a/test/QuGAN.jl b/test/QuGAN.jl new file mode 100644 index 000000000..9fffa5ba9 --- /dev/null +++ b/test/QuGAN.jl @@ -0,0 +1,22 @@ +using Test +using Yao +using QuAlgorithmZoo + +function run_test(nbit::Int, depth_gen::Int, depth_disc::Int; g_lr=0.1, d_lr=0.2, niter=1000) + qg = toy_qugan(rand_state(nbit), depth_gen, depth_disc) + for info in QuGANGo!(qg, g_lr, d_lr, niter) end + qg +end + +@testset "quantum circuit gan - opdiff" begin + N = 3 + target = rand_state(N) + qcg = toy_qugan(target, 2, 2) + grad = gradient(qcg) + @test isapprox(grad, num_gradient(qcg), atol=1e-4) + qg = run_test(3, 4, 4, g_lr=0.2, d_lr=0.5, niter=300) + @test qg |> loss < 0.1 + qg = run_test(3, 4, 4, g_lr=Adam(lr=0.005), d_lr=Adam(lr=0.5), niter=300) + @test qg |> loss < 0.1 +end + diff --git a/test/RotBasis.jl b/test/RotBasis.jl index 8b3cdbc41..6ad3ed6ea 100644 --- a/test/RotBasis.jl +++ b/test/RotBasis.jl @@ -25,6 +25,10 @@ using QuAlgorithmZoo @test state(apply!(psi, rb)) ≈ [1, 0] @test nparameters(rot_basis(3)) == 6 + dispatch!(rb, :zero) + @test parameters(rb)[1] == 0 + dispatch!(rb, :random) + @test parameters(rb)[1] != 0 end @testset "polar and u" begin diff --git a/test/runtests.jl b/test/runtests.jl index 294101484..e0ae55c4e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,8 +9,8 @@ using QuAlgorithmZoo include("QFT.jl") end -@testset "Differential" begin - include("Differential.jl") +@testset "CircuitBuild" begin + include("CircuitBuild.jl") end @testset "RotBasis" begin @@ -28,3 +28,7 @@ end @testset "HHL" begin include("HHL.jl") end + +@testset "QCOptProblem" begin + include("QCOptProblem.jl") +end From ed20cae19e4b12a8a6a6ac9d4adbbd2824983c30 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 22 Oct 2018 20:25:55 +0800 Subject: [PATCH 33/85] new hamiltonian solvers --- src/QuAlgorithmZoo.jl | 1 + src/hamiltonian_solvers.jl | 46 +++++++++++++++++++++++++++++++++++++ test/hamiltonian_solvers.jl | 22 ++++++++++++++++++ test/runtests.jl | 4 ++++ 4 files changed, 73 insertions(+) create mode 100644 src/hamiltonian_solvers.jl create mode 100644 test/hamiltonian_solvers.jl diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index 2592ecb9e..d9b2f1001 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -27,6 +27,7 @@ include("RotBasis.jl") include("Grover.jl") include("PhaseEstimation.jl") include("HHL.jl") +include("hamiltonian_solvers.jl") end # module diff --git a/src/hamiltonian_solvers.jl b/src/hamiltonian_solvers.jl new file mode 100644 index 000000000..f176f5a45 --- /dev/null +++ b/src/hamiltonian_solvers.jl @@ -0,0 +1,46 @@ +export heisenberg, iter_groundstate!, itime_groundstate! + +""" + heisenberg(nbit::Int; periodic::Bool=true) + +heisenberg hamiltonian, for its ground state, refer `PRB 48, 6141`. +""" +function heisenberg(nbit::Int; periodic::Bool=true) + sx = i->put(nbit, i=>X) + sy = i->put(nbit, i=>Y) + sz = i->put(nbit, i=>Z) + mapreduce(i->(j=i%nbit+1; sx(i)*sx(j)+sy(i)*sy(j)+sz(i)*sz(j)), +, 1:(periodic ? nbit : nbit-1)) +end + +""" + iter_groundstate!({reg::AbstractRegister}, h::MatrixBlock; niter::Int=100) -> AbstractRegister + +project wave function to ground state by iteratively apply -h. +""" +iter_groundstate!(h::MatrixBlock; niter::Int=100) = reg -> iter_groundstate!(reg, h, niter=niter) +function iter_groundstate!(reg::AbstractRegister, h::MatrixBlock; niter::Int=100) + for i = 1:niter + reg |> h + i%5 == 0 && reg |> normalize! + end + reg |> normalize! +end + +""" + itime_groundstate!({reg::AbstractRegister}, h::MatrixBlock; τ::Int=20, tol=1e-4) -> AbstractRegister + +project wave function to ground state by exp(-hτ). `tol` is for `expmv`. +""" +itime_groundstate!(h::MatrixBlock; τ::Real=20, tol=1e-4) = reg -> itime_groundstate!(reg, h; τ=τ, tol=tol) +function itime_groundstate!(reg::AbstractRegister, h::MatrixBlock; τ::Int=20, tol=1e-4) + span = 1 + te = timeevolve(h, -im*span) + for i = 1:τ÷span + reg |> te |> normalize! + end + if τ%span != 0 + reg |> timeevolve(h, τ%span) |> normalize! + end + reg +end + diff --git a/test/hamiltonian_solvers.jl b/test/hamiltonian_solvers.jl new file mode 100644 index 000000000..44b43b65e --- /dev/null +++ b/test/hamiltonian_solvers.jl @@ -0,0 +1,22 @@ +using Yao, Yao.Blocks +using LinearAlgebra +using Test +using QuAlgorithmZoo + +@testset "solving hamiltonian" begin + nbit = 8 + h = heisenberg(nbit) |> cache + @test ishermitian(h) + reg = rand_state(nqubits(h)) + E0 = expect(h, reg)/nbit + reg |> iter_groundstate!(h, niter=1000) + EG = expect(h, reg)/nbit/4 + @test isapprox(EG, -0.4564, atol=1e-4) + + # using Time Evolution + reg = rand_state(nqubits(h)) + reg |> itime_groundstate!(h, τ=20) + EG = expect(h, reg)/nbit/4 + @test isapprox(EG, -0.4564, atol=1e-4) +end + diff --git a/test/runtests.jl b/test/runtests.jl index e0ae55c4e..c87f836ad 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -32,3 +32,7 @@ end @testset "QCOptProblem" begin include("QCOptProblem.jl") end + +@testset "hamiltonian solvers" begin + include("hamiltonian_solvers.jl") +end From e6be71d85937dc4d619773748f2cea00699be37c Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 26 Oct 2018 19:48:31 +0800 Subject: [PATCH 34/85] Phase estimation and HHL algorithm (#2) * new HHL * fix example for v0.3 compacity * update QCGAN, new QuGAN * new hamiltonian solvers --- Project.toml | 3 +- docs/make.jl | 3 + docs/src/index.md | 18 ++ docs/src/man/zoo.md | 6 + examples/Grover.jl | 35 ++++ examples/QCBM.jl | 391 +++--------------------------------- examples/QuGAN.jl | 29 +++ src/Adam.jl | 40 ++++ src/CircuitBuild.jl | 137 +++++++++++++ src/Differential.jl | 95 --------- src/HHL.jl | 84 ++++++++ src/Kernels.jl | 38 ++++ src/Miscellaneous.jl | 19 ++ src/PhaseEstimation.jl | 47 +++++ src/QCBM.jl | 72 +++++++ src/QCOptProblem.jl | 57 ++++++ src/QFT.jl | 2 +- src/QuAlgorithmZoo.jl | 12 +- src/QuGAN.jl | 103 ++++++++++ src/RotBasis.jl | 11 +- src/hamiltonian_solvers.jl | 46 +++++ test/CircuitBuild.jl | 65 ++++++ test/Differential.jl | 21 -- test/Grover.jl | 2 +- test/HHL.jl | 87 ++++++++ test/PhaseEstimation.jl | 68 +++++++ test/QCBM.jl | 32 +++ test/QCOptProblem.jl | 7 + test/QuGAN.jl | 22 ++ test/RotBasis.jl | 4 + test/hamiltonian_solvers.jl | 22 ++ test/runtests.jl | 20 +- 32 files changed, 1109 insertions(+), 489 deletions(-) create mode 100644 docs/src/man/zoo.md create mode 100644 examples/Grover.jl create mode 100644 examples/QuGAN.jl create mode 100644 src/Adam.jl create mode 100644 src/CircuitBuild.jl delete mode 100644 src/Differential.jl create mode 100644 src/HHL.jl create mode 100644 src/Kernels.jl create mode 100644 src/Miscellaneous.jl create mode 100644 src/PhaseEstimation.jl create mode 100644 src/QCBM.jl create mode 100644 src/QCOptProblem.jl create mode 100644 src/QuGAN.jl create mode 100644 src/hamiltonian_solvers.jl create mode 100644 test/CircuitBuild.jl delete mode 100644 test/Differential.jl create mode 100644 test/HHL.jl create mode 100644 test/PhaseEstimation.jl create mode 100644 test/QCBM.jl create mode 100644 test/QCOptProblem.jl create mode 100644 test/QuGAN.jl create mode 100644 test/hamiltonian_solvers.jl diff --git a/Project.toml b/Project.toml index 0c08caf76..8e9891d4d 100644 --- a/Project.toml +++ b/Project.toml @@ -8,7 +8,8 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" +StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +Yao = "5a1af4f6-c801-11e8-08ea-1bad16a356b2" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/docs/make.jl b/docs/make.jl index 1805d5159..56e95dad8 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -42,6 +42,9 @@ makedocs( pages = [ "Home" => "index.md", "Tutorial" => TUTORIALS, + "Manual" => Any[ + "man/zoo.md", + ], ], html_prettyurls = !("local" in ARGS), html_canonical = "https://quantumbfs.github.io/QuAlgorithmZoo.jl/latest/", diff --git a/docs/src/index.md b/docs/src/index.md index 93c5c7397..5d4d05b07 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -5,3 +5,21 @@ CurrentModule = QuAlgorithmZoo # Quantum Algorithm Zoo A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) + +## Tutorial +```@contents +Pages = [ + "tutorial/Grover.md", + "tutorial/QCBM.md", + "tutorial/QuGAN.md", +] +Depth = 1 +``` + +## Manual +```@contents +Pages = [ + "man/zoo.md", +] +Depth = 1 +``` diff --git a/docs/src/man/zoo.md b/docs/src/man/zoo.md new file mode 100644 index 000000000..ff1ede4ae --- /dev/null +++ b/docs/src/man/zoo.md @@ -0,0 +1,6 @@ +# QuAlgorithmZoo + +```@autodocs +Modules = [QuAlgorithmZoo] +Order = [:module, :constant, :type, :macro, :function] +``` diff --git a/examples/Grover.jl b/examples/Grover.jl new file mode 100644 index 000000000..48d2006f3 --- /dev/null +++ b/examples/Grover.jl @@ -0,0 +1,35 @@ +# # Grover Search +using Yao +using LinearAlgebra +using QuAlgorithmZoo: groveriter, inference_oracle, prob_match_oracle + +# ## Target Space and Evidense +num_bit = 12 +oracle = matrixgate(Diagonal((v = ones(1<U for i = 1:n}) \iff kron(n, \text{i=>U for i=1:n}) \iff U \otimes \dots \otimes U -# ``` - -#---------------- - -@doc Val - -#---------------- - -roll(4, i=>X for i = 1:4) - -#---------------- - -rollrepeat(4, X) - -#---------------- - -kron(4, i=>X for i = 1:4) - -# However, `kron` is calculated differently comparing to `roll`. -# In principal, **Roller** will be able to calculate small blocks -# with same size with higher efficiency. But for large blocks **Roller** -# may be slower. In **幺**, we offer you this freedom to choose the most suitable solution. -# -# -# all factory methods will **lazy** evaluate **the first arguements**, -# which is the number of qubits. It will return a lambda function that -# requires a single interger input. The instance of desired block will -# only be constructed until all the information is filled. - -rollrepeat(X) - -#--------------- - -rollrepeat(X)(4) - -# When you filled all the information in somewhere of the declaration, -# 幺 will be able to infer the others. - -chain(4, rollrepeat(X), rollrepeat(Y)) - -#--------------- - -# We will now define the rest of rotation layers - -layer(::Val{:last}) = rollrepeat(chain(Rz(0), Rx(0))) -layer(::Val{:mid}) = rollrepeat(chain(Rz(0), Rx(0), Rz(0))) - -# ### CNOT Entangler - -# Another component of quantum circuit born machine is several **CNOT** -# operators applied on different qubits. - -entangler(pairs) = chain(control([ctrl, ], target=>X) for (ctrl, target) in pairs) - -# We can then define such a born machine - -function QCBM(n, nlayer, pairs) - circuit = chain(n) - push!(circuit, layer(:first)) - - for i = 1:(nlayer - 1) - push!(circuit, cache(entangler(pairs))) - push!(circuit, layer(:mid)) - end - - push!(circuit, cache(entangler(pairs))) - push!(circuit, layer(:last)) - - circuit -end - -# We use the method `cache` here to tag the entangler block that it should be -# cached after its first run, because it is actually a constant oracle. -# Let's see what will be constructed - -QCBM(4, 1, [1=>2, 2=>3, 3=>4, 4=>1]) - -# ## MMD Loss & Gradients -# -# The MMD loss is describe below: -# -# ```math -# \begin{aligned} -# \mathcal{L} &= \left| \sum_{x} p \theta(x) \phi(x) - \sum_{x} \pi(x) \phi(x) \right|^2\\ -# &= \langle K(x, y) \rangle_{x \sim p_{\theta}, y\sim p_{\theta}} - 2 \langle K(x, y) -# \rangle_{x\sim p_{\theta}, y\sim \pi} + \langle K(x, y) \rangle_{x\sim\pi, y\sim\pi} -# \end{aligned} -# ``` -# -# -# We will use a squared exponential kernel here. - -struct Kernel - sigma::Float64 - matrix::Matrix{Float64} -end - -function Kernel(nqubits, sigma) - basis = collect(0:(1<2, 3=>4, 5=>6, 2=>3, 4=>5, 6=>1]); - -# Now, we define its shorthand - -get_prob(qcbm) = apply!(register(bit"0"^nqubits(qcbm)), qcbm) |> statevec .|> abs2 - -# We will first iterate through each layer contains rotation gates and allocate -# an array to store our gradient - -function gradient(n, nlayers, qcbm, kernel, ptrain) - prob = get_prob(qcbm) - grad = zeros(real(datatype(qcbm)), nparameters(qcbm)) - idx = 0 - for ilayer = 1:2:(2 * nlayers + 1) - idx = grad_layer!(grad, idx, prob, qcbm, qcbm[ilayer], kernel, ptrain) - end - grad -end - -# Then we iterate through each rotation gate. - -function grad_layer!(grad, idx, prob, qcbm, layer, kernel, ptrain) - count = idx - for each_line in blocks(layer) - for each in blocks(each_line) - gradient!(grad, count+1, prob, qcbm, each, kernel, ptrain) - count += 1 - end - end - count -end - -# We update each parameter by rotate it $-\pi/2$ and $\pi/2$ - -function gradient!(grad, idx, prob, qcbm, gate, kernel, ptrain) - dispatch!(+, gate, pi / 2) - prob_pos = get_prob(qcbm) - - dispatch!(-, gate, pi) - prob_neg = get_prob(qcbm) - - dispatch!(+, gate, pi / 2) # set back - - grad_pos = expect(kernel, prob, prob_pos) - expect(kernel, prob, prob_neg) - grad_neg = expect(kernel, ptrain, prob_pos) - expect(kernel, ptrain, prob_neg) - grad[idx] = grad_pos - grad_neg - grad -end - -# ## Optimizer -# -# We will use the Adam optimizer. Since we don't want you to install another package for this, -# the following code for this optimizer is copied from [Knet.jl](https://github.com/denizyuret/Knet.jl) -# -# Reference: [Kingma, D. P., & Ba, -# J. L. (2015)](https://arxiv.org/abs/1412.6980). Adam: a Method for -# Stochastic Optimization. International Conference on Learning -# Representations, 1–13. - -using LinearAlgebra - -mutable struct Adam - lr::AbstractFloat - gclip::AbstractFloat - beta1::AbstractFloat - beta2::AbstractFloat - eps::AbstractFloat - t::Int - fstm - scndm -end - -Adam(; lr=0.001, gclip=0, beta1=0.9, beta2=0.999, eps=1e-8)=Adam(lr, gclip, beta1, beta2, eps, 0, nothing, nothing) - -function update!(w, g, p::Adam) - gclip!(g, p.gclip) - if p.fstm===nothing; p.fstm=zero(w); p.scndm=zero(w); end - p.t += 1 - lmul!(p.beta1, p.fstm) - BLAS.axpy!(1-p.beta1, g, p.fstm) - lmul!(p.beta2, p.scndm) - BLAS.axpy!(1-p.beta2, g .* g, p.scndm) - fstm_corrected = p.fstm / (1 - p.beta1 ^ p.t) - scndm_corrected = p.scndm / (1 - p.beta2 ^ p.t) - BLAS.axpy!(-p.lr, @.(fstm_corrected / (sqrt(scndm_corrected) + p.eps)), w) -end - -function gclip!(g, gclip) - if gclip == 0 - g - else - gnorm = vecnorm(g) - if gnorm <= gclip - g - else - BLAS.scale!(gclip/gnorm, g) - end - end -end - -# The training of the quantum circuit is simple, just iterate through the steps. - -function train!(qcbm, ptrain, optim; learning_rate=0.1, niter=50) - # initialize the parameters - params = 2pi * rand(nparameters(qcbm)) - dispatch!(qcbm, params) - kernel = Kernel(nqubits(qcbm), 0.25) - - n, nlayers = nqubits(qcbm), (length(qcbm)-1)÷2 - history = Float64[] - - for i = 1:niter - grad = gradient(n, nlayers, qcbm, kernel, ptrain) - curr_loss = loss(qcbm, kernel, ptrain) - push!(history, curr_loss) - params = collect(parameters(qcbm)) - update!(params, grad, optim) - dispatch!(qcbm, params) - end - history -end - -#--------------------- - +nbit = 6 +N = 1< autodiff(:QC); +dispatch!(circuit, :random) +qcbm = QCBM(circuit, kernel, pg); + +# ## TRAINING: Adam Optimizer +# We probide the QCBMGo! iterative interface for training +niter = 100 optim = Adam(lr=0.1) -his = train!(circuit, pg, optim, niter=50, learning_rate=0.1) -plot(1:50, his, xlabel="iteration", ylabel="loss") - -#---------------------- - -p = get_prob(circuit) -p = plot(0:1< loss)") +end diff --git a/src/Adam.jl b/src/Adam.jl new file mode 100644 index 000000000..3183e70a6 --- /dev/null +++ b/src/Adam.jl @@ -0,0 +1,40 @@ +export Adam + +mutable struct Adam + lr::AbstractFloat + gclip::AbstractFloat + beta1::AbstractFloat + beta2::AbstractFloat + eps::AbstractFloat + t::Int + fstm + scndm +end + +Adam(; lr=0.001, gclip=0, beta1=0.9, beta2=0.999, eps=1e-8)=Adam(lr, gclip, beta1, beta2, eps, 0, nothing, nothing) + +function update!(w, g, p::Adam) + gclip!(g, p.gclip) + if p.fstm===nothing; p.fstm=zero(w); p.scndm=zero(w); end + p.t += 1 + lmul!(p.beta1, p.fstm) + BLAS.axpy!(1-p.beta1, g, p.fstm) + lmul!(p.beta2, p.scndm) + BLAS.axpy!(1-p.beta2, g .* g, p.scndm) + fstm_corrected = p.fstm / (1 - p.beta1 ^ p.t) + scndm_corrected = p.scndm / (1 - p.beta2 ^ p.t) + BLAS.axpy!(-p.lr, @.(fstm_corrected / (sqrt(scndm_corrected) + p.eps)), w) +end + +function gclip!(g, gclip) + if gclip == 0 + g + else + gnorm = vecnorm(g) + if gnorm <= gclip + g + else + BLAS.scale!(gclip/gnorm, g) + end + end +end diff --git a/src/CircuitBuild.jl b/src/CircuitBuild.jl new file mode 100644 index 000000000..3b77bd5ee --- /dev/null +++ b/src/CircuitBuild.jl @@ -0,0 +1,137 @@ +using StatsBase: sample + +export pair_ring, pair_square, cnot_entangler +export rotor, merged_rotor, rotorset +export random_diff_circuit +export rand_single_gate, rand_gate, rand_circuit + +################## Entangler ################### +""" + pair_ring(n::Int) -> Vector + +Pair ring. +""" +pair_ring(n::Int) = [i=>mod(i, n)+1 for i=1:n] + +""" + pair_square(m::Int, n::Int) -> Vector + +Pair square. +""" +function pair_square(m::Int, n::Int) + nsite = m*n + res = Vector{Pair{Int, Int}}(undef, 2*nsite) + li = LinearIndices((m, n)) + k = 1 + for i = 1:2:m, j=1:n + res[k] = li[i, j] => li[i%m+1, j] + k+=1 + end + for i = 2:2:m, j=1:n + res[k] = li[i, j] => li[i%m+1, j] + k+=1 + end + for i = 1:m, j=1:2:n + res[k] = li[i, j] => li[i, j%n+1] + k+=1 + end + for i = 1:m, j=2:2:n + res[k] = li[i, j] => li[i, j%n+1] + k+=1 + end + res +end + +""" + cnot_entangler([n::Int, ] pairs::Vector{Pair}) = ChainBlock + +Arbitrary rotation unit, support lazy construction. +""" +cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) +cnot_entangler(pairs) = n->cnot_entangler(n, pairs) + +###################### rotor and rotorset ##################### +""" + merged_rotor(noleading::Bool=false, notrailing::Bool=false) -> ChainBlock{1, ComplexF64} + +Single qubit arbitrary rotation unit, set parameters notrailing, noleading true to remove trailing and leading Z gates. + +!!! note + + Here, `merged` means `Rz(η)⋅Rx(θ)⋅Rz(ξ)` are multiplied first, this kind of operation if now allowed in differentiable + circuit with back-propagation (`:BP`) mode (just because we are lazy to implement it!). + But is a welcoming component in quantum differentiation. +""" +merged_rotor(noleading::Bool=false, notrailing::Bool=false) = noleading ? (notrailing ? Rx(0) : chain(Rx(0), Rz(0))) : (notrailing ? chain(Rz(0), Rx(0)) : chain(Rz(0), Rx(0), Rz(0))) + +""" + rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) -> ChainBlock{nbit, ComplexF64} + +Arbitrary rotation unit (put in `nbit` space), set parameters notrailing, noleading true to remove trailing and leading Z gates. +""" +function rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) + rt = chain(nbit, [put(nbit, ibit=>Rz(0.0)), put(nbit, ibit=>Rx(0.0)), put(nbit, ibit=>Rz(0.0))]) + noleading && popfirst!(rt) + notrailing && pop!(rt) + rt +end + +rotorset(::Val{:Merged}, nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [put(nbit, j=>merged_rotor(noleading, notrailing)) for j=1:nbit]) +rotorset(::Val{:Split}, nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [rotor(nbit, j, noleading, notrailing) for j=1:nbit]) +rotorset(mode::Symbol, nbit::Int, noleading::Bool=false, notrailing::Bool=false) = rotorset(Val(mode), nbit, noleading, notrailing) + +""" + qdiff_circuit(n, nlayer, pairs) -> ChainBlock + +A kind of widely used differentiable quantum circuit, angles in the circuit is randomely initialized. + +ref: + 1. Kandala, A., Mezzacapo, A., Temme, K., Takita, M., Chow, J. M., & Gambetta, J. M. (2017). + Hardware-efficient Quantum Optimizer for Small Molecules and Quantum Magnets. Nature Publishing Group, 549(7671), 242–246. + https://doi.org/10.1038/nature23879. +""" +function random_diff_circuit(nbit, nlayer, pairs; mode=:Split) + circuit = chain(nbit) + + ent = cnot_entangler(pairs) |> cache + for i = 1:(nlayer + 1) + i!=1 && push!(circuit, ent) + push!(circuit, rotorset(mode, nbit, i==1, i==nlayer+1)) + end + circuit +end + +############### Completely random circuits (for testing and demo) ################ +randlocs(nbit::Int, mbit::Int) = sample(1:nbit, mbit, replace=false) +const SINGLE_GATES = [X, Y, Z, H, Rx, Ry, Rz, shift, phase] + +rand_single_gate(ngate::Int) = [rand_single_gates() for i=1:ngate] +function rand_single_gate() + gate = rand(SINGLE_GATES) + gate isa AbstractBlock ? gate : gate(rand()*2π) +end + +""" + rand_gate(nbit::Int, mbit::Int, [ngate::Int]) -> MatrixBlock + +random nbit gate. +""" +rand_gate(nbit::Int, mbit::Int) = rand_gate(nbit, Val(mbit)) +rand_gate(nbit::Int, mbit::Int, ngate::Int) = [rand_gate(nbit, mbit) for i=1:ngate] +rand_gate(nbit::Int, ::Val{1}) = put(nbit, rand(1:nbit)=>rand_single_gate()) +function rand_gate(nbit::Int, ::Val{M}) where M + locs = randlocs(nbit, M) + control(nbit, locs[1:M-1], last(locs)=>rand_single_gate()) +end + +function rand_circuit(nbit::Int; p1 = (nbit==1 ? 1.0 : 0.66), ngate=5*nbit) + c = chain(nbit) + for i=1:ngate + if rand() < p1 + push!(c, rand_gate(nbit, 1)) + else + push!(c, rand_gate(nbit, 2)) + end + end + c +end diff --git a/src/Differential.jl b/src/Differential.jl deleted file mode 100644 index d27c9e442..000000000 --- a/src/Differential.jl +++ /dev/null @@ -1,95 +0,0 @@ -export diff_circuit, num_gradient, rotter, cnot_entangler, opgrad, collect_rotblocks, perturb - -""" - rotter(noleading::Bool=false, notrailing::Bool=false) -> ChainBlock{1, ComplexF64} - -Arbitrary rotation unit, set parameters notrailing, noleading true to remove trailing and leading Z gates. -""" -rotter(noleading::Bool=false, notrailing::Bool=false) = noleading ? (notrailing ? Rx(0) : chain(Rx(0), Rz(0))) : (notrailing ? chain(Rz(0), Rx(0)) : chain(Rz(0), Rx(0), Rz(0))) - -""" - cnot_entangler([n::Int, ] pairs::Vector{Pair}) = ChainBlock - -Arbitrary rotation unit, support lazy construction. -""" -cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) -cnot_entangler(pairs) = n->cnot_entangler(n, pairs) - -""" - diff_circuit(n, nlayer, pairs) -> ChainBlock - -A kind of widely used differentiable quantum circuit, angles in the circuit is randomely initialized. - -ref: - 1. Kandala, A., Mezzacapo, A., Temme, K., Takita, M., Chow, J. M., & Gambetta, J. M. (2017). - Hardware-efficient Quantum Optimizer for Small Molecules and Quantum Magnets. Nature Publishing Group, 549(7671), 242–246. - https://doi.org/10.1038/nature23879. -""" -function diff_circuit(n, nlayer, pairs) - circuit = chain(n) - - for i = 1:(nlayer + 1) - if i!=1 push!(circuit, cnot_entangler(pairs) |> cache) end - push!(circuit, rollrepeat(n, rotter(i==1, i==nlayer+1))) - #for j = 1:n - # push!(circuit, put(n, j=>rotter(i==1, i==nlayer+1))) - #end - end - dispatch!(circuit, rand(nparameters(circuit))*2π) -end - -""" - collect_rotblocks(blk::AbstractBlock) -> Vector{RotationGate} - -filter out all rotation gates, which is differentiable. -""" -function collect_rotblocks(blk::AbstractBlock) - rots = blockfilter!(x->x isa RotationGate, Vector{RotationGate}([]), blk) - nparameters(blk)==length(rots) || warn("some parameters in this circuit are not differentiable!") - rots -end - -""" - perturb(func, gates::Vector{<:RotationGate}, diff::Real) -> Matrix - -perturb every rotation gates, and evaluate losses. -The i-th element of first column of resulting Matrix corresponds to Gi(θ+δ), and the second corresponds to Gi(θ-δ). -""" -function perturb(func, gates::Vector{<:RotationGate}, diff::Real) - ng = length(gates) - res = Matrix{Float64}(undef, ng, 2) - for i in 1:ng - gate = gates[i] - dispatch!(+, gate, diff) - res[i, 1] = func() - - dispatch!(+, gate, -2*diff) - res[i, 2] = func() - - dispatch!(+, gate, diff) # set back - end - res -end - -""" - num_gradient(lossfunc, rots::Vector{<:RotationGate}, δ::Float64=1e-2) -> Vector - -Compute gradient numerically. -""" -function num_gradient(lossfunc, rots::Vector{<:RotationGate}, δ::Float64=1e-2) - gperturb = perturb(lossfunc, rots, δ) - (gperturb[:,1] - gperturb[:,2])/(2δ) -end - -""" - opgrad(op_expect, rots::Vector{<:RotationGate}) -> Vector - -get the gradient of an operator expectation function. - -References: - Mitarai, K., Negoro, M., Kitagawa, M., & Fujii, K. (2018). Quantum Circuit Learning, 1–3. Retrieved from http://arxiv.org/abs/1803.00745 -""" -function opgrad(op_expect, rots::Vector{<:RotationGate}) - gperturb = perturb(op_expect, rots, π/2) - (gperturb[:,1] - gperturb[:,2])/2 -end diff --git a/src/HHL.jl b/src/HHL.jl new file mode 100644 index 000000000..d1c883629 --- /dev/null +++ b/src/HHL.jl @@ -0,0 +1,84 @@ +export hhlcircuit, hhlproject!, hhlsolve, HHLCRot + +""" + HHLCRot{N, NC, T} <: PrimitiveBlock{N, Complex{T}} + +Controlled rotation gate used in HHL algorithm, applied on N qubits. + + * cbits: control bits. + * ibit:: the ancilla bit. + * C_value:: the value of constant "C", should be smaller than the spectrum "gap". +""" +struct HHLCRot{N, NC, T} <: PrimitiveBlock{N, Complex{T}} + cbits::Vector{Int} + ibit::Int + C_value::T + HHLCRot{N}(cbits::Vector{Int}, ibit::Int, C_value::T) where {N, T} = new{N, length(cbits), T}(cbits, ibit, C_value) +end + +@inline function hhlrotmat(λ::Real, C_value::Real) + b = C_value/λ + a = sqrt(1-b^2) + a, -b, b, a +end + +function apply!(reg::DefaultRegister, hr::HHLCRot{N, NC, T}) where {N, NC, T} + mask = bmask(hr.ibit) + step = 1<<(hr.ibit-1) + step_2 = step*2 + nbit = nqubits(reg) + for j = 0:step_2:size(reg.state, 1)-step + for i = j+1:j+step + λ = bfloat(takebit(i-1, hr.cbits...), nbit=nbit-1) + if λ >= hr.C_value + u = hhlrotmat(λ, hr.C_value) + u1rows!(state(reg), i, i+step, u...) + end + end + end + reg +end + +""" + hhlproject!(all_bit::DefaultRegister, n_reg::Int) -> Vector + +project to aiming state |1>|00>|u>, and return |u> vector. +""" +function hhlproject!(all_bit::DefaultRegister, n_reg::Int) + all_bit |> focus!(1:(n_reg+1)...) |> select!(1) |> state |> vec +end + +""" +Function to build up a HHL circuit. +""" +function hhlcircuit(UG, n_reg::Int, C_value::Real) + n_b = nqubits(UG) + n_all = 1 + n_reg + n_b + pe = PEBlock(UG, n_reg, n_b) + cr = HHLCRot{n_reg+1}([2:n_reg+1...], 1, C_value) + chain(n_all, concentrate(n_all, pe, [2:n_all...,]), concentrate(n_all, cr, [1:(n_reg+1)...,]), concentrate(n_all, pe', [2:n_all...,])) +end + +""" + hhlsolve(A::Matrix, b::Vector) -> Vector + +solving linear system using HHL algorithm. Here, A must be hermitian. +""" +function hhlsolve(A::Matrix, b::Vector, n_reg::Int, C_value::Real) + if !ishermitian(A) + throw(ArgumentError("Input matrix not hermitian!")) + end + UG = matrixgate(exp(2π*im.*A)) + + # Generating input bits + all_bit = register(b) ⊗ zero_state(n_reg) ⊗ zero_state(1) + + # Construct HHL circuit. + circuit = hhlcircuit(UG, n_reg, C_value) + + # Apply bits to the circuit. + apply!(all_bit, circuit) + + # Get state of aiming state |1>|00>|u>. + hhlproject!(all_bit, n_reg) ./ C_value +end diff --git a/src/Kernels.jl b/src/Kernels.jl new file mode 100644 index 000000000..9abad2d2d --- /dev/null +++ b/src/Kernels.jl @@ -0,0 +1,38 @@ +export AbstractKernel, RBFKernel, kmat, rbf_kernel, kernel_expect + +abstract type AbstractKernel end + +""" + RBFKernel(σ, matrix) + +RBF Kernel with dense array as kernel matrix. +""" +struct RBFKernel <: AbstractKernel + sigma::Float64 + matrix::Matrix{Float64} +end + +""" + kmat(k::AbstractKernel) -> Matrix + +Returns Kernel Matrix. +""" +function kmat end +kmat(mbf::RBFKernel) = mbf.matrix + +""" + rbf_kernel(basis, σ::Real) -> RBFKernel + +Returns RBF Kernel Matrix. +""" +function rbf_kernel(basis, σ::Real) + dx2 = (basis .- basis').^2 + RBFKernel(σ, exp.(-1/2σ * dx2)) +end + +""" + kernel_expect(kernel::AbstractKernel, px::Vector{Float64}, py::Vector{Float64}) -> Float64 + +Returns the expectation value of kernel on specific distributions. +""" +kernel_expect(kernel::AbstractKernel, px::Vector, py::Vector=px) = px' * kmat(kernel) * py diff --git a/src/Miscellaneous.jl b/src/Miscellaneous.jl new file mode 100644 index 000000000..e077d5cdd --- /dev/null +++ b/src/Miscellaneous.jl @@ -0,0 +1,19 @@ +export inverselines + +""" + inverselines(nbit::Int; n_reg::Int=nbit) -> ChainBlock + +inverse first `n_reg` lines + +TODO: +deprecate this function, it is not used. +""" +function inverselines(nbit::Int; n_reg::Int=nbit) + c = chain(nbit) + for i = 1:(n_reg ÷ 2) + push!(c, swap(i,(n_reg-i+1))) + end + c +end + + diff --git a/src/PhaseEstimation.jl b/src/PhaseEstimation.jl new file mode 100644 index 000000000..ef8b148f1 --- /dev/null +++ b/src/PhaseEstimation.jl @@ -0,0 +1,47 @@ +export PEBlock, projection_analysis +""" + PEBlock(UG, n_reg, n_b) -> ChainBlock + +phase estimation circuit. + + * `UG`: the input unitary matrix. + * `n_reg`: the number of bits to store phases, + * `n_b`: the number of bits to store vector. +""" +function PEBlock(UG::GeneralMatrixGate, n_reg::Int, n_b::Int) + nbit = n_b + n_reg + # Apply Hadamard Gate. + hs = repeat(nbit, H, 1:n_reg) + + # Construct a control circuit. + control_circuit = chain(nbit) + for i = 1:n_reg + push!(control_circuit, control(nbit, (i,), (n_reg+1:nbit...,)=>UG)) + if i != n_reg + UG = matrixgate(mat(UG) * mat(UG)) + end + end + + # Inverse QFT Block. + iqft = concentrate(nbit, QFTBlock{n_reg}() |> adjoint,[1:n_reg...,]) + chain(hs, control_circuit, iqft) +end + +""" + projection_analysis(evec::Matrix, reg::DefaultRegister) -> Tuple + +Analyse using state projection. +It returns a tuple of (most probable configuration, the overlap matrix, the relative probability for this configuration) +""" +function projection_analysis(evec::Matrix, reg::DefaultRegister) + overlap = evec'*state(reg) + amp_relative = Float64[] + bs = Int[] + + for b in basis(overlap) + mc = argmax(view(overlap, b+1, :) .|> abs)-1 + push!(amp_relative, abs2(overlap[b+1, mc+1])/sum(overlap[b+1, :] .|> abs2)) + push!(bs, mc) + end + bs, overlap, amp_relative +end diff --git a/src/QCBM.jl b/src/QCBM.jl new file mode 100644 index 000000000..b38ea53d7 --- /dev/null +++ b/src/QCBM.jl @@ -0,0 +1,72 @@ +import Yao.Registers: probs +export QCBM, QCBMGo!, psi, mmdgrad + +include("Kernels.jl") + +##### QCBM methods ##### +struct QCBM{BT<:AbstractBlock, KT<:AbstractKernel} <: QCOptProblem + circuit::BT + kernel::KT + ptrain::Vector{Float64} + + dbs +end +function QCBM(circuit::AbstractBlock, kernel::AbstractKernel, ptrain::Vector) + QCBM(circuit, kernel, ptrain, collect(circuit, AbstractDiff)) +end + +# INTERFACES +circuit(qcbm::QCBM) = qcbm.circuit +diff_blocks(qcbm::QCBM) = qcbm.dbs +""" + loss(qcbm::QCBM, [p=qcbm|>probs]) -> Float64 + +loss function, optional parameter `p` is the probability distribution. +""" +function loss(qcbm::QCBM, p=qcbm|>probs) + p -= qcbm.ptrain + kernel_expect(qcbm.kernel, p, p) +end +""" + gradient(qcbm::QCBM, p0=qcbm |> probs) -> Vector + +gradient of MMD two sample test loss, `db` must be contained in qcbm. +optional parameter `p0` is current probability distribution. +""" +gradient(qcbm::QCBM, p0=qcbm |> probs) = mmdgrad.(Ref(qcbm), qcbm.dbs, p0=p0) + +"""generated wave function""" +psi(qcbm) = zero_state(qcbm.circuit |> nqubits) |> qcbm.circuit +"""generated probability distribution""" +probs(qcbm::QCBM) = qcbm |> psi |> probs +""" + mmdgrad(qcbm::QCBM, db::AbstractDiff; p0::Vector) -> Float64 + +gradient of MMD two sample test loss, `db` must be contained in qcbm. +`p0` is current probability distribution. +""" +function mmdgrad(qcbm::QCBM, db::AbstractDiff; p0::Vector) + vstatdiff(()->psi(qcbm), db, Vstat(kmat(qcbm.kernel)), p0=p0) - 2*vstatdiff(()->psi(qcbm), db, Vstat(kmat(qcbm.kernel)*qcbm.ptrain)) +end + +""" +quantum circuit born machine trainer. +""" +struct QCBMGo!{QT<:QCBM, OT} <: QCOptGo!{QT} + qcbm::QT + optimizer::OT + niter::Int +end +QCBMGo!(qcbm::QCBM, optimizer) = QCBMGo!(qcbm, optimizer, typemax(Int)) +Base.length(qo::QCBMGo!) = qo.niter + +function Base.iterate(qo::QCBMGo!, state=(1, parameters(qo.qcbm.circuit))) + state[1] > qo.niter && return nothing + + # initialize the parameters + p0 = qo.qcbm |> probs + grad = gradient(qo.qcbm, p0) + update!(state[2], grad, qo.optimizer) + dispatch!(qo.qcbm.circuit, state[2]) + Dict("probs"=>p0, "step"=>state[1], "gradient"=>grad), (state[1]+1, state[2]) +end diff --git a/src/QCOptProblem.jl b/src/QCOptProblem.jl new file mode 100644 index 000000000..b96b696e1 --- /dev/null +++ b/src/QCOptProblem.jl @@ -0,0 +1,57 @@ +import Yao: gradient +export num_gradient, diff_blocks, loss, circuit, QCOptProblem, QCOptGo! + +""" + QCOptProblem + +General quantum circuit optimization problem interface +""" +abstract type QCOptProblem end + +""" + circuit(qop::QCOptProblem) -> AbstractBlock + +circuit to optimize +""" +function circuit end + +""" + loss(qop::QCOptProblem) -> Real + +Return the loss. +""" +function loss end + +##################################################### + +""" + gradient(qop::QCOptProblem) -> Vector + +the gradients with respect to `diff_blocks`. +""" +function gradient end + +""" + diff_blocks(qop::QCOptProblem) -> iterable + +collection of all differentiable units. +""" +diff_blocks(qop::QCOptProblem) = collect(qop |> circuit, AbstractDiff) + +""" + num_gradient(qop::QCOptProblem) -> Vector + +obtain the gradient numerically +""" +num_gradient(qop::QCOptProblem) = numdiff.(()->loss(qop), qop |> diff_blocks) + +################# Optimization ################### +""" + QCOptGo!{QT} + +quantum circuit optimization problem optimizer. +""" +abstract type QCOptGo!{QT} end + +include("QuGAN.jl") +include("QCBM.jl") diff --git a/src/QFT.jl b/src/QFT.jl index 7f611c38e..37123bef3 100644 --- a/src/QFT.jl +++ b/src/QFT.jl @@ -20,7 +20,7 @@ isreflexive(q::QFTBlock{N}) where N = N==1 isunitary(q::QFTBlock{N}) where N = true openbox(q::QFTBlock{N}) where N = QFTCircuit(N) -openbox(q::Daggered{N,T,<:QFTBlock}) where {N, T} = adjoint(QFTCircuit(N)) +openbox(q::Daggered{<:QFTBlock, N}) where {N} = adjoint(QFTCircuit(N)) function print_block(io::IO, pb::QFTBlock{N}) where N printstyled(io, "QFT(1-$N)"; bold=true, color=:blue) diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index e389a4298..d9b2f1001 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -5,21 +5,29 @@ using Yao using Yao.Intrinsics using Yao.Registers using Yao.Blocks -import Yao.Blocks: mat, dispatch!, nparameters, parameters, cache_key, print_block, _make_rot_mat, apply!, PrimitiveBlock +import Yao.Blocks: mat, dispatch!, niparameters, iparameters, setiparameters!, cache_key, print_block, _make_rot_mat, apply!, PrimitiveBlock import Base: ==, copy, hash import Yao.Intrinsics: ishermitian, isreflexive, isunitary export openbox """ + openbox(block::AbstractBlock) -> AbstractBlock + For a black box, like QFTBlock, you can get its white box (loyal simulation) using this function. """ function openbox end +include("Miscellaneous.jl") +include("Adam.jl") include("QFT.jl") -include("Differential.jl") +include("CircuitBuild.jl") +include("QCOptProblem.jl") include("RotBasis.jl") include("Grover.jl") +include("PhaseEstimation.jl") +include("HHL.jl") +include("hamiltonian_solvers.jl") end # module diff --git a/src/QuGAN.jl b/src/QuGAN.jl new file mode 100644 index 000000000..7db37b6f3 --- /dev/null +++ b/src/QuGAN.jl @@ -0,0 +1,103 @@ +import Yao.Registers: tracedist + +export QuGAN, psi, toy_qugan, QuGANGo! +""" +Quantum GAN. + +Reference: + Benedetti, M., Grant, E., Wossnig, L., & Severini, S. (2018). Adversarial quantum circuit learning for pure state approximation, 1–14. +""" +struct QuGAN{N} <: QCOptProblem + target::DefaultRegister + generator::MatrixBlock{N} + discriminator::MatrixBlock + reg0::DefaultRegister + witness_op::MatrixBlock + circuit::AbstractBlock + gdiffs + ddiffs + + function QuGAN(target::DefaultRegister, gen::MatrixBlock, dis::MatrixBlock) + N = nqubits(target) + c = sequence(gen, addbit(1), dis) + witness_op = put(N+1, (N+1)=>P0) + gdiffs = collect(gen, AbstractDiff) + ddiffs = collect(dis, AbstractDiff) + new{N}(target, gen, dis, zero_state(N), witness_op, c, gdiffs, ddiffs) + end +end + +# INTERFACES +circuit(qg::QuGAN) = qg.circuit +diff_blocks(qg::QuGAN) = [qg.gdiffs...; qg.ddiffs...] +loss(qg::QuGAN) = p0t(qg) - p0g(qg) +function gradient(qg::QuGAN) + ggrad_g = opdiff.(()->psi_discgen(qg), qg.gdiffs, Ref(qg.witness_op)) + dgrad_g = opdiff.(()->psi_discgen(qg), qg.ddiffs, Ref(qg.witness_op)) + dgrad_t = opdiff.(()->psi_disctarget(qg), qg.ddiffs, Ref(qg.witness_op)) + [-ggrad_g; dgrad_t - dgrad_g] +end + +"""probability to get evidense qubit 0 on generation set.""" +p0g(qg::QuGAN) = expect(qg.witness_op, psi_discgen(qg)) |> real +"""probability to get evidense qubit 0 on target set.""" +p0t(qg::QuGAN) = expect(qg.witness_op, psi_disctarget(qg)) |> real +"""generated wave function""" +psi(qg::QuGAN) = copy(qg.reg0) |> qg.generator +"""input |> generator |> discriminator""" +psi_discgen(qg::QuGAN) = copy(qg.reg0) |> qg.circuit +"""target |> discriminator""" +psi_disctarget(qg::QuGAN) = copy(qg.target) |> qg.circuit[2:end] +"""tracedistance between target and generated wave function""" +tracedist(qg::QuGAN) = tracedist(qg.target, psi(qg))[] + +""" + toy_qugan(target::DefaultRegister, depth_gen::Int, depth_disc::Int) -> QuGAN + +Construct a toy qugan. +""" +function toy_qugan(target::DefaultRegister, depth_gen::Int, depth_disc::Int) + n = nqubits(target) + generator = dispatch!(random_diff_circuit(n, depth_gen, pair_ring(n)), :random) |> autodiff(:QC) + discriminator = dispatch!(random_diff_circuit(n+1, depth_disc, pair_ring(n+1)), :random) |> autodiff(:QC) + return QuGAN(target, generator, discriminator) +end + +# Optimization +""" + QuGANGo!{QT<:QuGAN, OT} <: QCOptGo!{QT} + +Iterative training of quantum generative optimization problem, +QT is the type of quantum optimization problem, +OT is the optimizer/learning_rate parameter type. +""" +struct QuGANGo!{QT<:QuGAN, OT} <: QCOptGo!{QT} + qop::QT + goptim::OT + doptim::OT + niter::Int +end +QuGANGo!(qg::QuGAN, glr, dlr) = QuGANGo!(qg, glr, dlr, typemax(Int)) +Base.length(qgg::QuGANGo!) = qgg.niter + +function Base.iterate(qgg::QuGANGo!{<:Any, <:Real}, state=1) + state > qgg.niter && return nothing + + qg = qgg.qop + ng = length(qg.gdiffs) + grad = gradient(qg) + dispatch!(+, qg.generator, -grad[1:ng]*qgg.goptim) + dispatch!(-, qg.discriminator, -grad[ng+1:end]*qgg.doptim) + Dict("step"=>state,"gradient"=>grad), state+1 +end + +function Base.iterate(qgg::QuGANGo!{<:Any, <:Adam}, state=(1, parameters(qgg.qop.generator), parameters(qgg.qop.discriminator))) + state[1] > qgg.niter && return nothing + + qg = qgg.qop + ng = length(qg.gdiffs) + grad = gradient(qg) + dispatch!(qg.generator, update!(state[2], grad[1:ng], qgg.goptim)) + dispatch!(qg.discriminator, update!(state[3], -grad[ng+1:end], qgg.doptim)) + Dict("step"=>state[1], "gradient"=>grad), (state[1]+1, state[2], state[3]) +end diff --git a/src/RotBasis.jl b/src/RotBasis.jl index 48f7b0df7..bdde812da 100644 --- a/src/RotBasis.jl +++ b/src/RotBasis.jl @@ -21,10 +21,15 @@ end ==(rb1::RotBasis, rb2::RotBasis) = rb1.theta == rb2.theta && rb1.phi == rb2.phi copy(block::RotBasis{T}) where T = RotBasis{T}(block.theta, block.phi) -dispatch!(block::RotBasis, params) = ((block.theta, block.phi) = params; block) +dispatch!(block::RotBasis, params::Vector) = ((block.theta, block.phi) = params; block) -parameters(rb::RotBasis) = (rb.theta, rb.phi) -nparameters(::Type{<:RotBasis}) = 2 +iparameters(rb::RotBasis) = (rb.theta, rb.phi) +function setiparameters!(rb::RotBasis, theta::Real, phi::Real) + rb.theta, rb.phi = theta, phi + rb +end +niparameters(::Type{<:RotBasis}) = 2 +render_params(r::RotBasis, ::Val{:random}) = rand()*π, rand()*2π function print_block(io::IO, R::RotBasis) print(io, "RotBasis($(R.theta), $(R.phi))") diff --git a/src/hamiltonian_solvers.jl b/src/hamiltonian_solvers.jl new file mode 100644 index 000000000..f176f5a45 --- /dev/null +++ b/src/hamiltonian_solvers.jl @@ -0,0 +1,46 @@ +export heisenberg, iter_groundstate!, itime_groundstate! + +""" + heisenberg(nbit::Int; periodic::Bool=true) + +heisenberg hamiltonian, for its ground state, refer `PRB 48, 6141`. +""" +function heisenberg(nbit::Int; periodic::Bool=true) + sx = i->put(nbit, i=>X) + sy = i->put(nbit, i=>Y) + sz = i->put(nbit, i=>Z) + mapreduce(i->(j=i%nbit+1; sx(i)*sx(j)+sy(i)*sy(j)+sz(i)*sz(j)), +, 1:(periodic ? nbit : nbit-1)) +end + +""" + iter_groundstate!({reg::AbstractRegister}, h::MatrixBlock; niter::Int=100) -> AbstractRegister + +project wave function to ground state by iteratively apply -h. +""" +iter_groundstate!(h::MatrixBlock; niter::Int=100) = reg -> iter_groundstate!(reg, h, niter=niter) +function iter_groundstate!(reg::AbstractRegister, h::MatrixBlock; niter::Int=100) + for i = 1:niter + reg |> h + i%5 == 0 && reg |> normalize! + end + reg |> normalize! +end + +""" + itime_groundstate!({reg::AbstractRegister}, h::MatrixBlock; τ::Int=20, tol=1e-4) -> AbstractRegister + +project wave function to ground state by exp(-hτ). `tol` is for `expmv`. +""" +itime_groundstate!(h::MatrixBlock; τ::Real=20, tol=1e-4) = reg -> itime_groundstate!(reg, h; τ=τ, tol=tol) +function itime_groundstate!(reg::AbstractRegister, h::MatrixBlock; τ::Int=20, tol=1e-4) + span = 1 + te = timeevolve(h, -im*span) + for i = 1:τ÷span + reg |> te |> normalize! + end + if τ%span != 0 + reg |> timeevolve(h, τ%span) |> normalize! + end + reg +end + diff --git a/test/CircuitBuild.jl b/test/CircuitBuild.jl new file mode 100644 index 000000000..b87e6c2d7 --- /dev/null +++ b/test/CircuitBuild.jl @@ -0,0 +1,65 @@ +using Test +using Yao, Yao.Blocks, QuAlgorithmZoo + +@testset "pairs geometries" begin + @test pair_ring(3) == [1=>2,2=>3,3=>1] + ps = pair_square(2, 2) + @test length(ps) == 8 + for item in [1=>2, 3=>4, 2=>1, 4=>3, 1=>3, 2=>4, 3=>1, 4=>2] + @test item in ps + end + @test cnot_entangler(4, ps) isa ChainBlock + @test cnot_entangler(4, ps) |> length == 8 +end + +@testset "random circuit" begin + c = rand_circuit(1) + @test c isa ChainBlock + @test length(c) == 5 + c = rand_circuit(9) + @test c isa ChainBlock + @test length(c) == 45 +end + +@testset "rotter, collect_rotblocks, num_gradient, opgrad" begin + @test merged_rotor(true, true) == Rx(0) + @test merged_rotor(false, false) == merged_rotor() == chain(Rz(0), Rx(0), Rz(0)) + @test merged_rotor(false, true) == chain(Rz(0), Rx(0)) + @test merged_rotor(true, false) == chain(Rx(0), Rz(0)) + @test collect(rotorset(:Merged, 5, true, false), RotationGate) |> length == 10 + + @test rotor(5, 2, true, true) isa ChainBlock + @test rotor(5, 2, true, true) |> length == 1 + @test rotor(5, 2, true, true) |> nqubits == 5 + @test collect(rotorset(:Split, 5, true, false), PutBlock{<:Any, <:Any, <:RotationGate}) |> length == 10 +end + +@testset "random diff circuit" begin + c = random_diff_circuit(4, 3, [1=>3, 2=>4, 2=>3, 4=>1]) + rots = collect(c, RotationGate) + @test length(rots) == nparameters(c) == 40 + @test dispatch!(+, c, ones(40)*0.1) |> parameters == ones(40)*0.1 + @test dispatch!(+, c, :random) |> parameters != ones(40)*0.1 + + nbit = 4 + c = random_diff_circuit(nbit, 1, pair_ring(nbit), mode=:Split) |> autodiff(:BP) + reg = rand_state(4) + dispatch!(c, randn(nparameters(c))) + + dbs = collect(c, BPDiff) + op = kron(4, 1=>Z, 2=>X) + loss1z() = expect(op, copy(reg) |> c) # return loss please + + # back propagation + ψ = copy(reg) |> c + δ = copy(ψ) |> op + backward!(δ, c) + bd = gradient(c) + + # get num gradient + nd = numdiff.(loss1z, dbs) + ed = opdiff.(()->copy(reg)|>c, dbs, Ref(op)) + + @test isapprox.(nd, ed, atol=1e-4) |> all + @test isapprox.(nd, bd, atol=1e-4) |> all +end diff --git a/test/Differential.jl b/test/Differential.jl deleted file mode 100644 index cb130b4da..000000000 --- a/test/Differential.jl +++ /dev/null @@ -1,21 +0,0 @@ -using Test, Random, LinearAlgebra, SparseArrays - - -using QuAlgorithmZoo, Yao.Blocks - -@testset "rotter, collect_rotblocks, num_gradient, opgrad" begin - c = diff_circuit(4, 3, [1=>3, 2=>4, 2=>3, 4=>1]) - rots = collect_rotblocks(c) - @test length(rots) == nparameters(c) == 40 - - obs = kron(nqubits(c), 2=>X) - #@test mean(opgrad(()->expect(obs, apply!(zero_state(4), c))|>real, rots) .|> abs) > 3e-4 - gradA = opgrad(()->expect(obs, apply!(zero_state(4), c)) |> real, rots) - gradB = num_gradient(()->expect(obs, apply!(zero_state(4),c)) |> real, rots) - @test isapprox(gradA, gradB, atol=1e-4) - - @test rotter(true, true) == Rx(0) - @test rotter(false, false) == rotter() == chain(Rz(0), Rx(0), Rz(0)) - @test rotter(false, true) == chain(Rz(0), Rx(0)) - @test rotter(true, false) == chain(Rx(0), Rz(0)) -end diff --git a/test/Grover.jl b/test/Grover.jl index 586fb4e74..f8e06a1ec 100644 --- a/test/Grover.jl +++ b/test/Grover.jl @@ -2,7 +2,7 @@ using Test, Random, LinearAlgebra, SparseArrays using QuAlgorithmZoo import QuAlgorithmZoo: _num_grover_step -using Yao.Blocks +using Yao, Yao.Blocks using Yao.Intrinsics function GroverSearch(oracle, num_bit::Int; psi::DefaultRegister = uniform_state(num_bit)) diff --git a/test/HHL.jl b/test/HHL.jl new file mode 100644 index 000000000..391a4ddd3 --- /dev/null +++ b/test/HHL.jl @@ -0,0 +1,87 @@ +using Yao +using Yao.Intrinsics +using QuAlgorithmZoo +using Test, LinearAlgebra + +function crot(n_reg::Int, C_value::Real) + n_rot = n_reg + 1 + rot = chain(n_rot) + θ = zeros(1< 1 + return println("C_value = $C_value, λ = $λ, sinθ = $sin_value > 1, please lower C_value.\n") + end + θ[(i)] = 2.0*asin(C_value / λ) + push!(rot, control(c_bit, 1=>Ry(θ[(i)]))) + end + rot +end + +@testset "HHLCRot" begin + hr = HHLCRot{4}([4,3,2], 1, 0.01) + reg = rand_state(4) + @test reg |> copy |> hr |> isnormalized + + hr2 = crot(3, 0.01) + reg1 = reg |> copy |> hr + reg2 = reg |> copy |> hr2 + @test fidelity(reg1, reg2)[] ≈ 1 +end + +""" + hhl_problem(nbit::Int) -> Tuple + +Returns (A, b), where + * `A` is positive definite, hermitian, with its maximum eigenvalue λ_max < 1. + * `b` is normalized. +""" +function hhl_problem(nbit::Int) + siz = 1< = |b>. + ## signs: Diagonal Matrix of eigen values of A. + ## base_space: the eigen space of A. + ## x: |x>. + using Random + Random.seed!(2) + N = 3 + A, b = hhl_problem(N) + x = A^(-1)*b # base_i = base_space[:,i] ϕ1 = (A*base_i./base_i)[1] + + ## n_b : number of bits for |b>. + ## n_reg: number of PE register. + ## n_all: number of all bits. + n_reg = 12 + + ## C_value: value of constant C in control rotation. + ## It should be samller than the minimum eigen value of A. + C_value = minimum(eigvals(A) .|> abs)*0.25 + #C_value = 1.0/(1<|00>|u>. + @test isapprox.(x, res, atol=0.5) |> all +end diff --git a/test/PhaseEstimation.jl b/test/PhaseEstimation.jl new file mode 100644 index 000000000..07c80a9c8 --- /dev/null +++ b/test/PhaseEstimation.jl @@ -0,0 +1,68 @@ +using Yao +using Yao.Intrinsics +using Test, LinearAlgebra +using QuAlgorithmZoo + +""" +random phase estimation problem setup. +""" +function rand_phaseest_setup(N::Int) + U = rand_unitary(1< adjoint) ≈ join(reg2, reg1) +end + +@testset "phaseest, non-eigen" begin + # Generate a random matrix. + N = 3 + U, b, ϕs, evec = rand_phaseest_setup(N) + + # Define registers and U operator. + M = 6 + reg1 = zero_state(M) + reg2 = register(b) + UG = matrixgate(U); + + # run circuit + reg= join(reg2, reg1) + pe = PEBlock(UG, M, N) + apply!(reg, pe) + + # measure + bs, proj, amp_relative = projection_analysis(evec, focus!(reg, M+1:M+N)) + @test isapprox(ϕs, bfloat.(bs, nbit=M), atol=0.05) +end diff --git a/test/QCBM.jl b/test/QCBM.jl new file mode 100644 index 000000000..0f523ecb8 --- /dev/null +++ b/test/QCBM.jl @@ -0,0 +1,32 @@ +using Test +using Yao +using QuAlgorithmZoo + +""" + gaussian_pdf(x, μ::Real, σ::Real) + +gaussian probability density function. +""" +function gaussian_pdf(x, μ::Real, σ::Real) + pl = @. 1 / sqrt(2pi * σ^2) * exp(-(x - μ)^2 / (2 * σ^2)) + pl / sum(pl) +end + +@testset "qcbm" begin + # problem setup + n = 6 + depth = 6 + + N = 1< autodiff(:QC) + dispatch!(circuit, :random) + qcbm = QCBM(circuit, kernel, pg) + + # training + niter = 100 + optim = Adam(lr=0.1) + for info in QCBMGo!(qcbm, optim, niter) end + @test qcbm |> loss < 1e-4 +end diff --git a/test/QCOptProblem.jl b/test/QCOptProblem.jl new file mode 100644 index 000000000..153ba97f5 --- /dev/null +++ b/test/QCOptProblem.jl @@ -0,0 +1,7 @@ +@testset "QuGAN" begin + include("QuGAN.jl") +end + +@testset "QCBM" begin + include("QCBM.jl") +end diff --git a/test/QuGAN.jl b/test/QuGAN.jl new file mode 100644 index 000000000..9fffa5ba9 --- /dev/null +++ b/test/QuGAN.jl @@ -0,0 +1,22 @@ +using Test +using Yao +using QuAlgorithmZoo + +function run_test(nbit::Int, depth_gen::Int, depth_disc::Int; g_lr=0.1, d_lr=0.2, niter=1000) + qg = toy_qugan(rand_state(nbit), depth_gen, depth_disc) + for info in QuGANGo!(qg, g_lr, d_lr, niter) end + qg +end + +@testset "quantum circuit gan - opdiff" begin + N = 3 + target = rand_state(N) + qcg = toy_qugan(target, 2, 2) + grad = gradient(qcg) + @test isapprox(grad, num_gradient(qcg), atol=1e-4) + qg = run_test(3, 4, 4, g_lr=0.2, d_lr=0.5, niter=300) + @test qg |> loss < 0.1 + qg = run_test(3, 4, 4, g_lr=Adam(lr=0.005), d_lr=Adam(lr=0.5), niter=300) + @test qg |> loss < 0.1 +end + diff --git a/test/RotBasis.jl b/test/RotBasis.jl index 8b3cdbc41..6ad3ed6ea 100644 --- a/test/RotBasis.jl +++ b/test/RotBasis.jl @@ -25,6 +25,10 @@ using QuAlgorithmZoo @test state(apply!(psi, rb)) ≈ [1, 0] @test nparameters(rot_basis(3)) == 6 + dispatch!(rb, :zero) + @test parameters(rb)[1] == 0 + dispatch!(rb, :random) + @test parameters(rb)[1] != 0 end @testset "polar and u" begin diff --git a/test/hamiltonian_solvers.jl b/test/hamiltonian_solvers.jl new file mode 100644 index 000000000..44b43b65e --- /dev/null +++ b/test/hamiltonian_solvers.jl @@ -0,0 +1,22 @@ +using Yao, Yao.Blocks +using LinearAlgebra +using Test +using QuAlgorithmZoo + +@testset "solving hamiltonian" begin + nbit = 8 + h = heisenberg(nbit) |> cache + @test ishermitian(h) + reg = rand_state(nqubits(h)) + E0 = expect(h, reg)/nbit + reg |> iter_groundstate!(h, niter=1000) + EG = expect(h, reg)/nbit/4 + @test isapprox(EG, -0.4564, atol=1e-4) + + # using Time Evolution + reg = rand_state(nqubits(h)) + reg |> itime_groundstate!(h, τ=20) + EG = expect(h, reg)/nbit/4 + @test isapprox(EG, -0.4564, atol=1e-4) +end + diff --git a/test/runtests.jl b/test/runtests.jl index ed04c17f0..c87f836ad 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,8 +9,8 @@ using QuAlgorithmZoo include("QFT.jl") end -@testset "Differential" begin - include("Differential.jl") +@testset "CircuitBuild" begin + include("CircuitBuild.jl") end @testset "RotBasis" begin @@ -20,3 +20,19 @@ end @testset "Grover" begin include("Grover.jl") end + +@testset "PhaseEstimation" begin + include("PhaseEstimation.jl") +end + +@testset "HHL" begin + include("HHL.jl") +end + +@testset "QCOptProblem" begin + include("QCOptProblem.jl") +end + +@testset "hamiltonian solvers" begin + include("hamiltonian_solvers.jl") +end From dfd5375cb6546f55e43402b9391dd4a75060659e Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 26 Oct 2018 20:12:27 +0800 Subject: [PATCH 35/85] update README.md --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2a0dee345..37a461a61 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,14 @@ Please notice, this package is still under development and needs further polish. ## Contents - [x] QCBM tutorial -- [ ] differentiable circuit -- [ ] grover search -- [ ] HHL -- [ ] QFT +- [x] grover search +- [x] HHL +- [x] QFT +- [x] QuGAN +- [x] QCBM +- [x] Hamiltonian Solver - [ ] QAOA +- [ ] Quantum Chemistry ## License From 7ccbdcf22e125d89e68cd26de8c074f938d4962b Mon Sep 17 00:00:00 2001 From: Leo Date: Sun, 6 Jan 2019 23:48:57 +0800 Subject: [PATCH 36/85] new vqe and hadamard test --- examples/VQE.jl | 17 ++++++++++++++ src/HadamardTest.jl | 46 +++++++++++++++++++++++++++++++++++++ src/QCBM.jl | 3 ++- src/QuAlgorithmZoo.jl | 1 + src/hamiltonian_solvers.jl | 25 ++++++++++++++++++-- test/HadamardTest.jl | 41 +++++++++++++++++++++++++++++++++ test/PhaseEstimation.jl | 2 +- test/hamiltonian_solvers.jl | 11 ++++++++- test/runtests.jl | 4 ++++ 9 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 examples/VQE.jl create mode 100644 src/HadamardTest.jl create mode 100644 test/HadamardTest.jl diff --git a/examples/VQE.jl b/examples/VQE.jl new file mode 100644 index 000000000..ede756172 --- /dev/null +++ b/examples/VQE.jl @@ -0,0 +1,17 @@ +using Yao, Yao.Blocks +using QuAlgorithmZoo +using KrylovKit + +function ed_groundstate(h::MatrixBlock) + E, V = eigsolve(h |> mat, 1, :SR, ishermitian=true) + println("Ground State Energy is $(E[1])") + register(V[1]) +end + +N = 5 +c = random_diff_circuit(N, N, [i=>mod(i,N)+1 for i=1:N], mode=:Merged) |> autodiff(:QC) +dispatch!(c, :random) +hami = heisenberg(N) +ed_groundstate(hami) + +vqe_solve(c, hami) diff --git a/src/HadamardTest.jl b/src/HadamardTest.jl new file mode 100644 index 000000000..0a9e6df7d --- /dev/null +++ b/src/HadamardTest.jl @@ -0,0 +1,46 @@ +export hadamard_test, hadamard_test_circuit, swap_test, swap_test_circuit, singlet_block, state_overlap_circuit + +""" +see WiKi. +""" +function hadamard_test_circuit(U::MatrixBlock{N}) where N + chain(N+1, put(N+1, 1=>H), + control(N+1, 1, 2:N+1=>U), # get matrix first, very inefficient + put(N+1, 1=>H) + ) +end + +function hadamard_test(U::MatrixBlock{N}, reg::AbstractRegister) where N + c = hadamard_test_circuit(U) + reg = join(reg, zero_state(1)) + expect(put(N+1, 1=>Z), reg |> c) +end + +swap_test_circuit() = hadamard_test_circuit(SWAP) +swap_test(reg::AbstractRegister) = hadamard_test(SWAP, reg) + +function singlet_block(::Type{T}, nbit::Int, i::Int, j::Int) where T + unit = chain(nbit) + push!(unit, put(nbit, i=>chain(XGate{T}(), HGate{T}()))) + push!(unit, control(nbit, -i, j=>XGate{T}())) +end + +singlet_block(nbit::Int, i::Int, j::Int) = singlet_block(ComplexF64, nbit, i, j) +singlet_block() = singlet_block(2,1,2) + +""" +Estimation of overlap between multiple density matrices. + +PRL 88.217901 +""" +function state_overlap_circuit(nbit::Int, nstate::Int, ϕ::Real) + N = nstate*nbit + 1 + + chain(N, put(N, 1=>H), + put(N, 1=>shift(ϕ)), + chain(N, [chain(N, [control(N, 1, (i+(k*nbit-nbit)+1, i+k*nbit+1)=>SWAP) for i=1:nbit]) for k=1:nstate-1]), # get matrix first, very inefficient + put(N, 1=>H) + ) +end + +Yao.mat(ρ::DensityMatrix{1}) = dropdims(state(ρ), dims=3) diff --git a/src/QCBM.jl b/src/QCBM.jl index b38ea53d7..8d4296a15 100644 --- a/src/QCBM.jl +++ b/src/QCBM.jl @@ -46,7 +46,8 @@ gradient of MMD two sample test loss, `db` must be contained in qcbm. `p0` is current probability distribution. """ function mmdgrad(qcbm::QCBM, db::AbstractDiff; p0::Vector) - vstatdiff(()->psi(qcbm), db, Vstat(kmat(qcbm.kernel)), p0=p0) - 2*vstatdiff(()->psi(qcbm), db, Vstat(kmat(qcbm.kernel)*qcbm.ptrain)) + statdiff(()->probs(qcbm) |> as_weights, db, StatFunctional(kmat(qcbm.kernel)), initial=p0 |> as_weights) - + 2*statdiff(()->probs(qcbm) |> as_weights, db, StatFunctional(kmat(qcbm.kernel)*qcbm.ptrain)) end """ diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index d9b2f1001..85bd7929f 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -28,6 +28,7 @@ include("Grover.jl") include("PhaseEstimation.jl") include("HHL.jl") include("hamiltonian_solvers.jl") +include("HadamardTest.jl") end # module diff --git a/src/hamiltonian_solvers.jl b/src/hamiltonian_solvers.jl index f176f5a45..7185da33a 100644 --- a/src/hamiltonian_solvers.jl +++ b/src/hamiltonian_solvers.jl @@ -1,4 +1,4 @@ -export heisenberg, iter_groundstate!, itime_groundstate! +export heisenberg, iter_groundstate!, itime_groundstate!, vqe_solve! """ heisenberg(nbit::Int; periodic::Bool=true) @@ -29,7 +29,7 @@ end """ itime_groundstate!({reg::AbstractRegister}, h::MatrixBlock; τ::Int=20, tol=1e-4) -> AbstractRegister -project wave function to ground state by exp(-hτ). `tol` is for `expmv`. +Imaginary time evolution method to get ground state, i.e. by projecting wave function to ground state by exp(-hτ). `tol` is for `expmv`. """ itime_groundstate!(h::MatrixBlock; τ::Real=20, tol=1e-4) = reg -> itime_groundstate!(reg, h; τ=τ, tol=tol) function itime_groundstate!(reg::AbstractRegister, h::MatrixBlock; τ::Int=20, tol=1e-4) @@ -44,3 +44,24 @@ function itime_groundstate!(reg::AbstractRegister, h::MatrixBlock; τ::Int=20, t reg end +# a patch for Yao.expect, to make it faster +function Yao.expect(op::AddBlock, reg::AbstractRegister{1}) + sum(opi->expect(opi, reg), op) +end + +""" + vqe_solve!(circuit::MatrixBlock{N}, hamiltonian::AbstractBlock; niter::Int=100) -> circuit + +variational quantum eigensolver, faithful simulation with optimizer Adam(lr=0.01). +""" +function vqe_solve!(circuit::MatrixBlock{N}, hamiltonian::AbstractBlock; niter::Int=100) where N + optimizer = Adam(lr=0.01) + dbs = collect(circuit, AbstractDiff) + params = parameters(circuit) + for i = 1:niter + grad = opdiff.(()->zero_state(N) |> circuit, dbs, Ref(hamiltonian)) + dispatch!(circuit, update!(params, grad, optimizer)) + println("Step $i, Energy = $(expect(hamiltonian, zero_state(N) |> circuit))") + end + circuit +end diff --git a/test/HadamardTest.jl b/test/HadamardTest.jl new file mode 100644 index 000000000..2dba4dd29 --- /dev/null +++ b/test/HadamardTest.jl @@ -0,0 +1,41 @@ +using Yao, Yao.Blocks +using Test +using LinearAlgebra +using QuAlgorithmZoo + +@testset "state overlap" begin + reg1 = rand_state(3) |> focus!(1,2) + rho1 = reg1 |> ρ + reg2 = rand_state(3) |> focus!(1,2) + rho2 = reg2 |> ρ + reg3 = rand_state(3) |> focus!(1,2) + rho3 = reg3 |> ρ + desired = tr(mat(rho1)*mat(rho2)) + c = state_overlap_circuit(2, 2, 0) + res = expect(put(5, 1=>Z), join(join(reg2, reg1), zero_state(1)) |> c) |> tr + @test desired ≈ res + desired = tr(mat(rho1)*mat(rho2)*mat(rho3)) |> real + c = state_overlap_circuit(2, 3, 0) + res = expect(put(7, 1=>Z), reduce(⊗, [reg3, reg2, reg1, zero_state(1)]) |> c) |> tr |> real + @test desired ≈ res + desired = tr(mat(rho1)*mat(rho2)*mat(rho3)) |> imag + c = state_overlap_circuit(2, 3, -π/2) + res = expect(put(7, 1=>Z), reduce(⊗, [reg3, reg2, reg1, zero_state(1)]) |> c) |> tr |> real + @test desired ≈ res +end + +@testset "hadamard test" begin + nbit = 4 + U = put(nbit, 2=>Rx(0.2)) + reg = rand_state(nbit) + + @test hadamard_test(U, reg) ≈ real(expect(U, reg)) + + reg = zero_state(2) |> singlet_block() + @test swap_test(reg) ≈ -1 + + reg = zero_state(2) + @test swap_test(reg) ≈ 1 + reg = product_state(2, 0b11) + @test swap_test(reg) ≈ 1 +end diff --git a/test/PhaseEstimation.jl b/test/PhaseEstimation.jl index 07c80a9c8..0db8d35f6 100644 --- a/test/PhaseEstimation.jl +++ b/test/PhaseEstimation.jl @@ -40,7 +40,7 @@ end reg = apply!(join(reg2, reg1), circuit) # measure - res = breflect(M, measure(focus!(copy(reg), 1:M), 10)[1]) / (1< adjoint) ≈ join(reg2, reg1) diff --git a/test/hamiltonian_solvers.jl b/test/hamiltonian_solvers.jl index 44b43b65e..fa64cab61 100644 --- a/test/hamiltonian_solvers.jl +++ b/test/hamiltonian_solvers.jl @@ -18,5 +18,14 @@ using QuAlgorithmZoo reg |> itime_groundstate!(h, τ=20) EG = expect(h, reg)/nbit/4 @test isapprox(EG, -0.4564, atol=1e-4) -end + # using VQE + N = 4 + h = heisenberg(N) + E = eigen(h |> mat |> Matrix).values[1] + c = random_diff_circuit(N, 5, [i=>mod(i,N)+1 for i=1:N], mode=:Merged) |> autodiff(:QC) + dispatch!(c, :random) + vqe_solve!(c, h) + E2 = expect(h, zero_state(N) |> c) + @test isapprox(E, E2, atol=1e-1) +end diff --git a/test/runtests.jl b/test/runtests.jl index c87f836ad..e2c4a88ce 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -36,3 +36,7 @@ end @testset "hamiltonian solvers" begin include("hamiltonian_solvers.jl") end + +@testset "hadamard test" begin + include("HadamardTest.jl") +end From 5b3594482524d74ebd406d7356259ed84a93f303 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 9 Jan 2019 19:36:00 +0800 Subject: [PATCH 37/85] new QAOA example --- examples/QAOA.jl | 76 +++++++++++++++++++++++++++++++++++++++ examples/maxcut_gw.jl | 83 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 examples/QAOA.jl create mode 100644 examples/maxcut_gw.jl diff --git a/examples/QAOA.jl b/examples/QAOA.jl new file mode 100644 index 000000000..b5f33a76c --- /dev/null +++ b/examples/QAOA.jl @@ -0,0 +1,76 @@ +using Yao, Yao.Blocks +using NLopt + +include("maxcut_gw.jl") + +HB(nbit::Int) = sum([put(nbit, i=>X) for i=1:nbit]) +tb = TimeEvolution(HB(3), 0.1) +function HC(W::AbstractMatrix) + nbit = size(W, 1) + ab = add(nbit) + for i=1:nbit,j=i+1:nbit + if W[i,j] != 0 + push!(ab, 0.5*W[i,j]*repeat(nbit, Z, [i,j])) + end + end + ab +end + +function qaoa_circuit(W::AbstractMatrix, depth::Int; use_cache::Bool=false) + nbit = size(W, 1) + hb = HB(nbit) + hc = HC(W) + use_cache && (hb = hb |> cache; hc = hc |> cache) + c = chain(nbit, [repeat(nbit, H, 1:nbit)]) + append!(c, [chain(nbit, [timeevolve(hc, 0.0, tol=1e-5), timeevolve(hb, 0.0, tol=1e-5)]) for i=1:depth]) +end + + +function cobyla_optimize(circuit::MatrixBlock{N}, hc::MatrixBlock; niter::Int) where N + function f(params, grad) + reg = zero_state(N) |> dispatch!(circuit, params) + loss = expect(hc, reg) |> real + #println(loss) + loss + end + opt = Opt(:LN_COBYLA, nparameters(circuit)) + min_objective!(opt, f) + maxeval!(opt, niter) + cost, params, info = optimize(opt, parameters(circuit)) + pl = zero_state(N) |> circuit |> probs + cost, params, pl +end + +using Random, Test +@testset "qaoa circuit" begin + Random.seed!(2) + nbit = 5 + W = [0 5 2 1 0; + 5 0 3 2 0; + 2 3 0 0 0; + 1 2 0 0 4; + 0 0 0 4 0] + + # the exact solution + exact_cost, sets = goemansWilliamson(W) + + hc = HC(W) + @test ishermitian(hc) + # the actual loss is - + sum(wij)/2 + @test -expect(hc, product_state(5, bmask(sets[1]...)))+sum(W)/4 ≈ exact_cost + + # build a QAOA circuit and start training. + qc = qaoa_circuit(W, 20; use_cache=false) + opt_pl = nothing + opt_cost = Inf + for i = 1:10 + dispatch!(qc, :random) + cost, params, pl = cobyla_optimize(qc, hc; niter=2000) + @show cost + cost < opt_cost && (opt_pl = pl) + end + + # check the correctness of result + config = argmax(opt_pl)-1 + @test config in [bmask(set...) for set in sets] +end diff --git a/examples/maxcut_gw.jl b/examples/maxcut_gw.jl new file mode 100644 index 000000000..ad913b78a --- /dev/null +++ b/examples/maxcut_gw.jl @@ -0,0 +1,83 @@ +using Convex +using SCS +using LinearAlgebra + +" +The Goemans-Williamson algorithm for the MAXCUT problem. + +From: https://github.com/ericproffitt/MaxCut +" + +function goemansWilliamson(W::Matrix{T}; tol::Real=1e-1, iter::Int=100) where T<:Real + "Partition a graph into two disjoint sets such that the sum of the edge weights + which cross the partition is as large as possible (known to be NP-hard)." + + "A cut of a graph can be produced by assigning either 1 or -1 to each vertex. The Goemans-Williamson + algorithm relaxes this binary condition to allow for vector assignments drawn from the (n-1)-sphere + (choosing an n-1 dimensional space will ensure seperability). This relaxation can then be written as + an SDP. Once the optimal vector assignments are found, origin centered hyperplanes are generated and + their corresponding cuts evaluated. After 'iter' trials, or when the desired tolerance is reached, + the hyperplane with the highest corresponding binary cut is used to partition the vertices." + + "W: Adjacency matrix." + "tol: Maximum acceptable distance between a cut and the MAXCUT upper bound." + "iter: Maximum number of hyperplane iterations before a cut is chosen." + + LinearAlgebra.checksquare(W) + @assert LinearAlgebra.issymmetric(W) "Adjacency matrix must be symmetric." + @assert all(W .>= 0) "Entries of the adjacency matrix must be nonnegative." + @assert all(diag(W) .== 0) "Diagonal entries of adjacency matrix must be zero." + @assert tol > 0 "The tolerance 'tol' must be positive." + @assert iter > 0 "The number of iterations 'iter' must be a positive integer." + + "This is the standard SDP Relaxation of the MAXCUT problem, a reference can be found at + http://www.sfu.ca/~mdevos/notes/semidef/GW.pdf." + k = size(W, 1) + S = Semidefinite(k) + + expr = dot(W, S) + constr = [S[i,i] == 1.0 for i in 1:k] + problem = minimize(expr, constr...) + solve!(problem, SCSSolver(verbose=0)) + + ### Ensure symmetric positive-definite. + A = 0.5 * (S.value + S.value') + A += (max(0, -eigmin(A)) + eps(1e3))*I + + X = Matrix(cholesky(A)) + + ### A non-trivial upper bound on MAXCUT. + upperbound = (sum(W) - dot(W, S.value)) / 4 + + "Random origin-centered hyperplanes, generated to produce partitions of the graph." + maxcut = 0 + maxpartition = nothing + + for i in 1:iter + gweval = X' * randn(k) + partition = (findall(x->x>0, gweval), findall(x->x<0, gweval)) + cut = sum(W[partition...]) + + if cut > maxcut + maxpartition = partition + maxcut = cut + end + + upperbound - maxcut < tol && break + i == iter && println("Max iterations reached.") + end + return round(maxcut; digits=3), maxpartition +end + +using Test +@testset "max cut" begin + W = [0 5 2 1 0; + 5 0 3 2 0; + 2 3 0 0 0; + 1 2 0 0 4; + 0 0 0 4 0] + + maxcut, maxpartition = goemansWilliamson(W) + @test maxcut == 14 + @test [2,5] in maxpartition +end From 071b02aaee0e1a035e9166a048a0273338788477 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Fri, 8 Feb 2019 20:52:14 +0800 Subject: [PATCH 38/85] fix Project.toml --- Project.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 8e9891d4d..5bd30b6cd 100644 --- a/Project.toml +++ b/Project.toml @@ -9,10 +9,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -Yao = "5a1af4f6-c801-11e8-08ea-1bad16a356b2" +Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[targets] -test = ["Test"] From 987528099d23962388c287634877cd5b88d93cd8 Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Tue, 5 Mar 2019 01:41:22 +0530 Subject: [PATCH 39/85] Forward Euler implementation using HHL --- src/QuAlgorithmZoo.jl | 1 + src/lin_diffEq_HHL.jl | 86 +++++++++++++++++++++++++++++++++++++++++ test/lin_diffEq_test.jl | 32 +++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/lin_diffEq_HHL.jl create mode 100644 test/lin_diffEq_test.jl diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index 85bd7929f..977b995cb 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -29,6 +29,7 @@ include("PhaseEstimation.jl") include("HHL.jl") include("hamiltonian_solvers.jl") include("HadamardTest.jl") +include("lin_diffEq_HHL.jl") end # module diff --git a/src/lin_diffEq_HHL.jl b/src/lin_diffEq_HHL.jl new file mode 100644 index 000000000..0dcb472ea --- /dev/null +++ b/src/lin_diffEq_HHL.jl @@ -0,0 +1,86 @@ +export Array_QuEuler, prepare_init_state, solve_QuEuler + +""" + Based on : arxiv.org/abs/1010.2745v2 + + QuArray_Euler(N_t,N,h,A) + prepare_init_state(b,x,h,N_t) + + x' = Ax + b + + * A - input matrix. + * b - input vector. + * x - inital vector + * N - dimension of b (as a power of 2). + * h - step size. + * tspan - time span. +""" +function prepare_init_state(tspan::Tuple,x::Vector,h::Float64,g::Function) + init_state = x; + N_t = round(2*(tspan[2] - tspan[1])/h + 3) + b = similar(g(1)) + for i = 1:N_t + if i < (N_t+1)/2 + b = g(h*i + tspan[1]) + init_state = [init_state;h*b] + else + init_state = [init_state;zero(b)] + end + + end + init_state = [init_state;zero(init_state)] + init_state +end + +function Array_QuEuler(tspan::Tuple,h::Float64,g::Function) + N_t = round(2*(tspan[2] - tspan[1])/h + 3) + I_mat = Matrix{Float64}(I, size(g(1))); + A_ = I_mat; + zero_mat = zero(I_mat); + tmp_A = Array{Float64,2}(undef,size(g(1))) + + for j = 2: N_t +1 + A_ = [A_ zero_mat] + end + + for i = 2 : N_t+1 + tmp_A = g(i*h + tspan[1]) + tmp_A = -1*(I_mat + h*tmp_A) + tA_ = copy(zero_mat) + if i<3 + tA_ = [tmp_A I_mat] + else + for j = 1:i-3 + tA_ = [tA_ zero_mat] + end + if i < (N_t + 1)/2 + 1 + tA_ = [tA_ tmp_A I_mat] + else + tA_ = [tA_ -1*I_mat I_mat] + end + end + if i< N_t+1 + for j = i+1: N_t+1 + tA_ = [tA_ zero_mat] + end + end + A_ = [A_;tA_] + + end + A_ = [zero(A_) A_;A_' zero(A_)] + A_ +end + +function solve_QuEuler(A::Function, b::Function, x::Vector,tspan::Tuple, h::Float64) + + mat = Array_QuEuler(tspan,h,A) + state = prepare_init_state(tspan,x,h,b) + λ = maximum(eigvals(mat)) + C_value = minimum(eigvals(mat) .|> abs)*0.1; + n_reg = 12; + mat = 1/(λ*2)*mat + state = state*1/(2*λ) |> normalize! + res = hhlsolve(mat,state, n_reg, C_value) + res = res/λ + res +end diff --git a/test/lin_diffEq_test.jl b/test/lin_diffEq_test.jl new file mode 100644 index 000000000..1a30b96c3 --- /dev/null +++ b/test/lin_diffEq_test.jl @@ -0,0 +1,32 @@ +using Yao +using Yao.Intrinsics +using QuAlgorithmZoo +using Test, LinearAlgebra +using OrdinaryDiffEq + +function diffEq_problem(nbit::Int) + siz = 1< all +end From 50ac94a5959b75de0607c1ba96aaac6b90d1d9de Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Tue, 5 Mar 2019 18:42:23 +0530 Subject: [PATCH 40/85] Added OrdinaryDiffEq to [extra] in project.toml --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 5bd30b6cd..eb71c8541 100644 --- a/Project.toml +++ b/Project.toml @@ -13,3 +13,4 @@ Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" From 1680b09ca97a19cd7c6e9c03a8595b9f28935d69 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Tue, 5 Mar 2019 21:39:28 +0800 Subject: [PATCH 41/85] fix ci --- Project.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Project.toml b/Project.toml index eb71c8541..0f1509407 100644 --- a/Project.toml +++ b/Project.toml @@ -14,3 +14,6 @@ Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" + +[targets] +test = ["Test", "OrdinaryDiffEq"] From d2d2cc541aba0f7ac7fef3ab661f4af3d6c1ca48 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Tue, 5 Mar 2019 21:40:15 +0800 Subject: [PATCH 42/85] update travis config to 1.0 --- .travis.yml | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7b90c2620..0835afc40 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,38 +1,18 @@ -## Documentation: http://docs.travis-ci.com/user/languages/julia/ +# Documentation: http://docs.travis-ci.com/user/languages/julia/ language: julia os: - linux - osx julia: - - 0.7 - 1.0 + - 1.1 - nightly +matrix: + allow_failures: + - julia: nightly + fast_finish: true notifications: email: false -git: - depth: 99999999 - -## uncomment the following lines to allow failures on nightly julia -## (tests will run but not make your overall status red) -#matrix: -# allow_failures: -# - julia: nightly - -## uncomment and modify the following lines to manually install system packages -#addons: -# apt: # apt-get for linux -# packages: -# - gfortran -#before_script: # homebrew for mac -# - if [ $TRAVIS_OS_NAME = osx ]; then brew install gcc; fi - -## uncomment the following lines to override the default test script -#script: -# - julia -e 'Pkg.clone(pwd()); Pkg.build("QuAlgorithmZoo"); Pkg.test("QuAlgorithmZoo"; coverage=true)' after_success: - # push coverage results to Coveralls - - julia -e 'using Pkg; cd(Pkg.dir("QuAlgorithmZoo")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())' - # push coverage results to Codecov - - julia -e 'using Pkg; cd(Pkg.dir("QuAlgorithmZoo")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' - # add Documenter - - julia -e 'using Pkg; cd(Pkg.dir("QuAlgorithmZoo")); include(joinpath("docs", "make.jl"))' + - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' + - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder())' From b9ed54b46e2531ad703181c8060403407ffac4e5 Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta <34926286+dgan181@users.noreply.github.com> Date: Tue, 26 Mar 2019 21:40:22 +0530 Subject: [PATCH 43/85] Added Higher-order linear multistep methods using HHL (#4) Added Higher-order linear multistep --- Project.toml | 3 +- src/lin_diffEq_HHL.jl | 199 +++++++++++++++++++++++++++------------- test/lin_diffEq_test.jl | 54 +++++++---- 3 files changed, 173 insertions(+), 83 deletions(-) diff --git a/Project.toml b/Project.toml index 0f1509407..dca314734 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,7 @@ authors = ["Roger-luo "] version = "0.1.0" [deps] +DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" @@ -12,8 +13,8 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" [extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test", "OrdinaryDiffEq"] diff --git a/src/lin_diffEq_HHL.jl b/src/lin_diffEq_HHL.jl index 0dcb472ea..3e57b6a81 100644 --- a/src/lin_diffEq_HHL.jl +++ b/src/lin_diffEq_HHL.jl @@ -1,10 +1,14 @@ -export Array_QuEuler, prepare_init_state, solve_QuEuler +export array_qudiff, prepare_init_state, LDEMSAlgHHL, bval, aval +export QuEuler, QuLeapfrog, QuAB2, QuAB3, QuAB4 +export QuLDEMSProblem +using DiffEqBase """ Based on : arxiv.org/abs/1010.2745v2 - QuArray_Euler(N_t,N,h,A) - prepare_init_state(b,x,h,N_t) + * array_qudiff(N_t,N,h,A) - generates matrix for k-step solver + * prepare_init_state(b,x,h,N_t) - generates inital states + * solve_qudiff - solver x' = Ax + b @@ -15,72 +19,139 @@ export Array_QuEuler, prepare_init_state, solve_QuEuler * h - step size. * tspan - time span. """ -function prepare_init_state(tspan::Tuple,x::Vector,h::Float64,g::Function) - init_state = x; - N_t = round(2*(tspan[2] - tspan[1])/h + 3) - b = similar(g(1)) - for i = 1:N_t - if i < (N_t+1)/2 - b = g(h*i + tspan[1]) - init_state = [init_state;h*b] - else - init_state = [init_state;zero(b)] - end - - end - init_state = [init_state;zero(init_state)] - init_state + +""" + LDEMSAlgHHL + * step - step for multistep method + * α - coefficients for xₙ + * β - coefficent for xₙ' +""" +abstract type QuODEAlgorithm <: DiffEqBase.AbstractODEAlgorithm end +#abstract type QuODEProblem{uType,tType,isinplace} <: DiffEqBase.AbstractODEProblem{uType,tType,isinplace} end +abstract type LDEMSAlgHHL <: QuODEAlgorithm end + +struct QuLDEMSProblem{F,C,U,T} #<: QuODEProblem{uType,tType,isinplace} + A::F + b::C + u0::U + tspan::NTuple{2,T} + + #function QuLDEMSProblem(A,b,u0,tspan) + # new{typeof(u0),typeof(tspan),false,typeof(A),typeof(b)}(A,b,u0,tspan) + #end end -function Array_QuEuler(tspan::Tuple,h::Float64,g::Function) - N_t = round(2*(tspan[2] - tspan[1])/h + 3) - I_mat = Matrix{Float64}(I, size(g(1))); - A_ = I_mat; - zero_mat = zero(I_mat); - tmp_A = Array{Float64,2}(undef,size(g(1))) - - for j = 2: N_t +1 - A_ = [A_ zero_mat] - end - - for i = 2 : N_t+1 - tmp_A = g(i*h + tspan[1]) - tmp_A = -1*(I_mat + h*tmp_A) - tA_ = copy(zero_mat) - if i<3 - tA_ = [tmp_A I_mat] - else - for j = 1:i-3 - tA_ = [tA_ zero_mat] - end - if i < (N_t + 1)/2 + 1 - tA_ = [tA_ tmp_A I_mat] - else - tA_ = [tA_ -1*I_mat I_mat] - end +""" + Explicit Linear Multistep Methods +""" +struct QuEuler{T}<:LDEMSAlgHHL + step::Int + α::Vector{T} + β::Vector{T} + + QuEuler(::Type{T} = Float64) where {T} = new{T}(1,[1.0,],[1.0,]) +end + +struct QuLeapfrog{T}<:LDEMSAlgHHL + step::Int + α::Vector{T} + β::Vector{T} + + QuLeapfrog(::Type{T} = Float64) where {T} = new{T}(2,[0, 1.0],[2.0, 0]) +end +struct QuAB2{T}<:LDEMSAlgHHL + step::Int + α::Vector{T} + β::Vector{T} + + QuAB2(::Type{T} = Float64) where {T} = new{T}(2,[1.0, 0], [1.5, -0.5]) +end +struct QuAB3{T}<:LDEMSAlgHHL + step::Int + α::Vector{T} + β::Vector{T} + QuAB3(::Type{T} = Float64) where {T} = new{T}(3,[1.0, 0, 0], [23/12, -16/12, 5/12]) +end +struct QuAB4{T}<:LDEMSAlgHHL + step::Int + α::Vector{T} + β::Vector{T} + + QuAB4(::Type{T} = Float64) where {T} = new{T}(4,[1.0, 0, 0, 0], [55/24, -59/24, 37/24, -9/24]) +end + +function bval(alg::LDEMSAlgHHL,t,h,g::Function) + b = zero(g(1)) + for i in 1:(alg.step) + b += alg.β[i]*g(t-(i-1)*h) end - if i< N_t+1 - for j = i+1: N_t+1 - tA_ = [tA_ zero_mat] - end + return b +end + +function aval(alg::LDEMSAlgHHL,t,h,g::Function) + sz, = size(g(1)) + A = Array{ComplexF64}(undef,sz,(alg.step + 1)*sz) + i_mat = Matrix{Float64}(I, size(g(1))) + A[1:sz,sz*(alg.step) + 1:sz*(alg.step + 1)] = i_mat + for i in 1:alg.step + A[1:sz,sz*(i - 1) + 1: sz*i] = -1*(alg.α[alg.step - i + 1]*i_mat + h*alg.β[alg.step - i + 1]*g(t - (alg.step - i)*h)) end - A_ = [A_;tA_] + return A +end - end - A_ = [zero(A_) A_;A_' zero(A_)] - A_ +function prepare_init_state(tspan::NTuple{2, Float64},x::Vector,h::Float64,g::Function,alg::LDEMSAlgHHL) + N_t = round(Int, (tspan[2] - tspan[1])/h + 1) #number of time steps + N = nextpow(2,2*N_t + 1) # To ensure we have a power of 2 dimension for matrix + sz, = size(g(1)) + init_state = zeros(ComplexF64,2*(N)*sz) + #inital value + init_state[1:sz] = x + for i in 2:N_t + b = bval(alg,h*(i - 1) + tspan[1],h,g) + init_state[Int(sz*(i - 1) + 1):Int(sz*(i))] = h*b + end + return init_state end -function solve_QuEuler(A::Function, b::Function, x::Vector,tspan::Tuple, h::Float64) - - mat = Array_QuEuler(tspan,h,A) - state = prepare_init_state(tspan,x,h,b) - λ = maximum(eigvals(mat)) - C_value = minimum(eigvals(mat) .|> abs)*0.1; - n_reg = 12; - mat = 1/(λ*2)*mat - state = state*1/(2*λ) |> normalize! - res = hhlsolve(mat,state, n_reg, C_value) - res = res/λ - res +function array_qudiff(tspan::NTuple{2, Float64},h::Float64,g::Function,alg::LDEMSAlgHHL) + sz, = size(g(1)) + i_mat = Matrix{Float64}(I, size(g(1))) + N_t = round(Int, (tspan[2] - tspan[1])/h + 1) #number of time steps + N = nextpow(2,2*N_t + 1) # To ensure we have a power of 2 dimension for matrix + A_ = zeros(ComplexF64, N*sz, N*sz) + # Generates First two rows + @inbounds A_[1:sz, 1:sz] = i_mat + @inbounds A_[sz + 1:2*sz, 1:sz] = -1*(i_mat + h*g(tspan[1])) + @inbounds A_[sz + 1:2*sz,sz+1:sz*2] = i_mat + #Generates additional rows based on k - step + for i in 3:alg.step + @inbounds A_[sz*(i - 1) + 1:sz*i, sz*(i - 3) + 1:sz*i] = aval(QuAB2(),(i-2)*h + tspan[1],h,g) + end + for i in alg.step + 1:N_t + @inbounds A_[sz*(i - 1) + 1:sz*(i), sz*(i - alg.step - 1) + 1:sz*i] = aval(alg,(i - 2)*h + tspan[1],h,g) + end + #Generates half mirroring matrix + for i in N_t + 1:N + @inbounds A_[sz*(i - 1) + 1:sz*(i), sz*(i - 2) + 1:sz*(i - 1)] = -1*i_mat + @inbounds A_[sz*(i - 1) + 1:sz*(i), sz*(i - 1) + 1:sz*i] = i_mat + end + A_ = [zero(A_) A_;A_' zero(A_)] + return A_ end + +function DiffEqBase.solve(prob::QuLDEMSProblem{F,C,U,T}, alg::LDEMSAlgHHL, dt = (prob.tspan[2]-prob.tspan[1])/100, n_reg::Int = 12) where {F,C,U,T} + A = prob.A + b = prob.b + tspan = prob.tspan + x = prob.u0 + + mat = array_qudiff(tspan, dt, A, alg) + state = prepare_init_state(tspan, x, dt, b, alg) + λ = maximum(eigvals(mat)) + C_value = minimum(eigvals(mat) .|> abs)*0.01; + mat = 1/(λ*2)*mat + state = state*1/(2*λ) |> normalize! + res = hhlsolve(mat,state, n_reg, C_value) + res = res/λ + return res +end; diff --git a/test/lin_diffEq_test.jl b/test/lin_diffEq_test.jl index 1a30b96c3..4cd420bd1 100644 --- a/test/lin_diffEq_test.jl +++ b/test/lin_diffEq_test.jl @@ -4,29 +4,47 @@ using QuAlgorithmZoo using Test, LinearAlgebra using OrdinaryDiffEq -function diffEq_problem(nbit::Int) +function diffeq_problem(nbit::Int) siz = 1< all -end + res = solve(qprob, QuEuler(), h, n_reg) + r = res[(N_t + 1)*2 + 2^N - 1: (N_t + 1)*2 + 2^N + N_t - 3] # range of relevant values in the obtained state. + @test isapprox.(s, r, atol = 0.5) |> all + + res = solve(qprob, QuLeapfrog(), h, n_reg) + r = res[(N_t + 1)*2 + 2^N - 1: (N_t + 1)*2 + 2^N + N_t - 3] # range of relevant values in the obtained state. + @test isapprox.(s, r, atol = 0.3) |> all + + res = solve(qprob, QuAB2(), h,n_reg) + r = res[(N_t + 1)*2 + 2^N - 1: (N_t + 1)*2 + 2^N + N_t - 3] # range of relevant values in the obtained state. + @test isapprox.(s, r, atol = 0.3) |> all + + res = solve(qprob, QuAB3(), h,n_reg) + r = res[(N_t + 1)*2 + 2^N - 1: (N_t + 1)*2 + 2^N + N_t - 3] # range of relevant values in the obtained state. + @test isapprox.(s, r, atol = 0.3) |> all + + res = solve(qprob, QuAB4(), h ,n_reg) + r = res[(N_t + 1)*2 + 2^N - 1: (N_t + 1)*2 + 2^N + N_t - 3] # range of relevant values in the obtained state. + @test isapprox.(s, r, atol = 0.3) |> all +end; From 4bc18a6f21c01f91d3ce447c39b97320755e0195 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 29 Apr 2019 17:04:36 +0800 Subject: [PATCH 44/85] For upgrade (#5) * update to 0.4.0 * ... * make proram runable * finish Zoo plish * update README.md * fix several bugs * update --- Project.toml | 5 + README.md | 5 +- examples/Grover.jl | 2 +- examples/QAOA.jl | 8 +- examples/VQE.jl | 8 +- examples/make.jl | 3 - src/Diff.jl | 244 ++++++++++++++++++++++++++++++++++++ src/Grover.jl | 26 ++-- src/HHL.jl | 14 +-- src/HadamardTest.jl | 27 ++-- src/Miscellaneous.jl | 11 +- src/PhaseEstimation.jl | 10 +- src/QCBM.jl | 4 +- src/QCOptProblem.jl | 3 +- src/QFT.jl | 8 +- src/QuAlgorithmZoo.jl | 13 +- src/QuGAN.jl | 20 +-- src/RotBasis.jl | 10 +- src/hamiltonian_solvers.jl | 29 ++--- src/sequence.jl | 39 ++++++ test/CircuitBuild.jl | 12 +- test/Diff.jl | 172 +++++++++++++++++++++++++ test/Grover.jl | 10 +- test/HHL.jl | 4 +- test/HadamardTest.jl | 21 ++-- test/PhaseEstimation.jl | 18 +-- test/QFT.jl | 3 +- test/QuGAN.jl | 6 +- test/RotBasis.jl | 2 +- test/Sequence.jl | 62 +++++++++ test/hamiltonian_solvers.jl | 2 +- test/lin_diffEq_test.jl | 4 +- test/runtests.jl | 11 ++ 33 files changed, 676 insertions(+), 140 deletions(-) delete mode 100644 examples/make.jl create mode 100644 src/Diff.jl create mode 100644 src/sequence.jl create mode 100644 test/Diff.jl create mode 100644 test/Sequence.jl diff --git a/Project.toml b/Project.toml index dca314734..6bbf1725f 100644 --- a/Project.toml +++ b/Project.toml @@ -4,15 +4,20 @@ authors = ["Roger-luo "] version = "0.1.0" [deps] +BitBasis = "50ba71b6-fa0f-514d-ae9a-0916efc90dcf" DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" +MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" +YaoArrayRegister = "e600142f-9330-5003-8abb-0ebd767abc51" +YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df" [extras] +MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/README.md b/README.md index 37a461a61..dbf8a3e1c 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,9 @@ Please notice, this package is still under development and needs further polish. - [x] QuGAN - [x] QCBM - [x] Hamiltonian Solver -- [ ] QAOA -- [ ] Quantum Chemistry +- [x] QAOA +- [x] Quantum Chemistry +- [x] QuODE ## License diff --git a/examples/Grover.jl b/examples/Grover.jl index 48d2006f3..891a56077 100644 --- a/examples/Grover.jl +++ b/examples/Grover.jl @@ -5,7 +5,7 @@ using QuAlgorithmZoo: groveriter, inference_oracle, prob_match_oracle # ## Target Space and Evidense num_bit = 12 -oracle = matrixgate(Diagonal((v = ones(1<X) for i=1:nbit]) tb = TimeEvolution(HB(3), 0.1) function HC(W::AbstractMatrix) nbit = size(W, 1) - ab = add(nbit) + ab = Any[] for i=1:nbit,j=i+1:nbit if W[i,j] != 0 push!(ab, 0.5*W[i,j]*repeat(nbit, Z, [i,j])) end end - ab + sum(ab) end function qaoa_circuit(W::AbstractMatrix, depth::Int; use_cache::Bool=false) @@ -22,7 +22,7 @@ function qaoa_circuit(W::AbstractMatrix, depth::Int; use_cache::Bool=false) hc = HC(W) use_cache && (hb = hb |> cache; hc = hc |> cache) c = chain(nbit, [repeat(nbit, H, 1:nbit)]) - append!(c, [chain(nbit, [timeevolve(hc, 0.0, tol=1e-5), timeevolve(hb, 0.0, tol=1e-5)]) for i=1:depth]) + append!(c, [chain(nbit, [time_evolve(hc, 0.0, tol=1e-5), time_evolve(hb, 0.0, tol=1e-5)]) for i=1:depth]) end diff --git a/examples/VQE.jl b/examples/VQE.jl index ede756172..d45986ec3 100644 --- a/examples/VQE.jl +++ b/examples/VQE.jl @@ -1,17 +1,17 @@ -using Yao, Yao.Blocks +using Yao using QuAlgorithmZoo using KrylovKit function ed_groundstate(h::MatrixBlock) E, V = eigsolve(h |> mat, 1, :SR, ishermitian=true) println("Ground State Energy is $(E[1])") - register(V[1]) + ArrayReg(V[1]) end N = 5 c = random_diff_circuit(N, N, [i=>mod(i,N)+1 for i=1:N], mode=:Merged) |> autodiff(:QC) dispatch!(c, :random) hami = heisenberg(N) -ed_groundstate(hami) -vqe_solve(c, hami) +# vqe ground state +vqe_solve!(c, hami) diff --git a/examples/make.jl b/examples/make.jl deleted file mode 100644 index 0daf18e84..000000000 --- a/examples/make.jl +++ /dev/null @@ -1,3 +0,0 @@ -using Literate - -Literate.notebook("QCBM.jl", joinpath(@__DIR__, "../notebooks"), execute=false) diff --git a/src/Diff.jl b/src/Diff.jl new file mode 100644 index 000000000..94b63d9cd --- /dev/null +++ b/src/Diff.jl @@ -0,0 +1,244 @@ +export Rotor, generator, AbstractDiff, BPDiff, QDiff, backward!, gradient, CPhaseGate, DiffBlock +import Yao: expect, content, chcontent +using StatsBase + +############# General Rotor ############ +const Rotor{N, T} = Union{RotationGate{N, T}, PutBlock{N, <:Any, <:RotationGate, <:Complex{T}}} +const CphaseGate{N, T} = ControlBlock{N,<:ShiftGate{T},<:Any} +const DiffBlock{N, T} = Union{Rotor{N, T}, CphaseGate{N, T}} +""" + generator(rot::Rotor) -> MatrixBlock + +Return the generator of rotation block. +""" +generator(rot::RotationGate) = rot.block +generator(rot::PutBlock{N, C, GT}) where {N, C, GT<:RotationGate} = PutBlock{N}(generator(rot|>content), rot |> occupied_locs) +generator(c::CphaseGate{N}) where N = ControlBlock(N, c.ctrol_locs, ctrl_config, control(2,1,2=>Z), c.locs) + +abstract type AbstractDiff{GT, N, T} <: TagBlock{GT, N, T} end +Base.adjoint(df::AbstractDiff) = Daggered(df) + +istraitkeeper(::AbstractDiff) = Val(true) + +#################### The Basic Diff ################# +""" + QDiff{GT, N, T} <: AbstractDiff{GT, N, Complex{T}} + QDiff(block) -> QDiff + +Mark a block as quantum differentiable. +""" +mutable struct QDiff{GT, N, T} <: AbstractDiff{GT, N, Complex{T}} + block::GT + grad::T + QDiff(block::DiffBlock{N, T}) where {N, T} = new{typeof(block), N, T}(block, T(0)) +end +content(cb::QDiff) = cb.block +chcontent(cb::QDiff, blk::DiffBlock) = QDiff(blk) + +@forward QDiff.block mat, apply! +Base.adjoint(df::QDiff) = QDiff(content(df)') + +function YaoBlocks.print_annotation(io::IO, df::QDiff) + printstyled(io, "[̂∂] "; bold=true, color=:yellow) +end + +#################### The Back Propagation Diff ################# +""" + BPDiff{GT, N, T, PT} <: AbstractDiff{GT, N, Complex{T}} + BPDiff(block, [grad]) -> BPDiff + +Mark a block as differentiable, here `GT`, `PT` is gate type, parameter type. + +Warning: + please don't use the `adjoint` after `BPDiff`! `adjoint` is reserved for special purpose! (back propagation) +""" +mutable struct BPDiff{GT, N, T, PT} <: AbstractDiff{GT, N, T} + block::GT + grad::PT + input::AbstractRegister + BPDiff(block::MatrixBlock{N, T}, grad::PT) where {N, T, PT} = new{typeof(block), N, T, typeof(grad)}(block, grad) +end +BPDiff(block::MatrixBlock) = BPDiff(block, zeros(parameters_eltype(block), nparameters(block))) +BPDiff(block::DiffBlock{N, T}) where {N, T} = BPDiff(block, T(0)) + +content(cb::BPDiff) = cb.block +chcontent(cb::BPDiff, blk::MatrixBlock) = BPDiff(blk) + +@forward BPDiff.block mat +function apply!(reg::AbstractRegister, df::BPDiff) + if isdefined(df, :input) + copyto!(df.input, reg) + else + df.input = copy(reg) + end + apply!(reg, content(df)) + reg +end + +function apply!(δ::AbstractRegister, adf::Daggered{<:BPDiff{<:Rotor}}) + df = adf |> content + apply!(δ, content(df)') + df.grad = -statevec(df.input |> generator(content(df)))' * statevec(δ) |> imag + δ +end + +function YaoBlocks.print_annotation(io::IO, df::BPDiff) + printstyled(io, "[∂] "; bold=true, color=:yellow) +end + + +#### interface ##### +export autodiff, numdiff, opdiff, StatFunctional, statdiff, as_weights + +as_weights(probs::AbstractVector{T}) where T = Weights(probs, T(1)) +""" + autodiff(mode::Symbol, block::AbstractBlock) -> AbstractBlock + autodiff(mode::Symbol) -> Function + +automatically mark differentiable items in a block tree as differentiable. +""" +function autodiff end +autodiff(mode::Symbol) = block->autodiff(mode, block) +autodiff(mode::Symbol, block::AbstractBlock) = autodiff(Val(mode), block) + +# for BP +autodiff(::Val{:BP}, block::DiffBlock) = BPDiff(block) +autodiff(::Val{:BP}, block::AbstractBlock) = block +# Sequential, Roller and ChainBlock can propagate. +function autodiff(mode::Val{:BP}, blk::Union{ChainBlock, Roller, Sequential}) + chsubblocks(blk, autodiff.(mode, subblocks(blk))) +end + +# for QC +autodiff(::Val{:QC}, block::Union{RotationGate, CphaseGate}) = QDiff(block) +# escape control blocks. +autodiff(::Val{:QC}, block::ControlBlock) = block + +function autodiff(mode::Val{:QC}, blk::AbstractBlock) + blks = subblocks(blk) + isempty(blks) ? blk : chsubblocks(blk, autodiff.(mode, blks)) + end + +@inline function _perturb(func, gate::AbstractDiff{<:DiffBlock}, δ::Real) + dispatch!(-, gate, (δ,)) + r1 = func() + dispatch!(+, gate, (2δ,)) + r2 = func() + dispatch!(-, gate, (δ,)) + r1, r2 +end + +@inline function _perturb(func, gate::AbstractDiff{<:Rotor}, δ::Real) # for put + dispatch!(-, gate, (δ,)) + r1 = func() + dispatch!(+, gate, (2δ,)) + r2 = func() + dispatch!(-, gate, (δ,)) + r1, r2 +end + +""" + numdiff(loss, diffblock::AbstractDiff; δ::Real=1e-2) + +Numeric differentiation. +""" +@inline function numdiff(loss, diffblock::AbstractDiff; δ::Real=1e-2) + r1, r2 = _perturb(loss, diffblock, δ) + diffblock.grad = (r2 - r1)/2δ +end + +""" + opdiff(psifunc, diffblock::AbstractDiff, op::MatrixBlock) + +Operator differentiation. +""" +@inline function opdiff(psifunc, diffblock::AbstractDiff, op::MatrixBlock) + r1, r2 = _perturb(()->expect(op, psifunc()) |> real, diffblock, π/2) + diffblock.grad = (r2 - r1)/2 +end + +""" + StatFunctional{N, AT} + StatFunctional(array::AT<:Array) -> StatFunctional{N, <:Array} + StatFunctional{N}(func::AT<:Function) -> StatFunctional{N, <:Function} + +statistic functional, i.e. + * if `AT` is an array, A[i,j,k...], it is defined on finite Hilbert space, which is `∫A[i,j,k...]p[i]p[j]p[k]...` + * if `AT` is a function, F(xᵢ,xⱼ,xₖ...), this functional is `1/C(r,n)... ∑ᵢⱼₖ...F(xᵢ,xⱼ,xₖ...)`, see U-statistics for detail. + +References: + U-statistics, http://personal.psu.edu/drh20/asymp/fall2006/lectures/ANGELchpt10.pdf +""" +struct StatFunctional{N, AT} + data::AT + StatFunctional{N}(data::AT) where {N, AT<:Function} = new{N, AT}(data) + StatFunctional(data::AT) where {N, AT<:AbstractArray{<:Real, N}} = new{N, AT}(data) +end + +@forward StatFunctional.data Base.ndims +Base.parent(stat::StatFunctional) = stat.data + +expect(stat::StatFunctional{2, <:AbstractArray}, px::Weights, py::Weights=px) = px.values' * stat.data * py.values +expect(stat::StatFunctional{1, <:AbstractArray}, px::Weights) = stat.data' * px.values +function expect(stat::StatFunctional{2, <:Function}, xs::AbstractVector{T}) where T + N = length(xs) + res = zero(stat.data(xs[1], xs[1])) + for i = 2:N + for j = 1:i-1 + @inbounds res += stat.data(xs[i], xs[j]) + end + end + res/binomial(N,2) +end +function expect(stat::StatFunctional{2, <:Function}, xs::AbstractVector, ys::AbstractVector) + M = length(xs) + N = length(ys) + ci = CartesianIndices((M, N)) + @inbounds mapreduce(ind->stat.data(xs[ind[1]], ys[ind[2]]), +, ci)/M/N +end +expect(stat::StatFunctional{1, <:Function}, xs::AbstractVector) = mean(stat.data.(xs)) +Base.ndims(stat::StatFunctional{N}) where N = N + +""" + statdiff(probfunc, diffblock::AbstractDiff, stat::StatFunctional{<:Any, <:AbstractArray}; initial::AbstractVector=probfunc()) + statdiff(samplefunc, diffblock::AbstractDiff, stat::StatFunctional{<:Any, <:Function}; initial::AbstractVector=samplefunc()) + +Differentiation for statistic functionals. +""" +@inline function statdiff(probfunc, diffblock::AbstractDiff, stat::StatFunctional{2}; initial::AbstractVector=probfunc()) + r1, r2 = _perturb(()->expect(stat, probfunc(), initial), diffblock, π/2) + diffblock.grad = (r2 - r1)*ndims(stat)/2 +end +@inline function statdiff(probfunc, diffblock::AbstractDiff, stat::StatFunctional{1}) + r1, r2 = _perturb(()->expect(stat, probfunc()), diffblock, π/2) + diffblock.grad = (r2 - r1)*ndims(stat)/2 +end + +""" + backward!(δ::AbstractRegister, circuit::MatrixBlock) -> AbstractRegister + +back propagate and calculate the gradient ∂f/∂θ = 2*Re(∂f/∂ψ*⋅∂ψ*/∂θ), given ∂f/∂ψ*. + +Note: +Here, the input circuit should be a matrix block, otherwise the back propagate may not apply (like Measure operations). +""" +backward!(δ::AbstractRegister, circuit::MatrixBlock) = apply!(δ, circuit') + +""" + gradient(circuit::AbstractBlock, mode::Symbol=:ANY) -> Vector + +collect all gradients in a circuit, mode can be :BP/:QC/:ANY, they will collect `grad` from BPDiff/QDiff/AbstractDiff respectively. +""" +gradient(circuit::AbstractBlock, mode::Symbol=:ANY) = gradient!(circuit, parameters_eltype(circuit)[], mode) + +gradient!(circuit::AbstractBlock, grad, mode::Symbol) = gradient!(circuit, grad, Val(mode)) +function gradient!(circuit::AbstractBlock, grad, mode::Val) + for block in subblocks(circuit) + gradient!(block, grad, mode) + end + grad +end + +gradient!(circuit::BPDiff, grad, mode::Val{:BP}) = append!(grad, circuit.grad) +gradient!(circuit::QDiff, grad, mode::Val{:QC}) = push!(grad, circuit.grad) +gradient!(circuit::AbstractDiff, grad, mode::Val{:ANY}) = append!(grad, circuit.grad) diff --git a/src/Grover.jl b/src/Grover.jl index 3207680f0..ed369a8b7 100644 --- a/src/Grover.jl +++ b/src/Grover.jl @@ -14,47 +14,47 @@ inference_oracle(nbit::Int, locs::Vector{Int}) = inference_oracle(locs)(nbit) Return a mask, that disired subspace of an oracle are masked true. """ function target_space(nbit::Int, oracle) - r = register(ones(ComplexF64, 1< oracle real(statevec(r)) .< 0 end -prob_inspace(psi::DefaultRegister, ts) = norm(statevec(psi)[ts])^2 +prob_inspace(psi::ArrayReg, ts) = norm(statevec(psi)[ts])^2 """ prob_match_oracle(psi, oracle) -> Float64 Return the probability that `psi` matches oracle. """ -prob_match_oracle(psi::DefaultRegister, oracle) = prob_inspace(psi, target_space(nqubits(psi), oracle)) +prob_match_oracle(psi::ArrayReg, oracle) = prob_inspace(psi, target_space(nqubits(psi), oracle)) """ - num_grover_step(psi::DefaultRegister, oracle) -> Int + num_grover_step(psi::ArrayReg, oracle) -> Int Return number of grover steps needed to match the oracle. """ -num_grover_step(psi::DefaultRegister, oracle) = _num_grover_step(prob_match_oracle(psi, oracle)) +num_grover_step(psi::ArrayReg, oracle) = _num_grover_step(prob_match_oracle(psi, oracle)) _num_grover_step(prob::Real) = Int(round(pi/4/sqrt(prob)))-1 """ GroverIter{N, T} - GroverIter(oracle, ref::ReflectBlock{N, T}, psi::DefaultRegister, niter::Int) + GroverIter(oracle, ref::ReflectBlock{N, T}, psi::ArrayReg, niter::Int) an iterator that perform Grover operations step by step. An Grover operation consists of applying oracle and Reflection. """ struct GroverIter{N, T} - psi::DefaultRegister + psi::ArrayReg oracle ref::ReflectBlock{N, T} niter::Int end -groveriter(psi::DefaultRegister, oracle, ref::ReflectBlock{N, T}, niter::Int) where {N, T} = GroverIter{N, T}(psi, oracle, ref, niter) -groveriter(psi::DefaultRegister, oracle, niter::Int) = groveriter(psi, oracle, ReflectBlock(psi |> copy), niter) -groveriter(psi::DefaultRegister, oracle) = groveriter(psi, oracle, ReflectBlock(psi |> copy), num_grover_step(psi, oracle)) +groveriter(psi::ArrayReg, oracle, ref::ReflectBlock{N, T}, niter::Int) where {N, T} = GroverIter{N, T}(psi, oracle, ref, niter) +groveriter(psi::ArrayReg, oracle, niter::Int) = groveriter(psi, oracle, ReflectBlock(psi |> copy), niter) +groveriter(psi::ArrayReg, oracle) = groveriter(psi, oracle, ReflectBlock(psi |> copy), num_grover_step(psi, oracle)) function Base.iterate(it::GroverIter, st=1) if it.niter + 1 == st @@ -69,7 +69,7 @@ Base.length(it::GroverIter) = it.niter """ groverblock(oracle, ref::ReflectBlock{N, T}, niter::Int=-1) - groverblock(oracle, psi::DefaultRegister, niter::Int=-1) + groverblock(oracle, psi::ArrayReg, niter::Int=-1) Return a ChainBlock/Sequential as Grover Iteration, the default `niter` will stop at the first optimal step. """ @@ -83,4 +83,4 @@ function groverblock(oracle, ref::ReflectBlock{N, T}, niter::Int=-1) where {N, T sequence(sequence(oracle, ref) for i = 1:niter) end -groverblock(oracle, psi::DefaultRegister, niter::Int=-1) = groverblock(oracle, ReflectBlock(psi |> copy), niter) +groverblock(oracle, psi::ArrayReg, niter::Int=-1) = groverblock(oracle, ReflectBlock(psi |> copy), niter) diff --git a/src/HHL.jl b/src/HHL.jl index d1c883629..dc76331d1 100644 --- a/src/HHL.jl +++ b/src/HHL.jl @@ -22,14 +22,14 @@ end a, -b, b, a end -function apply!(reg::DefaultRegister, hr::HHLCRot{N, NC, T}) where {N, NC, T} +function apply!(reg::ArrayReg, hr::HHLCRot{N, NC, T}) where {N, NC, T} mask = bmask(hr.ibit) step = 1<<(hr.ibit-1) step_2 = step*2 nbit = nqubits(reg) for j = 0:step_2:size(reg.state, 1)-step for i = j+1:j+step - λ = bfloat(takebit(i-1, hr.cbits...), nbit=nbit-1) + λ = bfloat(readbit(i-1, hr.cbits...), nbits=nbit-1) if λ >= hr.C_value u = hhlrotmat(λ, hr.C_value) u1rows!(state(reg), i, i+step, u...) @@ -40,11 +40,11 @@ function apply!(reg::DefaultRegister, hr::HHLCRot{N, NC, T}) where {N, NC, T} end """ - hhlproject!(all_bit::DefaultRegister, n_reg::Int) -> Vector + hhlproject!(all_bit::ArrayReg, n_reg::Int) -> Vector project to aiming state |1>|00>|u>, and return |u> vector. """ -function hhlproject!(all_bit::DefaultRegister, n_reg::Int) +function hhlproject!(all_bit::ArrayReg, n_reg::Int) all_bit |> focus!(1:(n_reg+1)...) |> select!(1) |> state |> vec end @@ -61,17 +61,17 @@ end """ hhlsolve(A::Matrix, b::Vector) -> Vector - + solving linear system using HHL algorithm. Here, A must be hermitian. """ function hhlsolve(A::Matrix, b::Vector, n_reg::Int, C_value::Real) if !ishermitian(A) throw(ArgumentError("Input matrix not hermitian!")) end - UG = matrixgate(exp(2π*im.*A)) + UG = matblock(exp(2π*im.*A)) # Generating input bits - all_bit = register(b) ⊗ zero_state(n_reg) ⊗ zero_state(1) + all_bit = ArrayReg(b) ⊗ zero_state(n_reg) ⊗ zero_state(1) # Construct HHL circuit. circuit = hhlcircuit(UG, n_reg, C_value) diff --git a/src/HadamardTest.jl b/src/HadamardTest.jl index 0a9e6df7d..8dd8e8a84 100644 --- a/src/HadamardTest.jl +++ b/src/HadamardTest.jl @@ -1,46 +1,33 @@ -export hadamard_test, hadamard_test_circuit, swap_test, swap_test_circuit, singlet_block, state_overlap_circuit +export hadamard_test, hadamard_test_circuit, swap_test_circuit """ see WiKi. """ -function hadamard_test_circuit(U::MatrixBlock{N}) where N +function hadamard_test_circuit(U::MatrixBlock{N}, ϕ::Real) where N chain(N+1, put(N+1, 1=>H), + put(N+1, 1=>Rz(ϕ)), control(N+1, 1, 2:N+1=>U), # get matrix first, very inefficient put(N+1, 1=>H) ) end -function hadamard_test(U::MatrixBlock{N}, reg::AbstractRegister) where N - c = hadamard_test_circuit(U) +function hadamard_test(U::MatrixBlock{N}, reg::AbstractRegister, ϕ::Real) where N + c = hadamard_test_circuit(U, ϕ::Real) reg = join(reg, zero_state(1)) expect(put(N+1, 1=>Z), reg |> c) end -swap_test_circuit() = hadamard_test_circuit(SWAP) -swap_test(reg::AbstractRegister) = hadamard_test(SWAP, reg) - -function singlet_block(::Type{T}, nbit::Int, i::Int, j::Int) where T - unit = chain(nbit) - push!(unit, put(nbit, i=>chain(XGate{T}(), HGate{T}()))) - push!(unit, control(nbit, -i, j=>XGate{T}())) -end - -singlet_block(nbit::Int, i::Int, j::Int) = singlet_block(ComplexF64, nbit, i, j) -singlet_block() = singlet_block(2,1,2) - """ Estimation of overlap between multiple density matrices. PRL 88.217901 """ -function state_overlap_circuit(nbit::Int, nstate::Int, ϕ::Real) +function swap_test_circuit(nbit::Int, nstate::Int, ϕ::Real) N = nstate*nbit + 1 chain(N, put(N, 1=>H), - put(N, 1=>shift(ϕ)), + put(N, 1=>Rz(ϕ)), chain(N, [chain(N, [control(N, 1, (i+(k*nbit-nbit)+1, i+k*nbit+1)=>SWAP) for i=1:nbit]) for k=1:nstate-1]), # get matrix first, very inefficient put(N, 1=>H) ) end - -Yao.mat(ρ::DensityMatrix{1}) = dropdims(state(ρ), dims=3) diff --git a/src/Miscellaneous.jl b/src/Miscellaneous.jl index e077d5cdd..68659b116 100644 --- a/src/Miscellaneous.jl +++ b/src/Miscellaneous.jl @@ -1,4 +1,4 @@ -export inverselines +export inverselines, singlet_block """ inverselines(nbit::Int; n_reg::Int=nbit) -> ChainBlock @@ -16,4 +16,13 @@ function inverselines(nbit::Int; n_reg::Int=nbit) c end +function singlet_block(::Type{T}, nbit::Int, i::Int, j::Int) where T + unit = chain(nbit) + push!(unit, put(nbit, i=>chain(XGate{T}(), HGate{T}()))) + push!(unit, control(nbit, -i, j=>XGate{T}())) +end + +singlet_block(nbit::Int, i::Int, j::Int) = singlet_block(ComplexF64, nbit, i, j) +singlet_block() = singlet_block(2,1,2) +Yao.mat(ρ::DensityMatrix{1}) = dropdims(state(ρ), dims=3) diff --git a/src/PhaseEstimation.jl b/src/PhaseEstimation.jl index ef8b148f1..63ba53b7c 100644 --- a/src/PhaseEstimation.jl +++ b/src/PhaseEstimation.jl @@ -8,7 +8,7 @@ phase estimation circuit. * `n_reg`: the number of bits to store phases, * `n_b`: the number of bits to store vector. """ -function PEBlock(UG::GeneralMatrixGate, n_reg::Int, n_b::Int) +function PEBlock(UG::GeneralMatrixBlock, n_reg::Int, n_b::Int) nbit = n_b + n_reg # Apply Hadamard Gate. hs = repeat(nbit, H, 1:n_reg) @@ -18,22 +18,22 @@ function PEBlock(UG::GeneralMatrixGate, n_reg::Int, n_b::Int) for i = 1:n_reg push!(control_circuit, control(nbit, (i,), (n_reg+1:nbit...,)=>UG)) if i != n_reg - UG = matrixgate(mat(UG) * mat(UG)) + UG = matblock(mat(UG) * mat(UG)) end end # Inverse QFT Block. - iqft = concentrate(nbit, QFTBlock{n_reg}() |> adjoint,[1:n_reg...,]) + iqft = concentrate(nbit, QFTBlock{n_reg}()',[1:n_reg...,]) chain(hs, control_circuit, iqft) end """ - projection_analysis(evec::Matrix, reg::DefaultRegister) -> Tuple + projection_analysis(evec::Matrix, reg::ArrayReg) -> Tuple Analyse using state projection. It returns a tuple of (most probable configuration, the overlap matrix, the relative probability for this configuration) """ -function projection_analysis(evec::Matrix, reg::DefaultRegister) +function projection_analysis(evec::Matrix, reg::ArrayReg) overlap = evec'*state(reg) amp_relative = Float64[] bs = Int[] diff --git a/src/QCBM.jl b/src/QCBM.jl index 8d4296a15..9e4b6949c 100644 --- a/src/QCBM.jl +++ b/src/QCBM.jl @@ -1,4 +1,4 @@ -import Yao.Registers: probs +import Yao: probs export QCBM, QCBMGo!, psi, mmdgrad include("Kernels.jl") @@ -12,7 +12,7 @@ struct QCBM{BT<:AbstractBlock, KT<:AbstractKernel} <: QCOptProblem dbs end function QCBM(circuit::AbstractBlock, kernel::AbstractKernel, ptrain::Vector) - QCBM(circuit, kernel, ptrain, collect(circuit, AbstractDiff)) + QCBM(circuit, kernel, ptrain, collect_blocks(AbstractDiff, circuit)) end # INTERFACES diff --git a/src/QCOptProblem.jl b/src/QCOptProblem.jl index b96b696e1..9e8574970 100644 --- a/src/QCOptProblem.jl +++ b/src/QCOptProblem.jl @@ -1,4 +1,3 @@ -import Yao: gradient export num_gradient, diff_blocks, loss, circuit, QCOptProblem, QCOptGo! """ @@ -36,7 +35,7 @@ function gradient end collection of all differentiable units. """ -diff_blocks(qop::QCOptProblem) = collect(qop |> circuit, AbstractDiff) +diff_blocks(qop::QCOptProblem) = collect_blocks(AbstractDiff, qop |> circuit) """ num_gradient(qop::QCOptProblem) -> Vector diff --git a/src/QFT.jl b/src/QFT.jl index 37123bef3..16250ea31 100644 --- a/src/QFT.jl +++ b/src/QFT.jl @@ -5,7 +5,7 @@ end export QFTCircuit, QFTBlock, invorder_firstdim CRk(i::Int, j::Int, k::Int) = control([i, ], j=>shift(2π/(1<H) : CRk(j, i, j-i+1) for j = i:n) +CRot(n::Int, i::Int) = chain(n, i==j ? kron(i=>H) : CRk(j, i, j-i+1) for j = i:n) QFTCircuit(n::Int) = chain(n, CRot(n, i) for i = 1:n) struct QFTBlock{N} <: PrimitiveBlock{N,ComplexF64} end @@ -20,7 +20,7 @@ isreflexive(q::QFTBlock{N}) where N = N==1 isunitary(q::QFTBlock{N}) where N = true openbox(q::QFTBlock{N}) where N = QFTCircuit(N) -openbox(q::Daggered{<:QFTBlock, N}) where {N} = adjoint(QFTCircuit(N)) +openbox(q::Daggered{<:QFTBlock, N}) where {N} = QFTCircuit(N)' function print_block(io::IO, pb::QFTBlock{N}) where N printstyled(io, "QFT(1-$N)"; bold=true, color=:blue) @@ -36,7 +36,7 @@ function invorder_firstdim(v::Matrix) n_2 = n ÷ 2 mask = [bmask(i, n-i+1) for i in 1:n_2] @simd for b in basis(n) - @inbounds w[breflect(n, b, mask)+1,:] = v[b+1,:] + @inbounds w[breflect(b, mask; nbits=n)+1,:] = v[b+1,:] end w end @@ -48,7 +48,7 @@ function invorder_firstdim(v::Vector) #mask = SVector{n_2, Int}([bmask(i, n-i+1)::Int for i in 1:n_2]) mask = [bmask(i, n-i+1)::Int for i in 1:n_2] @simd for b in basis(n) - @inbounds w[breflect(n, b, mask)+1] = v[b+1] + @inbounds w[breflect(b, mask; nbits=n)+1] = v[b+1] end w end diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index 977b995cb..3b78067cf 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -1,13 +1,12 @@ module QuAlgorithmZoo using LuxurySparse, LinearAlgebra -using Yao -using Yao.Intrinsics -using Yao.Registers -using Yao.Blocks -import Yao.Blocks: mat, dispatch!, niparameters, iparameters, setiparameters!, cache_key, print_block, _make_rot_mat, apply!, PrimitiveBlock +using MacroTools: @forward +using Yao, Yao.ConstGate, BitBasis +using YaoArrayRegister: u1rows! +import Yao: mat, dispatch!, niparams, getiparams, setiparams!, cache_key, print_block, apply!, PrimitiveBlock, ishermitian, isunitary, isreflexive +import YaoBlocks: render_params import Base: ==, copy, hash -import Yao.Intrinsics: ishermitian, isreflexive, isunitary export openbox @@ -19,6 +18,8 @@ For a black box, like QFTBlock, you can get its white box (loyal simulation) usi function openbox end include("Miscellaneous.jl") +include("sequence.jl") +include("Diff.jl") include("Adam.jl") include("QFT.jl") include("CircuitBuild.jl") diff --git a/src/QuGAN.jl b/src/QuGAN.jl index 7db37b6f3..b6bcede1c 100644 --- a/src/QuGAN.jl +++ b/src/QuGAN.jl @@ -1,6 +1,8 @@ -import Yao.Registers: tracedist +using MacroTools: @forward +import Yao: tracedist export QuGAN, psi, toy_qugan, QuGANGo! + """ Quantum GAN. @@ -8,21 +10,21 @@ Reference: Benedetti, M., Grant, E., Wossnig, L., & Severini, S. (2018). Adversarial quantum circuit learning for pure state approximation, 1–14. """ struct QuGAN{N} <: QCOptProblem - target::DefaultRegister + target::ArrayReg generator::MatrixBlock{N} discriminator::MatrixBlock - reg0::DefaultRegister + reg0::ArrayReg witness_op::MatrixBlock circuit::AbstractBlock gdiffs ddiffs - function QuGAN(target::DefaultRegister, gen::MatrixBlock, dis::MatrixBlock) + function QuGAN(target::ArrayReg, gen::MatrixBlock, dis::MatrixBlock) N = nqubits(target) - c = sequence(gen, addbit(1), dis) + c = Sequence([gen, addbits!(1), dis]) witness_op = put(N+1, (N+1)=>P0) - gdiffs = collect(gen, AbstractDiff) - ddiffs = collect(dis, AbstractDiff) + gdiffs = collect_blocks(AbstractDiff, gen) + ddiffs = collect_blocks(AbstractDiff, dis) new{N}(target, gen, dis, zero_state(N), witness_op, c, gdiffs, ddiffs) end end @@ -52,11 +54,11 @@ psi_disctarget(qg::QuGAN) = copy(qg.target) |> qg.circuit[2:end] tracedist(qg::QuGAN) = tracedist(qg.target, psi(qg))[] """ - toy_qugan(target::DefaultRegister, depth_gen::Int, depth_disc::Int) -> QuGAN + toy_qugan(target::ArrayReg, depth_gen::Int, depth_disc::Int) -> QuGAN Construct a toy qugan. """ -function toy_qugan(target::DefaultRegister, depth_gen::Int, depth_disc::Int) +function toy_qugan(target::ArrayReg, depth_gen::Int, depth_disc::Int) n = nqubits(target) generator = dispatch!(random_diff_circuit(n, depth_gen, pair_ring(n)), :random) |> autodiff(:QC) discriminator = dispatch!(random_diff_circuit(n+1, depth_disc, pair_ring(n+1)), :random) |> autodiff(:QC) diff --git a/src/RotBasis.jl b/src/RotBasis.jl index bdde812da..91b1067ea 100644 --- a/src/RotBasis.jl +++ b/src/RotBasis.jl @@ -10,6 +10,7 @@ mutable struct RotBasis{T} <: PrimitiveBlock{1, Complex{T}} phi::T end +_make_rot_mat(I, block, theta) = I * cos(theta / 2) - im * sin(theta / 2) * block # chain -> * # mat(rb::RotBasis{T}) where T = mat(Ry(-rb.theta))*mat(Rz(-rb.phi)) function mat(x::RotBasis{T}) where T @@ -23,12 +24,13 @@ end copy(block::RotBasis{T}) where T = RotBasis{T}(block.theta, block.phi) dispatch!(block::RotBasis, params::Vector) = ((block.theta, block.phi) = params; block) -iparameters(rb::RotBasis) = (rb.theta, rb.phi) -function setiparameters!(rb::RotBasis, theta::Real, phi::Real) - rb.theta, rb.phi = theta, phi +getiparams(rb::RotBasis) = (rb.theta, rb.phi) +function setiparams!(rb::RotBasis, θ::Real, ϕ::Real) + rb.theta, rb.phi = θ, ϕ rb end -niparameters(::Type{<:RotBasis}) = 2 +niparams(::Type{<:RotBasis}) = 2 +niparams(::RotBasis) = 2 render_params(r::RotBasis, ::Val{:random}) = rand()*π, rand()*2π function print_block(io::IO, R::RotBasis) diff --git a/src/hamiltonian_solvers.jl b/src/hamiltonian_solvers.jl index 7185da33a..4fee6f269 100644 --- a/src/hamiltonian_solvers.jl +++ b/src/hamiltonian_solvers.jl @@ -13,12 +13,12 @@ function heisenberg(nbit::Int; periodic::Bool=true) end """ - iter_groundstate!({reg::AbstractRegister}, h::MatrixBlock; niter::Int=100) -> AbstractRegister + iter_groundstate!({reg::AbstractRegister}, h::AbstractBlock; niter::Int=100) -> AbstractRegister project wave function to ground state by iteratively apply -h. """ -iter_groundstate!(h::MatrixBlock; niter::Int=100) = reg -> iter_groundstate!(reg, h, niter=niter) -function iter_groundstate!(reg::AbstractRegister, h::MatrixBlock; niter::Int=100) +iter_groundstate!(h::AbstractBlock; niter::Int=100) = reg -> iter_groundstate!(reg, h, niter=niter) +function iter_groundstate!(reg::AbstractRegister, h::AbstractBlock; niter::Int=100) for i = 1:niter reg |> h i%5 == 0 && reg |> normalize! @@ -27,36 +27,31 @@ function iter_groundstate!(reg::AbstractRegister, h::MatrixBlock; niter::Int=100 end """ - itime_groundstate!({reg::AbstractRegister}, h::MatrixBlock; τ::Int=20, tol=1e-4) -> AbstractRegister + itime_groundstate!({reg::AbstractRegister}, h::AbstractBlock; τ::Int=20, tol=1e-4) -> AbstractRegister Imaginary time evolution method to get ground state, i.e. by projecting wave function to ground state by exp(-hτ). `tol` is for `expmv`. """ -itime_groundstate!(h::MatrixBlock; τ::Real=20, tol=1e-4) = reg -> itime_groundstate!(reg, h; τ=τ, tol=tol) -function itime_groundstate!(reg::AbstractRegister, h::MatrixBlock; τ::Int=20, tol=1e-4) - span = 1 - te = timeevolve(h, -im*span) +itime_groundstate!(h::AbstractBlock; τ::Real=20, tol=1e-4) = reg -> itime_groundstate!(reg, h; τ=τ, tol=tol) +function itime_groundstate!(reg::AbstractRegister, h::AbstractBlock; τ::Int=20, tol=1e-4) + span = 1.0 + te = time_evolve(h, -im*span) for i = 1:τ÷span reg |> te |> normalize! end if τ%span != 0 - reg |> timeevolve(h, τ%span) |> normalize! + reg |> time_evolve(h, τ%span) |> normalize! end reg end -# a patch for Yao.expect, to make it faster -function Yao.expect(op::AddBlock, reg::AbstractRegister{1}) - sum(opi->expect(opi, reg), op) -end - """ - vqe_solve!(circuit::MatrixBlock{N}, hamiltonian::AbstractBlock; niter::Int=100) -> circuit + vqe_solve!(circuit::AbstractBlock{N}, hamiltonian::AbstractBlock; niter::Int=100) -> circuit variational quantum eigensolver, faithful simulation with optimizer Adam(lr=0.01). """ -function vqe_solve!(circuit::MatrixBlock{N}, hamiltonian::AbstractBlock; niter::Int=100) where N +function vqe_solve!(circuit::AbstractBlock{N}, hamiltonian::AbstractBlock; niter::Int=100) where N optimizer = Adam(lr=0.01) - dbs = collect(circuit, AbstractDiff) + dbs = collect_blocks(AbstractDiff, circuit) params = parameters(circuit) for i = 1:niter grad = opdiff.(()->zero_state(N) |> circuit, dbs, Ref(hamiltonian)) diff --git a/src/sequence.jl b/src/sequence.jl new file mode 100644 index 000000000..12924a19e --- /dev/null +++ b/src/sequence.jl @@ -0,0 +1,39 @@ +export Sequence +import YaoBlocks: subblocks, chsubblocks, apply! +using YaoBlocks: _check_size + +struct Sequence <: CompositeBlock{Any, Bool} + blocks::Vector +end + +Sequence(args...) = Sequence(collect(AbstractBlock, args)) + +subblocks(seq::Sequence) = filter(x->x isa AbstractBlock, seq.blocks) +chsubblocks(pb::Sequence, blocks::Vector) = Sequence(blocks) + +function apply!(reg::ArrayReg, seq::Sequence) + for x in seq.blocks + reg |> x + end + reg +end + +for PROP in [:lastindex, :firstindex, :getindex, :length, :eltype, :iterate, :eachindex, :popfirst!, :pop!] + @eval Base.$PROP(c::Sequence, args...; kwargs...) = $PROP(c.blocks, args...; kwargs...) +end + +function Base.:(==)(lhs::Sequence, rhs::Sequence) + (length(lhs.blocks) == length(rhs.blocks)) && all(lhs.blocks .== rhs.blocks) +end + +Base.copy(c::Sequence) = Sequence(copy(c.blocks)) +Base.similar(c::Sequence) = Sequence(empty!(similar(c.blocks))) +Base.getindex(c::Sequence, index::Union{UnitRange, Vector}) = Sequence(getindex(c.blocks, index)) + +Base.setindex!(c::Sequence, val::AbstractBlock{N}, index::Integer) where N = (setindex!(c.blocks, val, index); c) +Base.insert!(c::Sequence, index::Integer, val::AbstractBlock{N}) where N = (insert!(c.blocks, index, val); c) +Base.push!(c::Sequence, m) where N = (push!(c.blocks, m); c) +Base.append!(c::Sequence, list::Vector) where N = (append!(c.blocks, list); c) +Base.append!(c1::Sequence, c2::Sequence) where N = (append!(c1.blocks, c2.blocks); c1) +Base.prepend!(c1::Sequence, list::Vector{<:AbstractBlock{N}}) where N = (prepend!(c1.blocks, list); c1) +Base.prepend!(c1::Sequence, c2::Sequence) where N = (prepend!(c1.blocks, c2.blocks); c1) diff --git a/test/CircuitBuild.jl b/test/CircuitBuild.jl index b87e6c2d7..d64687c13 100644 --- a/test/CircuitBuild.jl +++ b/test/CircuitBuild.jl @@ -1,5 +1,5 @@ using Test -using Yao, Yao.Blocks, QuAlgorithmZoo +using Yao, QuAlgorithmZoo @testset "pairs geometries" begin @test pair_ring(3) == [1=>2,2=>3,3=>1] @@ -21,22 +21,22 @@ end @test length(c) == 45 end -@testset "rotter, collect_rotblocks, num_gradient, opgrad" begin +@testset "rotter, collect_blocks, num_gradient, opgrad" begin @test merged_rotor(true, true) == Rx(0) @test merged_rotor(false, false) == merged_rotor() == chain(Rz(0), Rx(0), Rz(0)) @test merged_rotor(false, true) == chain(Rz(0), Rx(0)) @test merged_rotor(true, false) == chain(Rx(0), Rz(0)) - @test collect(rotorset(:Merged, 5, true, false), RotationGate) |> length == 10 + @test collect_blocks(RotationGate, rotorset(:Merged, 5, true, false)) |> length == 10 @test rotor(5, 2, true, true) isa ChainBlock @test rotor(5, 2, true, true) |> length == 1 @test rotor(5, 2, true, true) |> nqubits == 5 - @test collect(rotorset(:Split, 5, true, false), PutBlock{<:Any, <:Any, <:RotationGate}) |> length == 10 + @test collect_blocks(PutBlock{<:Any, <:Any, <:RotationGate}, rotorset(:Split, 5, true, false)) |> length == 10 end @testset "random diff circuit" begin c = random_diff_circuit(4, 3, [1=>3, 2=>4, 2=>3, 4=>1]) - rots = collect(c, RotationGate) + rots = collect_blocks(RotationGate, c) @test length(rots) == nparameters(c) == 40 @test dispatch!(+, c, ones(40)*0.1) |> parameters == ones(40)*0.1 @test dispatch!(+, c, :random) |> parameters != ones(40)*0.1 @@ -46,7 +46,7 @@ end reg = rand_state(4) dispatch!(c, randn(nparameters(c))) - dbs = collect(c, BPDiff) + dbs = collect_blocks(BPDiff, c) op = kron(4, 1=>Z, 2=>X) loss1z() = expect(op, copy(reg) |> c) # return loss please diff --git a/test/Diff.jl b/test/Diff.jl new file mode 100644 index 000000000..d69d15fe0 --- /dev/null +++ b/test/Diff.jl @@ -0,0 +1,172 @@ +using Yao, QuAlgorithmZoo +using Yao.ConstGate +using LinearAlgebra, Test, Random + +@testset "BP diff" begin + reg = rand_state(4) + block = put(4, 2=>rot(X, 0.3)) + df = BPDiff(block) + @test df.grad == 0 + @test nqubits(df) == 4 + + df2 = BPDiff(rot(CNOT, 0.3)) + @test df2.grad == 0 + @test nqubits(df2) == 2 +end + +@testset "Qi diff" begin + reg = rand_state(4) + df2 = QDiff(rot(CNOT, 0.3)) + @test df2.grad == 0 + @test nqubits(df2) == 2 + + @test df2' isa QDiff + @test mat(df2) == mat(df2')' +end + +""" + loss_expect!(circuit::AbstractBlock, op::AbstractBlock) -> Function + +Return function "loss!(ψ, θ) -> Vector" +""" +function loss_expect!(circuit::AbstractBlock, op::AbstractBlock) + N = nqubits(circuit) + function loss!(ψ::AbstractRegister, θ::Vector) + params = parameters(circuit) + dispatch!(circuit, θ) + ψ |> circuit + popdispatch!(circuit, params) + expect(op, ψ) + end +end + +""" + loss_Z1!(circuit::AbstractBlock; ibit::Int=1) -> Function + +Return the loss function f = (means measuring the ibit-th bit in computation basis). +""" +loss_Z1!(circuit::AbstractBlock; ibit::Int=1) = loss_expect!(circuit, put(nqubits(circuit), ibit=>Z)) + +cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) + +function rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) + rt = chain(nbit, [put(nbit, ibit=>Rz(0.0)), put(nbit, ibit=>Rx(0.0)), put(nbit, ibit=>Rz(0.0))]) + noleading && popfirst!(rt) + notrailing && pop!(rt) + rt +end + +rset(nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [rotor(nbit, j, noleading, notrailing) for j=1:nbit]) + +function ibm_diff_circuit(nbit, nlayer, pairs) + circuit = chain(nbit) + + ent = cnot_entangler(nbit, pairs) + for i = 1:(nlayer + 1) + i!=1 && push!(circuit, ent) + push!(circuit, rset(nbit, i==1, i==nlayer+1)) + end + circuit +end + +@testset "BP diff" begin + c = put(4, 3=>Rx(0.5)) |> autodiff(:BP) + cad = c' + @test mat(cad) == mat(c)' + + circuit = chain(4, repeat(4, H, 1:4), put(4, 3=>Rz(0.5)) |> autodiff(:BP), control(4, 2, 1=>shift(0.4)) |> autodiff(:BP), control(2, 1=>X), put(4, 4=>Ry(0.2)) |> autodiff(:BP)) + op = put(4, 3=>Y) + θ = [0.1, 0.2, 0.3] + dispatch!(circuit, θ) + loss! = loss_expect!(circuit, op) + ψ0 = rand_state(4) + ψ = copy(ψ0) |> circuit + + # get gradient + δ = ψ |> op + backward!(δ, circuit) + g1 = gradient(circuit) + + g2 = zero(θ) + η = 0.01 + for i in 1:length(θ) + θ1 = copy(θ) + θ2 = copy(θ) + θ1[i] -= 0.5η + θ2[i] += 0.5η + g2[i] = (loss!(copy(ψ0), θ2) - loss!(copy(ψ0), θ1))/η |> real + end + g3 = opdiff.(() -> copy(ψ0) |> circuit, collect_blocks(BPDiff, circuit), Ref(op)) + @test isapprox.(g1, g2, atol=1e-5) |> all + @test isapprox.(g2, g3, atol=1e-5) |> all +end + +@testset "constructor" begin + @test generator(put(4, 1=>Rx(0.1))) == put(4, 1=>X) + @test generator(Rx(0.1)) == X + circuit = chain(put(4, 1=>Rx(0.1)), control(4, 2, 1=>Ry(0.3))) + c2 = circuit |> autodiff(:BP) + @test c2[1] isa BPDiff + @test !(c2[2] isa BPDiff) +end + +@testset "numdiff & opdiff" begin + @test collect_blocks(XGate, chain([X, Y, Z])) == [X] + + c = chain(put(4, 1=>Rx(0.5))) |> autodiff(:QC) + nd = numdiff(c[1].content) do + expect(put(4, 1=>Z), zero_state(4) |> c) |> real # return loss please + end + + ed = opdiff(c[1].content, put(4, 1=>Z)) do + zero_state(4) |> c # a function get output + end + @test isapprox(nd, ed, atol=1e-4) + + reg = rand_state(4) + c = chain(put(4, 1=>Rx(0.5)), control(4, 1, 2=>Ry(0.5)), control(4, 1, 2=>shift(0.3)), kron(4, 2=>Rz(0.3), 3=>Rx(0.7))) |> autodiff(:QC) + dbs = collect_blocks(QDiff, c) + loss1z() = expect(kron(4, 1=>Z, 2=>X), copy(reg) |> c) |> real # return loss please + nd = numdiff.(loss1z, dbs) + ed = opdiff.(()->copy(reg) |> c, dbs, Ref(kron(4, 1=>Z, 2=>X))) + gd = gradient(c) + @test gradient(c, :QC) == gd + @test gradient(c, :BP) == [] + @test isapprox(nd, ed, atol=1e-4) + @test ed == gd +end + +@testset "stat" begin + nbit = 3 + f(x::Number, y::Number) = Float64(abs(x-y) < 1.5) + x = 0:1<2, 2=>3, 3=>1] + c = ibm_diff_circuit(nbit, 2, prs) |> autodiff(:QC) + dispatch!(c, :random) + dbs = collect_blocks(AbstractDiff,c) + + p0 = zero_state(nbit) |> c |> probs + sample0 = measure(zero_state(nbit) |> c; nshots=5000) + loss0 = expect(V, p0 |> as_weights) + gradsn = numdiff.(()->expect(V, zero_state(nbit) |> c |> probs |> as_weights), dbs) + gradse = statdiff.(()->zero_state(nbit) |> c |> probs |> as_weights, dbs, Ref(V), initial=p0 |> as_weights) + gradsf = statdiff.(()->measure(zero_state(nbit) |> c; nshots=5000), dbs, Ref(VF), initial=sample0) + @test all(isapprox.(gradse, gradsn, atol=1e-4)) + @test norm(gradsf-gradse)/norm(gradsf) <= 0.2 + + # 1D + h = randn(1< autodiff(:QC) + dispatch!(c, :random) + dbs = collect_blocks(AbstractDiff, c) + + p0 = zero_state(nbit) |> c |> probs |> as_weights + loss0 = expect(V, p0 |> as_weights) + gradsn = numdiff.(()->expect(V, zero_state(nbit) |> c |> probs |> as_weights), dbs) + gradse = statdiff.(()->zero_state(nbit) |> c |> probs |> as_weights, dbs, Ref(V)) + @test all(isapprox.(gradse, gradsn, atol=1e-4)) +end diff --git a/test/Grover.jl b/test/Grover.jl index f8e06a1ec..968808de0 100644 --- a/test/Grover.jl +++ b/test/Grover.jl @@ -1,9 +1,9 @@ using Test, Random, LinearAlgebra, SparseArrays +using BitBasis using QuAlgorithmZoo import QuAlgorithmZoo: _num_grover_step -using Yao, Yao.Blocks -using Yao.Intrinsics +using Yao function GroverSearch(oracle, num_bit::Int; psi::DefaultRegister = uniform_state(num_bit)) it = groveriter(psi, oracle) @@ -42,7 +42,7 @@ end @testset "groverblock" begin psi = uniform_state(5) or = inference_oracle(5, [-1,2,5,4,3]) - func_or = FunctionBlock{:Oracle}(reg->apply!(reg, or)) + func_or = or gb = groverblock(or, psi) gb2 = groverblock(func_or, psi) @test apply!(copy(psi), gb) == (for l_psi in groveriter(copy(psi), func_or) psi = l_psi end; psi) @@ -58,7 +58,7 @@ end # the desired subspace basis = collect(UInt, 0:1<0)) + subinds = [itercontrol(num_bit, abs.(evidense), Int.(evidense.>0))...] v_desired = statevec(psi0)[subinds .+ 1] p = norm(v_desired)^2 @@ -67,5 +67,5 @@ end # search the subspace num_iter = _num_grover_step(p) niter, psi = inference(psi0, evidense, num_iter) - @test isapprox((psi.state[subinds .+ 1]'*v_desired) |> abs2, 1, atol=1e-2) + @test isapprox((psi.state[subinds .+ 1]'*v_desired) |> abs2, 1, atol=3e-2) end diff --git a/test/HHL.jl b/test/HHL.jl index 391a4ddd3..287c9c00d 100644 --- a/test/HHL.jl +++ b/test/HHL.jl @@ -1,5 +1,5 @@ using Yao -using Yao.Intrinsics +using BitBasis using QuAlgorithmZoo using Test, LinearAlgebra @@ -11,7 +11,7 @@ function crot(n_reg::Int, C_value::Real) c_bit = Vector(2:n_rot) λ = 0.0 for j = 1:n_reg - if (takebit(i,j) == 0) + if (readbit(i,j) == 0) c_bit[j] = -c_bit[j] end end diff --git a/test/HadamardTest.jl b/test/HadamardTest.jl index 2dba4dd29..9cb205bb2 100644 --- a/test/HadamardTest.jl +++ b/test/HadamardTest.jl @@ -1,7 +1,11 @@ -using Yao, Yao.Blocks +using Yao using Test using LinearAlgebra using QuAlgorithmZoo +using Yao.ConstGate + +single_swap_test_circuit(ϕ::Real) = hadamard_test_circuit(SWAP, ϕ) +single_swap_test(reg::AbstractRegister, ϕ::Real) = hadamard_test(SWAP, reg, ϕ) @testset "state overlap" begin reg1 = rand_state(3) |> focus!(1,2) @@ -11,15 +15,15 @@ using QuAlgorithmZoo reg3 = rand_state(3) |> focus!(1,2) rho3 = reg3 |> ρ desired = tr(mat(rho1)*mat(rho2)) - c = state_overlap_circuit(2, 2, 0) + c = swap_test_circuit(2, 2, 0) res = expect(put(5, 1=>Z), join(join(reg2, reg1), zero_state(1)) |> c) |> tr @test desired ≈ res desired = tr(mat(rho1)*mat(rho2)*mat(rho3)) |> real - c = state_overlap_circuit(2, 3, 0) + c = swap_test_circuit(2, 3, 0) res = expect(put(7, 1=>Z), reduce(⊗, [reg3, reg2, reg1, zero_state(1)]) |> c) |> tr |> real @test desired ≈ res desired = tr(mat(rho1)*mat(rho2)*mat(rho3)) |> imag - c = state_overlap_circuit(2, 3, -π/2) + c = swap_test_circuit(2, 3, -π/2) res = expect(put(7, 1=>Z), reduce(⊗, [reg3, reg2, reg1, zero_state(1)]) |> c) |> tr |> real @test desired ≈ res end @@ -29,13 +33,14 @@ end U = put(nbit, 2=>Rx(0.2)) reg = rand_state(nbit) - @test hadamard_test(U, reg) ≈ real(expect(U, reg)) + @test hadamard_test(U, reg, 0.0) ≈ real(expect(U, reg)) + @test hadamard_test(U, reg, -π/2) ≈ imag(expect(U, reg)) reg = zero_state(2) |> singlet_block() - @test swap_test(reg) ≈ -1 + @test single_swap_test(reg, 0) ≈ -1 reg = zero_state(2) - @test swap_test(reg) ≈ 1 + @test single_swap_test(reg, 0) ≈ 1 reg = product_state(2, 0b11) - @test swap_test(reg) ≈ 1 + @test single_swap_test(reg, 0) ≈ 1 end diff --git a/test/PhaseEstimation.jl b/test/PhaseEstimation.jl index 0db8d35f6..1aa3f2713 100644 --- a/test/PhaseEstimation.jl +++ b/test/PhaseEstimation.jl @@ -1,5 +1,5 @@ using Yao -using Yao.Intrinsics +using BitBasis using Test, LinearAlgebra using QuAlgorithmZoo @@ -27,11 +27,11 @@ end U = V*Diagonal(signs)*V' b = V[:,3] - # Define registers and U operator. + # Define ArrayReg and U operator. M = 6 reg1 = zero_state(M) - reg2 = register(b) - UG = matrixgate(U) + reg2 = ArrayReg(b) + UG = matblock(U) # circuit circuit = PEBlock(UG, M, N) @@ -40,7 +40,7 @@ end reg = apply!(join(reg2, reg1), circuit) # measure - res = breflect(M, measure(focus!(copy(reg), 1:M); nshot=10)[1]) / (1< adjoint) ≈ join(reg2, reg1) @@ -51,11 +51,11 @@ end N = 3 U, b, ϕs, evec = rand_phaseest_setup(N) - # Define registers and U operator. + # Define ArrayReg and U operator. M = 6 reg1 = zero_state(M) - reg2 = register(b) - UG = matrixgate(U); + reg2 = ArrayReg(b) + UG = matblock(U); # run circuit reg= join(reg2, reg1) @@ -64,5 +64,5 @@ end # measure bs, proj, amp_relative = projection_analysis(evec, focus!(reg, M+1:M+N)) - @test isapprox(ϕs, bfloat.(bs, nbit=M), atol=0.05) + @test isapprox(ϕs, bfloat.(bs, nbits=M), atol=0.05) end diff --git a/test/QFT.jl b/test/QFT.jl index e17d0eae7..a2961fe8e 100644 --- a/test/QFT.jl +++ b/test/QFT.jl @@ -1,13 +1,14 @@ using Test, Random, LinearAlgebra, SparseArrays, LuxurySparse using Yao +using YaoArrayRegister: invorder using QuAlgorithmZoo using FFTW @testset "QFT" begin num_bit = 5 fftblock = QFTCircuit(num_bit) - ifftblock = adjoint(fftblock) + ifftblock = fftblock' reg = rand_state(num_bit) rv = copy(statevec(reg)) diff --git a/test/QuGAN.jl b/test/QuGAN.jl index 9fffa5ba9..17c81e266 100644 --- a/test/QuGAN.jl +++ b/test/QuGAN.jl @@ -1,6 +1,7 @@ using Test using Yao using QuAlgorithmZoo +using Random function run_test(nbit::Int, depth_gen::Int, depth_disc::Int; g_lr=0.1, d_lr=0.2, niter=1000) qg = toy_qugan(rand_state(nbit), depth_gen, depth_disc) @@ -8,7 +9,9 @@ function run_test(nbit::Int, depth_gen::Int, depth_disc::Int; g_lr=0.1, d_lr=0.2 qg end +# to fix @testset "quantum circuit gan - opdiff" begin + Random.seed!(2) N = 3 target = rand_state(N) qcg = toy_qugan(target, 2, 2) @@ -16,7 +19,6 @@ end @test isapprox(grad, num_gradient(qcg), atol=1e-4) qg = run_test(3, 4, 4, g_lr=0.2, d_lr=0.5, niter=300) @test qg |> loss < 0.1 - qg = run_test(3, 4, 4, g_lr=Adam(lr=0.005), d_lr=Adam(lr=0.5), niter=300) + qg = run_test(3, 4, 4, g_lr=Adam(lr=0.005), d_lr=Adam(lr=0.5), niter=1000) @test qg |> loss < 0.1 end - diff --git a/test/RotBasis.jl b/test/RotBasis.jl index 6ad3ed6ea..3921eebef 100644 --- a/test/RotBasis.jl +++ b/test/RotBasis.jl @@ -18,7 +18,7 @@ using QuAlgorithmZoo rb = roll(1, RotBasis(0.1, 0.3))#rot_basis(1) angles = randpolar(1) # prepair a state in the angles direction. - psi = angles |> polar2u |> register + psi = angles |> polar2u |> ArrayReg # rotate to the same direction for measurements. dispatch!(rb, vec(angles)) diff --git a/test/Sequence.jl b/test/Sequence.jl new file mode 100644 index 000000000..35feb760f --- /dev/null +++ b/test/Sequence.jl @@ -0,0 +1,62 @@ +using Test + +using Yao +using QuAlgorithmZoo + +@testset "constructor" begin + + g = Sequence( + kron(2, X, Y), + kron(2, 1=>phase(0.1)), + ) + + @test g isa Sequence + @test g.blocks == [kron(2, X, Y), kron(2, 1=>phase(0.1))] +end + +@testset "apply" begin + g = Sequence( + kron(2, X, Y), + kron(2, 1=>phase(0.1)), + ) + + reg = rand_state(2) + @test statevec(apply!(copy(reg), g)) ≈ mat(chain(g...)) * reg.state +end + +@testset "iteration" begin + test_list = [X, Y, phase(0.1), rot(X, 0.0)] + g = Sequence(test_list) + + for (src, tg) in zip(g, test_list) + @test src == tg + end + + for (src, tg) in zip(eachindex(g), 1:length(test_list)) + @test src == tg + end +end + +@testset "additional" begin + g = Sequence(X, Y) + push!(g, Z) + @test g[3] == Z + + append!(g, [rot(X, 0.0), rot(Y, 0.0)]) + @test g[4] == rot(X, 0.0) + @test g[5] == rot(Y, 0.0) + + prepend!(g, [phase(0.1)]) + @test g[1] == phase(0.1) + @test g[2] == X + @test g[end] == rot(Y, 0.0) + gg = insert!(g, 4, Z) + @test gg[4] == Z +end + +@testset "traits" begin + # TODO: check traits when primitive blocks' traits are all defined + g = Sequence(X, Y) + @test length(g) == 2 + @test eltype(g) == eltype(g.blocks) +end diff --git a/test/hamiltonian_solvers.jl b/test/hamiltonian_solvers.jl index fa64cab61..5b6e5aef4 100644 --- a/test/hamiltonian_solvers.jl +++ b/test/hamiltonian_solvers.jl @@ -1,4 +1,4 @@ -using Yao, Yao.Blocks +using Yao using LinearAlgebra using Test using QuAlgorithmZoo diff --git a/test/lin_diffEq_test.jl b/test/lin_diffEq_test.jl index 4cd420bd1..11a7af89a 100644 --- a/test/lin_diffEq_test.jl +++ b/test/lin_diffEq_test.jl @@ -1,5 +1,6 @@ using Yao -using Yao.Intrinsics +using BitBasis +using Random using QuAlgorithmZoo using Test, LinearAlgebra using OrdinaryDiffEq @@ -14,6 +15,7 @@ function diffeq_problem(nbit::Int) end @testset "Linear_differential_equations_HHL" begin + Random.seed!(2) N = 1 h = 0.1 tspan = (0.0,0.6) diff --git a/test/runtests.jl b/test/runtests.jl index e2c4a88ce..100b85496 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -28,6 +28,9 @@ end @testset "HHL" begin include("HHL.jl") end +@testset "diff Eq" begin + include("lin_diffEq_test.jl") +end @testset "QCOptProblem" begin include("QCOptProblem.jl") @@ -40,3 +43,11 @@ end @testset "hadamard test" begin include("HadamardTest.jl") end + +@testset "Sequence" begin + include("Sequence.jl") +end + +@testset "Diff" begin + include("Diff.jl") +end From 23dfbf238a2b8163478d8e5ef7e7b9695359af76 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Tue, 30 Apr 2019 01:26:56 +0800 Subject: [PATCH 45/85] add cache switch --- src/CircuitBuild.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/CircuitBuild.jl b/src/CircuitBuild.jl index 3b77bd5ee..e6ed6ddde 100644 --- a/src/CircuitBuild.jl +++ b/src/CircuitBuild.jl @@ -81,7 +81,7 @@ rotorset(::Val{:Split}, nbit::Int, noleading::Bool=false, notrailing::Bool=false rotorset(mode::Symbol, nbit::Int, noleading::Bool=false, notrailing::Bool=false) = rotorset(Val(mode), nbit, noleading, notrailing) """ - qdiff_circuit(n, nlayer, pairs) -> ChainBlock + random_diff_circuit(nbit, nlayer, pairs; mode=:Split, do_cache=false) A kind of widely used differentiable quantum circuit, angles in the circuit is randomely initialized. @@ -90,10 +90,13 @@ ref: Hardware-efficient Quantum Optimizer for Small Molecules and Quantum Magnets. Nature Publishing Group, 549(7671), 242–246. https://doi.org/10.1038/nature23879. """ -function random_diff_circuit(nbit, nlayer, pairs; mode=:Split) +function random_diff_circuit(nbit, nlayer, pairs; mode=:Split, do_cache=false) circuit = chain(nbit) - ent = cnot_entangler(pairs) |> cache + ent = cnot_entangler(pairs) + if do_cache + ent = ent |> cache + end for i = 1:(nlayer + 1) i!=1 && push!(circuit, ent) push!(circuit, rotorset(mode, nbit, i==1, i==nlayer+1)) From a61c22201b7eaf3dcf36c6c282988de1d88f4879 Mon Sep 17 00:00:00 2001 From: Frank WsW <41162655+frankwswang@users.noreply.github.com> Date: Tue, 30 Apr 2019 13:55:20 +0800 Subject: [PATCH 46/85] Update Diff.jl (#6) import method QDiff to Yao.mat and Yao.apply! --- src/Diff.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Diff.jl b/src/Diff.jl index 94b63d9cd..9c9d53148 100644 --- a/src/Diff.jl +++ b/src/Diff.jl @@ -1,5 +1,5 @@ export Rotor, generator, AbstractDiff, BPDiff, QDiff, backward!, gradient, CPhaseGate, DiffBlock -import Yao: expect, content, chcontent +import Yao: expect, content, chcontent, mat, apply! using StatsBase ############# General Rotor ############ From 3f676e56aa8f43cd0cbdbe5efdcdc013af119189 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Wed, 1 May 2019 19:03:09 +0800 Subject: [PATCH 47/85] update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dbf8a3e1c..090d91a14 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ QuAlgorithmZoo.jl is not registered yet, please use the following command: pkg> add https://github.com/QuantumBFS/QuAlgorithmZoo.jl.git ``` -Please notice, this package is still under development and needs further polish. A crazy refactor is coming! +Disclaimer: **this package is still under development and needs further polish.** ## Contents From 7a76d790dc508b847ed3a9fd6a775cd6a53deed9 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Wed, 1 May 2019 19:04:44 +0800 Subject: [PATCH 48/85] rm authors, add compat --- Project.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6bbf1725f..60d7322f3 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,5 @@ name = "QuAlgorithmZoo" uuid = "65c24e16-9b0a-11e8-1353-efc5bc5f6586" -authors = ["Roger-luo "] version = "0.1.0" [deps] @@ -16,6 +15,10 @@ Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" YaoArrayRegister = "e600142f-9330-5003-8abb-0ebd767abc51" YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df" +[compat] +julia = ">=1.0.0" +Yao = ">=0.4.0" + [extras] MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" From 18ff6fa26254c367eb7ffca72a9f8d8f46a1d855 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Wed, 1 May 2019 19:07:42 +0800 Subject: [PATCH 49/85] fix test --- test/Diff.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Diff.jl b/test/Diff.jl index d69d15fe0..cd9804b65 100644 --- a/test/Diff.jl +++ b/test/Diff.jl @@ -47,7 +47,7 @@ Return the loss function f = (means measuring the ibit-th bit in computatio """ loss_Z1!(circuit::AbstractBlock; ibit::Int=1) = loss_expect!(circuit, put(nqubits(circuit), ibit=>Z)) -cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) +_cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) function rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) rt = chain(nbit, [put(nbit, ibit=>Rz(0.0)), put(nbit, ibit=>Rx(0.0)), put(nbit, ibit=>Rz(0.0))]) @@ -61,7 +61,7 @@ rset(nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [ro function ibm_diff_circuit(nbit, nlayer, pairs) circuit = chain(nbit) - ent = cnot_entangler(nbit, pairs) + ent = _cnot_entangler(nbit, pairs) for i = 1:(nlayer + 1) i!=1 && push!(circuit, ent) push!(circuit, rset(nbit, i==1, i==nlayer+1)) From 803f30fc3f0e4d3570945234950b3a3d33abfdf0 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Wed, 1 May 2019 19:13:30 +0800 Subject: [PATCH 50/85] fix test --- test/Diff.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Diff.jl b/test/Diff.jl index cd9804b65..af3a249ab 100644 --- a/test/Diff.jl +++ b/test/Diff.jl @@ -49,14 +49,14 @@ loss_Z1!(circuit::AbstractBlock; ibit::Int=1) = loss_expect!(circuit, put(nqubit _cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) -function rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) +function _rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) rt = chain(nbit, [put(nbit, ibit=>Rz(0.0)), put(nbit, ibit=>Rx(0.0)), put(nbit, ibit=>Rz(0.0))]) noleading && popfirst!(rt) notrailing && pop!(rt) rt end -rset(nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [rotor(nbit, j, noleading, notrailing) for j=1:nbit]) +rset(nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [_rotor(nbit, j, noleading, notrailing) for j=1:nbit]) function ibm_diff_circuit(nbit, nlayer, pairs) circuit = chain(nbit) From 3abf98e54c787430dc0b746821eb2647346c05c1 Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 30 May 2019 09:12:22 +0800 Subject: [PATCH 51/85] upgrade (#8) * upgrade * new QSVD * polish QSVD --- examples/QAOA.jl | 8 ++--- examples/VQE.jl | 2 +- src/CircuitBuild.jl | 2 +- src/Diff.jl | 35 ++++++++++---------- src/Grover.jl | 16 ++++----- src/HHL.jl | 4 +-- src/HadamardTest.jl | 4 +-- src/Miscellaneous.jl | 7 ++-- src/QFT.jl | 8 ++--- src/QSVD.jl | 66 +++++++++++++++++++++++++++++++++++++ src/QuAlgorithmZoo.jl | 3 +- src/QuGAN.jl | 8 ++--- src/RotBasis.jl | 8 ++--- src/sequence.jl | 2 +- test/Diff.jl | 2 +- test/HadamardTest.jl | 2 +- test/QSVD.jl | 21 ++++++++++++ test/RotBasis.jl | 2 +- test/hamiltonian_solvers.jl | 1 + test/runtests.jl | 2 -- 20 files changed, 145 insertions(+), 58 deletions(-) create mode 100644 src/QSVD.jl create mode 100644 test/QSVD.jl diff --git a/examples/QAOA.jl b/examples/QAOA.jl index 7997ab219..280e243eb 100644 --- a/examples/QAOA.jl +++ b/examples/QAOA.jl @@ -4,7 +4,7 @@ using NLopt include("maxcut_gw.jl") HB(nbit::Int) = sum([put(nbit, i=>X) for i=1:nbit]) -tb = TimeEvolution(HB(3), 0.1) +tb = TimeEvolution(HB(3) |> cache, 0.1; check_hermicity=false) function HC(W::AbstractMatrix) nbit = size(W, 1) ab = Any[] @@ -22,11 +22,11 @@ function qaoa_circuit(W::AbstractMatrix, depth::Int; use_cache::Bool=false) hc = HC(W) use_cache && (hb = hb |> cache; hc = hc |> cache) c = chain(nbit, [repeat(nbit, H, 1:nbit)]) - append!(c, [chain(nbit, [time_evolve(hc, 0.0, tol=1e-5), time_evolve(hb, 0.0, tol=1e-5)]) for i=1:depth]) + append!(c, [chain(nbit, [time_evolve(hc, 0.0, tol=1e-5, check_hermicity=false), time_evolve(hb, 0.0, tol=1e-5, check_hermicity=false)]) for i=1:depth]) end -function cobyla_optimize(circuit::MatrixBlock{N}, hc::MatrixBlock; niter::Int) where N +function cobyla_optimize(circuit::AbstractBlock{N}, hc::AbstractBlock; niter::Int) where N function f(params, grad) reg = zero_state(N) |> dispatch!(circuit, params) loss = expect(hc, reg) |> real @@ -60,7 +60,7 @@ using Random, Test @test -expect(hc, product_state(5, bmask(sets[1]...)))+sum(W)/4 ≈ exact_cost # build a QAOA circuit and start training. - qc = qaoa_circuit(W, 20; use_cache=false) + qc = qaoa_circuit(W, 20; use_cache=true) opt_pl = nothing opt_cost = Inf for i = 1:10 diff --git a/examples/VQE.jl b/examples/VQE.jl index d45986ec3..f6f4df2d3 100644 --- a/examples/VQE.jl +++ b/examples/VQE.jl @@ -2,7 +2,7 @@ using Yao using QuAlgorithmZoo using KrylovKit -function ed_groundstate(h::MatrixBlock) +function ed_groundstate(h::AbstractBlock) E, V = eigsolve(h |> mat, 1, :SR, ishermitian=true) println("Ground State Energy is $(E[1])") ArrayReg(V[1]) diff --git a/src/CircuitBuild.jl b/src/CircuitBuild.jl index e6ed6ddde..86808c98a 100644 --- a/src/CircuitBuild.jl +++ b/src/CircuitBuild.jl @@ -115,7 +115,7 @@ function rand_single_gate() end """ - rand_gate(nbit::Int, mbit::Int, [ngate::Int]) -> MatrixBlock + rand_gate(nbit::Int, mbit::Int, [ngate::Int]) -> AbstractBlock random nbit gate. """ diff --git a/src/Diff.jl b/src/Diff.jl index 9c9d53148..f237ee3d8 100644 --- a/src/Diff.jl +++ b/src/Diff.jl @@ -3,11 +3,11 @@ import Yao: expect, content, chcontent, mat, apply! using StatsBase ############# General Rotor ############ -const Rotor{N, T} = Union{RotationGate{N, T}, PutBlock{N, <:Any, <:RotationGate, <:Complex{T}}} +const Rotor{N, T} = Union{RotationGate{N, T}, PutBlock{N, <:Any, <:RotationGate{<:Any, T}}} const CphaseGate{N, T} = ControlBlock{N,<:ShiftGate{T},<:Any} const DiffBlock{N, T} = Union{Rotor{N, T}, CphaseGate{N, T}} """ - generator(rot::Rotor) -> MatrixBlock + generator(rot::Rotor) -> AbstractBlock Return the generator of rotation block. """ @@ -15,19 +15,19 @@ generator(rot::RotationGate) = rot.block generator(rot::PutBlock{N, C, GT}) where {N, C, GT<:RotationGate} = PutBlock{N}(generator(rot|>content), rot |> occupied_locs) generator(c::CphaseGate{N}) where N = ControlBlock(N, c.ctrol_locs, ctrl_config, control(2,1,2=>Z), c.locs) -abstract type AbstractDiff{GT, N, T} <: TagBlock{GT, N, T} end +abstract type AbstractDiff{GT, N, T} <: TagBlock{GT, N} end Base.adjoint(df::AbstractDiff) = Daggered(df) istraitkeeper(::AbstractDiff) = Val(true) #################### The Basic Diff ################# """ - QDiff{GT, N, T} <: AbstractDiff{GT, N, Complex{T}} + QDiff{GT, N} <: AbstractDiff{GT, N, T} QDiff(block) -> QDiff Mark a block as quantum differentiable. """ -mutable struct QDiff{GT, N, T} <: AbstractDiff{GT, N, Complex{T}} +mutable struct QDiff{GT, N, T} <: AbstractDiff{GT, N, T} block::GT grad::T QDiff(block::DiffBlock{N, T}) where {N, T} = new{typeof(block), N, T}(block, T(0)) @@ -35,7 +35,8 @@ end content(cb::QDiff) = cb.block chcontent(cb::QDiff, blk::DiffBlock) = QDiff(blk) -@forward QDiff.block mat, apply! +@forward QDiff.block apply! +mat(::Type{T}, df::QDiff) where T = mat(T, df.block) Base.adjoint(df::QDiff) = QDiff(content(df)') function YaoBlocks.print_annotation(io::IO, df::QDiff) @@ -52,19 +53,19 @@ Mark a block as differentiable, here `GT`, `PT` is gate type, parameter type. Warning: please don't use the `adjoint` after `BPDiff`! `adjoint` is reserved for special purpose! (back propagation) """ -mutable struct BPDiff{GT, N, T, PT} <: AbstractDiff{GT, N, T} +mutable struct BPDiff{GT, N, T} <: AbstractDiff{GT, N, T} block::GT - grad::PT + grad::T input::AbstractRegister - BPDiff(block::MatrixBlock{N, T}, grad::PT) where {N, T, PT} = new{typeof(block), N, T, typeof(grad)}(block, grad) + BPDiff(block::AbstractBlock{N}, grad::T) where {N, T} = new{typeof(block), N, T}(block, grad) end -BPDiff(block::MatrixBlock) = BPDiff(block, zeros(parameters_eltype(block), nparameters(block))) +BPDiff(block::AbstractBlock) = BPDiff(block, zeros(parameters_eltype(block), nparameters(block))) BPDiff(block::DiffBlock{N, T}) where {N, T} = BPDiff(block, T(0)) content(cb::BPDiff) = cb.block -chcontent(cb::BPDiff, blk::MatrixBlock) = BPDiff(blk) +chcontent(cb::BPDiff, blk::AbstractBlock) = BPDiff(blk) -@forward BPDiff.block mat +mat(::Type{T}, df::BPDiff) where T = mat(T, df.block) function apply!(reg::AbstractRegister, df::BPDiff) if isdefined(df, :input) copyto!(df.input, reg) @@ -105,7 +106,7 @@ autodiff(mode::Symbol, block::AbstractBlock) = autodiff(Val(mode), block) autodiff(::Val{:BP}, block::DiffBlock) = BPDiff(block) autodiff(::Val{:BP}, block::AbstractBlock) = block # Sequential, Roller and ChainBlock can propagate. -function autodiff(mode::Val{:BP}, blk::Union{ChainBlock, Roller, Sequential}) +function autodiff(mode::Val{:BP}, blk::Union{ChainBlock, Sequential}) chsubblocks(blk, autodiff.(mode, subblocks(blk))) end @@ -148,11 +149,11 @@ Numeric differentiation. end """ - opdiff(psifunc, diffblock::AbstractDiff, op::MatrixBlock) + opdiff(psifunc, diffblock::AbstractDiff, op::AbstractBlock) Operator differentiation. """ -@inline function opdiff(psifunc, diffblock::AbstractDiff, op::MatrixBlock) +@inline function opdiff(psifunc, diffblock::AbstractDiff, op::AbstractBlock) r1, r2 = _perturb(()->expect(op, psifunc()) |> real, diffblock, π/2) diffblock.grad = (r2 - r1)/2 end @@ -215,14 +216,14 @@ end end """ - backward!(δ::AbstractRegister, circuit::MatrixBlock) -> AbstractRegister + backward!(δ::AbstractRegister, circuit::AbstractBlock) -> AbstractRegister back propagate and calculate the gradient ∂f/∂θ = 2*Re(∂f/∂ψ*⋅∂ψ*/∂θ), given ∂f/∂ψ*. Note: Here, the input circuit should be a matrix block, otherwise the back propagate may not apply (like Measure operations). """ -backward!(δ::AbstractRegister, circuit::MatrixBlock) = apply!(δ, circuit') +backward!(δ::AbstractRegister, circuit::AbstractBlock) = apply!(δ, circuit') """ gradient(circuit::AbstractBlock, mode::Symbol=:ANY) -> Vector diff --git a/src/Grover.jl b/src/Grover.jl index ed369a8b7..a17d50cb7 100644 --- a/src/Grover.jl +++ b/src/Grover.jl @@ -38,21 +38,21 @@ num_grover_step(psi::ArrayReg, oracle) = _num_grover_step(prob_match_oracle(psi, _num_grover_step(prob::Real) = Int(round(pi/4/sqrt(prob)))-1 """ - GroverIter{N, T} + GroverIter{N} - GroverIter(oracle, ref::ReflectBlock{N, T}, psi::ArrayReg, niter::Int) + GroverIter(oracle, ref::ReflectBlock{N}, psi::ArrayReg, niter::Int) an iterator that perform Grover operations step by step. An Grover operation consists of applying oracle and Reflection. """ -struct GroverIter{N, T} +struct GroverIter{N} psi::ArrayReg oracle - ref::ReflectBlock{N, T} + ref::ReflectBlock{N} niter::Int end -groveriter(psi::ArrayReg, oracle, ref::ReflectBlock{N, T}, niter::Int) where {N, T} = GroverIter{N, T}(psi, oracle, ref, niter) +groveriter(psi::ArrayReg, oracle, ref::ReflectBlock{N}, niter::Int) where {N} = GroverIter{N}(psi, oracle, ref, niter) groveriter(psi::ArrayReg, oracle, niter::Int) = groveriter(psi, oracle, ReflectBlock(psi |> copy), niter) groveriter(psi::ArrayReg, oracle) = groveriter(psi, oracle, ReflectBlock(psi |> copy), num_grover_step(psi, oracle)) @@ -68,17 +68,17 @@ end Base.length(it::GroverIter) = it.niter """ - groverblock(oracle, ref::ReflectBlock{N, T}, niter::Int=-1) + groverblock(oracle, ref::ReflectBlock{N}, niter::Int=-1) groverblock(oracle, psi::ArrayReg, niter::Int=-1) Return a ChainBlock/Sequential as Grover Iteration, the default `niter` will stop at the first optimal step. """ -function groverblock(oracle::MatrixBlock{N, T}, ref::ReflectBlock{N, T}, niter::Int=-1) where {N, T} +function groverblock(oracle::AbstractBlock{N}, ref::ReflectBlock{N}, niter::Int=-1) where {N} if niter == -1 niter = num_grover_step(ref.psi, oracle) end chain(N, chain(oracle, ref) for i = 1:niter) end -function groverblock(oracle, ref::ReflectBlock{N, T}, niter::Int=-1) where {N, T} +function groverblock(oracle, ref::ReflectBlock{N}, niter::Int=-1) where {N} if niter == -1 niter = num_grover_step(ref.psi, oracle) end sequence(sequence(oracle, ref) for i = 1:niter) end diff --git a/src/HHL.jl b/src/HHL.jl index dc76331d1..c364fe856 100644 --- a/src/HHL.jl +++ b/src/HHL.jl @@ -1,7 +1,7 @@ export hhlcircuit, hhlproject!, hhlsolve, HHLCRot """ - HHLCRot{N, NC, T} <: PrimitiveBlock{N, Complex{T}} + HHLCRot{N, NC} <: PrimitiveBlock{N} Controlled rotation gate used in HHL algorithm, applied on N qubits. @@ -9,7 +9,7 @@ Controlled rotation gate used in HHL algorithm, applied on N qubits. * ibit:: the ancilla bit. * C_value:: the value of constant "C", should be smaller than the spectrum "gap". """ -struct HHLCRot{N, NC, T} <: PrimitiveBlock{N, Complex{T}} +struct HHLCRot{N, NC, T} <: PrimitiveBlock{N} cbits::Vector{Int} ibit::Int C_value::T diff --git a/src/HadamardTest.jl b/src/HadamardTest.jl index 8dd8e8a84..1d823930e 100644 --- a/src/HadamardTest.jl +++ b/src/HadamardTest.jl @@ -3,7 +3,7 @@ export hadamard_test, hadamard_test_circuit, swap_test_circuit """ see WiKi. """ -function hadamard_test_circuit(U::MatrixBlock{N}, ϕ::Real) where N +function hadamard_test_circuit(U::AbstractBlock{N}, ϕ::Real) where N chain(N+1, put(N+1, 1=>H), put(N+1, 1=>Rz(ϕ)), control(N+1, 1, 2:N+1=>U), # get matrix first, very inefficient @@ -11,7 +11,7 @@ function hadamard_test_circuit(U::MatrixBlock{N}, ϕ::Real) where N ) end -function hadamard_test(U::MatrixBlock{N}, reg::AbstractRegister, ϕ::Real) where N +function hadamard_test(U::AbstractBlock{N}, reg::AbstractRegister, ϕ::Real) where N c = hadamard_test_circuit(U, ϕ::Real) reg = join(reg, zero_state(1)) expect(put(N+1, 1=>Z), reg |> c) diff --git a/src/Miscellaneous.jl b/src/Miscellaneous.jl index 68659b116..570a8c636 100644 --- a/src/Miscellaneous.jl +++ b/src/Miscellaneous.jl @@ -16,13 +16,12 @@ function inverselines(nbit::Int; n_reg::Int=nbit) c end -function singlet_block(::Type{T}, nbit::Int, i::Int, j::Int) where T +function singlet_block(nbit::Int, i::Int, j::Int) unit = chain(nbit) - push!(unit, put(nbit, i=>chain(XGate{T}(), HGate{T}()))) - push!(unit, control(nbit, -i, j=>XGate{T}())) + push!(unit, put(nbit, i=>chain(X, H))) + push!(unit, control(nbit, -i, j=>X)) end -singlet_block(nbit::Int, i::Int, j::Int) = singlet_block(ComplexF64, nbit, i, j) singlet_block() = singlet_block(2,1,2) Yao.mat(ρ::DensityMatrix{1}) = dropdims(state(ρ), dims=3) diff --git a/src/QFT.jl b/src/QFT.jl index 16250ea31..47cd06185 100644 --- a/src/QFT.jl +++ b/src/QFT.jl @@ -8,11 +8,11 @@ CRk(i::Int, j::Int, k::Int) = control([i, ], j=>shift(2π/(1<H) : CRk(j, i, j-i+1) for j = i:n) QFTCircuit(n::Int) = chain(n, CRot(n, i) for i = 1:n) -struct QFTBlock{N} <: PrimitiveBlock{N,ComplexF64} end -mat(q::QFTBlock{N}) where N = applymatrix(q) +struct QFTBlock{N} <: PrimitiveBlock{N} end +mat(::Type{T}, q::QFTBlock{N}) where {T, N} = T.(applymatrix(q)) apply!(reg::DefaultRegister{B}, ::QFTBlock) where B = (reg.state = ifft!(invorder_firstdim(reg |> state), 1)*sqrt(1<state, 1)/sqrt(1<state, 1)/sqrt(1< autodiff(:QC) # construct a differentiable circuit for training + + obs = -mapreduce(i->put(nbit, i=>Z), +, (1:Na..., Na+Nc+1:Na+Nb...)) + params = parameters(c) + for i = 1:maxiter + grad = opdiff.(() -> copy(reg) |> c, collect_blocks(AbstractDiff, c), Ref(obs)) + QuAlgorithmZoo.update!(params, grad, optimizer) + println("Iter $i, Loss = $(Na+expect(obs, copy(reg) |> c))") + dispatch!(c, params) + end +end + +"""build the circuit for quantum SVD training.""" +function circuit_qsvd(circuit_a::AbstractBlock{Na}, circuit_b::AbstractBlock{Nb}, Nc::Int) where {Na, Nb} + nbit = Na+Nb + cnots = chain(control(nbit, i+Na, i=>X) for i=1:Nc) + c = chain(concentrate(nbit, circuit_a, 1:Na), concentrate(nbit, circuit_b, Na+1:nbit), cnots) +end + +"""read QSVD results""" +function readout_qsvd(reg::AbstractRegister, circuit_a::AbstractBlock{Na}, circuit_b::AbstractBlock{Nb}, Nc::Int) where {Na, Nb} + reg = copy(reg) |> concentrate(Na+Nb, circuit_a, 1:Na) |> concentrate(Na+Nb, circuit_b, Na+1:Na+Nb) + _S = [select(reg, b|b<P0) diff --git a/src/RotBasis.jl b/src/RotBasis.jl index 91b1067ea..2c9cdb18e 100644 --- a/src/RotBasis.jl +++ b/src/RotBasis.jl @@ -5,7 +5,7 @@ export RotBasis, randpolar, polar2u, u2polar, rot_basis A special rotation block that transform basis to angle θ and ϕ in bloch sphere. """ -mutable struct RotBasis{T} <: PrimitiveBlock{1, Complex{T}} +mutable struct RotBasis{T} <: PrimitiveBlock{1} theta::T phi::T end @@ -13,9 +13,9 @@ end _make_rot_mat(I, block, theta) = I * cos(theta / 2) - im * sin(theta / 2) * block # chain -> * # mat(rb::RotBasis{T}) where T = mat(Ry(-rb.theta))*mat(Rz(-rb.phi)) -function mat(x::RotBasis{T}) where T - R1 = _make_rot_mat(IMatrix{2, Complex{T}}(), mat(Z), -x.phi) - R2 = _make_rot_mat(IMatrix{2, Complex{T}}(), mat(Y), -x.theta) +function mat(::Type{TM}, x::RotBasis{T}) where {TM, T} + R1 = _make_rot_mat(IMatrix{2, Complex{T}}(), mat(TM, Z), -x.phi) + R2 = _make_rot_mat(IMatrix{2, Complex{T}}(), mat(TM, Y), -x.theta) R2 * R1 end diff --git a/src/sequence.jl b/src/sequence.jl index 12924a19e..a19822a52 100644 --- a/src/sequence.jl +++ b/src/sequence.jl @@ -2,7 +2,7 @@ export Sequence import YaoBlocks: subblocks, chsubblocks, apply! using YaoBlocks: _check_size -struct Sequence <: CompositeBlock{Any, Bool} +struct Sequence <: CompositeBlock{Any} blocks::Vector end diff --git a/test/Diff.jl b/test/Diff.jl index af3a249ab..29dc559fc 100644 --- a/test/Diff.jl +++ b/test/Diff.jl @@ -1,5 +1,5 @@ using Yao, QuAlgorithmZoo -using Yao.ConstGate +using YaoBlocks.ConstGate using LinearAlgebra, Test, Random @testset "BP diff" begin diff --git a/test/HadamardTest.jl b/test/HadamardTest.jl index 9cb205bb2..edb4b02fa 100644 --- a/test/HadamardTest.jl +++ b/test/HadamardTest.jl @@ -2,7 +2,7 @@ using Yao using Test using LinearAlgebra using QuAlgorithmZoo -using Yao.ConstGate +using YaoBlocks.ConstGate single_swap_test_circuit(ϕ::Real) = hadamard_test_circuit(SWAP, ϕ) single_swap_test(reg::AbstractRegister, ϕ::Real) = hadamard_test(SWAP, reg, ϕ) diff --git a/test/QSVD.jl b/test/QSVD.jl new file mode 100644 index 000000000..91aa5de89 --- /dev/null +++ b/test/QSVD.jl @@ -0,0 +1,21 @@ +using QuAlgorithmZoo, Yao +using Test +using Random, LinearAlgebra + +@testset "QSVD" begin + Random.seed!(2) + # define a matrix of size (2^Na, 2^Nb) + Na = 2 + Nb = 2 + + # the exact result + M = reshape(rand_state(Na+Nb).state, 1< abs2, I, atol=1e-2) + @test isapprox(V'*V_exact .|> abs2, I, atol=1e-2) +end diff --git a/test/RotBasis.jl b/test/RotBasis.jl index 3921eebef..dad47af82 100644 --- a/test/RotBasis.jl +++ b/test/RotBasis.jl @@ -15,7 +15,7 @@ using QuAlgorithmZoo end # check consistency - rb = roll(1, RotBasis(0.1, 0.3))#rot_basis(1) + rb = put(1, 1=>RotBasis(0.1, 0.3))#rot_basis(1) angles = randpolar(1) # prepair a state in the angles direction. psi = angles |> polar2u |> ArrayReg diff --git a/test/hamiltonian_solvers.jl b/test/hamiltonian_solvers.jl index 5b6e5aef4..897b10572 100644 --- a/test/hamiltonian_solvers.jl +++ b/test/hamiltonian_solvers.jl @@ -2,6 +2,7 @@ using Yao using LinearAlgebra using Test using QuAlgorithmZoo +using YaoBlocks: ConstGate @testset "solving hamiltonian" begin nbit = 8 diff --git a/test/runtests.jl b/test/runtests.jl index 100b85496..331bbd6da 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,4 @@ using Test, Random, LinearAlgebra, SparseArrays - - using Yao using QuAlgorithmZoo From cb1d5ab43f3847a6c68b8d8613931b8f87c44593 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Fri, 31 May 2019 15:10:33 +0800 Subject: [PATCH 52/85] update readme --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 090d91a14..1a052ba87 100644 --- a/README.md +++ b/README.md @@ -19,16 +19,20 @@ Disclaimer: **this package is still under development and needs further polish.* ## Contents -- [x] QCBM tutorial -- [x] grover search +- [x] Quantum Circuit Born Machine +- [x] Grover search - [x] HHL - [x] QFT +- [x] Phase Estimation - [x] QuGAN - [x] QCBM - [x] Hamiltonian Solver - [x] QAOA -- [x] Quantum Chemistry +- [x] Variational quantum eigensolver - [x] QuODE +- [x] Hadamard Test +- [x] State Overlap Algorithms +- [x] Quantum SVD ## License From 10433da59c751265e07572d9765194fd15c9b743 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Thu, 20 Jun 2019 03:22:19 +0800 Subject: [PATCH 53/85] new shor --- examples/order_finding.jl | 282 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 examples/order_finding.jl diff --git a/examples/order_finding.jl b/examples/order_finding.jl new file mode 100644 index 000000000..b2628f6ee --- /dev/null +++ b/examples/order_finding.jl @@ -0,0 +1,282 @@ +using Yao, YaoBlocks, BitBasis, LuxurySparse, LinearAlgebra + +function YaoBlocks.cunmat(nbit::Int, cbits::NTuple{C, Int}, cvals::NTuple{C, Int}, U0::Adjoint, locs::NTuple{M, Int}) where {C, M} + YaoBlocks.cunmat(nbit, cbits, cvals, copy(U0), locs) +end + +"""x^Nz%N""" +function powermod(x::Int, k::Int, N::Int) + rem = 1 + for i=1:k + rem = mod(rem*x, N) + end + rem +end + +Z_star(N::Int) = filter(i->gcd(i, N)==1, 0:N-1) +Eulerφ(N) = length(Z_star(N)) + +"""obtain `s` and `r` from `ϕ` that satisfies `|s/r - ϕ| ≦ 1/2r²`""" +continued_fraction(ϕ, niter::Int) = niter==0 ? floor(Int, ϕ) : floor(Int, ϕ) + 1//continued_fraction(1/mod(ϕ, 1), niter-1) +continued_fraction(ϕ::Rational, niter::Int) = niter==0 || ϕ.den==1 ? floor(Int, ϕ) : floor(Int, ϕ) + 1//continued_fraction(1/mod(ϕ, 1), niter-1) + +""" +Return `y` that `(x*y)%N == 1`, notice the `(x*y)%N` operations in Z* forms a group. +""" +function mod_inverse(x::Int, N::Int) + for i=1:N + (x*i)%N == 1 && return i + end + throw(ArgumentError("Can not find the inverse, $x is probably not in Z*($N)!")) +end + +is_order(r, x, N) = powermod(x, r, N) == 1 + +"""get the order, the classical approach.""" +function get_order(::Val{:classical}, x::Int, N::Int) + findfirst(r->is_order(r, x, N), 1:N) +end + +function rand_primeto(L) + while true + x = rand(2:L-1) + d = gcd(x, L) + if d == 1 + return x + end + end +end + +function shor(L, ver=Val(:quantum); maxiter=100) + L%2 == 0 && return 2 + # some classical method to accelerate the solution finding + for i in 1:maxiter + x = rand_primeto(L) + r = get_order(ver, x, L) + # if `x^(r/2)` is non-trivil, go on. + # Here, we do not condsier `powermod(x, r÷2, L) == 1`, since in this case the order should be `r/2` + if r%2 == 0 && powermod(x, r÷2, L) != L-1 + f1, f2 = gcd(powermod(x, r÷2, L)-1, L), gcd(powermod(x, r÷2, L)+1, L) + if f1!=1 + return f1 + elseif f2!=1 + return f2 + else + error("Algorithm Fail!") + end + end + end +end + +""" + Mod{N} <: PrimitiveBlock{N} + +calculates `mod(a*x, L)`, notice `gcd(a, L)` should be 1. +""" +struct Mod{N} <: PrimitiveBlock{N} + a::Int + L::Int + function Mod{N}(a, L) where N + @assert gcd(a, L) == 1 && L<=1<= m.L ? i+1 : mod(i*m.a, m.L)+1 + for j in 1:B + @inbounds nstate[_i,j] = reg.state[i+1,j] + end + end + reg.state = nstate + reg +end + +function Yao.mat(::Type{T}, m::Mod{N}) where {T, N} + perm = Vector{Int}(undef, 1<= m.L ? i+1 : mod(i*m.a, m.L)+1] = i+1 + end + PermMatrix(perm, ones(T, 1< (b&mask, b>>k) +end + +function Yao.apply!(reg::ArrayReg{B}, m::KMod{N, K}) where {B, N, K} + YaoBlocks._check_size(reg, m) + nstate = zero(reg.state) + + reader = bint2_reader(Int, K) + for b in basis(reg) + k, i = reader(b) + _i = i >= m.L ? i : mod(i*powermod(m.a, k, m.L), m.L) + _b = k + _i<= m.L ? i : mod(i*powermod(m.a, k, m.L), m.L) + _b = k + _i< c; nshots=nshots) + reader = bint2_reader(Int, K) + for r in res + k, i = reader(r) + # get s/r + ϕ = bfloat(k) # + ϕ == 0 && continue + + order = order_from_float(ϕ, x, L) + if order === nothing + continue + else + return order + end + end + return nothing +end + +function order_from_float(ϕ, x, L) + k = 1 + rnum = continued_fraction(ϕ, k) + while rnum.den < L + r = rnum.den + @show r + if is_order(r, x, L) + return r + end + k += 1 + rnum = continued_fraction(ϕ, k) + end + return nothing +end + +using Test +function check_Euler_theorem(N::Int) + Z = Z_star(N) + Nz = length(Z) # Eulerφ + for x in Z + @test powermod(x,Nz,N) == 1 # the order is a devisor of Eulerφ + end +end + +@testset "Euler" begin + check_Euler_theorem(150) +end + +@testset "Mod" begin + @test_throws AssertionError Mod{4}(4,10) + @test_throws AssertionError Mod{2}(3,10) + m = Mod{4}(3,10) + @test mat(m) ≈ applymatrix(m) + @test isunitary(m) + @test isunitary(mat(m)) + @test m' == Mod{4}(7,10) +end + +@testset "KMod" begin + @test_throws AssertionError KMod{6, 2}(4,10) + @test_throws AssertionError KMod{4, 2}(3,10) + m = KMod{6, 2}(3,10) + @test mat(m) ≈ applymatrix(m) + @test isunitary(m) + @test isunitary(mat(m)) + @test m' == KMod{6, 2}(7,10) +end + +using Random +@testset "shor_classical" begin + Random.seed!(129) + L = 35 + f = shor(L, Val(:classical)) + @test f == 5 || f == 7 + + L = 25 + f = shor(L, Val(:classical)) + @test_broken f == 5 + + L = 7*11 + f = shor(L, Val(:classical)) + @test f == 7 || f == 11 + + L = 14 + f = shor(L, Val(:classical)) + @test f == 2 || f == 7 +end + +using Random +@testset "shor quantum" begin + Random.seed!(129) + L = 15 + f = shor(L, Val(:quantum)) + @test f == 5 || f == 3 +end From 7ffee33966848d9eb9b9486cd791333f62311e0a Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 24 Jun 2019 15:42:06 +0800 Subject: [PATCH 54/85] Back propagation does not cache! (#10) * update Shor algorithm * fix test * add shor to readme * update BP to avoid caching * throw error for measure block --- README.md | 1 + examples/order_finding.jl | 282 ------------------------------------- src/Diff.jl | 143 ++++++++----------- src/Mod.jl | 105 ++++++++++++++ src/QCBM.jl | 6 +- src/QCOptProblem.jl | 2 +- src/QSVD.jl | 2 +- src/QuAlgorithmZoo.jl | 1 + src/QuGAN.jl | 4 +- src/hamiltonian_solvers.jl | 2 +- src/number_theory.jl | 101 +++++++++++++ src/shor.jl | 80 +++++++++++ test/CircuitBuild.jl | 4 +- test/Diff.jl | 33 ++--- test/runtests.jl | 4 + test/shor.jl | 65 +++++++++ 16 files changed, 443 insertions(+), 392 deletions(-) delete mode 100644 examples/order_finding.jl create mode 100644 src/Mod.jl create mode 100644 src/number_theory.jl create mode 100644 src/shor.jl create mode 100644 test/shor.jl diff --git a/README.md b/README.md index 1a052ba87..433f5deeb 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Disclaimer: **this package is still under development and needs further polish.* - [x] Hadamard Test - [x] State Overlap Algorithms - [x] Quantum SVD +- [x] Shor ## License diff --git a/examples/order_finding.jl b/examples/order_finding.jl deleted file mode 100644 index b2628f6ee..000000000 --- a/examples/order_finding.jl +++ /dev/null @@ -1,282 +0,0 @@ -using Yao, YaoBlocks, BitBasis, LuxurySparse, LinearAlgebra - -function YaoBlocks.cunmat(nbit::Int, cbits::NTuple{C, Int}, cvals::NTuple{C, Int}, U0::Adjoint, locs::NTuple{M, Int}) where {C, M} - YaoBlocks.cunmat(nbit, cbits, cvals, copy(U0), locs) -end - -"""x^Nz%N""" -function powermod(x::Int, k::Int, N::Int) - rem = 1 - for i=1:k - rem = mod(rem*x, N) - end - rem -end - -Z_star(N::Int) = filter(i->gcd(i, N)==1, 0:N-1) -Eulerφ(N) = length(Z_star(N)) - -"""obtain `s` and `r` from `ϕ` that satisfies `|s/r - ϕ| ≦ 1/2r²`""" -continued_fraction(ϕ, niter::Int) = niter==0 ? floor(Int, ϕ) : floor(Int, ϕ) + 1//continued_fraction(1/mod(ϕ, 1), niter-1) -continued_fraction(ϕ::Rational, niter::Int) = niter==0 || ϕ.den==1 ? floor(Int, ϕ) : floor(Int, ϕ) + 1//continued_fraction(1/mod(ϕ, 1), niter-1) - -""" -Return `y` that `(x*y)%N == 1`, notice the `(x*y)%N` operations in Z* forms a group. -""" -function mod_inverse(x::Int, N::Int) - for i=1:N - (x*i)%N == 1 && return i - end - throw(ArgumentError("Can not find the inverse, $x is probably not in Z*($N)!")) -end - -is_order(r, x, N) = powermod(x, r, N) == 1 - -"""get the order, the classical approach.""" -function get_order(::Val{:classical}, x::Int, N::Int) - findfirst(r->is_order(r, x, N), 1:N) -end - -function rand_primeto(L) - while true - x = rand(2:L-1) - d = gcd(x, L) - if d == 1 - return x - end - end -end - -function shor(L, ver=Val(:quantum); maxiter=100) - L%2 == 0 && return 2 - # some classical method to accelerate the solution finding - for i in 1:maxiter - x = rand_primeto(L) - r = get_order(ver, x, L) - # if `x^(r/2)` is non-trivil, go on. - # Here, we do not condsier `powermod(x, r÷2, L) == 1`, since in this case the order should be `r/2` - if r%2 == 0 && powermod(x, r÷2, L) != L-1 - f1, f2 = gcd(powermod(x, r÷2, L)-1, L), gcd(powermod(x, r÷2, L)+1, L) - if f1!=1 - return f1 - elseif f2!=1 - return f2 - else - error("Algorithm Fail!") - end - end - end -end - -""" - Mod{N} <: PrimitiveBlock{N} - -calculates `mod(a*x, L)`, notice `gcd(a, L)` should be 1. -""" -struct Mod{N} <: PrimitiveBlock{N} - a::Int - L::Int - function Mod{N}(a, L) where N - @assert gcd(a, L) == 1 && L<=1<= m.L ? i+1 : mod(i*m.a, m.L)+1 - for j in 1:B - @inbounds nstate[_i,j] = reg.state[i+1,j] - end - end - reg.state = nstate - reg -end - -function Yao.mat(::Type{T}, m::Mod{N}) where {T, N} - perm = Vector{Int}(undef, 1<= m.L ? i+1 : mod(i*m.a, m.L)+1] = i+1 - end - PermMatrix(perm, ones(T, 1< (b&mask, b>>k) -end - -function Yao.apply!(reg::ArrayReg{B}, m::KMod{N, K}) where {B, N, K} - YaoBlocks._check_size(reg, m) - nstate = zero(reg.state) - - reader = bint2_reader(Int, K) - for b in basis(reg) - k, i = reader(b) - _i = i >= m.L ? i : mod(i*powermod(m.a, k, m.L), m.L) - _b = k + _i<= m.L ? i : mod(i*powermod(m.a, k, m.L), m.L) - _b = k + _i< c; nshots=nshots) - reader = bint2_reader(Int, K) - for r in res - k, i = reader(r) - # get s/r - ϕ = bfloat(k) # - ϕ == 0 && continue - - order = order_from_float(ϕ, x, L) - if order === nothing - continue - else - return order - end - end - return nothing -end - -function order_from_float(ϕ, x, L) - k = 1 - rnum = continued_fraction(ϕ, k) - while rnum.den < L - r = rnum.den - @show r - if is_order(r, x, L) - return r - end - k += 1 - rnum = continued_fraction(ϕ, k) - end - return nothing -end - -using Test -function check_Euler_theorem(N::Int) - Z = Z_star(N) - Nz = length(Z) # Eulerφ - for x in Z - @test powermod(x,Nz,N) == 1 # the order is a devisor of Eulerφ - end -end - -@testset "Euler" begin - check_Euler_theorem(150) -end - -@testset "Mod" begin - @test_throws AssertionError Mod{4}(4,10) - @test_throws AssertionError Mod{2}(3,10) - m = Mod{4}(3,10) - @test mat(m) ≈ applymatrix(m) - @test isunitary(m) - @test isunitary(mat(m)) - @test m' == Mod{4}(7,10) -end - -@testset "KMod" begin - @test_throws AssertionError KMod{6, 2}(4,10) - @test_throws AssertionError KMod{4, 2}(3,10) - m = KMod{6, 2}(3,10) - @test mat(m) ≈ applymatrix(m) - @test isunitary(m) - @test isunitary(mat(m)) - @test m' == KMod{6, 2}(7,10) -end - -using Random -@testset "shor_classical" begin - Random.seed!(129) - L = 35 - f = shor(L, Val(:classical)) - @test f == 5 || f == 7 - - L = 25 - f = shor(L, Val(:classical)) - @test_broken f == 5 - - L = 7*11 - f = shor(L, Val(:classical)) - @test f == 7 || f == 11 - - L = 14 - f = shor(L, Val(:classical)) - @test f == 2 || f == 7 -end - -using Random -@testset "shor quantum" begin - Random.seed!(129) - L = 15 - f = shor(L, Val(:quantum)) - @test f == 5 || f == 3 -end diff --git a/src/Diff.jl b/src/Diff.jl index f237ee3d8..f54a89569 100644 --- a/src/Diff.jl +++ b/src/Diff.jl @@ -1,4 +1,4 @@ -export Rotor, generator, AbstractDiff, BPDiff, QDiff, backward!, gradient, CPhaseGate, DiffBlock +export Rotor, generator, Diff, backward!, gradient, CPhaseGate, DiffBlock import Yao: expect, content, chcontent, mat, apply! using StatsBase @@ -13,81 +13,33 @@ Return the generator of rotation block. """ generator(rot::RotationGate) = rot.block generator(rot::PutBlock{N, C, GT}) where {N, C, GT<:RotationGate} = PutBlock{N}(generator(rot|>content), rot |> occupied_locs) -generator(c::CphaseGate{N}) where N = ControlBlock(N, c.ctrol_locs, ctrl_config, control(2,1,2=>Z), c.locs) - -abstract type AbstractDiff{GT, N, T} <: TagBlock{GT, N} end -Base.adjoint(df::AbstractDiff) = Daggered(df) - -istraitkeeper(::AbstractDiff) = Val(true) +generator(c::CphaseGate{N}) where N = ControlBlock{N}(c.ctrl_locs, c.ctrl_config, Z, c.locs) #################### The Basic Diff ################# """ - QDiff{GT, N} <: AbstractDiff{GT, N, T} - QDiff(block) -> QDiff + Diff{GT, N, T} <: TagBlock{GT, N} + Diff(block) -> Diff Mark a block as quantum differentiable. """ -mutable struct QDiff{GT, N, T} <: AbstractDiff{GT, N, T} - block::GT - grad::T - QDiff(block::DiffBlock{N, T}) where {N, T} = new{typeof(block), N, T}(block, T(0)) -end -content(cb::QDiff) = cb.block -chcontent(cb::QDiff, blk::DiffBlock) = QDiff(blk) - -@forward QDiff.block apply! -mat(::Type{T}, df::QDiff) where T = mat(T, df.block) -Base.adjoint(df::QDiff) = QDiff(content(df)') - -function YaoBlocks.print_annotation(io::IO, df::QDiff) - printstyled(io, "[̂∂] "; bold=true, color=:yellow) -end - -#################### The Back Propagation Diff ################# -""" - BPDiff{GT, N, T, PT} <: AbstractDiff{GT, N, Complex{T}} - BPDiff(block, [grad]) -> BPDiff - -Mark a block as differentiable, here `GT`, `PT` is gate type, parameter type. - -Warning: - please don't use the `adjoint` after `BPDiff`! `adjoint` is reserved for special purpose! (back propagation) -""" -mutable struct BPDiff{GT, N, T} <: AbstractDiff{GT, N, T} +mutable struct Diff{GT, N, T} <: TagBlock{GT, N} block::GT grad::T - input::AbstractRegister - BPDiff(block::AbstractBlock{N}, grad::T) where {N, T} = new{typeof(block), N, T}(block, grad) + Diff(block::DiffBlock{N, T}) where {N, T} = new{typeof(block), N, T}(block, T(0)) end -BPDiff(block::AbstractBlock) = BPDiff(block, zeros(parameters_eltype(block), nparameters(block))) -BPDiff(block::DiffBlock{N, T}) where {N, T} = BPDiff(block, T(0)) +content(cb::Diff) = cb.block +chcontent(cb::Diff, blk::DiffBlock) = Diff(blk) -content(cb::BPDiff) = cb.block -chcontent(cb::BPDiff, blk::AbstractBlock) = BPDiff(blk) +istraitkeeper(::Diff) = Val(true) -mat(::Type{T}, df::BPDiff) where T = mat(T, df.block) -function apply!(reg::AbstractRegister, df::BPDiff) - if isdefined(df, :input) - copyto!(df.input, reg) - else - df.input = copy(reg) - end - apply!(reg, content(df)) - reg -end - -function apply!(δ::AbstractRegister, adf::Daggered{<:BPDiff{<:Rotor}}) - df = adf |> content - apply!(δ, content(df)') - df.grad = -statevec(df.input |> generator(content(df)))' * statevec(δ) |> imag - δ -end +@forward Diff.block apply! +mat(::Type{T}, df::Diff) where T = mat(T, df.block) +Base.adjoint(df::Diff) = chcontent(df, content(df)') -function YaoBlocks.print_annotation(io::IO, df::BPDiff) +function YaoBlocks.print_annotation(io::IO, df::Diff) printstyled(io, "[∂] "; bold=true, color=:yellow) end - #### interface ##### export autodiff, numdiff, opdiff, StatFunctional, statdiff, as_weights @@ -103,7 +55,7 @@ autodiff(mode::Symbol) = block->autodiff(mode, block) autodiff(mode::Symbol, block::AbstractBlock) = autodiff(Val(mode), block) # for BP -autodiff(::Val{:BP}, block::DiffBlock) = BPDiff(block) +autodiff(::Val{:BP}, block::DiffBlock) = Diff(block) autodiff(::Val{:BP}, block::AbstractBlock) = block # Sequential, Roller and ChainBlock can propagate. function autodiff(mode::Val{:BP}, blk::Union{ChainBlock, Sequential}) @@ -111,16 +63,16 @@ function autodiff(mode::Val{:BP}, blk::Union{ChainBlock, Sequential}) end # for QC -autodiff(::Val{:QC}, block::Union{RotationGate, CphaseGate}) = QDiff(block) +autodiff(::Val{:QC}, block::Union{RotationGate, CphaseGate}) = Diff(block) # escape control blocks. autodiff(::Val{:QC}, block::ControlBlock) = block function autodiff(mode::Val{:QC}, blk::AbstractBlock) blks = subblocks(blk) isempty(blks) ? blk : chsubblocks(blk, autodiff.(mode, blks)) - end +end -@inline function _perturb(func, gate::AbstractDiff{<:DiffBlock}, δ::Real) +@inline function _perturb(func, gate::Diff{<:DiffBlock}, δ::Real) dispatch!(-, gate, (δ,)) r1 = func() dispatch!(+, gate, (2δ,)) @@ -129,7 +81,7 @@ function autodiff(mode::Val{:QC}, blk::AbstractBlock) r1, r2 end -@inline function _perturb(func, gate::AbstractDiff{<:Rotor}, δ::Real) # for put +@inline function _perturb(func, gate::Diff{<:Rotor}, δ::Real) # for put dispatch!(-, gate, (δ,)) r1 = func() dispatch!(+, gate, (2δ,)) @@ -139,21 +91,21 @@ end end """ - numdiff(loss, diffblock::AbstractDiff; δ::Real=1e-2) + numdiff(loss, diffblock::Diff; δ::Real=1e-2) Numeric differentiation. """ -@inline function numdiff(loss, diffblock::AbstractDiff; δ::Real=1e-2) +@inline function numdiff(loss, diffblock::Diff; δ::Real=1e-2) r1, r2 = _perturb(loss, diffblock, δ) diffblock.grad = (r2 - r1)/2δ end """ - opdiff(psifunc, diffblock::AbstractDiff, op::AbstractBlock) + opdiff(psifunc, diffblock::Diff, op::AbstractBlock) Operator differentiation. """ -@inline function opdiff(psifunc, diffblock::AbstractDiff, op::AbstractBlock) +@inline function opdiff(psifunc, diffblock::Diff, op::AbstractBlock) r1, r2 = _perturb(()->expect(op, psifunc()) |> real, diffblock, π/2) diffblock.grad = (r2 - r1)/2 end @@ -201,45 +153,68 @@ expect(stat::StatFunctional{1, <:Function}, xs::AbstractVector) = mean(stat.data Base.ndims(stat::StatFunctional{N}) where N = N """ - statdiff(probfunc, diffblock::AbstractDiff, stat::StatFunctional{<:Any, <:AbstractArray}; initial::AbstractVector=probfunc()) - statdiff(samplefunc, diffblock::AbstractDiff, stat::StatFunctional{<:Any, <:Function}; initial::AbstractVector=samplefunc()) + statdiff(probfunc, diffblock::Diff, stat::StatFunctional{<:Any, <:AbstractArray}; initial::AbstractVector=probfunc()) + statdiff(samplefunc, diffblock::Diff, stat::StatFunctional{<:Any, <:Function}; initial::AbstractVector=samplefunc()) Differentiation for statistic functionals. """ -@inline function statdiff(probfunc, diffblock::AbstractDiff, stat::StatFunctional{2}; initial::AbstractVector=probfunc()) +@inline function statdiff(probfunc, diffblock::Diff, stat::StatFunctional{2}; initial::AbstractVector=probfunc()) r1, r2 = _perturb(()->expect(stat, probfunc(), initial), diffblock, π/2) diffblock.grad = (r2 - r1)*ndims(stat)/2 end -@inline function statdiff(probfunc, diffblock::AbstractDiff, stat::StatFunctional{1}) +@inline function statdiff(probfunc, diffblock::Diff, stat::StatFunctional{1}) r1, r2 = _perturb(()->expect(stat, probfunc()), diffblock, π/2) diffblock.grad = (r2 - r1)*ndims(stat)/2 end """ - backward!(δ::AbstractRegister, circuit::AbstractBlock) -> AbstractRegister + backward!(state, circuit::AbstractBlock) -> AbstractRegister back propagate and calculate the gradient ∂f/∂θ = 2*Re(∂f/∂ψ*⋅∂ψ*/∂θ), given ∂f/∂ψ*. +`state` is a pair of output_register => the corresponding adjoint. Note: Here, the input circuit should be a matrix block, otherwise the back propagate may not apply (like Measure operations). """ -backward!(δ::AbstractRegister, circuit::AbstractBlock) = apply!(δ, circuit') +function backward!(state, block::AbstractBlock) + out, outδ = state + adjblock = block' + backward_params!((out, outδ), block) + in = apply!(out, adjblock) + inδ = apply!(outδ, adjblock) + return (in, inδ) +end + +function backward!(state, circuit::Union{ChainBlock, Concentrator}) + for blk in Base.Iterators.reverse(subblocks(circuit)) + state = backward!(state, blk) + end + return state +end + +backward!(state, block::Measure) = throw(MethodError(backward!, (state, block))) + +backward_params!(state, block::AbstractBlock) = nothing +function backward_params!(state, block::Diff{<:DiffBlock}) + in, outδ = state + Σ = generator(content(block)) + block.grad = -statevec(in |> Σ)' * statevec(outδ) |> imag + in |> Σ + nothing +end """ gradient(circuit::AbstractBlock, mode::Symbol=:ANY) -> Vector -collect all gradients in a circuit, mode can be :BP/:QC/:ANY, they will collect `grad` from BPDiff/QDiff/AbstractDiff respectively. +collect all gradients in a circuit, mode can be :BP/:QC/:ANY, they will collect `grad` from Diff respectively. """ -gradient(circuit::AbstractBlock, mode::Symbol=:ANY) = gradient!(circuit, parameters_eltype(circuit)[], mode) +gradient(circuit::AbstractBlock) = gradient!(circuit, parameters_eltype(circuit)[]) -gradient!(circuit::AbstractBlock, grad, mode::Symbol) = gradient!(circuit, grad, Val(mode)) -function gradient!(circuit::AbstractBlock, grad, mode::Val) +function gradient!(circuit::AbstractBlock, grad) for block in subblocks(circuit) - gradient!(block, grad, mode) + gradient!(block, grad) end grad end -gradient!(circuit::BPDiff, grad, mode::Val{:BP}) = append!(grad, circuit.grad) -gradient!(circuit::QDiff, grad, mode::Val{:QC}) = push!(grad, circuit.grad) -gradient!(circuit::AbstractDiff, grad, mode::Val{:ANY}) = append!(grad, circuit.grad) +gradient!(circuit::Diff, grad) = append!(grad, circuit.grad) diff --git a/src/Mod.jl b/src/Mod.jl new file mode 100644 index 000000000..cdd07df12 --- /dev/null +++ b/src/Mod.jl @@ -0,0 +1,105 @@ +# TODO +# compile Mod and KMod to elementary gates. + +export Mod, KMod + +""" + Mod{N} <: PrimitiveBlock{N} + +calculates `mod(a*x, L)`, notice `gcd(a, L)` should be 1. +""" +struct Mod{N} <: PrimitiveBlock{N} + a::Int + L::Int + function Mod{N}(a, L) where N + @assert gcd(a, L) == 1 && L<=1<= m.L ? i+1 : mod(i*m.a, m.L)+1 + for j in 1:B + @inbounds nstate[_i,j] = reg.state[i+1,j] + end + end + reg.state = nstate + reg +end + +function Yao.mat(::Type{T}, m::Mod{N}) where {T, N} + perm = Vector{Int}(undef, 1<= m.L ? i+1 : mod(i*m.a, m.L)+1] = i+1 + end + PermMatrix(perm, ones(T, 1< (b&mask, b>>k) +end + +function Yao.apply!(reg::ArrayReg{B}, m::KMod{N, K}) where {B, N, K} + YaoBlocks._check_size(reg, m) + nstate = zero(reg.state) + + reader = bint2_reader(Int, K) + for b in basis(reg) + k, i = reader(b) + _i = i >= m.L ? i : mod(i*powermod(m.a, k, m.L), m.L) + _b = k + _i<= m.L ? i : mod(i*powermod(m.a, k, m.L), m.L) + _b = k + _i< nqubits) |> qcbm.circuit """generated probability distribution""" probs(qcbm::QCBM) = qcbm |> psi |> probs """ - mmdgrad(qcbm::QCBM, db::AbstractDiff; p0::Vector) -> Float64 + mmdgrad(qcbm::QCBM, db::Diff; p0::Vector) -> Float64 gradient of MMD two sample test loss, `db` must be contained in qcbm. `p0` is current probability distribution. """ -function mmdgrad(qcbm::QCBM, db::AbstractDiff; p0::Vector) +function mmdgrad(qcbm::QCBM, db::Diff; p0::Vector) statdiff(()->probs(qcbm) |> as_weights, db, StatFunctional(kmat(qcbm.kernel)), initial=p0 |> as_weights) - 2*statdiff(()->probs(qcbm) |> as_weights, db, StatFunctional(kmat(qcbm.kernel)*qcbm.ptrain)) end diff --git a/src/QCOptProblem.jl b/src/QCOptProblem.jl index 9e8574970..ba25c1aa0 100644 --- a/src/QCOptProblem.jl +++ b/src/QCOptProblem.jl @@ -35,7 +35,7 @@ function gradient end collection of all differentiable units. """ -diff_blocks(qop::QCOptProblem) = collect_blocks(AbstractDiff, qop |> circuit) +diff_blocks(qop::QCOptProblem) = collect_blocks(Diff, qop |> circuit) """ num_gradient(qop::QCOptProblem) -> Vector diff --git a/src/QSVD.jl b/src/QSVD.jl index ab6b0b3b0..1a8d82871 100644 --- a/src/QSVD.jl +++ b/src/QSVD.jl @@ -19,7 +19,7 @@ function train_qsvd!(reg, circuit_a::AbstractBlock{Na}, circuit_b::AbstractBlock obs = -mapreduce(i->put(nbit, i=>Z), +, (1:Na..., Na+Nc+1:Na+Nb...)) params = parameters(c) for i = 1:maxiter - grad = opdiff.(() -> copy(reg) |> c, collect_blocks(AbstractDiff, c), Ref(obs)) + grad = opdiff.(() -> copy(reg) |> c, collect_blocks(Diff, c), Ref(obs)) QuAlgorithmZoo.update!(params, grad, optimizer) println("Iter $i, Loss = $(Na+expect(obs, copy(reg) |> c))") dispatch!(c, params) diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index 6726b423f..5f0458930 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -32,6 +32,7 @@ include("hamiltonian_solvers.jl") include("HadamardTest.jl") include("lin_diffEq_HHL.jl") include("QSVD.jl") +include("shor.jl") end # module diff --git a/src/QuGAN.jl b/src/QuGAN.jl index 70b591712..26d30499e 100644 --- a/src/QuGAN.jl +++ b/src/QuGAN.jl @@ -23,8 +23,8 @@ struct QuGAN{N} <: QCOptProblem N = nqubits(target) c = Sequence([gen, addbits!(1), dis]) witness_op = put(N+1, (N+1)=>P0) - gdiffs = collect_blocks(AbstractDiff, gen) - ddiffs = collect_blocks(AbstractDiff, dis) + gdiffs = collect_blocks(Diff, gen) + ddiffs = collect_blocks(Diff, dis) new{N}(target, gen, dis, zero_state(N), witness_op, c, gdiffs, ddiffs) end end diff --git a/src/hamiltonian_solvers.jl b/src/hamiltonian_solvers.jl index 4fee6f269..d34469287 100644 --- a/src/hamiltonian_solvers.jl +++ b/src/hamiltonian_solvers.jl @@ -51,7 +51,7 @@ variational quantum eigensolver, faithful simulation with optimizer Adam(lr=0.01 """ function vqe_solve!(circuit::AbstractBlock{N}, hamiltonian::AbstractBlock; niter::Int=100) where N optimizer = Adam(lr=0.01) - dbs = collect_blocks(AbstractDiff, circuit) + dbs = collect_blocks(Diff, circuit) params = parameters(circuit) for i = 1:niter grad = opdiff.(()->zero_state(N) |> circuit, dbs, Ref(hamiltonian)) diff --git a/src/number_theory.jl b/src/number_theory.jl new file mode 100644 index 000000000..355e281ae --- /dev/null +++ b/src/number_theory.jl @@ -0,0 +1,101 @@ +export NumberTheory + +module NumberTheory + +export Z_star, Eulerφ, continued_fraction, mod_inverse, rand_primeto, factor_a_power_b +export is_order, order_from_float, find_order + +""" + Z_star(N::Int) -> Vector + +returns the Z* group elements of `N`, i.e. {x | gcd(x, N) == 1} +""" +Z_star(N::Int) = filter(i->gcd(i, N)==1, 0:N-1) +Eulerφ(N) = length(Z_star(N)) + +""" + continued_fraction(ϕ, niter::Int) -> Rational + +obtain `s` and `r` from `ϕ` that satisfies `|s/r - ϕ| ≦ 1/2r²` +""" +continued_fraction(ϕ, niter::Int) = niter==0 ? floor(Int, ϕ) : floor(Int, ϕ) + 1//continued_fraction(1/mod(ϕ, 1), niter-1) +continued_fraction(ϕ::Rational, niter::Int) = niter==0 || ϕ.den==1 ? floor(Int, ϕ) : floor(Int, ϕ) + 1//continued_fraction(1/mod(ϕ, 1), niter-1) + +""" + mod_inverse(x::Int, N::Int) -> Int + +Return `y` that `(x*y)%N == 1`, notice the `(x*y)%N` operations in Z* forms a group and this is the definition of inverse. +""" +function mod_inverse(x::Int, N::Int) + for i=1:N + (x*i)%N == 1 && return i + end + throw(ArgumentError("Can not find the inverse, $x is probably not in Z*($N)!")) +end + +""" + is_order(r, x, N) -> Bool + +Returns true if `r` is the order of `x`, i.e. `r` satisfies `x^r % N == 1`. +""" +is_order(r, x, N) = powermod(x, r, N) == 1 + +""" + find_order(x::Int, N::Int) -> Int + +Find the order of `x` by brute force search. +""" +function find_order(x::Int, N::Int) + findfirst(r->is_order(r, x, N), 1:N) +end + +""" + rand_primeto(N::Int) -> Int + +Returns a random number `2 ≦ x < N` that is prime to `N`. +""" +function rand_primeto(N::Int) + while true + x = rand(2:N-1) + d = gcd(x, N) + if d == 1 + return x + end + end +end + +""" + order_from_float(ϕ, x, L) -> Int + +Estimate the order of `x` to `L`, `r`, from a floating point number `ϕ ∼ s/r` using the continued fraction method. +""" +function order_from_float(ϕ, x, L) + k = 1 + rnum = continued_fraction(ϕ, k) + while rnum.den < L + r = rnum.den + if is_order(r, x, L) + return r + end + k += 1 + rnum = continued_fraction(ϕ, k) + end + return nothing +end + +""" + factor_a_power_b(N::Int) -> (Int, Int) or nothing + +Factorize `N` into the power form `a^b`. +""" +function factor_a_power_b(N::Int) + y = log2(N) + for b = 2:ceil(Int, y) + x = 2^(y/b) + u1 = floor(Int, x) + u1^b == N && return (u1, b) + (u1+1)^b == N && return (u1+1, b) + end +end + +end diff --git a/src/shor.jl b/src/shor.jl new file mode 100644 index 000000000..c64fe8974 --- /dev/null +++ b/src/shor.jl @@ -0,0 +1,80 @@ +include("number_theory.jl") +include("Mod.jl") + +export shor, order_finding_circuit, get_order + +""" + shor(L::Int, ver=Val(:quantum); maxtry=100) + +factorize an integer `L`, `ver` can be either `Val(:quantum)` or `Val(:classical)`. +""" +function shor(L::Int, ver=Val(:quantum); maxtry=100) + L%2 == 0 && return 2 + + # find solutions like `a^b` + res = NumberTheory.factor_a_power_b(L) + res !== nothing && return res[1] + + for i in 1:maxtry + x = NumberTheory.rand_primeto(L) + r = get_order(ver, x, L) + # if `x^(r/2)` is non-trivil, go on. + # Here, we do not condsier `powermod(x, r÷2, L) == 1`, since in this case the order should be `r/2` + if r%2 == 0 && powermod(x, r÷2, L) != L-1 + f1, f2 = gcd(powermod(x, r÷2, L)-1, L), gcd(powermod(x, r÷2, L)+1, L) + if f1!=1 + return f1 + elseif f2!=1 + return f2 + else + error("Algorithm Fail!") + end + end + end +end + +"""estimate the required size of the output register.""" +estimate_ncbit(nbit::Int, ϵ::Real) = 2*nbit + 1 + ceil(Int,log2(2+1/2ϵ)) + +""" + order_finding_circuit(x::Int, L::Int; nbit::Int=bit_length(L-1), ncbit::Int=estimate_ncbit(nbit, 0.25)) -> AbstractBlock + +Returns the circuit for finding the order of `x` to `L`, +feeding input `|1>⊗|0>` will get the resulting quantum register with the desired "phase" information. +""" +function order_finding_circuit(x::Int, L::Int; nbit::Int=bit_length(L-1), ncbit::Int=estimate_ncbit(nbit, 0.25)) + N = nbit+ncbit + chain(N, repeat(N, H, 1:ncbit),KMod{N, ncbit}(x, L), concentrate(N, QFTBlock{ncbit}()', 1:ncbit)) +end + +""" + find_order([ver], x::Int, N::Int; nshots=10) -> Union{Int, Nothing} + +Get the order of `x`, `ver` can be `Val(:classical)` (default) or `Val(:quantum)`, +when using the quantum approach, we can set key word arguments `nshot`, +`nbit` (size of input data register) and `ncbit` (size of control register, or output register). +""" +get_order(::Val{:classical}, x::Int, N::Int; kwargs...) = NumberTheory.find_order(x, N) +function get_order(::Val{:quantum}, x::Int, N::Int; nshots=10, kwargs...) + c = order_finding_circuit(x, N; kwargs...) + n = nqubits_data(c[2]) + ncbit = nqubits_control(c[2]) + reg = join(product_state(n, 1), zero_state(ncbit)) + + res = measure(copy(reg) |> c; nshots=nshots) + reader = bint2_reader(Int, ncbit) + for r in res + k, i = reader(r) + # get s/r + ϕ = bfloat(k) # + ϕ == 0 && continue + + order = NumberTheory.order_from_float(ϕ, x, N) + if order === nothing + continue + else + return order + end + end + return nothing +end diff --git a/test/CircuitBuild.jl b/test/CircuitBuild.jl index d64687c13..4e078e2a4 100644 --- a/test/CircuitBuild.jl +++ b/test/CircuitBuild.jl @@ -46,14 +46,14 @@ end reg = rand_state(4) dispatch!(c, randn(nparameters(c))) - dbs = collect_blocks(BPDiff, c) + dbs = collect_blocks(Diff, c) op = kron(4, 1=>Z, 2=>X) loss1z() = expect(op, copy(reg) |> c) # return loss please # back propagation ψ = copy(reg) |> c δ = copy(ψ) |> op - backward!(δ, c) + backward!((ψ, δ), c) bd = gradient(c) # get num gradient diff --git a/test/Diff.jl b/test/Diff.jl index 29dc559fc..164fcbbe2 100644 --- a/test/Diff.jl +++ b/test/Diff.jl @@ -5,22 +5,23 @@ using LinearAlgebra, Test, Random @testset "BP diff" begin reg = rand_state(4) block = put(4, 2=>rot(X, 0.3)) - df = BPDiff(block) + df = Diff(block) @test df.grad == 0 @test nqubits(df) == 4 - df2 = BPDiff(rot(CNOT, 0.3)) + df2 = Diff(rot(CNOT, 0.3)) @test df2.grad == 0 @test nqubits(df2) == 2 + @test_throws MethodError backward!((reg, reg), Measure(4)) end @testset "Qi diff" begin reg = rand_state(4) - df2 = QDiff(rot(CNOT, 0.3)) + df2 = Diff(rot(CNOT, 0.3)) @test df2.grad == 0 @test nqubits(df2) == 2 - @test df2' isa QDiff + @test df2' isa Diff @test mat(df2) == mat(df2')' end @@ -76,19 +77,20 @@ end circuit = chain(4, repeat(4, H, 1:4), put(4, 3=>Rz(0.5)) |> autodiff(:BP), control(4, 2, 1=>shift(0.4)) |> autodiff(:BP), control(2, 1=>X), put(4, 4=>Ry(0.2)) |> autodiff(:BP)) op = put(4, 3=>Y) - θ = [0.1, 0.2, 0.3] + θ = [0.9, 0.2, 0.3] dispatch!(circuit, θ) loss! = loss_expect!(circuit, op) ψ0 = rand_state(4) ψ = copy(ψ0) |> circuit # get gradient - δ = ψ |> op - backward!(δ, circuit) + δ = copy(ψ) |> op + in, inδ = backward!((ψ, δ), circuit) + @test in ≈ ψ0 g1 = gradient(circuit) g2 = zero(θ) - η = 0.01 + η = 1e-5 for i in 1:length(θ) θ1 = copy(θ) θ2 = copy(θ) @@ -96,7 +98,7 @@ end θ2[i] += 0.5η g2[i] = (loss!(copy(ψ0), θ2) - loss!(copy(ψ0), θ1))/η |> real end - g3 = opdiff.(() -> copy(ψ0) |> circuit, collect_blocks(BPDiff, circuit), Ref(op)) + g3 = opdiff.(() -> copy(ψ0) |> circuit, collect_blocks(Diff, circuit), Ref(op)) @test isapprox.(g1, g2, atol=1e-5) |> all @test isapprox.(g2, g3, atol=1e-5) |> all end @@ -106,8 +108,8 @@ end @test generator(Rx(0.1)) == X circuit = chain(put(4, 1=>Rx(0.1)), control(4, 2, 1=>Ry(0.3))) c2 = circuit |> autodiff(:BP) - @test c2[1] isa BPDiff - @test !(c2[2] isa BPDiff) + @test c2[1] isa Diff + @test !(c2[2] isa Diff) end @testset "numdiff & opdiff" begin @@ -125,13 +127,12 @@ end reg = rand_state(4) c = chain(put(4, 1=>Rx(0.5)), control(4, 1, 2=>Ry(0.5)), control(4, 1, 2=>shift(0.3)), kron(4, 2=>Rz(0.3), 3=>Rx(0.7))) |> autodiff(:QC) - dbs = collect_blocks(QDiff, c) + dbs = collect_blocks(Diff, c) loss1z() = expect(kron(4, 1=>Z, 2=>X), copy(reg) |> c) |> real # return loss please nd = numdiff.(loss1z, dbs) ed = opdiff.(()->copy(reg) |> c, dbs, Ref(kron(4, 1=>Z, 2=>X))) gd = gradient(c) - @test gradient(c, :QC) == gd - @test gradient(c, :BP) == [] + @test gradient(c) == gd @test isapprox(nd, ed, atol=1e-4) @test ed == gd end @@ -146,7 +147,7 @@ end prs = [1=>2, 2=>3, 3=>1] c = ibm_diff_circuit(nbit, 2, prs) |> autodiff(:QC) dispatch!(c, :random) - dbs = collect_blocks(AbstractDiff,c) + dbs = collect_blocks(Diff,c) p0 = zero_state(nbit) |> c |> probs sample0 = measure(zero_state(nbit) |> c; nshots=5000) @@ -162,7 +163,7 @@ end V = StatFunctional(h) c = ibm_diff_circuit(nbit, 2, prs) |> autodiff(:QC) dispatch!(c, :random) - dbs = collect_blocks(AbstractDiff, c) + dbs = collect_blocks(Diff, c) p0 = zero_state(nbit) |> c |> probs |> as_weights loss0 = expect(V, p0 |> as_weights) diff --git a/test/runtests.jl b/test/runtests.jl index 331bbd6da..b5402211e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -49,3 +49,7 @@ end @testset "Diff" begin include("Diff.jl") end + +@testset "Shore" begin + include("shor.jl") +end diff --git a/test/shor.jl b/test/shor.jl new file mode 100644 index 000000000..b909d26d2 --- /dev/null +++ b/test/shor.jl @@ -0,0 +1,65 @@ +using Test, QuAlgorithmZoo, Yao +using Random +using QuAlgorithmZoo.NumberTheory + +"""Euler theorem states that the order is a devisor of Eulerφ (or the size of Z* group of `N`)""" +function check_Euler_theorem(N::Int) + Z = Z_star(N) + Nz = length(Z) # Eulerφ + for x in Z + @test powermod(x,Nz,N) == 1 # the order is a devisor of Eulerφ + end +end + +@testset "Euler" begin + check_Euler_theorem(150) +end + +@testset "Mod" begin + @test_throws AssertionError Mod{4}(4,10) + @test_throws AssertionError Mod{2}(3,10) + m = Mod{4}(3,10) + @test mat(m) ≈ applymatrix(m) + @test isunitary(m) + @test isunitary(mat(m)) + @test m' == Mod{4}(7,10) +end + +@testset "KMod" begin + @test_throws AssertionError KMod{6, 2}(4,10) + @test_throws AssertionError KMod{4, 2}(3,10) + m = KMod{6, 2}(3,10) + @test mat(m) ≈ applymatrix(m) + @test isunitary(m) + @test isunitary(mat(m)) + @test m' == KMod{6, 2}(7,10) +end + +@testset "shor_classical" begin + Random.seed!(129) + L = 35 + f = shor(L, Val(:classical)) + @test f == 5 || f == 7 + + L = 25 + f = shor(L, Val(:classical)) + @test f == 5 + + L = 7*11 + f = shor(L, Val(:classical)) + @test f == 7 || f == 11 + + L = 14 + f = shor(L, Val(:classical)) + @test f == 2 || f == 7 + + @test factor_a_power_b(25) == (5, 2) + @test factor_a_power_b(15) == nothing +end + +@testset "shor quantum" begin + Random.seed!(129) + L = 15 + f = shor(L, Val(:quantum)) + @test f == 5 || f == 3 +end From af61ed72af61b19112ea7fb223d70315e8e2b4b1 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 24 Jun 2019 15:42:36 +0800 Subject: [PATCH 55/85] update Shor algorithm (#9) * update Shor algorithm * fix test * add shor to readme From 5c497d500f2954696e769e9136e7af92d7532fb4 Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 19 Sep 2019 07:08:41 +0800 Subject: [PATCH 56/85] Fix trait (#11) * monor fix * seperate YaoExtensions * new H2 atoms * update docs * update Grover search --- .gitignore | 1 + Project.toml | 14 +- README.md | 24 +- docs/Project.toml | 12 + docs/make.jl | 76 +++--- docs/src/index.md | 5 +- docs/src/man/zoo.md | 5 + examples/Grover.jl | 35 --- examples/Grover/Grover.jl | 119 ++++++++++ test/Grover.jl => examples/Grover/tests.jl | 50 ++-- {test => examples/HHL}/HHL.jl | 5 +- src/HHL.jl => examples/HHL/HHLlib.jl | 4 +- .../HHL/lin_diffEq_HHL.jl | 0 .../HHL/lin_diffEq_HHLlib.jl | 0 examples/{ => QAOA}/QAOA.jl | 0 examples/{ => QAOA}/maxcut_gw.jl | 0 {src => examples/QCBM}/Kernels.jl | 0 examples/{ => QCBM}/QCBM.jl | 20 +- src/QCBM.jl => examples/QCBM/qcbmlib.jl | 35 ++- examples/{ => QuGAN}/QuGAN.jl | 12 +- src/QuGAN.jl => examples/QuGAN/QuGANlib.jl | 37 ++- examples/README.md | 20 -- examples/Shor/Shor.jl | 125 ++++++++++ test/shor.jl => examples/Shor/tests.jl | 31 +-- examples/VQE.jl | 17 -- examples/VQE/H2.jl | 40 ++++ examples/VQE/README.md | 14 ++ examples/VQE/VQE.jl | 64 +++++ examples/run_examples.jl | 9 + src/CircuitBuild.jl | 140 ----------- src/Diff.jl | 220 ------------------ src/Grover.jl | 86 ------- src/Miscellaneous.jl | 27 --- src/Mod.jl | 105 --------- src/QCOptProblem.jl | 56 ----- src/QFT.jl | 54 ----- src/QSVD.jl | 4 +- src/QuAlgorithmZoo.jl | 32 +-- src/RotBasis.jl | 76 ------ src/hamiltonian_solvers.jl | 14 +- src/sequence.jl | 39 ---- src/shor.jl | 80 ------- test/CircuitBuild.jl | 65 ------ test/Diff.jl | 173 -------------- test/HadamardTest.jl | 6 +- test/QCBM.jl | 32 --- test/QCOptProblem.jl | 7 - test/QFT.jl | 51 ---- test/QuGAN.jl | 24 -- test/RotBasis.jl | 38 --- test/Sequence.jl | 62 ----- test/hamiltonian_solvers.jl | 4 +- test/runtests.jl | 41 +--- 53 files changed, 574 insertions(+), 1636 deletions(-) create mode 100644 docs/Project.toml delete mode 100644 examples/Grover.jl create mode 100644 examples/Grover/Grover.jl rename test/Grover.jl => examples/Grover/tests.jl (50%) rename {test => examples/HHL}/HHL.jl (96%) rename src/HHL.jl => examples/HHL/HHLlib.jl (94%) rename test/lin_diffEq_test.jl => examples/HHL/lin_diffEq_HHL.jl (100%) rename src/lin_diffEq_HHL.jl => examples/HHL/lin_diffEq_HHLlib.jl (100%) rename examples/{ => QAOA}/QAOA.jl (100%) rename examples/{ => QAOA}/maxcut_gw.jl (100%) rename {src => examples/QCBM}/Kernels.jl (100%) rename examples/{ => QCBM}/QCBM.jl (62%) rename src/QCBM.jl => examples/QCBM/qcbmlib.jl (71%) rename examples/{ => QuGAN}/QuGAN.jl (77%) rename src/QuGAN.jl => examples/QuGAN/QuGANlib.jl (71%) delete mode 100644 examples/README.md create mode 100644 examples/Shor/Shor.jl rename test/shor.jl => examples/Shor/tests.jl (52%) delete mode 100644 examples/VQE.jl create mode 100644 examples/VQE/H2.jl create mode 100644 examples/VQE/README.md create mode 100644 examples/VQE/VQE.jl create mode 100644 examples/run_examples.jl delete mode 100644 src/CircuitBuild.jl delete mode 100644 src/Diff.jl delete mode 100644 src/Grover.jl delete mode 100644 src/Miscellaneous.jl delete mode 100644 src/Mod.jl delete mode 100644 src/QCOptProblem.jl delete mode 100644 src/QFT.jl delete mode 100644 src/RotBasis.jl delete mode 100644 src/sequence.jl delete mode 100644 src/shor.jl delete mode 100644 test/CircuitBuild.jl delete mode 100644 test/Diff.jl delete mode 100644 test/QCBM.jl delete mode 100644 test/QCOptProblem.jl delete mode 100644 test/QFT.jl delete mode 100644 test/QuGAN.jl delete mode 100644 test/RotBasis.jl delete mode 100644 test/Sequence.jl diff --git a/.gitignore b/.gitignore index 8e512f4e4..334a91acc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.jl.*.cov *.jl.mem +build/ docs/build/ docs/site/ docs/src/tutorial/ diff --git a/Project.toml b/Project.toml index 60d7322f3..a5f8bbd44 100644 --- a/Project.toml +++ b/Project.toml @@ -4,25 +4,19 @@ version = "0.1.0" [deps] BitBasis = "50ba71b6-fa0f-514d-ae9a-0916efc90dcf" -DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" -FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -LuxurySparse = "d05aeea4-b7d4-55ac-b691-9e7fabb07ba2" -MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" YaoArrayRegister = "e600142f-9330-5003-8abb-0ebd767abc51" YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df" +YaoExtensions = "7a06699c-c960-11e9-3c98-9f78548b5f0f" [compat] -julia = ">=1.0.0" Yao = ">=0.4.0" +julia = ">=1.0.0" [extras] -MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "OrdinaryDiffEq"] +test = ["Test"] diff --git a/README.md b/README.md index 433f5deeb..9639d1af3 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) +*Note*: part of functionalities has been moved to [YaoExtensions](https://github.com/QuantumBFS/YaoExtensions.jl). + ## Installation QuAlgorithmZoo.jl is not registered yet, please use the following command: @@ -19,22 +21,26 @@ Disclaimer: **this package is still under development and needs further polish.* ## Contents -- [x] Quantum Circuit Born Machine +- [x] [QFT](https://github.com/QuantumBFS/YaoExtensions.jl) - [x] Grover search -- [x] HHL -- [x] QFT - [x] Phase Estimation -- [x] QuGAN -- [x] QCBM -- [x] Hamiltonian Solver -- [x] QAOA -- [x] Variational quantum eigensolver -- [x] QuODE +- [x] Imaginary Time Evolution Quantum Eigensolver +- [x] Variational Quantum Eigensolver - [x] Hadamard Test - [x] State Overlap Algorithms - [x] Quantum SVD + +In examples folder, you will find + +- [x] HHL +- [x] QAOA +- [x] Quantum Circuit Born Machine +- [x] QuGAN - [x] Shor +- [x] [QuODE](https://github.com/QuantumBFS/QuDiffEq.jl) +- [x] [TensorNetwork Inspired Circuits](https://github.com/GiggleLiu/QuantumPEPS.jl) + ## License QuAlgorithmZoo.jl is released under Apache License 2.0. diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 000000000..df7e87967 --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,12 @@ +[deps] +BitBasis = "50ba71b6-fa0f-514d-ae9a-0916efc90dcf" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c" +KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" +Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" +Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" +Weave = "44d3d7a6-8a23-5bf8-98c5-b353f8df5ec9" +YaoArrayRegister = "e600142f-9330-5003-8abb-0ebd767abc51" +YaoBase = "a8f54c17-34bc-5a9d-b050-f522fe3f755f" +YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df" diff --git a/docs/make.jl b/docs/make.jl index 56e95dad8..1c709bf87 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -6,54 +6,56 @@ add Documenter Literate Plots Yao using Documenter, Literate, QuAlgorithmZoo +const Examples = ["Grover", "VQE", "Shor"] + const PATH = ( tutorial = joinpath(@__DIR__, "src/tutorial"), examples = joinpath(@__DIR__, "..", "examples") ) -function process_literate_scripts(;excludes=["make.jl", "README.md"]) +function process_literate_scripts() TUTORIALS = [] - for (root, dirs, files) in walkdir(PATH.examples) - for file in files - file in excludes && continue - filepath = joinpath(root, file) - Literate.markdown(filepath, PATH.tutorial) - - filename, _ = splitext(file) - mdfile = join([filename, ".md"]) - # TODO: use PATH.tutorial rather then manual path - push!(TUTORIALS, relpath(joinpath("tutorial", mdfile))) - end + for token in Examples + file = "$token.jl" + filepath = joinpath(PATH.examples, token, file) + Literate.markdown(filepath, PATH.tutorial) + + filename, _ = splitext(file) + mdfile = join([filename, ".md"]) + # TODO: use PATH.tutorial rather then manual path + push!(TUTORIALS, relpath(joinpath("tutorial", mdfile))) end TUTORIALS end -const TUTORIALS = process_literate_scripts() - #----------------------------------------------- -makedocs( - modules = [QuAlgorithmZoo], - clean = false, - format = :html, - sitename = "Quantum Algorithm Zoo", - linkcheck = !("skiplinks" in ARGS), - analytics = "UA-89508993-1", - pages = [ - "Home" => "index.md", - "Tutorial" => TUTORIALS, - "Manual" => Any[ - "man/zoo.md", +function generate(islocal::Bool="local" in ARGS) + makedocs( + modules = [QuAlgorithmZoo], + clean = false, + format = :html, + sitename = "Quantum Algorithm Zoo", + linkcheck = !("skiplinks" in ARGS), + analytics = "UA-89508993-1", + pages = [ + "Home" => "index.md", + "Algorithms" => process_literate_scripts(), + "Manual" => Any[ + "man/zoo.md", + ], ], - ], - html_prettyurls = !("local" in ARGS), - html_canonical = "https://quantumbfs.github.io/QuAlgorithmZoo.jl/latest/", -) + html_prettyurls = !islocal, + html_canonical = "https://quantumbfs.github.io/QuAlgorithmZoo.jl/latest/", + ) + + deploydocs( + repo = "github.com/QuantumBFS/QuAlgorithmZoo.jl.git", + target = "build", + julia = "1.0", + deps = nothing, + make = nothing, + ) +end -deploydocs( - repo = "github.com/QuantumBFS/QuAlgorithmZoo.jl.git", - target = "build", - julia = "1.0", - deps = nothing, - make = nothing, -) +generate(true) diff --git a/docs/src/index.md b/docs/src/index.md index 5d4d05b07..208ea3622 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -9,9 +9,8 @@ A curated implementation of quantum algorithms with [Yao.jl](https://github.com/ ## Tutorial ```@contents Pages = [ - "tutorial/Grover.md", - "tutorial/QCBM.md", - "tutorial/QuGAN.md", + "tutorial/VQE.md", + "tutorial/Shor.md", ] Depth = 1 ``` diff --git a/docs/src/man/zoo.md b/docs/src/man/zoo.md index ff1ede4ae..672f4fe89 100644 --- a/docs/src/man/zoo.md +++ b/docs/src/man/zoo.md @@ -4,3 +4,8 @@ Modules = [QuAlgorithmZoo] Order = [:module, :constant, :type, :macro, :function] ``` + +```@autodocs +Modules = [QuAlgorithmZoo.NumberTheory] +Order = [:module, :constant, :type, :macro, :function] +``` diff --git a/examples/Grover.jl b/examples/Grover.jl deleted file mode 100644 index 891a56077..000000000 --- a/examples/Grover.jl +++ /dev/null @@ -1,35 +0,0 @@ -# # Grover Search -using Yao -using LinearAlgebra -using QuAlgorithmZoo: groveriter, inference_oracle, prob_match_oracle - -# ## Target Space and Evidense -num_bit = 12 -oracle = matblock(Diagonal((v = ones(1<<0|U'`. +function grover_step!(reg::AbstractRegister, oracle, U::AbstractBlock) + apply!(reg |> oracle, reflect_circuit(U)) +end + +function reflect_circuit(gen::AbstractBlock{N}) where N + reflect0 = control(N, -collect(1:N-1), N=>-Z) + chain(gen', reflect0, gen) +end + +# Compute the propotion of target states to estimate the number of iterations, +# which requires computing the output state. +function solution_state(oracle, gen::AbstractBlock{N}) where N + reg= zero_state(N) |> gen + reg.state[real.(statevec(ArrayReg(ones(ComplexF64, 1< oracle)) .> 0] .= 0 + normalize!(reg) +end + +function num_grover_step(oracle, gen::AbstractBlock{N}) where N + reg = zero_state(N) |> gen + ratio = abs2(solution_state(oracle, gen)'*reg) + Int(round(pi/4/sqrt(ratio)))-1 +end + +# #### Run +# First, we define the problem by an oracle, it finds bit string `bit"000001100100"`. +num_bit = 12 +oracle = matblock(Diagonal((v = ones(ComplexF64, 1< gen + +target_state = solution_state(oracle, gen) + +for i = 1:num_grover_step(oracle, gen) + grover_step!(reg, oracle, gen) + overlap = abs(reg'*target_state) + println("step $(i-1), overlap = $overlap") +end + +# ## Rejection Sampling +# In practise, it is often not possible to determine the number of iterations before actual running. +# we can use rejection sampling technique to avoid estimating the number of grover steps. + +using Random; Random.seed!(2) #src + +# In a single try, we `apply` the grover algorithm for `nstep` times. +function single_try(oracle, gen::AbstractBlock{N}, nstep::Int; nbatch::Int) where N + reg = zero_state(N+1; nbatch=nshot) + focus!(reg, 1:N) do r + r |> gen + for i = 1:nstep + grover_step!(r, oracle, gen) + end + return r + end + reg |> checker + res = measure_remove!(reg, (N+1)) + return res, reg +end + +# After running the grover search, we have a checker program that flips the ancilla qubit +# if the output is the desired value, we assume the checker program can be implemented in polynomial time. +# to gaurante the output is correct. +# We contruct a checker "program", if the result is correct, flip the ancilla qubit +ctrl = -collect(1:num_bit); ctrl[[3,6,7]] *= -1 +checker = control(num_bit+1,ctrl, num_bit+1=>X) + +# The register is batched, with batch dimension `nshot`. +# [`focus!`](@ref Yao.focus!) views the first 1-N qubts as system. +# For a batched register, [`measure_remove!`](@ref Yao.measure_remove!) +# returns a vector of bitstring as output. + +# #### Run +maxtry = 100 +nshot = 3 + +for nstep = 0:maxtry + println("number of iter = $nstep") + res, reg = single_try(oracle, gen, nstep; nbatch=3) + + ## success! + if any(==(1), res) + overlap_final = viewbatch(reg, findfirst(==(1), res))'*target_state + println("success, overlap = $(overlap_final)") + break + end +end + +# The final state has an overlap of `1` with the target state. + +# ## Amplitude Amplification +# Given a circuit to generate a state, +# now we want to project out the subspace with [1,3,5,8,9,11,12] fixed to 1 and [4,6] fixed to 0. +# We can construct an oracle +evidense = [1, 3, -4, 5, -6, 8, 9, 11, 12] +function inference_oracle(nbit::Int, locs::Vector{Int}) + control(nbit, locs[1:end-1], abs(locs[end]) => (locs[end]>0 ? Z : -Z)) +end +oracle = inference_oracle(nqubits(reg), evidense) + +# We use a variational circuit generator defined in `YaoExtensions` +gen = dispatch!(variational_circuit(num_bit), :random) +reg = zero_state(num_bit) |> gen + +# #### Run +solution = solution_state(oracle, gen) +for i = 1:num_grover_step(oracle, gen) + grover_step!(reg, oracle, gen) + println("step $(i-1), overlap = $(abs(reg'*solution))") +end diff --git a/test/Grover.jl b/examples/Grover/tests.jl similarity index 50% rename from test/Grover.jl rename to examples/Grover/tests.jl index 968808de0..a9ba1e048 100644 --- a/test/Grover.jl +++ b/examples/Grover/tests.jl @@ -1,25 +1,23 @@ -using Test, Random, LinearAlgebra, SparseArrays -using BitBasis +include("Grover.jl") +using Test, BitBasis -using QuAlgorithmZoo -import QuAlgorithmZoo: _num_grover_step -using Yao - -function GroverSearch(oracle, num_bit::Int; psi::DefaultRegister = uniform_state(num_bit)) - it = groveriter(psi, oracle) - for l_psi in it psi = l_psi end - return (it.niter, psi) +"""traditional grover search algorithm.""" +function grover_search(oracle::AbstractBlock{N}, gen::AbstractBlock{N}=repeat(N,H,1:N)) where N + reg = zero_state(N) |> gen + for i = 1:num_grover_step(oracle, gen) + grover_step!(reg, oracle, gen) + end + return reg end -function inference(psi::DefaultRegister, evidense::Vector{Int}, num_iter::Int) - oracle = inference_oracle(evidense)(nqubits(psi)) - it = groveriter(psi, oracle) - for l_psi in it psi = l_psi end - it.niter, psi +function grover_circuit(oracle::AbstractBlock{N}, gen::AbstractBlock{N}, niter::Int=num_grover_step(oracle, gen)) where {N} + chain(N, chain(oracle, reflect_circuit(gen)) for i = 1:niter) end +#################### Tests ################## + @testset "oracle" begin - oracle = inference_oracle([2,-1,3])(3) + oracle = inference_oracle(3, [2,-1,3]) # ≈ method for Identity/PermuteMultiply/Sparse # add and mimus between sparse matrices. # alway use sorted CSC format. @@ -32,26 +30,25 @@ end @testset "Grover Search" begin ####### Construct Grover Search Using Reflection Block num_bit = 12 - oracle = inference_oracle(push!(collect(Int, 1:num_bit-1), num_bit))(num_bit) + oracle = inference_oracle(num_bit, push!(collect(Int, 1:num_bit-1), num_bit)) - niter, psi = GroverSearch(oracle, 12) + psi = grover_search(oracle) target_state = zeros(1< gen, gb) == grover_search(or) end @testset "test inference" begin + Random.seed!(2) num_bit = 12 - psi0 = rand_state(num_bit) + gen = dispatch!(variational_circuit(num_bit), :random) + psi0 = zero_state(num_bit) |> gen #psi0 = uniform_state(num_bit) evidense = [1, 2, 3, 4, 5, 6, 7, 8, 9] #evidense = collect(1:num_bit) @@ -65,7 +62,6 @@ end v_desired[:] ./= sqrt(p) # search the subspace - num_iter = _num_grover_step(p) - niter, psi = inference(psi0, evidense, num_iter) + psi = grover_search(inference_oracle(num_bit, evidense), gen) @test isapprox((psi.state[subinds .+ 1]'*v_desired) |> abs2, 1, atol=3e-2) end diff --git a/test/HHL.jl b/examples/HHL/HHL.jl similarity index 96% rename from test/HHL.jl rename to examples/HHL/HHL.jl index 287c9c00d..27822ef22 100644 --- a/test/HHL.jl +++ b/examples/HHL/HHL.jl @@ -1,7 +1,10 @@ using Yao using BitBasis -using QuAlgorithmZoo +using YaoArrayRegister using Test, LinearAlgebra +using QuAlgorithmZoo: PEBlock + +include("HHLlib.jl") function crot(n_reg::Int, C_value::Real) n_rot = n_reg + 1 diff --git a/src/HHL.jl b/examples/HHL/HHLlib.jl similarity index 94% rename from src/HHL.jl rename to examples/HHL/HHLlib.jl index c364fe856..a4318ff36 100644 --- a/src/HHL.jl +++ b/examples/HHL/HHLlib.jl @@ -22,7 +22,7 @@ end a, -b, b, a end -function apply!(reg::ArrayReg, hr::HHLCRot{N, NC, T}) where {N, NC, T} +function Yao.apply!(reg::ArrayReg, hr::HHLCRot{N, NC, T}) where {N, NC, T} mask = bmask(hr.ibit) step = 1<<(hr.ibit-1) step_2 = step*2 @@ -32,7 +32,7 @@ function apply!(reg::ArrayReg, hr::HHLCRot{N, NC, T}) where {N, NC, T} λ = bfloat(readbit(i-1, hr.cbits...), nbits=nbit-1) if λ >= hr.C_value u = hhlrotmat(λ, hr.C_value) - u1rows!(state(reg), i, i+step, u...) + YaoArrayRegister.u1rows!(state(reg), i, i+step, u...) end end end diff --git a/test/lin_diffEq_test.jl b/examples/HHL/lin_diffEq_HHL.jl similarity index 100% rename from test/lin_diffEq_test.jl rename to examples/HHL/lin_diffEq_HHL.jl diff --git a/src/lin_diffEq_HHL.jl b/examples/HHL/lin_diffEq_HHLlib.jl similarity index 100% rename from src/lin_diffEq_HHL.jl rename to examples/HHL/lin_diffEq_HHLlib.jl diff --git a/examples/QAOA.jl b/examples/QAOA/QAOA.jl similarity index 100% rename from examples/QAOA.jl rename to examples/QAOA/QAOA.jl diff --git a/examples/maxcut_gw.jl b/examples/QAOA/maxcut_gw.jl similarity index 100% rename from examples/maxcut_gw.jl rename to examples/QAOA/maxcut_gw.jl diff --git a/src/Kernels.jl b/examples/QCBM/Kernels.jl similarity index 100% rename from src/Kernels.jl rename to examples/QCBM/Kernels.jl diff --git a/examples/QCBM.jl b/examples/QCBM/QCBM.jl similarity index 62% rename from examples/QCBM.jl rename to examples/QCBM/QCBM.jl index cb2e4085c..c6ef9ff08 100644 --- a/examples/QCBM.jl +++ b/examples/QCBM/QCBM.jl @@ -1,19 +1,11 @@ # # Quantum Circuit Born Machine -using Yao -using QuAlgorithmZoo +using Yao, YaoExtensions +import QuAlgorithmZoo +include("qcbmlib.jl") # ## DATA: Target Probability Distribution # The gaussian probability disctribution in phase space of 2^6 -""" - gaussian_pdf(x, μ::Real, σ::Real) - -gaussian probability density function. -""" -function gaussian_pdf(x, μ::Real, σ::Real) - pl = @. 1 / sqrt(2pi * σ^2) * exp(-(x - μ)^2 / (2 * σ^2)) - pl / sum(pl) -end nbit = 6 N = 1< autodiff(:QC); -dispatch!(circuit, :random) -qcbm = QCBM(circuit, kernel, pg); +c = variational_circuit(nbit, depth, pair_ring(nbit)) |> autodiff(:QC); +dispatch!(c, :random) +qcbm = QCBM(c, kernel, pg); # ## TRAINING: Adam Optimizer # We probide the QCBMGo! iterative interface for training diff --git a/src/QCBM.jl b/examples/QCBM/qcbmlib.jl similarity index 71% rename from src/QCBM.jl rename to examples/QCBM/qcbmlib.jl index 51bd9476b..79a5620c8 100644 --- a/src/QCBM.jl +++ b/examples/QCBM/qcbmlib.jl @@ -4,7 +4,7 @@ export QCBM, QCBMGo!, psi, mmdgrad include("Kernels.jl") ##### QCBM methods ##### -struct QCBM{BT<:AbstractBlock, KT<:AbstractKernel} <: QCOptProblem +struct QCBM{BT<:AbstractBlock, KT<:AbstractKernel} circuit::BT kernel::KT ptrain::Vector{Float64} @@ -53,7 +53,7 @@ end """ quantum circuit born machine trainer. """ -struct QCBMGo!{QT<:QCBM, OT} <: QCOptGo!{QT} +struct QCBMGo!{QT<:QCBM, OT} qcbm::QT optimizer::OT niter::Int @@ -67,7 +67,36 @@ function Base.iterate(qo::QCBMGo!, state=(1, parameters(qo.qcbm.circuit))) # initialize the parameters p0 = qo.qcbm |> probs grad = gradient(qo.qcbm, p0) - update!(state[2], grad, qo.optimizer) + QuAlgorithmZoo.update!(state[2], grad, qo.optimizer) dispatch!(qo.qcbm.circuit, state[2]) Dict("probs"=>p0, "step"=>state[1], "gradient"=>grad), (state[1]+1, state[2]) end + +""" + gaussian_pdf(x, μ::Real, σ::Real) + +gaussian probability density function. +""" +function gaussian_pdf(x, μ::Real, σ::Real) + pl = @. 1 / sqrt(2pi * σ^2) * exp(-(x - μ)^2 / (2 * σ^2)) + pl / sum(pl) +end + +@testset "qcbm" begin + # problem setup + n = 6 + depth = 6 + + N = 1< autodiff(:QC) + dispatch!(circuit, :random) + qcbm = QCBM(circuit, kernel, pg) + + # training + niter = 100 + optim = QuAlgorithmZoo.Adam(lr=0.1) + for info in QCBMGo!(qcbm, optim, niter) end + @test qcbm |> loss < 1e-4 +end diff --git a/examples/QuGAN.jl b/examples/QuGAN/QuGAN.jl similarity index 77% rename from examples/QuGAN.jl rename to examples/QuGAN/QuGAN.jl index e06aaabed..5ec779536 100644 --- a/examples/QuGAN.jl +++ b/examples/QuGAN/QuGAN.jl @@ -1,6 +1,10 @@ # # Quantum GAN -using Yao -using QuAlgorithmZoo +using Yao, YaoExtensions +using Yao.ConstGate: P0 +import QuAlgorithmZoo +using Test, Random + +include("QuGANlib.jl") # ## DATA: Target Wave Function # here we learn a 3 qubit state @@ -11,11 +15,11 @@ target_state = rand_state(nbit) # using a 4-layer random differential circuit for both generator and discriminator # we build the qcgan setup. depth_gen = 4 -generator = dispatch!(random_diff_circuit(nbit, depth_gen, pair_ring(nbit)), :random) |> autodiff(:QC); +generator = dispatch!(variational_circuit(nbit, depth_gen, pair_ring(nbit)), :random) |> autodiff(:QC); #------------------------------ depth_disc = 4 -discriminator = dispatch!(random_diff_circuit(nbit+1, depth_disc, pair_ring(nbit+1)), :random) |> autodiff(:QC) +discriminator = dispatch!(variational_circuit(nbit+1, depth_disc, pair_ring(nbit+1)), :random) |> autodiff(:QC) qg = QuGAN(target_state, generator, discriminator); # ## TRAINING: Gradient Descent diff --git a/src/QuGAN.jl b/examples/QuGAN/QuGANlib.jl similarity index 71% rename from src/QuGAN.jl rename to examples/QuGAN/QuGANlib.jl index 26d30499e..1d1aaae49 100644 --- a/src/QuGAN.jl +++ b/examples/QuGAN/QuGANlib.jl @@ -1,4 +1,3 @@ -using MacroTools: @forward import Yao: tracedist export QuGAN, psi, toy_qugan, QuGANGo! @@ -9,7 +8,7 @@ Quantum GAN. Reference: Benedetti, M., Grant, E., Wossnig, L., & Severini, S. (2018). Adversarial quantum circuit learning for pure state approximation, 1–14. """ -struct QuGAN{N} <: QCOptProblem +struct QuGAN{N} target::ArrayReg generator::AbstractBlock{N} discriminator::AbstractBlock @@ -60,8 +59,8 @@ Construct a toy qugan. """ function toy_qugan(target::ArrayReg, depth_gen::Int, depth_disc::Int) n = nqubits(target) - generator = dispatch!(random_diff_circuit(n, depth_gen, pair_ring(n)), :random) |> autodiff(:QC) - discriminator = dispatch!(random_diff_circuit(n+1, depth_disc, pair_ring(n+1)), :random) |> autodiff(:QC) + generator = dispatch!(variational_circuit(n, depth_gen, pair_ring(n)), :random) |> autodiff(:QC) + discriminator = dispatch!(variational_circuit(n+1, depth_disc, pair_ring(n+1)), :random) |> autodiff(:QC) return QuGAN(target, generator, discriminator) end @@ -73,7 +72,7 @@ Iterative training of quantum generative optimization problem, QT is the type of quantum optimization problem, OT is the optimizer/learning_rate parameter type. """ -struct QuGANGo!{QT<:QuGAN, OT} <: QCOptGo!{QT} +struct QuGANGo!{QT<:QuGAN, OT} qop::QT goptim::OT doptim::OT @@ -93,13 +92,35 @@ function Base.iterate(qgg::QuGANGo!{<:Any, <:Real}, state=1) Dict("step"=>state,"gradient"=>grad), state+1 end -function Base.iterate(qgg::QuGANGo!{<:Any, <:Adam}, state=(1, parameters(qgg.qop.generator), parameters(qgg.qop.discriminator))) +function Base.iterate(qgg::QuGANGo!{<:Any, <:QuAlgorithmZoo.Adam}, state=(1, parameters(qgg.qop.generator), parameters(qgg.qop.discriminator))) state[1] > qgg.niter && return nothing qg = qgg.qop ng = length(qg.gdiffs) grad = gradient(qg) - dispatch!(qg.generator, update!(state[2], grad[1:ng], qgg.goptim)) - dispatch!(qg.discriminator, update!(state[3], -grad[ng+1:end], qgg.doptim)) + dispatch!(qg.generator, QuAlgorithmZoo.update!(state[2], grad[1:ng], qgg.goptim)) + dispatch!(qg.discriminator, QuAlgorithmZoo.update!(state[3], -grad[ng+1:end], qgg.doptim)) Dict("step"=>state[1], "gradient"=>grad), (state[1]+1, state[2], state[3]) end + +function run_test(nbit::Int, depth_gen::Int, depth_disc::Int; g_lr=0.1, d_lr=0.2, niter=1000) + qg = toy_qugan(rand_state(nbit), depth_gen, depth_disc) + for info in QuGANGo!(qg, g_lr, d_lr, niter) end + qg +end + +num_gradient(qop::QuGAN) = numdiff.(()->loss(qop), qop |> diff_blocks) + +# to fix +@testset "quantum circuit gan - opdiff" begin + Random.seed!(2) + N = 3 + target = rand_state(N) + qcg = toy_qugan(target, 2, 2) + grad = gradient(qcg) + @test isapprox(grad, num_gradient(qcg), atol=1e-4) + qg = run_test(3, 4, 4, g_lr=0.2, d_lr=0.5, niter=300) + @test qg |> loss < 0.1 + qg = run_test(3, 4, 4, g_lr=QuAlgorithmZoo.Adam(lr=0.005), d_lr=QuAlgorithmZoo.Adam(lr=0.5), niter=1000) + @test qg |> loss < 0.1 +end diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 2a27f3403..000000000 --- a/examples/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Quantum Algorithm Zoo - -This folder contains the examples of the quantum algorithms implemented in the Zoo -in jupyter notebook form. - -## Usage - -1. Install IJulia for jupyter notebook kernels - -```julia -pkg> add IJulia -``` - -If you have IJulia installed. - -```julia -julia> using IJulia - -julia> notebook() -``` diff --git a/examples/Shor/Shor.jl b/examples/Shor/Shor.jl new file mode 100644 index 000000000..bcf8294e7 --- /dev/null +++ b/examples/Shor/Shor.jl @@ -0,0 +1,125 @@ +# # [Shor's Algorithm](@id Shor) + +# ## References +# * [Neilsen](https://aapt.scitation.org/doi/abs/10.1119/1.1463744?journalCode=ajp) +# * [An Insightful Blog](https://algassert.com/post/1718) + +# ## Main Program +# The main program of a Shor's algorithm can be summrized in several lines of code. +# For the theory part, please refer the reference materials above. +# It factorize an integer `L`, and returns one of the factors. +# Here, the input `ver` can be either `Val(:quantum)` or `Val(:classical)`, +# where the classical version is for comparison. + +using Yao, BitBasis +using YaoExtensions: KMod, QFTCircuit +using QuAlgorithmZoo: NumberTheory + +function shor(L::Int, ver=Val(:quantum); maxtry=100) + L%2 == 0 && return 2 + + ## find short cut solutions like `a^b` + res = NumberTheory.factor_a_power_b(L) + res !== nothing && return res[1] + + for i in 1:maxtry + ## step 1 + x = NumberTheory.rand_primeto(L) + + ## step 2 + r = get_order(ver, x, L; ) + if r%2 == 0 && powermod(x, r÷2, L) != L-1 + ## step 3 + f1, f2 = gcd(powermod(x, r÷2, L)-1, L), gcd(powermod(x, r÷2, L)+1, L) + if f1!=1 + return f1 + elseif f2!=1 + return f2 + else + error("Algorithm Fail!") + end + end + end +end + +# Except some shortcuts, in each try, the main program can be summarized in several steps +# 1. randomly pick a number that prime to the input numebr `L`, i.e. `gcd(x, L) = 1`. +# The complexity of this algorithm is polynoial. +# 2. get the order `x`, i.e. finding a number `r` that satisfies `mod(x^r, L) = 1`. +# If `r` is even and `x^(r÷2)` is non-trivil, go on, otherwise start another try. +# Here, trivil means equal to `L-1 (mod L)`. +# 3. According to Theorem 5.2 in Neilsen book, +# one of `gcd(x^(r÷2)-1, L)` and `gcd(x^(r÷2)+1, L)` must be a non-trivil (`!=1`) factor of `L`. +# Notice `powermod(x, r÷2, L)` must be `-1` rather than `1`, +# otherwise the order should be `r/2` according to definition. + +# The only difference between classical and quantum version is the order finding algorithm. + +# ## Order Finding +# We provided a classical order finding algorithm in `NumberTheory`, +# here we focus on the quantum version. +# The algorithm is consisted +# 1. run the circuit to get a bitstring, +# 2. interpret this bitstring in output register as a rational number `s/r`. +# To achieve this, we first interpret it as a floating point number, +# then the continued fraction algorithm can find the best match for us. +# +# When using the quantum version, we have the flexibility to set key word arguments `nshot`, +# `nbit` (size of input data register) and `ncbit` (size of control register, or output register). +# `nbit` can be simply chosen as the minimum register size to store input, +# while `ncbit` can be estimated with the following function +"""estimate the required size of the output register.""" +estimate_ncbit(nbit::Int, ϵ::Real) = 2*nbit + 1 + ceil(Int,log2(2+1/2ϵ)) + +get_order(::Val{:classical}, x::Int, L::Int; kwargs...) = NumberTheory.find_order(x, L) +function get_order(::Val{:quantum}, x::Int, L::Int; nshots::Int=10, + nbit::Int=bit_length(L-1), ncbit::Int=estimate_ncbit(nbit, 0.25)) + c = order_finding_circuit(x, L; nbit=nbit, ncbit=ncbit) + reg = join(product_state(nbit, 1), zero_state(ncbit)) + + res = measure(copy(reg) |> c; nshots=nshots) + for r in res + ## split bit string b into lower bits `k` and higher bits `r`. + mask = bmask(1:ncbit) + k,i = r&mask, r>>ncbit + ## get s/r + ϕ = bfloat(k) # + ϕ == 0 && continue + + ## order_from_float: given a floating point number, + ## return the closest rational number with bounded number of continued fraction steps. + order = NumberTheory.order_from_float(ϕ, x, L) + if order === nothing + continue + else + return order + end + end + return nothing +end + +# #### The circuit used for finding order +""" + order_finding_circuit(x::Int, L::Int; nbit::Int=bit_length(L-1), ncbit::Int=estimate_ncbit(nbit, 0.25)) -> AbstractBlock + +Returns the circuit for finding the order of `x` to `L`, +feeding input `|1>⊗|0>` will get the resulting quantum register with the desired "phase" information. +""" +function order_finding_circuit(x::Int, L::Int; nbit::Int, ncbit::Int) + N = nbit+ncbit + chain(N, repeat(N, H, 1:ncbit), KMod{N, ncbit}(x, L), + concentrate(N, QFTCircuit(ncbit)', 1:ncbit)) +end + +# The circuit for order finding is consisted of three parts +# 1. Hadamard gates, +# 2. `KMod` that computes a classical function `mod(a^k*x, L)`. +# `k` is the integer stored in first `K` (or `ncbit`) qubits and the rest `N-K` qubits stores `a`. +# Notice it is not a basic gate, it should have been compiled to multiple gates, which is not implemented in `Yao` for the moment. +# To learn more about implementing arithmatics on a quantum circuit, please read [this paper](https://arxiv.org/abs/1805.12445). +# 3. Inverse quantum fourier transformation. + +# ## Run +# Factorizing `15`, you should see `3` or `5`, please report a bug if it is not... +using Random; Random.seed!(129) #src +shor(15, Val(:quantum)) diff --git a/test/shor.jl b/examples/Shor/tests.jl similarity index 52% rename from test/shor.jl rename to examples/Shor/tests.jl index b909d26d2..8c0c104ab 100644 --- a/test/shor.jl +++ b/examples/Shor/tests.jl @@ -1,10 +1,9 @@ -using Test, QuAlgorithmZoo, Yao -using Random -using QuAlgorithmZoo.NumberTheory +include("Shor.jl") +using Test """Euler theorem states that the order is a devisor of Eulerφ (or the size of Z* group of `N`)""" function check_Euler_theorem(N::Int) - Z = Z_star(N) + Z = NumberTheory.Z_star(N) Nz = length(Z) # Eulerφ for x in Z @test powermod(x,Nz,N) == 1 # the order is a devisor of Eulerφ @@ -15,26 +14,6 @@ end check_Euler_theorem(150) end -@testset "Mod" begin - @test_throws AssertionError Mod{4}(4,10) - @test_throws AssertionError Mod{2}(3,10) - m = Mod{4}(3,10) - @test mat(m) ≈ applymatrix(m) - @test isunitary(m) - @test isunitary(mat(m)) - @test m' == Mod{4}(7,10) -end - -@testset "KMod" begin - @test_throws AssertionError KMod{6, 2}(4,10) - @test_throws AssertionError KMod{4, 2}(3,10) - m = KMod{6, 2}(3,10) - @test mat(m) ≈ applymatrix(m) - @test isunitary(m) - @test isunitary(mat(m)) - @test m' == KMod{6, 2}(7,10) -end - @testset "shor_classical" begin Random.seed!(129) L = 35 @@ -53,8 +32,8 @@ end f = shor(L, Val(:classical)) @test f == 2 || f == 7 - @test factor_a_power_b(25) == (5, 2) - @test factor_a_power_b(15) == nothing + @test NumberTheory.factor_a_power_b(25) == (5, 2) + @test NumberTheory.factor_a_power_b(15) == nothing end @testset "shor quantum" begin diff --git a/examples/VQE.jl b/examples/VQE.jl deleted file mode 100644 index f6f4df2d3..000000000 --- a/examples/VQE.jl +++ /dev/null @@ -1,17 +0,0 @@ -using Yao -using QuAlgorithmZoo -using KrylovKit - -function ed_groundstate(h::AbstractBlock) - E, V = eigsolve(h |> mat, 1, :SR, ishermitian=true) - println("Ground State Energy is $(E[1])") - ArrayReg(V[1]) -end - -N = 5 -c = random_diff_circuit(N, N, [i=>mod(i,N)+1 for i=1:N], mode=:Merged) |> autodiff(:QC) -dispatch!(c, :random) -hami = heisenberg(N) - -# vqe ground state -vqe_solve!(c, hami) diff --git a/examples/VQE/H2.jl b/examples/VQE/H2.jl new file mode 100644 index 000000000..604a7d86b --- /dev/null +++ b/examples/VQE/H2.jl @@ -0,0 +1,40 @@ +using Yao + +using YaoExtensions + +# arXiv: 1704.05018, table S2 +function hydrogen_hamiltonian() + Z1 = put(2,1=>Z) + Z2 = put(2,2=>Z) + X1 = put(2,1=>X) + X2 = put(2,2=>X) + 0.011280*Z1*Z2 + 0.397936*Z1 + 0.397936*Z2 + 0.180931*X1*X2 +end + +function get_gradient(circ, hamiltonian) + ψ = zero_state(2) |> circ + dψ = copy(ψ) |> hamiltonian + backward!((copy(ψ), dψ), circ) + gradient(circ) +end + +using Flux: Optimise +function train!(circ, hamiltonian; optimizer, niter::Int=100) + circ = circ |> autodiff(:BP) + params = parameters(circ) + dispatch!(circ, :random) + for i=1:niter + Optimise.update!(optimizer, params, get_gradient(circ, hamiltonian)) + dispatch!(circ, params) + println("Energy = $(expect(hamiltonian, zero_state(nqubits(h)) |> circ))") + end + return expect(hamiltonian, zero_state(nqubits(h)) |> circ) +end + +h = hydrogen_hamiltonian() +c = variational_circuit(2) +emin_vqe = train!(c, h; optimizer=Optimise.ADAM(0.1)) + +using LinearAlgebra +emin = eigvals(Matrix(mat(h)))[1] +@assert isapprox(emin, emin, atol=1e-1) diff --git a/examples/VQE/README.md b/examples/VQE/README.md new file mode 100644 index 000000000..3ab48aece --- /dev/null +++ b/examples/VQE/README.md @@ -0,0 +1,14 @@ +# Variational Quantum Eigensolver + +## Start + +1. install [Julia 1.1](https://julialang.org/downloads/) +2. type `]` in julia REPL to enter `pkg` mode, and install packages with +```julia +pkg> add Yao KrylovKit +pkg> dev git@github.com:QuantumBFS/YaoExtensions.jl.git +``` +3. run H2 example with +```bash +julia examples/VQE/H2.jl +``` diff --git a/examples/VQE/VQE.jl b/examples/VQE/VQE.jl new file mode 100644 index 000000000..0d27a585a --- /dev/null +++ b/examples/VQE/VQE.jl @@ -0,0 +1,64 @@ +# # [Variational Quantum Eigensolver](@id VQE) + +# ## References +# * [A variational eigenvalue solver on a quantum processor](https://arxiv.org/abs/1304.3061) +# * [Variational Quantum Eigensolver with Fewer Qubits](https://arxiv.org/abs/1902.02663) + +# ## Define a hamiltonian + +# construct a 5-site heisenberg hamiltonian + +using Yao, YaoExtensions + +N = 5 +hami = heisenberg(N) + +# The ground state can be obtained by a sparse matrix ground state solver. +# The high performance `mat` function in `Yao.jl` makes computation time lower than `10s` +# to construct a `20` site Heisenberg hamltonian +using KrylovKit: eigsolve + +function ed_groundstate(h::AbstractBlock) + E, V = eigsolve(h |> mat, 1, :SR, ishermitian=true) + E[1], V[1] +end + +ed_groundstate(hami) + +# Here we use the `heisenberg` hamiltonian that defined in [`YaoExtensions.jl`](https://github.com/QuantumBFS/YaoExtensions.jl), +# for tutorial purpose, we pasted the code for construction here. +# ```julia +# function heisenberg(nbit::Int; periodic::Bool=true) +# sx = i->put(nbit, i=>X) +# sy = i->put(nbit, i=>Y) +# sz = i->put(nbit, i=>Z) +# map(1:(periodic ? nbit : nbit-1)) do i +# j=i%nbit+1 +# sx(i)*sx(j)+sy(i)*sy(j)+sz(i)*sz(j) +# end |> sum +# end +# ``` + +# ## Define an ansatz +# As an ansatz, we use the canonical circuit for demonstration [`variational_circuit`](@ref YaoExtensions.variational_circuit) +# defined in [`YaoExtensions.jl`](https://github.com/QuantumBFS/YaoExtensions.jl). +c = variational_circuit(N) +dispatch!(c, :random) + +# ## Run +# Use the [`Adam`](@ref) optimizer for parameter optimization, +# we provide a poorman's implementation in `QuAlgorithmZoo` +using QuAlgorithmZoo: Adam, update! + +optimizer = Adam(lr=0.01) +params = parameters(c) +niter = 100 + +for i = 1:niter + ## `expect'` gives the gradient of an observable. + grad_input, grad_params = expect'(hami, zero_state(N) => c) + + ## feed the gradients into the circuit. + dispatch!(c, update!(params, grad_params, optimizer)) + println("Step $i, Energy = $(expect(hami, zero_state(N) |> c))") +end diff --git a/examples/run_examples.jl b/examples/run_examples.jl new file mode 100644 index 000000000..222510dc2 --- /dev/null +++ b/examples/run_examples.jl @@ -0,0 +1,9 @@ +# run this file to make sure all examples work +# Note: running all these examples are too heavy for CI. +include("../examples/Grover/Grover.jl") +include("../examples/QAOA/QAOA.jl") +include("../examples/HHL/HHL.jl") +include("../examples/QCBM/QCBM.jl") +include("../examples/QuGAN/QuGAN.jl") +include("../examples/Shor/Shor.jl") +include("../examples/VQE/VQE.jl") diff --git a/src/CircuitBuild.jl b/src/CircuitBuild.jl deleted file mode 100644 index 86808c98a..000000000 --- a/src/CircuitBuild.jl +++ /dev/null @@ -1,140 +0,0 @@ -using StatsBase: sample - -export pair_ring, pair_square, cnot_entangler -export rotor, merged_rotor, rotorset -export random_diff_circuit -export rand_single_gate, rand_gate, rand_circuit - -################## Entangler ################### -""" - pair_ring(n::Int) -> Vector - -Pair ring. -""" -pair_ring(n::Int) = [i=>mod(i, n)+1 for i=1:n] - -""" - pair_square(m::Int, n::Int) -> Vector - -Pair square. -""" -function pair_square(m::Int, n::Int) - nsite = m*n - res = Vector{Pair{Int, Int}}(undef, 2*nsite) - li = LinearIndices((m, n)) - k = 1 - for i = 1:2:m, j=1:n - res[k] = li[i, j] => li[i%m+1, j] - k+=1 - end - for i = 2:2:m, j=1:n - res[k] = li[i, j] => li[i%m+1, j] - k+=1 - end - for i = 1:m, j=1:2:n - res[k] = li[i, j] => li[i, j%n+1] - k+=1 - end - for i = 1:m, j=2:2:n - res[k] = li[i, j] => li[i, j%n+1] - k+=1 - end - res -end - -""" - cnot_entangler([n::Int, ] pairs::Vector{Pair}) = ChainBlock - -Arbitrary rotation unit, support lazy construction. -""" -cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) -cnot_entangler(pairs) = n->cnot_entangler(n, pairs) - -###################### rotor and rotorset ##################### -""" - merged_rotor(noleading::Bool=false, notrailing::Bool=false) -> ChainBlock{1, ComplexF64} - -Single qubit arbitrary rotation unit, set parameters notrailing, noleading true to remove trailing and leading Z gates. - -!!! note - - Here, `merged` means `Rz(η)⋅Rx(θ)⋅Rz(ξ)` are multiplied first, this kind of operation if now allowed in differentiable - circuit with back-propagation (`:BP`) mode (just because we are lazy to implement it!). - But is a welcoming component in quantum differentiation. -""" -merged_rotor(noleading::Bool=false, notrailing::Bool=false) = noleading ? (notrailing ? Rx(0) : chain(Rx(0), Rz(0))) : (notrailing ? chain(Rz(0), Rx(0)) : chain(Rz(0), Rx(0), Rz(0))) - -""" - rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) -> ChainBlock{nbit, ComplexF64} - -Arbitrary rotation unit (put in `nbit` space), set parameters notrailing, noleading true to remove trailing and leading Z gates. -""" -function rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) - rt = chain(nbit, [put(nbit, ibit=>Rz(0.0)), put(nbit, ibit=>Rx(0.0)), put(nbit, ibit=>Rz(0.0))]) - noleading && popfirst!(rt) - notrailing && pop!(rt) - rt -end - -rotorset(::Val{:Merged}, nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [put(nbit, j=>merged_rotor(noleading, notrailing)) for j=1:nbit]) -rotorset(::Val{:Split}, nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [rotor(nbit, j, noleading, notrailing) for j=1:nbit]) -rotorset(mode::Symbol, nbit::Int, noleading::Bool=false, notrailing::Bool=false) = rotorset(Val(mode), nbit, noleading, notrailing) - -""" - random_diff_circuit(nbit, nlayer, pairs; mode=:Split, do_cache=false) - -A kind of widely used differentiable quantum circuit, angles in the circuit is randomely initialized. - -ref: - 1. Kandala, A., Mezzacapo, A., Temme, K., Takita, M., Chow, J. M., & Gambetta, J. M. (2017). - Hardware-efficient Quantum Optimizer for Small Molecules and Quantum Magnets. Nature Publishing Group, 549(7671), 242–246. - https://doi.org/10.1038/nature23879. -""" -function random_diff_circuit(nbit, nlayer, pairs; mode=:Split, do_cache=false) - circuit = chain(nbit) - - ent = cnot_entangler(pairs) - if do_cache - ent = ent |> cache - end - for i = 1:(nlayer + 1) - i!=1 && push!(circuit, ent) - push!(circuit, rotorset(mode, nbit, i==1, i==nlayer+1)) - end - circuit -end - -############### Completely random circuits (for testing and demo) ################ -randlocs(nbit::Int, mbit::Int) = sample(1:nbit, mbit, replace=false) -const SINGLE_GATES = [X, Y, Z, H, Rx, Ry, Rz, shift, phase] - -rand_single_gate(ngate::Int) = [rand_single_gates() for i=1:ngate] -function rand_single_gate() - gate = rand(SINGLE_GATES) - gate isa AbstractBlock ? gate : gate(rand()*2π) -end - -""" - rand_gate(nbit::Int, mbit::Int, [ngate::Int]) -> AbstractBlock - -random nbit gate. -""" -rand_gate(nbit::Int, mbit::Int) = rand_gate(nbit, Val(mbit)) -rand_gate(nbit::Int, mbit::Int, ngate::Int) = [rand_gate(nbit, mbit) for i=1:ngate] -rand_gate(nbit::Int, ::Val{1}) = put(nbit, rand(1:nbit)=>rand_single_gate()) -function rand_gate(nbit::Int, ::Val{M}) where M - locs = randlocs(nbit, M) - control(nbit, locs[1:M-1], last(locs)=>rand_single_gate()) -end - -function rand_circuit(nbit::Int; p1 = (nbit==1 ? 1.0 : 0.66), ngate=5*nbit) - c = chain(nbit) - for i=1:ngate - if rand() < p1 - push!(c, rand_gate(nbit, 1)) - else - push!(c, rand_gate(nbit, 2)) - end - end - c -end diff --git a/src/Diff.jl b/src/Diff.jl deleted file mode 100644 index f54a89569..000000000 --- a/src/Diff.jl +++ /dev/null @@ -1,220 +0,0 @@ -export Rotor, generator, Diff, backward!, gradient, CPhaseGate, DiffBlock -import Yao: expect, content, chcontent, mat, apply! -using StatsBase - -############# General Rotor ############ -const Rotor{N, T} = Union{RotationGate{N, T}, PutBlock{N, <:Any, <:RotationGate{<:Any, T}}} -const CphaseGate{N, T} = ControlBlock{N,<:ShiftGate{T},<:Any} -const DiffBlock{N, T} = Union{Rotor{N, T}, CphaseGate{N, T}} -""" - generator(rot::Rotor) -> AbstractBlock - -Return the generator of rotation block. -""" -generator(rot::RotationGate) = rot.block -generator(rot::PutBlock{N, C, GT}) where {N, C, GT<:RotationGate} = PutBlock{N}(generator(rot|>content), rot |> occupied_locs) -generator(c::CphaseGate{N}) where N = ControlBlock{N}(c.ctrl_locs, c.ctrl_config, Z, c.locs) - -#################### The Basic Diff ################# -""" - Diff{GT, N, T} <: TagBlock{GT, N} - Diff(block) -> Diff - -Mark a block as quantum differentiable. -""" -mutable struct Diff{GT, N, T} <: TagBlock{GT, N} - block::GT - grad::T - Diff(block::DiffBlock{N, T}) where {N, T} = new{typeof(block), N, T}(block, T(0)) -end -content(cb::Diff) = cb.block -chcontent(cb::Diff, blk::DiffBlock) = Diff(blk) - -istraitkeeper(::Diff) = Val(true) - -@forward Diff.block apply! -mat(::Type{T}, df::Diff) where T = mat(T, df.block) -Base.adjoint(df::Diff) = chcontent(df, content(df)') - -function YaoBlocks.print_annotation(io::IO, df::Diff) - printstyled(io, "[∂] "; bold=true, color=:yellow) -end - -#### interface ##### -export autodiff, numdiff, opdiff, StatFunctional, statdiff, as_weights - -as_weights(probs::AbstractVector{T}) where T = Weights(probs, T(1)) -""" - autodiff(mode::Symbol, block::AbstractBlock) -> AbstractBlock - autodiff(mode::Symbol) -> Function - -automatically mark differentiable items in a block tree as differentiable. -""" -function autodiff end -autodiff(mode::Symbol) = block->autodiff(mode, block) -autodiff(mode::Symbol, block::AbstractBlock) = autodiff(Val(mode), block) - -# for BP -autodiff(::Val{:BP}, block::DiffBlock) = Diff(block) -autodiff(::Val{:BP}, block::AbstractBlock) = block -# Sequential, Roller and ChainBlock can propagate. -function autodiff(mode::Val{:BP}, blk::Union{ChainBlock, Sequential}) - chsubblocks(blk, autodiff.(mode, subblocks(blk))) -end - -# for QC -autodiff(::Val{:QC}, block::Union{RotationGate, CphaseGate}) = Diff(block) -# escape control blocks. -autodiff(::Val{:QC}, block::ControlBlock) = block - -function autodiff(mode::Val{:QC}, blk::AbstractBlock) - blks = subblocks(blk) - isempty(blks) ? blk : chsubblocks(blk, autodiff.(mode, blks)) -end - -@inline function _perturb(func, gate::Diff{<:DiffBlock}, δ::Real) - dispatch!(-, gate, (δ,)) - r1 = func() - dispatch!(+, gate, (2δ,)) - r2 = func() - dispatch!(-, gate, (δ,)) - r1, r2 -end - -@inline function _perturb(func, gate::Diff{<:Rotor}, δ::Real) # for put - dispatch!(-, gate, (δ,)) - r1 = func() - dispatch!(+, gate, (2δ,)) - r2 = func() - dispatch!(-, gate, (δ,)) - r1, r2 -end - -""" - numdiff(loss, diffblock::Diff; δ::Real=1e-2) - -Numeric differentiation. -""" -@inline function numdiff(loss, diffblock::Diff; δ::Real=1e-2) - r1, r2 = _perturb(loss, diffblock, δ) - diffblock.grad = (r2 - r1)/2δ -end - -""" - opdiff(psifunc, diffblock::Diff, op::AbstractBlock) - -Operator differentiation. -""" -@inline function opdiff(psifunc, diffblock::Diff, op::AbstractBlock) - r1, r2 = _perturb(()->expect(op, psifunc()) |> real, diffblock, π/2) - diffblock.grad = (r2 - r1)/2 -end - -""" - StatFunctional{N, AT} - StatFunctional(array::AT<:Array) -> StatFunctional{N, <:Array} - StatFunctional{N}(func::AT<:Function) -> StatFunctional{N, <:Function} - -statistic functional, i.e. - * if `AT` is an array, A[i,j,k...], it is defined on finite Hilbert space, which is `∫A[i,j,k...]p[i]p[j]p[k]...` - * if `AT` is a function, F(xᵢ,xⱼ,xₖ...), this functional is `1/C(r,n)... ∑ᵢⱼₖ...F(xᵢ,xⱼ,xₖ...)`, see U-statistics for detail. - -References: - U-statistics, http://personal.psu.edu/drh20/asymp/fall2006/lectures/ANGELchpt10.pdf -""" -struct StatFunctional{N, AT} - data::AT - StatFunctional{N}(data::AT) where {N, AT<:Function} = new{N, AT}(data) - StatFunctional(data::AT) where {N, AT<:AbstractArray{<:Real, N}} = new{N, AT}(data) -end - -@forward StatFunctional.data Base.ndims -Base.parent(stat::StatFunctional) = stat.data - -expect(stat::StatFunctional{2, <:AbstractArray}, px::Weights, py::Weights=px) = px.values' * stat.data * py.values -expect(stat::StatFunctional{1, <:AbstractArray}, px::Weights) = stat.data' * px.values -function expect(stat::StatFunctional{2, <:Function}, xs::AbstractVector{T}) where T - N = length(xs) - res = zero(stat.data(xs[1], xs[1])) - for i = 2:N - for j = 1:i-1 - @inbounds res += stat.data(xs[i], xs[j]) - end - end - res/binomial(N,2) -end -function expect(stat::StatFunctional{2, <:Function}, xs::AbstractVector, ys::AbstractVector) - M = length(xs) - N = length(ys) - ci = CartesianIndices((M, N)) - @inbounds mapreduce(ind->stat.data(xs[ind[1]], ys[ind[2]]), +, ci)/M/N -end -expect(stat::StatFunctional{1, <:Function}, xs::AbstractVector) = mean(stat.data.(xs)) -Base.ndims(stat::StatFunctional{N}) where N = N - -""" - statdiff(probfunc, diffblock::Diff, stat::StatFunctional{<:Any, <:AbstractArray}; initial::AbstractVector=probfunc()) - statdiff(samplefunc, diffblock::Diff, stat::StatFunctional{<:Any, <:Function}; initial::AbstractVector=samplefunc()) - -Differentiation for statistic functionals. -""" -@inline function statdiff(probfunc, diffblock::Diff, stat::StatFunctional{2}; initial::AbstractVector=probfunc()) - r1, r2 = _perturb(()->expect(stat, probfunc(), initial), diffblock, π/2) - diffblock.grad = (r2 - r1)*ndims(stat)/2 -end -@inline function statdiff(probfunc, diffblock::Diff, stat::StatFunctional{1}) - r1, r2 = _perturb(()->expect(stat, probfunc()), diffblock, π/2) - diffblock.grad = (r2 - r1)*ndims(stat)/2 -end - -""" - backward!(state, circuit::AbstractBlock) -> AbstractRegister - -back propagate and calculate the gradient ∂f/∂θ = 2*Re(∂f/∂ψ*⋅∂ψ*/∂θ), given ∂f/∂ψ*. -`state` is a pair of output_register => the corresponding adjoint. - -Note: -Here, the input circuit should be a matrix block, otherwise the back propagate may not apply (like Measure operations). -""" -function backward!(state, block::AbstractBlock) - out, outδ = state - adjblock = block' - backward_params!((out, outδ), block) - in = apply!(out, adjblock) - inδ = apply!(outδ, adjblock) - return (in, inδ) -end - -function backward!(state, circuit::Union{ChainBlock, Concentrator}) - for blk in Base.Iterators.reverse(subblocks(circuit)) - state = backward!(state, blk) - end - return state -end - -backward!(state, block::Measure) = throw(MethodError(backward!, (state, block))) - -backward_params!(state, block::AbstractBlock) = nothing -function backward_params!(state, block::Diff{<:DiffBlock}) - in, outδ = state - Σ = generator(content(block)) - block.grad = -statevec(in |> Σ)' * statevec(outδ) |> imag - in |> Σ - nothing -end - -""" - gradient(circuit::AbstractBlock, mode::Symbol=:ANY) -> Vector - -collect all gradients in a circuit, mode can be :BP/:QC/:ANY, they will collect `grad` from Diff respectively. -""" -gradient(circuit::AbstractBlock) = gradient!(circuit, parameters_eltype(circuit)[]) - -function gradient!(circuit::AbstractBlock, grad) - for block in subblocks(circuit) - gradient!(block, grad) - end - grad -end - -gradient!(circuit::Diff, grad) = append!(grad, circuit.grad) diff --git a/src/Grover.jl b/src/Grover.jl deleted file mode 100644 index a17d50cb7..000000000 --- a/src/Grover.jl +++ /dev/null @@ -1,86 +0,0 @@ -export num_grover_step, inference_oracle, GroverIter, groverblock, groveriter, prob_match_oracle - -""" - inference_oracle([nbit::Int,] locs::Vector{Int}) -> ControlBlock - -A simple inference oracle, e.g. inference([-1, -8, 5]) is a control block that flip the bit if values of bits on position [1, 8, 5] match [0, 0, 1]. -""" -inference_oracle(locs::Vector{Int}) = control(locs[1:end-1], abs(locs[end]) => (locs[end]>0 ? Z : chain(phase(π), Z))) -inference_oracle(nbit::Int, locs::Vector{Int}) = inference_oracle(locs)(nbit) - -""" - target_space(oracle) -> Vector{Bool} - -Return a mask, that disired subspace of an oracle are masked true. -""" -function target_space(nbit::Int, oracle) - r = ArrayReg(ones(ComplexF64, 1< oracle - real(statevec(r)) .< 0 -end - -prob_inspace(psi::ArrayReg, ts) = norm(statevec(psi)[ts])^2 - -""" - prob_match_oracle(psi, oracle) -> Float64 - -Return the probability that `psi` matches oracle. -""" -prob_match_oracle(psi::ArrayReg, oracle) = prob_inspace(psi, target_space(nqubits(psi), oracle)) - -""" - num_grover_step(psi::ArrayReg, oracle) -> Int - -Return number of grover steps needed to match the oracle. -""" -num_grover_step(psi::ArrayReg, oracle) = _num_grover_step(prob_match_oracle(psi, oracle)) - -_num_grover_step(prob::Real) = Int(round(pi/4/sqrt(prob)))-1 - -""" - GroverIter{N} - - GroverIter(oracle, ref::ReflectBlock{N}, psi::ArrayReg, niter::Int) - -an iterator that perform Grover operations step by step. -An Grover operation consists of applying oracle and Reflection. -""" -struct GroverIter{N} - psi::ArrayReg - oracle - ref::ReflectBlock{N} - niter::Int -end - -groveriter(psi::ArrayReg, oracle, ref::ReflectBlock{N}, niter::Int) where {N} = GroverIter{N}(psi, oracle, ref, niter) -groveriter(psi::ArrayReg, oracle, niter::Int) = groveriter(psi, oracle, ReflectBlock(psi |> copy), niter) -groveriter(psi::ArrayReg, oracle) = groveriter(psi, oracle, ReflectBlock(psi |> copy), num_grover_step(psi, oracle)) - -function Base.iterate(it::GroverIter, st=1) - if it.niter + 1 == st - nothing - else - apply!(it.psi, it.oracle) - apply!(it.psi, it.ref), st+1 - end -end - -Base.length(it::GroverIter) = it.niter - -""" - groverblock(oracle, ref::ReflectBlock{N}, niter::Int=-1) - groverblock(oracle, psi::ArrayReg, niter::Int=-1) - -Return a ChainBlock/Sequential as Grover Iteration, the default `niter` will stop at the first optimal step. -""" -function groverblock(oracle::AbstractBlock{N}, ref::ReflectBlock{N}, niter::Int=-1) where {N} - if niter == -1 niter = num_grover_step(ref.psi, oracle) end - chain(N, chain(oracle, ref) for i = 1:niter) -end - -function groverblock(oracle, ref::ReflectBlock{N}, niter::Int=-1) where {N} - if niter == -1 niter = num_grover_step(ref.psi, oracle) end - sequence(sequence(oracle, ref) for i = 1:niter) -end - -groverblock(oracle, psi::ArrayReg, niter::Int=-1) = groverblock(oracle, ReflectBlock(psi |> copy), niter) diff --git a/src/Miscellaneous.jl b/src/Miscellaneous.jl deleted file mode 100644 index 570a8c636..000000000 --- a/src/Miscellaneous.jl +++ /dev/null @@ -1,27 +0,0 @@ -export inverselines, singlet_block - -""" - inverselines(nbit::Int; n_reg::Int=nbit) -> ChainBlock - -inverse first `n_reg` lines - -TODO: -deprecate this function, it is not used. -""" -function inverselines(nbit::Int; n_reg::Int=nbit) - c = chain(nbit) - for i = 1:(n_reg ÷ 2) - push!(c, swap(i,(n_reg-i+1))) - end - c -end - -function singlet_block(nbit::Int, i::Int, j::Int) - unit = chain(nbit) - push!(unit, put(nbit, i=>chain(X, H))) - push!(unit, control(nbit, -i, j=>X)) -end - -singlet_block() = singlet_block(2,1,2) - -Yao.mat(ρ::DensityMatrix{1}) = dropdims(state(ρ), dims=3) diff --git a/src/Mod.jl b/src/Mod.jl deleted file mode 100644 index cdd07df12..000000000 --- a/src/Mod.jl +++ /dev/null @@ -1,105 +0,0 @@ -# TODO -# compile Mod and KMod to elementary gates. - -export Mod, KMod - -""" - Mod{N} <: PrimitiveBlock{N} - -calculates `mod(a*x, L)`, notice `gcd(a, L)` should be 1. -""" -struct Mod{N} <: PrimitiveBlock{N} - a::Int - L::Int - function Mod{N}(a, L) where N - @assert gcd(a, L) == 1 && L<=1<= m.L ? i+1 : mod(i*m.a, m.L)+1 - for j in 1:B - @inbounds nstate[_i,j] = reg.state[i+1,j] - end - end - reg.state = nstate - reg -end - -function Yao.mat(::Type{T}, m::Mod{N}) where {T, N} - perm = Vector{Int}(undef, 1<= m.L ? i+1 : mod(i*m.a, m.L)+1] = i+1 - end - PermMatrix(perm, ones(T, 1< (b&mask, b>>k) -end - -function Yao.apply!(reg::ArrayReg{B}, m::KMod{N, K}) where {B, N, K} - YaoBlocks._check_size(reg, m) - nstate = zero(reg.state) - - reader = bint2_reader(Int, K) - for b in basis(reg) - k, i = reader(b) - _i = i >= m.L ? i : mod(i*powermod(m.a, k, m.L), m.L) - _b = k + _i<= m.L ? i : mod(i*powermod(m.a, k, m.L), m.L) - _b = k + _i< AbstractBlock - -circuit to optimize -""" -function circuit end - -""" - loss(qop::QCOptProblem) -> Real - -Return the loss. -""" -function loss end - -##################################################### - -""" - gradient(qop::QCOptProblem) -> Vector - -the gradients with respect to `diff_blocks`. -""" -function gradient end - -""" - diff_blocks(qop::QCOptProblem) -> iterable - -collection of all differentiable units. -""" -diff_blocks(qop::QCOptProblem) = collect_blocks(Diff, qop |> circuit) - -""" - num_gradient(qop::QCOptProblem) -> Vector - -obtain the gradient numerically -""" -num_gradient(qop::QCOptProblem) = numdiff.(()->loss(qop), qop |> diff_blocks) - -################# Optimization ################### -""" - QCOptGo!{QT} - -quantum circuit optimization problem optimizer. -""" -abstract type QCOptGo!{QT} end - -include("QuGAN.jl") -include("QCBM.jl") diff --git a/src/QFT.jl b/src/QFT.jl deleted file mode 100644 index 47cd06185..000000000 --- a/src/QFT.jl +++ /dev/null @@ -1,54 +0,0 @@ -@static if VERSION >= v"0.7-" - using FFTW -end - -export QFTCircuit, QFTBlock, invorder_firstdim - -CRk(i::Int, j::Int, k::Int) = control([i, ], j=>shift(2π/(1<H) : CRk(j, i, j-i+1) for j = i:n) -QFTCircuit(n::Int) = chain(n, CRot(n, i) for i = 1:n) - -struct QFTBlock{N} <: PrimitiveBlock{N} end -mat(::Type{T}, q::QFTBlock{N}) where {T, N} = T.(applymatrix(q)) - -apply!(reg::DefaultRegister{B}, ::QFTBlock) where B = (reg.state = ifft!(invorder_firstdim(reg |> state), 1)*sqrt(1<state, 1)/sqrt(1< log2i - n_2 = n ÷ 2 - mask = [bmask(i, n-i+1) for i in 1:n_2] - @simd for b in basis(n) - @inbounds w[breflect(b, mask; nbits=n)+1,:] = v[b+1,:] - end - w -end - -function invorder_firstdim(v::Vector) - n = length(v) |> log2i - n_2 = n ÷ 2 - w = similar(v) - #mask = SVector{n_2, Int}([bmask(i, n-i+1)::Int for i in 1:n_2]) - mask = [bmask(i, n-i+1)::Int for i in 1:n_2] - @simd for b in basis(n) - @inbounds w[breflect(b, mask; nbits=n)+1] = v[b+1] - end - w -end diff --git a/src/QSVD.jl b/src/QSVD.jl index 1a8d82871..29705b3ab 100644 --- a/src/QSVD.jl +++ b/src/QSVD.jl @@ -54,8 +54,8 @@ kwargs includes * `optimizer`, default is `Adam(lr=0.1)`. """ function QuantumSVD(M::AbstractMatrix; Nc::Int=log2i(min(size(M)...)), - circuit_a=random_diff_circuit(log2i(size(M, 1)), 5, pair_ring(log2i(size(M, 1)))), - circuit_b=random_diff_circuit(log2i(size(M, 2)), 5, pair_ring(log2i(size(M, 2)))), + circuit_a=variational_circuit(log2i(size(M, 1)), 5, pair_ring(log2i(size(M, 1)))), + circuit_b=variational_circuit(log2i(size(M, 2)), 5, pair_ring(log2i(size(M, 2)))), maxiter=200, optimizer=Adam(lr=0.1)) dispatch!(circuit_a, :random) diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index 5f0458930..b434d9ebd 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -1,38 +1,16 @@ module QuAlgorithmZoo -using LuxurySparse, LinearAlgebra -using MacroTools: @forward -using Yao, YaoBlocks.ConstGate, BitBasis -using YaoArrayRegister: u1rows! -import Yao: mat, dispatch!, niparams, getiparams, setiparams!, cache_key, print_block, apply!, PrimitiveBlock, ishermitian, isunitary, isreflexive -import YaoBlocks: render_params -import Base: ==, copy, hash +using LinearAlgebra +using Yao, BitBasis +using YaoExtensions -export openbox - -""" - openbox(block::AbstractBlock) -> AbstractBlock - -For a black box, like QFTBlock, you can get its white box (loyal simulation) using this function. -""" -function openbox end - -include("Miscellaneous.jl") -include("sequence.jl") -include("Diff.jl") include("Adam.jl") -include("QFT.jl") -include("CircuitBuild.jl") -include("QCOptProblem.jl") -include("RotBasis.jl") -include("Grover.jl") include("PhaseEstimation.jl") -include("HHL.jl") include("hamiltonian_solvers.jl") include("HadamardTest.jl") -include("lin_diffEq_HHL.jl") include("QSVD.jl") -include("shor.jl") +include("number_theory.jl") +@deprecate random_diff_circuit variational_circuit end # module diff --git a/src/RotBasis.jl b/src/RotBasis.jl deleted file mode 100644 index 2c9cdb18e..000000000 --- a/src/RotBasis.jl +++ /dev/null @@ -1,76 +0,0 @@ -export RotBasis, randpolar, polar2u, u2polar, rot_basis - -""" - RotBasis{T} <: PrimitiveBlock{1, Complex{T}} - -A special rotation block that transform basis to angle θ and ϕ in bloch sphere. -""" -mutable struct RotBasis{T} <: PrimitiveBlock{1} - theta::T - phi::T -end - -_make_rot_mat(I, block, theta) = I * cos(theta / 2) - im * sin(theta / 2) * block -# chain -> * -# mat(rb::RotBasis{T}) where T = mat(Ry(-rb.theta))*mat(Rz(-rb.phi)) -function mat(::Type{TM}, x::RotBasis{T}) where {TM, T} - R1 = _make_rot_mat(IMatrix{2, Complex{T}}(), mat(TM, Z), -x.phi) - R2 = _make_rot_mat(IMatrix{2, Complex{T}}(), mat(TM, Y), -x.theta) - R2 * R1 -end - -==(rb1::RotBasis, rb2::RotBasis) = rb1.theta == rb2.theta && rb1.phi == rb2.phi - -copy(block::RotBasis{T}) where T = RotBasis{T}(block.theta, block.phi) -dispatch!(block::RotBasis, params::Vector) = ((block.theta, block.phi) = params; block) - -getiparams(rb::RotBasis) = (rb.theta, rb.phi) -function setiparams!(rb::RotBasis, θ::Real, ϕ::Real) - rb.theta, rb.phi = θ, ϕ - rb -end -niparams(::Type{<:RotBasis}) = 2 -niparams(::RotBasis) = 2 -render_params(r::RotBasis, ::Val{:random}) = rand()*π, rand()*2π - -function print_block(io::IO, R::RotBasis) - print(io, "RotBasis($(R.theta), $(R.phi))") -end - -function hash(gate::RotBasis, h::UInt) - hash(hash(gate.theta, gate.phi, objectid(gate)), h) -end - -cache_key(gate::RotBasis) = (gate.theta, gate.phi) - -rot_basis(num_bit::Int) = dispatch!(chain(num_bit, put(i=>RotBasis(0.0, 0.0)) for i=1:num_bit), randpolar(num_bit) |> vec) - -""" - u2polar(vec::Array) -> Array - -transform su(2) state vector to polar angle, apply to the first dimension of size 2. -""" -function u2polar(vec::Vector) - ratio = vec[2]/vec[1] - [atan(abs(ratio))*2, angle(ratio)] -end - -""" - polar2u(vec::Array) -> Array - -transform polar angle to su(2) state vector, apply to the first dimension of size 2. -""" -function polar2u(polar::Vector) - theta, phi = polar - [cos(theta/2)*exp(-im*phi/2), sin(theta/2)*exp(im*phi/2)] -end - -u2polar(arr::Array) = mapslices(u2polar, arr, dims=[1]) -polar2u(arr::Array) = mapslices(polar2u, arr, dims=[1]) - -""" - randpolar(params::Int...) -> Array - -random polar basis, number of basis -""" -randpolar(params::Int...) = rand(2, params...)*pi diff --git a/src/hamiltonian_solvers.jl b/src/hamiltonian_solvers.jl index d34469287..7b5b71dfb 100644 --- a/src/hamiltonian_solvers.jl +++ b/src/hamiltonian_solvers.jl @@ -1,16 +1,4 @@ -export heisenberg, iter_groundstate!, itime_groundstate!, vqe_solve! - -""" - heisenberg(nbit::Int; periodic::Bool=true) - -heisenberg hamiltonian, for its ground state, refer `PRB 48, 6141`. -""" -function heisenberg(nbit::Int; periodic::Bool=true) - sx = i->put(nbit, i=>X) - sy = i->put(nbit, i=>Y) - sz = i->put(nbit, i=>Z) - mapreduce(i->(j=i%nbit+1; sx(i)*sx(j)+sy(i)*sy(j)+sz(i)*sz(j)), +, 1:(periodic ? nbit : nbit-1)) -end +export iter_groundstate!, itime_groundstate!, vqe_solve! """ iter_groundstate!({reg::AbstractRegister}, h::AbstractBlock; niter::Int=100) -> AbstractRegister diff --git a/src/sequence.jl b/src/sequence.jl deleted file mode 100644 index a19822a52..000000000 --- a/src/sequence.jl +++ /dev/null @@ -1,39 +0,0 @@ -export Sequence -import YaoBlocks: subblocks, chsubblocks, apply! -using YaoBlocks: _check_size - -struct Sequence <: CompositeBlock{Any} - blocks::Vector -end - -Sequence(args...) = Sequence(collect(AbstractBlock, args)) - -subblocks(seq::Sequence) = filter(x->x isa AbstractBlock, seq.blocks) -chsubblocks(pb::Sequence, blocks::Vector) = Sequence(blocks) - -function apply!(reg::ArrayReg, seq::Sequence) - for x in seq.blocks - reg |> x - end - reg -end - -for PROP in [:lastindex, :firstindex, :getindex, :length, :eltype, :iterate, :eachindex, :popfirst!, :pop!] - @eval Base.$PROP(c::Sequence, args...; kwargs...) = $PROP(c.blocks, args...; kwargs...) -end - -function Base.:(==)(lhs::Sequence, rhs::Sequence) - (length(lhs.blocks) == length(rhs.blocks)) && all(lhs.blocks .== rhs.blocks) -end - -Base.copy(c::Sequence) = Sequence(copy(c.blocks)) -Base.similar(c::Sequence) = Sequence(empty!(similar(c.blocks))) -Base.getindex(c::Sequence, index::Union{UnitRange, Vector}) = Sequence(getindex(c.blocks, index)) - -Base.setindex!(c::Sequence, val::AbstractBlock{N}, index::Integer) where N = (setindex!(c.blocks, val, index); c) -Base.insert!(c::Sequence, index::Integer, val::AbstractBlock{N}) where N = (insert!(c.blocks, index, val); c) -Base.push!(c::Sequence, m) where N = (push!(c.blocks, m); c) -Base.append!(c::Sequence, list::Vector) where N = (append!(c.blocks, list); c) -Base.append!(c1::Sequence, c2::Sequence) where N = (append!(c1.blocks, c2.blocks); c1) -Base.prepend!(c1::Sequence, list::Vector{<:AbstractBlock{N}}) where N = (prepend!(c1.blocks, list); c1) -Base.prepend!(c1::Sequence, c2::Sequence) where N = (prepend!(c1.blocks, c2.blocks); c1) diff --git a/src/shor.jl b/src/shor.jl deleted file mode 100644 index c64fe8974..000000000 --- a/src/shor.jl +++ /dev/null @@ -1,80 +0,0 @@ -include("number_theory.jl") -include("Mod.jl") - -export shor, order_finding_circuit, get_order - -""" - shor(L::Int, ver=Val(:quantum); maxtry=100) - -factorize an integer `L`, `ver` can be either `Val(:quantum)` or `Val(:classical)`. -""" -function shor(L::Int, ver=Val(:quantum); maxtry=100) - L%2 == 0 && return 2 - - # find solutions like `a^b` - res = NumberTheory.factor_a_power_b(L) - res !== nothing && return res[1] - - for i in 1:maxtry - x = NumberTheory.rand_primeto(L) - r = get_order(ver, x, L) - # if `x^(r/2)` is non-trivil, go on. - # Here, we do not condsier `powermod(x, r÷2, L) == 1`, since in this case the order should be `r/2` - if r%2 == 0 && powermod(x, r÷2, L) != L-1 - f1, f2 = gcd(powermod(x, r÷2, L)-1, L), gcd(powermod(x, r÷2, L)+1, L) - if f1!=1 - return f1 - elseif f2!=1 - return f2 - else - error("Algorithm Fail!") - end - end - end -end - -"""estimate the required size of the output register.""" -estimate_ncbit(nbit::Int, ϵ::Real) = 2*nbit + 1 + ceil(Int,log2(2+1/2ϵ)) - -""" - order_finding_circuit(x::Int, L::Int; nbit::Int=bit_length(L-1), ncbit::Int=estimate_ncbit(nbit, 0.25)) -> AbstractBlock - -Returns the circuit for finding the order of `x` to `L`, -feeding input `|1>⊗|0>` will get the resulting quantum register with the desired "phase" information. -""" -function order_finding_circuit(x::Int, L::Int; nbit::Int=bit_length(L-1), ncbit::Int=estimate_ncbit(nbit, 0.25)) - N = nbit+ncbit - chain(N, repeat(N, H, 1:ncbit),KMod{N, ncbit}(x, L), concentrate(N, QFTBlock{ncbit}()', 1:ncbit)) -end - -""" - find_order([ver], x::Int, N::Int; nshots=10) -> Union{Int, Nothing} - -Get the order of `x`, `ver` can be `Val(:classical)` (default) or `Val(:quantum)`, -when using the quantum approach, we can set key word arguments `nshot`, -`nbit` (size of input data register) and `ncbit` (size of control register, or output register). -""" -get_order(::Val{:classical}, x::Int, N::Int; kwargs...) = NumberTheory.find_order(x, N) -function get_order(::Val{:quantum}, x::Int, N::Int; nshots=10, kwargs...) - c = order_finding_circuit(x, N; kwargs...) - n = nqubits_data(c[2]) - ncbit = nqubits_control(c[2]) - reg = join(product_state(n, 1), zero_state(ncbit)) - - res = measure(copy(reg) |> c; nshots=nshots) - reader = bint2_reader(Int, ncbit) - for r in res - k, i = reader(r) - # get s/r - ϕ = bfloat(k) # - ϕ == 0 && continue - - order = NumberTheory.order_from_float(ϕ, x, N) - if order === nothing - continue - else - return order - end - end - return nothing -end diff --git a/test/CircuitBuild.jl b/test/CircuitBuild.jl deleted file mode 100644 index 4e078e2a4..000000000 --- a/test/CircuitBuild.jl +++ /dev/null @@ -1,65 +0,0 @@ -using Test -using Yao, QuAlgorithmZoo - -@testset "pairs geometries" begin - @test pair_ring(3) == [1=>2,2=>3,3=>1] - ps = pair_square(2, 2) - @test length(ps) == 8 - for item in [1=>2, 3=>4, 2=>1, 4=>3, 1=>3, 2=>4, 3=>1, 4=>2] - @test item in ps - end - @test cnot_entangler(4, ps) isa ChainBlock - @test cnot_entangler(4, ps) |> length == 8 -end - -@testset "random circuit" begin - c = rand_circuit(1) - @test c isa ChainBlock - @test length(c) == 5 - c = rand_circuit(9) - @test c isa ChainBlock - @test length(c) == 45 -end - -@testset "rotter, collect_blocks, num_gradient, opgrad" begin - @test merged_rotor(true, true) == Rx(0) - @test merged_rotor(false, false) == merged_rotor() == chain(Rz(0), Rx(0), Rz(0)) - @test merged_rotor(false, true) == chain(Rz(0), Rx(0)) - @test merged_rotor(true, false) == chain(Rx(0), Rz(0)) - @test collect_blocks(RotationGate, rotorset(:Merged, 5, true, false)) |> length == 10 - - @test rotor(5, 2, true, true) isa ChainBlock - @test rotor(5, 2, true, true) |> length == 1 - @test rotor(5, 2, true, true) |> nqubits == 5 - @test collect_blocks(PutBlock{<:Any, <:Any, <:RotationGate}, rotorset(:Split, 5, true, false)) |> length == 10 -end - -@testset "random diff circuit" begin - c = random_diff_circuit(4, 3, [1=>3, 2=>4, 2=>3, 4=>1]) - rots = collect_blocks(RotationGate, c) - @test length(rots) == nparameters(c) == 40 - @test dispatch!(+, c, ones(40)*0.1) |> parameters == ones(40)*0.1 - @test dispatch!(+, c, :random) |> parameters != ones(40)*0.1 - - nbit = 4 - c = random_diff_circuit(nbit, 1, pair_ring(nbit), mode=:Split) |> autodiff(:BP) - reg = rand_state(4) - dispatch!(c, randn(nparameters(c))) - - dbs = collect_blocks(Diff, c) - op = kron(4, 1=>Z, 2=>X) - loss1z() = expect(op, copy(reg) |> c) # return loss please - - # back propagation - ψ = copy(reg) |> c - δ = copy(ψ) |> op - backward!((ψ, δ), c) - bd = gradient(c) - - # get num gradient - nd = numdiff.(loss1z, dbs) - ed = opdiff.(()->copy(reg)|>c, dbs, Ref(op)) - - @test isapprox.(nd, ed, atol=1e-4) |> all - @test isapprox.(nd, bd, atol=1e-4) |> all -end diff --git a/test/Diff.jl b/test/Diff.jl deleted file mode 100644 index 164fcbbe2..000000000 --- a/test/Diff.jl +++ /dev/null @@ -1,173 +0,0 @@ -using Yao, QuAlgorithmZoo -using YaoBlocks.ConstGate -using LinearAlgebra, Test, Random - -@testset "BP diff" begin - reg = rand_state(4) - block = put(4, 2=>rot(X, 0.3)) - df = Diff(block) - @test df.grad == 0 - @test nqubits(df) == 4 - - df2 = Diff(rot(CNOT, 0.3)) - @test df2.grad == 0 - @test nqubits(df2) == 2 - @test_throws MethodError backward!((reg, reg), Measure(4)) -end - -@testset "Qi diff" begin - reg = rand_state(4) - df2 = Diff(rot(CNOT, 0.3)) - @test df2.grad == 0 - @test nqubits(df2) == 2 - - @test df2' isa Diff - @test mat(df2) == mat(df2')' -end - -""" - loss_expect!(circuit::AbstractBlock, op::AbstractBlock) -> Function - -Return function "loss!(ψ, θ) -> Vector" -""" -function loss_expect!(circuit::AbstractBlock, op::AbstractBlock) - N = nqubits(circuit) - function loss!(ψ::AbstractRegister, θ::Vector) - params = parameters(circuit) - dispatch!(circuit, θ) - ψ |> circuit - popdispatch!(circuit, params) - expect(op, ψ) - end -end - -""" - loss_Z1!(circuit::AbstractBlock; ibit::Int=1) -> Function - -Return the loss function f = (means measuring the ibit-th bit in computation basis). -""" -loss_Z1!(circuit::AbstractBlock; ibit::Int=1) = loss_expect!(circuit, put(nqubits(circuit), ibit=>Z)) - -_cnot_entangler(n::Int, pairs) = chain(n, control(n, [ctrl], target=>X) for (ctrl, target) in pairs) - -function _rotor(nbit::Int, ibit::Int, noleading::Bool=false, notrailing::Bool=false) - rt = chain(nbit, [put(nbit, ibit=>Rz(0.0)), put(nbit, ibit=>Rx(0.0)), put(nbit, ibit=>Rz(0.0))]) - noleading && popfirst!(rt) - notrailing && pop!(rt) - rt -end - -rset(nbit::Int, noleading::Bool=false, notrailing::Bool=false) = chain(nbit, [_rotor(nbit, j, noleading, notrailing) for j=1:nbit]) - -function ibm_diff_circuit(nbit, nlayer, pairs) - circuit = chain(nbit) - - ent = _cnot_entangler(nbit, pairs) - for i = 1:(nlayer + 1) - i!=1 && push!(circuit, ent) - push!(circuit, rset(nbit, i==1, i==nlayer+1)) - end - circuit -end - -@testset "BP diff" begin - c = put(4, 3=>Rx(0.5)) |> autodiff(:BP) - cad = c' - @test mat(cad) == mat(c)' - - circuit = chain(4, repeat(4, H, 1:4), put(4, 3=>Rz(0.5)) |> autodiff(:BP), control(4, 2, 1=>shift(0.4)) |> autodiff(:BP), control(2, 1=>X), put(4, 4=>Ry(0.2)) |> autodiff(:BP)) - op = put(4, 3=>Y) - θ = [0.9, 0.2, 0.3] - dispatch!(circuit, θ) - loss! = loss_expect!(circuit, op) - ψ0 = rand_state(4) - ψ = copy(ψ0) |> circuit - - # get gradient - δ = copy(ψ) |> op - in, inδ = backward!((ψ, δ), circuit) - @test in ≈ ψ0 - g1 = gradient(circuit) - - g2 = zero(θ) - η = 1e-5 - for i in 1:length(θ) - θ1 = copy(θ) - θ2 = copy(θ) - θ1[i] -= 0.5η - θ2[i] += 0.5η - g2[i] = (loss!(copy(ψ0), θ2) - loss!(copy(ψ0), θ1))/η |> real - end - g3 = opdiff.(() -> copy(ψ0) |> circuit, collect_blocks(Diff, circuit), Ref(op)) - @test isapprox.(g1, g2, atol=1e-5) |> all - @test isapprox.(g2, g3, atol=1e-5) |> all -end - -@testset "constructor" begin - @test generator(put(4, 1=>Rx(0.1))) == put(4, 1=>X) - @test generator(Rx(0.1)) == X - circuit = chain(put(4, 1=>Rx(0.1)), control(4, 2, 1=>Ry(0.3))) - c2 = circuit |> autodiff(:BP) - @test c2[1] isa Diff - @test !(c2[2] isa Diff) -end - -@testset "numdiff & opdiff" begin - @test collect_blocks(XGate, chain([X, Y, Z])) == [X] - - c = chain(put(4, 1=>Rx(0.5))) |> autodiff(:QC) - nd = numdiff(c[1].content) do - expect(put(4, 1=>Z), zero_state(4) |> c) |> real # return loss please - end - - ed = opdiff(c[1].content, put(4, 1=>Z)) do - zero_state(4) |> c # a function get output - end - @test isapprox(nd, ed, atol=1e-4) - - reg = rand_state(4) - c = chain(put(4, 1=>Rx(0.5)), control(4, 1, 2=>Ry(0.5)), control(4, 1, 2=>shift(0.3)), kron(4, 2=>Rz(0.3), 3=>Rx(0.7))) |> autodiff(:QC) - dbs = collect_blocks(Diff, c) - loss1z() = expect(kron(4, 1=>Z, 2=>X), copy(reg) |> c) |> real # return loss please - nd = numdiff.(loss1z, dbs) - ed = opdiff.(()->copy(reg) |> c, dbs, Ref(kron(4, 1=>Z, 2=>X))) - gd = gradient(c) - @test gradient(c) == gd - @test isapprox(nd, ed, atol=1e-4) - @test ed == gd -end - -@testset "stat" begin - nbit = 3 - f(x::Number, y::Number) = Float64(abs(x-y) < 1.5) - x = 0:1<2, 2=>3, 3=>1] - c = ibm_diff_circuit(nbit, 2, prs) |> autodiff(:QC) - dispatch!(c, :random) - dbs = collect_blocks(Diff,c) - - p0 = zero_state(nbit) |> c |> probs - sample0 = measure(zero_state(nbit) |> c; nshots=5000) - loss0 = expect(V, p0 |> as_weights) - gradsn = numdiff.(()->expect(V, zero_state(nbit) |> c |> probs |> as_weights), dbs) - gradse = statdiff.(()->zero_state(nbit) |> c |> probs |> as_weights, dbs, Ref(V), initial=p0 |> as_weights) - gradsf = statdiff.(()->measure(zero_state(nbit) |> c; nshots=5000), dbs, Ref(VF), initial=sample0) - @test all(isapprox.(gradse, gradsn, atol=1e-4)) - @test norm(gradsf-gradse)/norm(gradsf) <= 0.2 - - # 1D - h = randn(1< autodiff(:QC) - dispatch!(c, :random) - dbs = collect_blocks(Diff, c) - - p0 = zero_state(nbit) |> c |> probs |> as_weights - loss0 = expect(V, p0 |> as_weights) - gradsn = numdiff.(()->expect(V, zero_state(nbit) |> c |> probs |> as_weights), dbs) - gradse = statdiff.(()->zero_state(nbit) |> c |> probs |> as_weights, dbs, Ref(V)) - @test all(isapprox.(gradse, gradsn, atol=1e-4)) -end diff --git a/test/HadamardTest.jl b/test/HadamardTest.jl index edb4b02fa..0a5a43dd5 100644 --- a/test/HadamardTest.jl +++ b/test/HadamardTest.jl @@ -14,15 +14,15 @@ single_swap_test(reg::AbstractRegister, ϕ::Real) = hadamard_test(SWAP, reg, ϕ) rho2 = reg2 |> ρ reg3 = rand_state(3) |> focus!(1,2) rho3 = reg3 |> ρ - desired = tr(mat(rho1)*mat(rho2)) + desired = tr(Matrix(rho1)*Matrix(rho2)) c = swap_test_circuit(2, 2, 0) res = expect(put(5, 1=>Z), join(join(reg2, reg1), zero_state(1)) |> c) |> tr @test desired ≈ res - desired = tr(mat(rho1)*mat(rho2)*mat(rho3)) |> real + desired = tr(Matrix(rho1)*Matrix(rho2)*Matrix(rho3)) |> real c = swap_test_circuit(2, 3, 0) res = expect(put(7, 1=>Z), reduce(⊗, [reg3, reg2, reg1, zero_state(1)]) |> c) |> tr |> real @test desired ≈ res - desired = tr(mat(rho1)*mat(rho2)*mat(rho3)) |> imag + desired = tr(Matrix(rho1)*Matrix(rho2)*Matrix(rho3)) |> imag c = swap_test_circuit(2, 3, -π/2) res = expect(put(7, 1=>Z), reduce(⊗, [reg3, reg2, reg1, zero_state(1)]) |> c) |> tr |> real @test desired ≈ res diff --git a/test/QCBM.jl b/test/QCBM.jl deleted file mode 100644 index 0f523ecb8..000000000 --- a/test/QCBM.jl +++ /dev/null @@ -1,32 +0,0 @@ -using Test -using Yao -using QuAlgorithmZoo - -""" - gaussian_pdf(x, μ::Real, σ::Real) - -gaussian probability density function. -""" -function gaussian_pdf(x, μ::Real, σ::Real) - pl = @. 1 / sqrt(2pi * σ^2) * exp(-(x - μ)^2 / (2 * σ^2)) - pl / sum(pl) -end - -@testset "qcbm" begin - # problem setup - n = 6 - depth = 6 - - N = 1< autodiff(:QC) - dispatch!(circuit, :random) - qcbm = QCBM(circuit, kernel, pg) - - # training - niter = 100 - optim = Adam(lr=0.1) - for info in QCBMGo!(qcbm, optim, niter) end - @test qcbm |> loss < 1e-4 -end diff --git a/test/QCOptProblem.jl b/test/QCOptProblem.jl deleted file mode 100644 index 153ba97f5..000000000 --- a/test/QCOptProblem.jl +++ /dev/null @@ -1,7 +0,0 @@ -@testset "QuGAN" begin - include("QuGAN.jl") -end - -@testset "QCBM" begin - include("QCBM.jl") -end diff --git a/test/QFT.jl b/test/QFT.jl deleted file mode 100644 index a2961fe8e..000000000 --- a/test/QFT.jl +++ /dev/null @@ -1,51 +0,0 @@ -using Test, Random, LinearAlgebra, SparseArrays, LuxurySparse - -using Yao -using YaoArrayRegister: invorder -using QuAlgorithmZoo -using FFTW - -@testset "QFT" begin - num_bit = 5 - fftblock = QFTCircuit(num_bit) - ifftblock = fftblock' - reg = rand_state(num_bit) - rv = copy(statevec(reg)) - - @test Matrix(mat(chain(3, QFTCircuit(3) |> adjoint, QFTCircuit(3)))) ≈ IMatrix(1<<3) - - # test ifft - reg1 = apply!(copy(reg), ifftblock) - - # permute lines (Manually) - kv = fft(statevec(reg))/sqrt(length(rv)) - @test statevec(reg1) ≈ invorder(kv) - - # test fft - reg2 = apply!(invorder!(copy(reg)), fftblock) - kv = ifft(rv) * sqrt(length(rv)) - @test statevec(reg2) ≈ kv -end - - -@testset "QFTBlock" begin - num_bit = 5 - qft = QFTCircuit(num_bit) - iqft = adjoint(qft) - qftblock = QFTBlock{num_bit}() - iqftblock = QFTBlock{num_bit}() |> adjoint - @test openbox(qftblock) == qft - @test openbox(iqftblock) == iqft - reg = rand_state(num_bit) - - @test Matrix(mat(chain(3, QFTBlock{3}() |> adjoint, QFTBlock{3}()))) ≈ IMatrix(1<<3) - - # permute lines (Manually) - @test apply!(copy(reg), iqft) ≈ apply!(copy(reg), QFTBlock{num_bit}() |> adjoint) - - # test fft - @test apply!(copy(reg), qft) ≈ apply!(copy(reg), qftblock) - - # regression test for nactive - @test apply!(focus!(copy(reg), 1:3), QFTBlock{3}()) |> isnormalized -end diff --git a/test/QuGAN.jl b/test/QuGAN.jl deleted file mode 100644 index 17c81e266..000000000 --- a/test/QuGAN.jl +++ /dev/null @@ -1,24 +0,0 @@ -using Test -using Yao -using QuAlgorithmZoo -using Random - -function run_test(nbit::Int, depth_gen::Int, depth_disc::Int; g_lr=0.1, d_lr=0.2, niter=1000) - qg = toy_qugan(rand_state(nbit), depth_gen, depth_disc) - for info in QuGANGo!(qg, g_lr, d_lr, niter) end - qg -end - -# to fix -@testset "quantum circuit gan - opdiff" begin - Random.seed!(2) - N = 3 - target = rand_state(N) - qcg = toy_qugan(target, 2, 2) - grad = gradient(qcg) - @test isapprox(grad, num_gradient(qcg), atol=1e-4) - qg = run_test(3, 4, 4, g_lr=0.2, d_lr=0.5, niter=300) - @test qg |> loss < 0.1 - qg = run_test(3, 4, 4, g_lr=Adam(lr=0.005), d_lr=Adam(lr=0.5), niter=1000) - @test qg |> loss < 0.1 -end diff --git a/test/RotBasis.jl b/test/RotBasis.jl deleted file mode 100644 index dad47af82..000000000 --- a/test/RotBasis.jl +++ /dev/null @@ -1,38 +0,0 @@ -using Test, Random, LinearAlgebra, SparseArrays - -using Yao -using QuAlgorithmZoo - -@testset "RotBasis" begin - rt = RotBasis(0.5, 0.4) - crt = chain(rt) - - dispatch!(crt, [2., 3.]) - @test nparameters(crt) == 2 - - for (t1, t2, t3) in zip(parameters(rt), (2, 3), parameters(crt)) - @test t1 == t2 == t3 - end - - # check consistency - rb = put(1, 1=>RotBasis(0.1, 0.3))#rot_basis(1) - angles = randpolar(1) - # prepair a state in the angles direction. - psi = angles |> polar2u |> ArrayReg - - # rotate to the same direction for measurements. - dispatch!(rb, vec(angles)) - @test state(apply!(psi, rb)) ≈ [1, 0] - - @test nparameters(rot_basis(3)) == 6 - dispatch!(rb, :zero) - @test parameters(rb)[1] == 0 - dispatch!(rb, :random) - @test parameters(rb)[1] != 0 -end - -@testset "polar and u" begin - polar = randpolar(10) - @test size(polar) == (2, 10) - @test polar |> polar2u |> u2polar ≈ polar -end diff --git a/test/Sequence.jl b/test/Sequence.jl deleted file mode 100644 index 35feb760f..000000000 --- a/test/Sequence.jl +++ /dev/null @@ -1,62 +0,0 @@ -using Test - -using Yao -using QuAlgorithmZoo - -@testset "constructor" begin - - g = Sequence( - kron(2, X, Y), - kron(2, 1=>phase(0.1)), - ) - - @test g isa Sequence - @test g.blocks == [kron(2, X, Y), kron(2, 1=>phase(0.1))] -end - -@testset "apply" begin - g = Sequence( - kron(2, X, Y), - kron(2, 1=>phase(0.1)), - ) - - reg = rand_state(2) - @test statevec(apply!(copy(reg), g)) ≈ mat(chain(g...)) * reg.state -end - -@testset "iteration" begin - test_list = [X, Y, phase(0.1), rot(X, 0.0)] - g = Sequence(test_list) - - for (src, tg) in zip(g, test_list) - @test src == tg - end - - for (src, tg) in zip(eachindex(g), 1:length(test_list)) - @test src == tg - end -end - -@testset "additional" begin - g = Sequence(X, Y) - push!(g, Z) - @test g[3] == Z - - append!(g, [rot(X, 0.0), rot(Y, 0.0)]) - @test g[4] == rot(X, 0.0) - @test g[5] == rot(Y, 0.0) - - prepend!(g, [phase(0.1)]) - @test g[1] == phase(0.1) - @test g[2] == X - @test g[end] == rot(Y, 0.0) - gg = insert!(g, 4, Z) - @test gg[4] == Z -end - -@testset "traits" begin - # TODO: check traits when primitive blocks' traits are all defined - g = Sequence(X, Y) - @test length(g) == 2 - @test eltype(g) == eltype(g.blocks) -end diff --git a/test/hamiltonian_solvers.jl b/test/hamiltonian_solvers.jl index 897b10572..c5476c433 100644 --- a/test/hamiltonian_solvers.jl +++ b/test/hamiltonian_solvers.jl @@ -1,7 +1,7 @@ using Yao using LinearAlgebra using Test -using QuAlgorithmZoo +using QuAlgorithmZoo, YaoExtensions using YaoBlocks: ConstGate @testset "solving hamiltonian" begin @@ -24,7 +24,7 @@ using YaoBlocks: ConstGate N = 4 h = heisenberg(N) E = eigen(h |> mat |> Matrix).values[1] - c = random_diff_circuit(N, 5, [i=>mod(i,N)+1 for i=1:N], mode=:Merged) |> autodiff(:QC) + c = YaoExtensions.variational_circuit(N, 5, [i=>mod(i,N)+1 for i=1:N], mode=:Merged) |> autodiff(:QC) dispatch!(c, :random) vqe_solve!(c, h) E2 = expect(h, zero_state(N) |> c) diff --git a/test/runtests.jl b/test/runtests.jl index b5402211e..1bf9b2928 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,39 +1,12 @@ using Test, Random, LinearAlgebra, SparseArrays using Yao +using YaoExtensions using QuAlgorithmZoo - -@testset "QFT" begin - include("QFT.jl") -end - -@testset "CircuitBuild" begin - include("CircuitBuild.jl") -end - -@testset "RotBasis" begin - include("RotBasis.jl") -end - -@testset "Grover" begin - include("Grover.jl") -end - @testset "PhaseEstimation" begin include("PhaseEstimation.jl") end -@testset "HHL" begin - include("HHL.jl") -end -@testset "diff Eq" begin - include("lin_diffEq_test.jl") -end - -@testset "QCOptProblem" begin - include("QCOptProblem.jl") -end - @testset "hamiltonian solvers" begin include("hamiltonian_solvers.jl") end @@ -42,14 +15,6 @@ end include("HadamardTest.jl") end -@testset "Sequence" begin - include("Sequence.jl") -end - -@testset "Diff" begin - include("Diff.jl") -end - -@testset "Shore" begin - include("shor.jl") +@testset "QSVD" begin + include("QSVD.jl") end From a237c95a808eb28212f2e49fa378fe95d6cc797b Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Thu, 24 Oct 2019 19:30:48 +0800 Subject: [PATCH 57/85] new trace operator --- README.md | 2 +- docs/make.jl | 2 +- docs/src/index.md | 6 ++++++ examples/TraceOperator/README.md | 8 ++++++++ examples/TraceOperator/TraceOperator.jl | 17 +++++++++++++++++ examples/TraceOperator/traceop.png | Bin 0 -> 26252 bytes 6 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 examples/TraceOperator/README.md create mode 100644 examples/TraceOperator/TraceOperator.jl create mode 100644 examples/TraceOperator/traceop.png diff --git a/README.md b/README.md index 9639d1af3..fb16e2164 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ Disclaimer: **this package is still under development and needs further polish.* ## Contents - [x] [QFT](https://github.com/QuantumBFS/YaoExtensions.jl) -- [x] Grover search - [x] Phase Estimation - [x] Imaginary Time Evolution Quantum Eigensolver - [x] Variational Quantum Eigensolver @@ -37,6 +36,7 @@ In examples folder, you will find - [x] Quantum Circuit Born Machine - [x] QuGAN - [x] Shor +- [x] Grover search - [x] [QuODE](https://github.com/QuantumBFS/QuDiffEq.jl) - [x] [TensorNetwork Inspired Circuits](https://github.com/GiggleLiu/QuantumPEPS.jl) diff --git a/docs/make.jl b/docs/make.jl index 1c709bf87..862e9b4b1 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -58,4 +58,4 @@ function generate(islocal::Bool="local" in ARGS) ) end -generate(true) +generate(false) diff --git a/docs/src/index.md b/docs/src/index.md index 208ea3622..bacf06ca4 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -9,13 +9,19 @@ A curated implementation of quantum algorithms with [Yao.jl](https://github.com/ ## Tutorial ```@contents Pages = [ + "tutorial/Grover.md", "tutorial/VQE.md", "tutorial/Shor.md", ] Depth = 1 ``` +Some examples are not yet documented, please refer the [README](https://github.com/QuantumBFS/QuAlgorithmZoo.jl) for a full list of algorithms. + ## Manual + +The manual is a references of utilities defined in QuAlgorithmZoo, +like `Adam` optimizer and `NumberTheory` submodule. ```@contents Pages = [ "man/zoo.md", diff --git a/examples/TraceOperator/README.md b/examples/TraceOperator/README.md new file mode 100644 index 000000000..523b5f3f2 --- /dev/null +++ b/examples/TraceOperator/README.md @@ -0,0 +1,8 @@ +# Tracing an operator + +To compute $\Re[{\rm Tr}(Ue^{-i\theta})]$, we utilize the Hadamard test. + +The circuit is +![trace circuit](traceop.png) + +This result can be proved from its tensor network representation. \ No newline at end of file diff --git a/examples/TraceOperator/TraceOperator.jl b/examples/TraceOperator/TraceOperator.jl new file mode 100644 index 000000000..992ff2f85 --- /dev/null +++ b/examples/TraceOperator/TraceOperator.jl @@ -0,0 +1,17 @@ +using Yao +using QuAlgorithmZoo +using YaoBlocks.ConstGate +using Test +using LinearAlgebra + +@testset "hadamard test" begin + n = 2 + U = chain(put(n, 2=>Rx(0.2)), put(n, 1=>Rz(1.2)), put(n, 1=>phase(0.4))) + US = chain(2n, put(2n, (3,4)=>U), + chain(2n, [swap(2n,i,i+n) for i=1:n])) + reg = zero_state(2n) + reg |> repeat(2n, H, 1:n) |> chain(2n, [cnot(2n,i,i+n) for i=1:n]) + @show real(tr(mat(U)))/(1<dJCbuQL)h`DlNda>re#& zA#_xBq(ntR4MieI3m8fafpFeQ_IJ)*cinZ@Id`4=$G6TmYq<=`%scPz;A96bX;5|R)kenWCO_@*e=RSW#L)c*+noFq8HBwcQUzgPI2IPVWZ zsynfNV!_XigTR-%0mp0u&U(8A5HI?Vdu6 zuLK16=&7mwj}uhA{oK`bqV#hhXgh>IdiY#0WsDh`>}unnGCz^$9bgkxNDgGV;)%O#f??_ae(D1Z04Rw=~8 z+^?W$)@&d*rUqqnz9T@4SEwF^UeTd@pxKV`cW6XzPEpR~oTX9MC(CrtIckeVz2P`| zzp`eA8V=Q=B_qwH&r^MKb9oa*_wH|Bdb~#JpQxsgohzz4zX$qPmwPRN_T1)%h!=nA z9o1E^ehf!6*BCNlsUky!oCE$}EmpWx2+B5sixg>3$p%I=6SBcB+6TPq3BSEGCf@5L zrxF)QW^KzU(inKX2@INy$4q;#9UOibQ8>wQLOe2u@}*u><(}ZY!_m$B?BR-CG=;Ye z%lIeO;B;rN7n_$^=b#}La1p7pN3T{dqxjG^&vSuLwL08-44xt2jUKV;4@BzdWM+}) z9g)avv_Zl_0XmdadVEPWZyn}?q)@b8sdPUrUb*94G}P(^UrZ-h1z0J}21* zHZ-QCAs4i&-PEi&gJ@ZBlqY6@_gq#1H`OQ>Sli#%kE?pbx@Sj7ddsMMQZ##m(YXq*is!WB7K7uA zGS0zH`U%&7SByHg9-E^3Yg9&wCs-y;3P%X`1Y?FBzj`b^XUH?Bs9uTJ;UB<7TpwTo zXzz7w!tYGsZ~`LJTu4>o(HNa2ZyB?DUsNzU?;+)vuA9i)!A^l0!YMUu$J{Z3g98_F zd5(PvLvI-oq!kv%YP8&Cv&P{ouvLQ1@Jw?gY!kZQ69*PzI0U3SG{Zy?N*MoPc9mKb zbdVP5(3g;xh1#9$0&^HT1Iwt82uSb7bw}bC!FC=(3{+?~jfE5G_>8K?*~MM7U!>Xb z6PynX9txQF%wwV#RVu1jj$=Bo%?3_VCVerWTH#0`gz;&=xhpx_%sEz4 za-7>g-mV6M?8^309}d6rE3e*WZMGa1F#3+AL|*%nRK)PwddgVgf^+YT_=q^rl1iV+ zG<$;gW+yn`4+}Pb*P$bwxL$oaI~wnJpqlOFsaDL}3hx8r zYfuEgs9?W%&8^1gX1MpJ<9iO?Fp6CVwrcrF9Z56cWnLj8OZWmCAel9D-38T^(I!8^ z{B7%QMCS)!viO;db|$dWHtDbyx1DK|QO%!VE6%I{#(22)ux-Yw0ab9T&879YQkRTy zq7rE+Va{RK_W8zyxo1ybsBJdfK}*7se2_c1mtXbkdy`ldctK84kekP`+Lf?oVh?Xl z2``WI_`8`+h;L9hF+mA;B8>lTzw7b^9Vwg6h?dV_eZtAMP<_JvL#i4gujVlKSN==$4%4_jCjnc*jJRw>{zI ztqbzisX&%B>K}H2Eujy{v}-pSqcVR;ddps_3OEEiWU#!+<%Shf(?fqqkXaFUK`3Hn z-Kz0Y{P^ZOXxZOrga$1ZAE7fOEkW-2IgxpS^9!AOj+V8l6I_0wBgV?5f6^x0*Y@|l z&BJr%aEoW))w0%}5aEu`n&tJ9N1Ss)%;6$m88K*&1?-U8^DS_SWDSQ8C6q<7mhio{ zoyi1qFjS&VxG@eEcNKd!FmmiQ_U})e8Et!6^k|?ye(0aXohhlQpYE(znTzSsr%m8U zesFlqmm4eXVX!}$O1!^Px=Os5sZy%%wy$5mzT@4SB;QOO-yNoLaE(G`AOi51u=g~g z>Y>8S1rIywkQGDiBqsw6xrjJuc!hrdXtDHSymu>|VThQ4q>y%Nd2etK$8_3%p^&+@fOl_M#RG zcqSPandCx<>{3d)fTZga-5MV4^EVnq%vAA&eb`dci9IRQiArkK=p4>F56$z+sfcVZ z@-H4dvzxha!ZxFiqk`b zzM!ALhWd{ewGC=y^31)e7G^1WfxCw-^yfaJa|MjfeRbKH^pp(bcTvh(X-iAXK~94j zeQsuy&ilPeR?B6mIlc7v_j~^Q@e=yV@Jup`jWgE2Q6Ofm)W3aYj3(yA?BzAtO9m%_ zhfnmCsTny?G1xcZrYF~3>}fCmyIMkZw;ZL_GKk%sGMYXCv zlflsjo6@HGB#eVk_xE{pm3Y&|6@FKTzEHIAj%~BCc{pnDbZQ!B#qKQ~L9_f?VViwm zysHBrV@rD%*hyw;6#;{TGS;oDqW@g)9JpS(b~1PXb9kIdUm5inE=UoqlZF%ZtH{He zLU95?H0!4KUh)KzJX8&Y?8=`V z7bgV!`5oYMC+LYgO%c|2oE4rEBR(2cc&@b=*UPMmoN2Y8Q!;|jYk9OM?8$w%BXgdm z!cWU^fb%Yld8kd=QKg>t9lYpml z$K*~WdPlYZ*D-lW|M9(yj;j5HoKn{;)EwAEahF$GM^cneaOJ+1(G!k7HOs^oqKh1cdsZ(&;*R<39z`cZrJZ4_~MeK6FOIN zop=;6D2TjlOFvxLS@9SZIPcn4vz1lR!!eJ1_2WFgQjNB8N-hXjvE@)t602_5VoQiU zqHj+y9lTO#e0Q>7S|Cq46k?`eU{G=TFAedUj>F&Wk#he#z0D_dhHRUb-pCINrH4zd zY&uGbhW7US03qJ+zI4_If^#aXR$NrSA2HM)=pPG0j}G^+*(tcAz|}0$q9k@WCrP|P zE8Aadcv)AA*hQqAbY)xOGnr34qmENe85IVF&1CNk!II{t&hVW(MkJIY*-ODQZt;Fc3?pKFStml;`M{fRL5PqUF*qXD4L|gD*{=?hgtm{ z&AQe_fud9>)w&jBu`M9X!Vl=(wKyY(M(dC7?8Ivum zI=y9`H=rZm(Ye;?<6=ho?lIBY&tf}A@S!PCbp_WA}yEN)UK`hvl$(!#l<`IB^YlD$P8%sHBfKk+7-}tXMLUhr?0yD zb}uc{KMl9MjQLvJtE6rY2Ra}n%Di!kVckDY%Dg9x&bAUpPxr0)t2artHQpc!KQ0PF zi)QB-oi@rO52S=1{1e7II>C`f6>dB7j}B%={g^s(u*$wA?Os~HF2YI$xlr|??$lqE zUeW{TOys9-LNbmPYDbt*IFc##tE^NjEEGvn9?n~<;{&FWx`oyKkTv%>;HXX5MWh6m zQhL0vx@m54G65HQi*deWArBXi4>6t!?dA7~PdGCsI7DQAx?2CDoL4LXS67_cGs;X| zo4L9*O@;@2t5MbjSDBZ{=+qwy=}x$oqAf=W_aKaWV);jJ0?UScLBC?u9x*6GD_<7P za(C_uRm~cv*rWCXrG%z+YibXG41Ozd)t=UT#^pYb=EG4%7mwzpNrnFryt!^WNZ%`a zw;hQbVg>roWT7Rq-c}YC&x^Ds7md%Jbzk)=Jbvodk5?%11y}LD1(ws$$SQ@t10S!? z`hp}a34}dyS)8sdXirbE>jNl<6M;X_(9o68c6 z))1M2jKGX4xnPNalZ5f3A)~rV_}TF;?MWlI^uj!aV2Q>6x_@nDINxuuG3B~e#Qg7H zVY7vq6+>Q#Z|q$$#%;9B8(B;%tg9g^lTLtgagU)K<#ka|cG0W`UOt7jQzOXGxB~K> z%?)3(>*Q$5MdQXailxchTTq4Ni4yz_=Xlgz*iDBy+ zzTDC1rtbKq>J9h-Bv;p*S7GTdpPM-|E%?NXbKNe`rBx|c4BvLYS=bOXlum3e=NsD- zqOBfWZ+-;|3X% zbrw`nM$seBQ2FGZxoCgg2Q2O7tupB|BREhYqYk?7dMsx( z4aY__y*~T0fY6qE{a~kELrzJLp8tj}dpp9SR>H@Wmg$4Us-CLI`fWs3`n{+&V6?PlMr0 z-AALVWt~pe+O|8~8_~dHS;h6GT~y*kGbkeX#CRp%byw|Am&&zh_Mqac-&fLIFn0Pe z2;2=S#w3PwMp6kw;CeoGNjeNWQ5?4uY11zZ@$yVNS>CtdI!Ot>9o<`*H#f~VXO8fd zNCeoSsC)Ke4u)$SB6h#4u8djzBpMAlf_PKhxi69VPuu6N`!(z7pRlf$9f#SM}=MZiXHE-9X$}Pemxm zPTw-T>>{318m7(|}E1F^s&6g+b(T>oBX zLbp=kr3upY+3$8LEdH+d2R3v}+Xr}su(8VuX%*JYERD;ZX1DT+f(VF;if+nugqn6w z@MU0zB|krx6g^^6i9=5n7Oq%k7 zG2^K4y0@wnE}SQft3CRv)%dcgxC)g|VksbDr;)L)5;X~Fxqsre>HE=?l3YOb_8Kje zCyso3ZEj)lBbUnl^w=b`IXP#{)txC5fC)InjL#ydK|y*-Z(xF|4mgobpKFdht7{7KVp^8bgfL>gO{wm6Qn zN_OuBTkdmby!J0ULTJ{=9&$O3wpxXjqTyZuoY5GQstrHfXJ716cWV{*LUN}2*6J?A z;Azq9;Mzx45*EfX#!57Qq+EwK$gq=^(@6kR#^H-SI*?dd?H5Il$SPKYz!&%fN5?9~ zJB~jY`u&z6s6^!xdv-OO;womA6306qf>Oj4jNegC^2bs<4fkf&&o57>V*Lc8(|`3a z{l}Nu-f*(uY-}k3HazERZs-7yw#a_a`RbFOS2P=k&NWXPl_%nQAF-Ij3t>@P%=ZHb zk;&l$r8bAtZ4zu?*{l&wd?hi%Bu<6oNAO(ay_FhzS`_rIXqGa)giUPhOxkH0=W^}Z zZi&_Mls$5klir?i0I>ZxgbkAHLXTImdPPCdRdzi5_U zTl0&M6RuNnk3%=(45#od)AfK?mv#lC#Yk^U-6J=D0+}$M_+IL>#uh_8Tvh}UY?o6f~e;Jvrz8oy!4 z3>+{l7{J4Uu86xb=|ad(RX3|;;p((da`}GRLlH)P9x} zJ?{&LCvw9lb%i-w0;BUaI#-H|)S)ZNBJ&sFa8G0r;J$4s`dKB(T{h2%ihL@P?esTQ z!O8ryWHk86IiyAV1*&Srd3`?SLa!Z_z`9vuU`L=L^X5qAYczd0=a_a&U`|oR)4cmr zt#GhPgH2lLDe!b4j4#6ygGDRTB1TUp3v9D^EQzaPX!KI|zgny}=|{d!cmyWD`bZFt|E$y?JAw?1EOO%2Sw-v0`%!z&|l zS93hK5e*4vh?5MbH^we6 zF$#=;x)g*-aO6M16jvAMC1Z2V9^qGJKvc*3PY^su-`%q{Lq~jWh)&&GQzkTh?4wvtS?pRa9qrxabX9>D3*0|Hw?( zWw_LFDU|<_R3`yWQIA18lUY{CyeDkv-aWJOEDR$~Cpb2EM@8IH$hNDz1GN00)bM^dY6cYmj`Awww=gFZ{G$QpT#7aXLK zUuajoMZemGcI3}(1atQ>0z>E5S~!jIxcJJ*x!!G4a@^NA?-QI)j86V>8!&N=SUuq~ zeJZhJa*r)+s0ey!JrvfTDJpW5pkv5{1*Zp{TSQ0tckWsXWPJZx+3Ei6pg9g^8MW~h zS`4>NDDn8X)v2Wuj@&XEtfxbx(J(ByR!Xh$W2~&Z_OBoK9tT(rMd@8Dpz6t0`odf0 zp95R~23mg(mL;yu-v9?*MYNSkwvk=n8mt$!O+Tn{<8oy@s4^Z=*<}Dun4p7Fo$L~h z$SCu$43jYaQ}UApYgdW3(8TgRastOqqKh}3U}<08HoOS8bJH*3UUqx1yLDx z(fO)~g(tG@?|d)kR?K!@0Br^hZR7nFU%=+wulZZFb=-3ZiFOl+`!Zv8hxz-A#yKs4vYm&nwtldIo0ttHbR#rq3 z!;#(#K~F(bzvGNhd!gvJn6Fq#b4SWUkA{PmsFdk98A2PGJZ2t3SkW_N#NA?BXX8k6 z1uZ;3P=ez~=iuRH_}Q?6 zM~>me4cp*W&;$wD5dP+cAvN+cXl_MQDXrz*L%~{x@9(U~+JO$4E)&)IOQIeQ zA*IC@RzD>7d{~X8P@~Ph-Gr7jw{N=TdcguQ=T<@vQv(&$1@!>=BCuXV zuu;N8NMN!(4666i-CY>v1hQUDSX0Y!&|@#L&bM!|-x>cec3M-g5QEDzLqNr~GKYNL z(|8BX(`0ldWF2S5;SnMXb!dY9-#-ou5VlZ z8Tfov&<09$mJ_^id2zPCE~dp!_V;2YR&oCRHLyUo&Lkf_yk_Kq5-#<_Aw0dEDJ`B|S82+~{SOLS}6n~Vg z03Pn6H|Bn~q&ex=KaSKMm^I59(KXq(5{lBq7!rHxyl1?}Ito>y8z-76x@0O_Z`9;d zm1gf7U`f|(_?>R&m2c6Weswx1-$>|4qbc|bC zShxZW)X+980St$ZRpL-SPU>c~RD~5GC;fH;urD#dRjmH>i-7NWIQ?Q-#qf(XKLF4u zX$MXA)pUnVHEC!soFPvjY zLC~1f&(mCCPw1!!B^n8S#Sb;6gyw35@46#p3PG0(y8pd?J9hmtK%Jnl2*{WPAZ2+| z$BVlIS}aQWY^u!4Kj=VS3w-X-P6gfxqlIBBy20#6TzqE6#|==_TG?|t?JAhOO0}@m zwa+7`C^8F`MF5QV6k!3u=?45fh4uaZgFTS{n0p|C!=hnrqL|eR&A>OhYjfN18DH$W zv_DON0xRQiOmuio(Rz618!j@Hg>Ol7vV*VYRCv&qi9?@PfT(GE9dI&|rB`I8v4+Uz zI2wRG{kTlx5SvQZ-R$FpDBw9|K}J(#V4~`Ee@O6e04v!05VMY(*K*Rmau(uoHqT@> zM0l)S%}>HvnfbwlajP5A5HzrV%qO27fO)Q)AoZ|*$LMsoBUJU+g`NU6DX&>qI`kw0 zXk@0v48h+3gnJ8vP^*ppD@ly!RlWPGT~|8G!hbBp9v~U7M#X?ko=S`6Kv^w{y3qYS za%1b)0f%EN$^l%r68{zBev!pC`Q!)c6eyh&FFCd8#fk{SrdR|1wnPn+n0i$ zlT8TYhY(OkpFRH+bRGgu{Px60C=0R2_);7wl&%PK4B+Tqzc8syx=)Qe4eY?&yXhDN zF?4ncZvjF7+xfnjrvJTO86BAfsq1Csj}Bm};K%~<+r05Q#xDewD@f3sljH7FqogM_ zgbuf_{QpvIVW;$YjQ+=^kh-2wP~hADv`pm5#X{o57o;aJeZsiclBhQfDQaDJhU(Us z%Pmb*FO)leBRJsDatAre>5qf}F>#%pjT_JOwdXlKwZQ z`)WhX6d`ETWc$)h!-tlF5C;z1KbI)x?~S=7@0_&O5i?~-2m5AaOd0?FXd1oLAu#35S?kuFdj;q-Kt6?wkVEB*#q{r3niZ_^5~sHU-PA;Q6IpP^-@h_NN- zGtCqo`O5AIoRBqMFOjLjlwmJ1!Y=sKBjchn^nsZAF@s|F6mx0 zRQ*LHo1h~6cIrVIy?Lw$PV~8_^ZPzq@cjDmC6INpaK6>KC22b!>qtVBUDvVlHtKYH zY~m?U2w2_{{eETa#fe=Yzf>_3dO_!NK_<8j5@J#5YlI$cb9!Lhjqlr`{IjB!J$)M| zynOJm==8UYKiAy_({^q1` zuuaE=gGFu0TC?KAVaPcVtS#y~+?s*v1^(m~gQzBLavU4h_H6~Wz3N)9z279Byn)7k+%qqaA zLGG34;b=T^ytf4>6CEib&)2gf=zcFidcim#6xrT;N}#X2DI1lxZUxN{qqO%mXDxM= zK(5n}VMruhD^{bGD3RRZ1zULBe*P^4$$4&Rh{dFFp!Bab?K$#iVSlHQWqn8CRvR2| z0mn^ecNMx@Ny{4~YtRie+$p}tXW7i)lMH6*48z|4=jX>4LdT1EfO4h1@E{zYk;z&C z&%9?>y3w4LL)Aa`aZUsCJw|-!TkFTo)R_3;Mhj1^}>;+YAJ_HBm;UW|8a@~M2 zhOq;?CnC6A2c^9Rzn!&eik6@Ln?md%rJOGvxrld!;f#7QXr*(8Xtk%x^8u8xxhjE0 zaoH)25t?)d?rT{7K8#{Llt$iArm^e_HA_x*sl^{q^@<)vmYtS6FfCr$6uM>!wKWjmQ~At4J&o0n z6rk#CIL{ycs5?aQKVt*tJ61?<4s3HslJzd%kC%2C*j=lT(hw~U`P3GNTGisz+QnSBjKvI6RY&b4tgB&W_Q*h#K_z=F*{zv?^X&RSG-g z8f=(H^-k%+0|%tob+oocTX+83ZLr^eR(|6RZ81N}u z1r`$3yGPksF&WoCtdYDpWTZb0-S52qTjQTw+|`Ba zx?qYW4q2!4p6Prp(gmR5X?)I;voF(wI*-U>*m#sL<_P)$upg+I-0e5gsoc-v+4)%ozvM}Kn;+wbRv9ieBdQa_6;jpA z{FY%JAWsjT9UMgrJlQH@?S}K+F3jH=RDq=hhl6t}Y1&^! zVVNc!SOrxpF9m{QSBCuDV%@qUsck@7uriLmDY7K@m-|J{bFE$Eu(ODqUcfH*4ANjX z-4~cvM_S}yWN1_l?_bfutpZ$L^Q^BrJ|lzWgP2jNGDe%B>eHA6)SbN3yf9k?@@E^( z`SupKg3@g--CQoLRx1W@|7Hux^X9y9JIEOVZm~M7;s7lXSIRCM^}|RKGd14t`fqiI zjOMhYDOhqCr5Qkg>x+-cg*7Y32(BQR>81NXJ|2yUbnq3`7-4Z}L0Hj5AAb`sr|UO8 zQ1Ol$-#8A8l#;*id!Gg%Ti&xOJ-9;=lo*)t3nF&lRb(6PN3i`f(;A{9IONR6ChcB> z3SJ6MExkb;DEpb>yV1-AGyCvnmZ8M4BarhcY@(DvZWkaFP0z*{&-Y_Zdth$#h}lA3 zqq~s?b#x0eoEaw%DlKx*0bie!pwha9`R%I7r)p@J7%zk?{?)D;QA#58}@@$52nN(jSkWDsXHNg9@s*JtqnDxdvq= zUY1d_M!wRT-06Z6RCRp4!(U6FJ{>F)Nqu8x=Ax!bfTvG4D^SCGmH+y<(ub%z-EiE1-vX0*B)bgSWbtnV zHTm}poG|`E%GQd~mUnJI)lWnbH=*^eXcdy+F5q}ta#ys`gJ1xLHPPuSv*<(pWsv(c zl%&)de*>pjckiwj-u1u7bh{WG9VMW^Q=%+a{p9EdiI+!#j>(I1lH=~%2KpWVxM1vo zD93n}?JwHc|41rgnQMes^toU`0A$X!UZQ#V91sl=RKeUVI^(G5jInD}UC~PJ{F|7h zIc$`8S$rE@PkclCDa;C;6U+FI0O*O>D3yk3f2?`UW}{^RZf(!WK9bO6l}H3*A>7)kveE)aW7tP;zm^W*(egLv6ec{=yDt z!xcBG3ncA?f8;M&dc-+VdX-_dgjU?d~>mkKla#Ncm~Awpnn$r0{(2+`CpX= z5X1kGrB{JS?QM|C z477GF2w&LYd{ksW;MuFS|MRC=+rrzjQ9v0n2G2yHXq6dvczbOZ8M$tuU*vVjeFNnwt%%C9lDfl4^V5*~lJnPrv8!^Hp zU(n(Go~2dhqN|m8bvQLPn@cC!fuV9xc?zouU#UZrv1wgT<$umUyYD!h{*V=k0~C4F zbFG)De6n~vXo>yx?k|AJ9fH$AFTjk`jEe-cjKV;<@_n>brsLG`!Wav`!$@Qr3uKJo z*_R}^$lK_wI5ZaGl)|bckKZF-sFcma8SDlUK$4|m|l3nVdS2&LVV*#DFZHrlzmS{Arr8?aiohx zg>73wi|r@77exU*Wru84m}cw^>fp&*i|>6o_>695kP_5;u{q%M##wPQhxg z(WYF`5#5d_F4`dzCl7>s70(LG3upl3Al1mXTV@4ohcndu+nkh3&yBEvFBEYBUQrd8 z%&ITUl16?vGu(ve^oaEs514!tZtRF_2=`p8aT=E-1_j+?ZorC52q>%VUub6<>qqGn z91Ymu{kZrMHc|C*91Xr1nasL~kBAt|9d5m0taUyWQ7)4n_i)_DkM)t zrD-hUuvYHYuwM1E%*WJTBsOb>lWrmZD=rB{X(-LUUU)BcP>e?{OWC_;?vqFPQ17=w zD&I8b-8G**%1fe*Fc2fCLwtVD@pcbdNPd1?3@R@9LkJr51!IG&kYz5rC2d-G z&5q!onrf`^#*vsVp=IBG{wdxJnjV~6_eCRP0_BmevCH57oVq3JI;1>d`ul!2>GcX6OCVi=-Gg3TKI8=b|G1{-q~F4R9i7O!>w+VnCFYKG=F;BrP?Qbc#h@)C80 zcr#8lZ8nBHFNi8^>}o*dGCYg{rSTw39h`b;i|AArY@!Oi+Jy_>*+vK2asCWuo9n=W zRGTbiFWD>*E-@Ntk0_rfLQW13T?hR>~G&-_L*6NBhgud!+2FW zA35-pe=&?}{5X~UG<^$Z8M@el-H)%ARB@|1-T+eCvFc5UlMmD6=JY{%3?=Wx);YMx zt+;-Zq=HfV22fhQS5BPF($*cjNM009|0Y-ZuWcw=7?UBk`CElsv9WIZh6WA3W>5X1 z1iQVaX%=q%Dk0{q$vdyV6*Tq3@e%uJ)>ccsEd20D3E%&XmO-1i^LQY))r4Ms=^Rmk zzsuPS{-L&3eI|-6XfLp|hP9P+6IiE+nQxx?M}VyT!pBm`R@RKKdvKG6@qid8teE54 z_CbHz06EWxIn%0E8HD#wRIa{)7?%sTv|(y>X8 z4F+$Y`DY+5i%2tg%o;fEs6&$a#0e1Cmd}*o7F`M!VCF(wgWj)06Ni8A!$M+y z6yBd=Uy>k~FTM%~4lh;AO-$#s&4-O+_kwoONG7Xm&QMZVM8v?ukI%p*;Y!^RC-Vai z^#Ut$Sg2OS{@B)`Qh6x9;J;VVQT^vyCHzgd&vS5QQa8kToRo1cf3Z5fPpUzOPwKWUVDT5bs9gzcQ;pX!WZ_*k%Fr6 z>oLV}+u5;GBJI+OYXdfayNmbh$D}TUoSECKgtY{etS!_I2Yj4)aa92tG(~6lle^su z!eL_Q1XjcP>=2bU&KW^d_mY9{CUYZ5y{thd*&w*uca!JrNDXnN7SQvp`YEDw<7F9(3;)O}$g2I(sT#gPu= z0_ycuw>Iu7&4wN3xwk0`sj9NqHbGH=g||g>^8uR$CC4WzmxdQCJVW(}|6Cj#`+}E0 z&>;vw+N5=W)H-cJ-3h91NM{RJN8@2)9_{%4$>A z=Hf^sINhl4g#Hf5X0S@8C! znvyP|VDv7|ShxEqXlHr<^Y1bZu=Ag?E464Xz~fh^s%p-S<12j&!tD{WGXAXC5O8i+ zvqa@16o+{x-BI>phQ6BSTpD=ggP5tFA{=-cX>bc#2qsp!zaoKf#N7QsSiuV8QT}GS zCFeD5Rvei>4W}FS8Pq>V1s4%*Tq)lr>G?4bVBY*&6Fct#V%%Hp0)m8c>BWN4zi^Qk z;Gvb;t+VKeVn?v=XrD`)DPlnT`UH#IKST@2-vV@G6A)2KEcX|cR51D&-veGwaiIHL zS{52f0VJ<(V!@~ezA_hx3QN?o2;2Tu8w}F7{hKWy;{Yc5*`7Oe5w=Ys&cXNLV;BszPO_vxw-3Hag;H(geec z+i9{GGj~|Z1mIDv%!Q1{gG(S=*HKZ84ZXl_{1;nLh>N%6RHiV2#sET7Z<209BOE

7M7xx zIcVfV7Ttd+Yx!y=o;JR+m}>my)s#4tUHC5qAk4>}uq&TaL4!TZZzyX;l_G6Ig?9=z ze2Gdok5b^7C^Y0S0_grT;6);XY!Fed5Em~I6*{JYH$Cp9E!tb>Zj>4yz5!Av!bQfk z$P*lKyrtm?8$7|O3|=chGDWIJ*e0go0*j7!e@p^*zQ1s>(W5+g<{yu}igT+z7T|WPHkFrAK_hW!iNCvbR z09aWD`O2AV^^o&Xgk^)wM*$2zp9;Dg5aSRG_~6ewByVz0`pvNhPG~)ntw$y2OFsOnaM)LLx=HmXd-Y1gh6CqXZ(iov$u#*JLe39G0}%m*;yMi! za%Km3AF{p}NUR6J|Kr8~S&&1Bs(^y@DcpLQY!GyhYTU+Dv-`f6-ZjW>3*u5AX##3s zwE;-NKrDVzugqI*-_1?L;#^l_B6ySoQ-ECt-iISzO+;OxKD$N4Vu%u+7#rvebGy;lK|E~-?BRoEU=IO*4*#WlW!_xB)X zRE>L090shE>}|lvz;s)fB`Tx|t(dSGv{Ou`Cn%Y4J?a2IH|DLhBxOCzh&g%RmuZq8!TXnH!2q?8JR#Jck%?#o)djlH`W~hPe4J456(?LnDx?jzNJq-q8kD%SHXI2A9Hthv9|e zND{7t9lok}bHhC#V}j*&Y~P++Ume44)WUX#H*0Ph*iXxc(9!O zby)ibWI{i&FcFNDs185Kv*h@IVgqWo5DjsX7>T1@M#4;y)oMQvg6On@xzNN6Z~Chl z$W~pnSw$_fjw!4feC2)`crn!5x|j-8TAotrJE}qtv#P4D9^A?n-xH1PYf&rH>4YB% zX~?=3i9Nxyi)Mq~vu;1v1eOy_1C|sdJ z+lcRZ#DYOrs#{_i)cyk<`Gt2(V|izd5QzRb%KI8^i((AhlQvif@^8Fjl{SQ9Q)n`U_)1YM8kn+)dhEK?+t`X-8&6C zrLj1;msUah4z814E2(`aI`gETFLANyN%IOQ8{Hp(PlB#Fq?tPj3F5TW=EZKi+Rc(*rA|It6GWqWC>K?kZ{ z-?*B#4$CbzZ6cgc^hvocWR85xz))9P)1v+@_?~qA`kFnhBDWk>YC`QCSDgk3ex z9>VEr11sIBK{Vb&1AVveqzv#3k@hef@YgSC^>pcCEd5wGaH9W(9toQt*7%89e-7rF zb8dNDP}FfaGwp*!Cb0-*{8h|wmqd*KZa+KXJQAbKQ_~=(C&kLViwG$&0MW#>W1kRb zv3vx4na>h*#0VqjkGcmfUPWR8xpY;%mGU`M<6b7+oD*0&)p{dInb(STmNbIWfbpSB z2+|25l|CJ~bpa{Yq}iAUF(1XQ;U=>MT{X+izA_^z7*2Pfknf~KvVg}G+;)S9PKtAd zw+%W8e4>st92kTcpXBt>a_jJwZ>Uah$Fi1}94Jkl1W&WOxN$+Z?-RgEKHCC3&66p_ zN`%mh&q_@FA`@7(v|PFhZ;l;N=X2IGhq~B{qo;PvG~pf3z?wo7&eK73)je#L9w3Wb zOCC=n8@!<|4n^VDo3l0)(mOCf2I_i+g7=7|lhaI*`Fo(i z09BF~bKwbYk=~dK-glI=Qgby30`{%{gRA2Ko=DzBQ=^ZCTm)7AsVT~{A*B?@@!%4i zTG2PabzoTU`yWgm`_R*fzjBwt2~OlHod*0c8$3;!;juS173U;l7P|!UAi%=8@4XA= zo__}e_%eWNNV7VBp1JTCM^Eo?eSI4Ae>dxi#>A#<`WiLFM})YwNm2P~w^H3LBUFiw z*QR;m&49_e>6Q>M%)bHxZ?!{sI5MTurO>ohbzjI^+L@>8ns}Bx1==?F^ zvKqF7bfmkr(5+a_z74>73?`WG9Ia`gBiI`x4sjRH3%BzT#zjirPx7u*vGnRmWi873 zP>#LxaO0n9qs718|5@({U|h(#i3zsTVpO_Tn{(cYxq~`s z&ppvBJ+N7VBtA8kJDgVLk#)HZ#!}wzDGv0`5ugz4YdykS-xAIcy!POQ+E}>?&1t1h z!;Xr*v~luyUJiQpmfUiHel+e8is%$(E&3M@7A_33*)_`NLDk6jSo#?UpsHFS{%58i ziQ2VTh}cY;%mmPPcvfpqRhdRKRnh}aXzpKvd|{T2f{zRk2d!`C46XO-i#cU0MHa#r^Y#Zks_+iyi zh)N&FdpUgPNjG|^X;@}#GHVvsC%&T6H;3G2K101erAGojOZE0L`|aJ&FMw3h$0k&x zdkUu}DULMv@vCi3r>+&WTAtCWTL^w-{)Js7Xjs5gFxC zAcHIdA}TJ%lBV@W!W+h+#Tt<^Fy$d2LQ81mC1%j@P*hSZZwW{=M3{XNJAc4@m`~h~ zXPtZQdHnX-XYc(x`VOvjV5^RoP_ds*BiQt z?#{!RJ$M}faAUaz(SXArboo2V@Ig|-xKXS%;`)H;;#};Sw8 zjRg{$T?p>d{*tfG`fmY74RRws{HFKQ`O3ahO@+e4UBy$kFOY*fhFKUUI93xEOF%Sd z1Q|0lT@}7mAnh)s8))q5KgYNH)mXw%o)l4Gzm1lV8vIgCE(%X@eX9OOmRx*13u1ZrnYUfBmRr=84Vj zm6+zO!U}gWG6!9j}^}p~@R13_mhcW)$(o1ZmO1 z?3vml$b|FvzmJVZ5Z;U-Hij(>4Vu@%4Qt9-$yL}(^ zWGSrw_?#E9$Z~=tmWfx#a%VXSN4VgI5AD2EtiQ2N07ek)ZmJnM6#)ooYbA_DQrk9!U-nc^jK>_DhJ3?A4PtDP;L~l(*HMN;Ac)$wb?T->hYPQyuSoC%(lxwdm z#6P73c?EO5sukWBTMN&&aoU+%-A_s4^eM6x*hJ{S+vZN9ojR?=#j9!h9U>-QUR0nR zQyAj8g&spk2yWp8Erl0l&sXc75nXm+#<1#U7~nsnGfj&gTZZ1gH;**sv_oKucVd&v%g`GUZRI*VMXE zqWp>i5=2xny>>Y@e3)6k$|qMlqZTA03n*V{t&b8@5!ffq%XR{>i#6vM%EmBBdu45Y z?6aMBJV_%YrrOW73hs^bJk>H4+`A6=Svd2(I=V$;T4UJaQU;yI)MzB~O?KOGRimrPb#2WlR@l*t;lsRm>r zIa+PTNtIBoUp%wz&511gmqV>n?ll$e6elW>CT7W$?W*(YJ5IbEUmK(^N>^g@A0Vv; z%<>lmz4LKyx~K5=%Rn-pG`gtZn(mg&TK%jeT|1bsop7WdfJnJ7FDhK@NA^FOUCi@M zX*e#KsYzHAU{dza{j?{RZo`|*tIy)2sm$Z{%E*QfR1UnK8x!i?z|1LsFe+uIHEuc_ zEfuXE-_H%)qZ`04bNnfEDt&UzAHZ+idLj>;8|Y+Yw-=&yT;f4qYhhZ1L((AKd#np* zZmrv_%2rx8@$X5ndIwWG`JZvE9A1P_V`(A$^XWwV;#>hhfP&gc{#GFkj6X_^-e4$m zOs!f-cWimoblGR=6J0y4|I0ajn1nKtWAiYUUDg>Fb4Y}etTAa?R;`O~3)0~MfF%su z=(;viR3HEgP`z=x9tzNN^TF@DkiY+z4PyI07rDnwfmM!aj=#t4y)UweyIJ6ZK6JC$ z#3u(#J7GIBtRG|88?6NVBLGWtGI7!>?DQR8AHCMa05!eVRhg4)x)cK){d&hyQ-jb( zTI283sgLYoQB04EnbC$}%SFpGv5X0mOe~ASvMBtYL_z0sr^agGbLqpw2zi8W0K0tW H!888>4PGM5 literal 0 HcmV?d00001 From 343fdccd1dd6010128cb2a60a4526a676f4b3598 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 28 Oct 2019 12:06:56 +0800 Subject: [PATCH 58/85] imag time qaoa and fix H2 training. (#13) --- examples/QAOA/QAOA.jl | 1 + examples/QAOA/imag_time_evolve.jl | 51 +++++++++++++++++++++++++++++++ examples/VQE/H2.jl | 7 ++--- 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 examples/QAOA/imag_time_evolve.jl diff --git a/examples/QAOA/QAOA.jl b/examples/QAOA/QAOA.jl index 280e243eb..c0d39427d 100644 --- a/examples/QAOA/QAOA.jl +++ b/examples/QAOA/QAOA.jl @@ -1,3 +1,4 @@ +# required packages: NLopt, SCS and Convex using Yao, BitBasis using NLopt diff --git a/examples/QAOA/imag_time_evolve.jl b/examples/QAOA/imag_time_evolve.jl new file mode 100644 index 000000000..299a2b309 --- /dev/null +++ b/examples/QAOA/imag_time_evolve.jl @@ -0,0 +1,51 @@ +"""imaginary time evolution solution to maximum cut problem.""" +using Yao + +@const_gate ZZ = mat(kron(Z,Z)) + +function maxcut_circuit(W::AbstractMatrix, τ) + nbit = size(W, 1) + ab = chain(nbit) + for i=1:nbit,j=i+1:nbit + if W[i,j] != 0 + #push!(ab, put(nbit, (i,j)=>0.5*W[i,j]*ZZ)) + push!(ab, put(nbit, (i,j)=>rot(ZZ, -im*W[i,j]*τ))) + end + end + return ab +end + +function maxcut_h(W::AbstractMatrix) + nbit = size(W, 1) + ab = Add{nbit}() + for i=1:nbit,j=i+1:nbit + if W[i,j] != 0 + #push!(ab, put(nbit, (i,j)=>0.5*W[i,j]*ZZ)) + push!(ab, put(nbit, (i,j)=>0.5*W[i,j]*ZZ)) + end + end + return ab +end + +using Random, Test +@testset "iqaoa circuit" begin + Random.seed!(2) + nbit = 5 + # exact solution is [(1,3,4), (2,5)] + W = [0 5 2 1 0; + 5 0 3 2 0; + 2 3 0 0 0; + 1 2 0 0 4; + 0 0 0 4 0] + + c = maxcut_circuit(W, 10) + h = maxcut_h(W) + reg = uniform_state(nbit) |> c + + # check the correctness of result + opt_pl = reg |> probs + config = argmax(opt_pl)-1 + @show BitStr64{nbit}(config), opt_pl[config+1] + @test config == bit"01101" || config == bit"10010" + @test isapprox(expect(h, reg |> normalize!), -5.5) +end diff --git a/examples/VQE/H2.jl b/examples/VQE/H2.jl index 604a7d86b..00a40a8e7 100644 --- a/examples/VQE/H2.jl +++ b/examples/VQE/H2.jl @@ -1,5 +1,4 @@ using Yao - using YaoExtensions # arXiv: 1704.05018, table S2 @@ -20,13 +19,13 @@ end using Flux: Optimise function train!(circ, hamiltonian; optimizer, niter::Int=100) - circ = circ |> autodiff(:BP) params = parameters(circ) dispatch!(circ, :random) for i=1:niter - Optimise.update!(optimizer, params, get_gradient(circ, hamiltonian)) + _, grad = expect'(hamiltonian, zero_state(nqubits(circ)) => circ) + Optimise.update!(optimizer, params, grad) dispatch!(circ, params) - println("Energy = $(expect(hamiltonian, zero_state(nqubits(h)) |> circ))") + println("Energy = $(expect(hamiltonian, zero_state(nqubits(h)) |> circ) |> real)") end return expect(hamiltonian, zero_state(nqubits(h)) |> circ) end From 07345545e71dab88c4eeb937c0abeb1984937f91 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Mon, 28 Oct 2019 15:03:51 +0800 Subject: [PATCH 59/85] new, port zygote --- examples/PortZygote/gate_learning.jl | 31 ++++++++++++++++++++ examples/PortZygote/simple_example.jl | 17 +++++++++++ examples/PortZygote/zygote_patch.jl | 42 +++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 examples/PortZygote/gate_learning.jl create mode 100644 examples/PortZygote/simple_example.jl create mode 100644 examples/PortZygote/zygote_patch.jl diff --git a/examples/PortZygote/gate_learning.jl b/examples/PortZygote/gate_learning.jl new file mode 100644 index 000000000..5a48d2d6f --- /dev/null +++ b/examples/PortZygote/gate_learning.jl @@ -0,0 +1,31 @@ +using YaoExtensions, Yao +using Test, Random +using QuAlgorithmZoo: Adam, update! + +include("zygote_patch.jl") + +function loss(u, ansatz) + m = Matrix(ansatz) + sum(abs.(u .- m)) +end + +function learn_su4(u::AbstractMatrix; optimizer=Adam(lr=0.1), niter=100) + ansatz = general_U4() * put(2, 1=>phase(0.0)) # initial values are 0, here, we attach a global phase. + params = parameters(ansatz) + for i=1:1000 + println("Step = $i, loss = $(loss(u,ansatz))") + grad = gradient(ansatz->loss(u, ansatz), ansatz)[1] + update!(params, grad, optimizer) + dispatch!(ansatz, params) + end + return ansatz +end + +using Random +Random.seed!(2) +u = rand_unitary(4) +using LinearAlgebra +#u[:,1] .*= -conj(det(u)) +#@show det(u) +c = learn_su4(u; optimizer=Adam(lr=0.005)) +det(mat(c)) diff --git a/examples/PortZygote/simple_example.jl b/examples/PortZygote/simple_example.jl new file mode 100644 index 000000000..ae10024f0 --- /dev/null +++ b/examples/PortZygote/simple_example.jl @@ -0,0 +1,17 @@ +include("zygote_patch.jl") + +import YaoExtensions, Random + +c = YaoExtensions.variational_circuit(5) +dispatch!(c, :random) + +function loss(reg::AbstractRegister, circuit::AbstractBlock{N}) where N + #copy(reg) |> circuit + reg = apply!(copy(reg), circuit) + st = state(reg) + sum(real(st.*st)) +end + +reg0 = zero_state(5) +paramsδ = gradient(c->loss(reg0, c), c)[1] +regδ = gradient(reg->loss(reg, c), reg0)[1] diff --git a/examples/PortZygote/zygote_patch.jl b/examples/PortZygote/zygote_patch.jl new file mode 100644 index 000000000..f57714604 --- /dev/null +++ b/examples/PortZygote/zygote_patch.jl @@ -0,0 +1,42 @@ +using Zygote +using Zygote: @adjoint +using Yao, Yao.AD + +@adjoint function apply!(reg::ArrayReg, block::AbstractBlock) + out = apply!(reg, block) + out, function (outδ) + (in, inδ), paramsδ = apply_back((out, outδ), block) + return (inδ, paramsδ) + end +end + +@adjoint function Matrix(block::AbstractBlock) + out = Matrix(block) + out, function (outδ) + paramsδ = mat_back(block, outδ) + return (paramsδ,) + end +end + +@adjoint function ArrayReg{B}(raw::AbstractArray) where B + ArrayReg{B}(raw), adjy->(reshape(adjy.state, size(raw)),) +end + +@adjoint function ArrayReg{B}(raw::ArrayReg) where B + ArrayReg{B}(raw), adjy->(adjy,) +end + +@adjoint function ArrayReg(raw::AbstractArray) + ArrayReg(raw), adjy->(reshape(adjy.state, size(raw)),) +end + +@adjoint function copy(reg::ArrayReg) where B + copy(reg), adjy->(adjy,) +end + +@adjoint state(reg::ArrayReg) = state(reg), adjy->(ArrayReg(adjy),) +@adjoint statevec(reg::ArrayReg) = statevec(reg), adjy->(ArrayReg(adjy),) +@adjoint state(reg::AdjointArrayReg) = state(reg), adjy->(ArrayReg(adjy')',) +@adjoint statevec(reg::AdjointArrayReg) = statevec(reg), adjy->(ArrayReg(adjy')',) +@adjoint parent(reg::AdjointArrayReg) = parent(reg), adjy->(adjy',) +@adjoint Base.adjoint(reg::ArrayReg) = Base.adjoint(reg), adjy->(parent(adjy),) From 07ff8be02f4b3168608dfe4fa2f3b58820cc3092 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Mon, 28 Oct 2019 15:43:55 +0800 Subject: [PATCH 60/85] switch to LBFGS --- examples/PortZygote/gate_learning.jl | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/PortZygote/gate_learning.jl b/examples/PortZygote/gate_learning.jl index 5a48d2d6f..ec4e9d596 100644 --- a/examples/PortZygote/gate_learning.jl +++ b/examples/PortZygote/gate_learning.jl @@ -1,7 +1,8 @@ using YaoExtensions, Yao using Test, Random -using QuAlgorithmZoo: Adam, update! +using Optim: LBFGS, optimize +# port the `Matrix` function to Yao's AD. include("zygote_patch.jl") function loss(u, ansatz) @@ -9,23 +10,22 @@ function loss(u, ansatz) sum(abs.(u .- m)) end -function learn_su4(u::AbstractMatrix; optimizer=Adam(lr=0.1), niter=100) +""" + learn_u4(u::AbstractMatrix; niter=100) + +Learn a general U4 gate. The optimizer is LBFGS. +""" +function learn_u4(u::AbstractMatrix; niter=100) ansatz = general_U4() * put(2, 1=>phase(0.0)) # initial values are 0, here, we attach a global phase. params = parameters(ansatz) - for i=1:1000 - println("Step = $i, loss = $(loss(u,ansatz))") - grad = gradient(ansatz->loss(u, ansatz), ansatz)[1] - update!(params, grad, optimizer) - dispatch!(ansatz, params) - end + g!(G, x) = (dispatch!(ansatz, x); G .= gradient(ansatz->loss(u, ansatz), ansatz)[1]) + optimize(x->(dispatch!(ansatz, x); loss(u, ansatz)), g!, parameters(ansatz), + LBFGS(), Optim.Options(iterations=niter)) + println("final loss = $(loss(u,ansatz))") return ansatz end using Random Random.seed!(2) u = rand_unitary(4) -using LinearAlgebra -#u[:,1] .*= -conj(det(u)) -#@show det(u) -c = learn_su4(u; optimizer=Adam(lr=0.005)) -det(mat(c)) +c = learn_u4(u) From 8f1bcd4b7a9595221515e359999a606c26f0adbd Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 13 Nov 2019 18:30:31 +0800 Subject: [PATCH 61/85] new, port zygote (#14) * new, port zygote * switch to LBFGS --- examples/PortZygote/gate_learning.jl | 31 ++++++++++++++++++++ examples/PortZygote/simple_example.jl | 17 +++++++++++ examples/PortZygote/zygote_patch.jl | 42 +++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 examples/PortZygote/gate_learning.jl create mode 100644 examples/PortZygote/simple_example.jl create mode 100644 examples/PortZygote/zygote_patch.jl diff --git a/examples/PortZygote/gate_learning.jl b/examples/PortZygote/gate_learning.jl new file mode 100644 index 000000000..ec4e9d596 --- /dev/null +++ b/examples/PortZygote/gate_learning.jl @@ -0,0 +1,31 @@ +using YaoExtensions, Yao +using Test, Random +using Optim: LBFGS, optimize + +# port the `Matrix` function to Yao's AD. +include("zygote_patch.jl") + +function loss(u, ansatz) + m = Matrix(ansatz) + sum(abs.(u .- m)) +end + +""" + learn_u4(u::AbstractMatrix; niter=100) + +Learn a general U4 gate. The optimizer is LBFGS. +""" +function learn_u4(u::AbstractMatrix; niter=100) + ansatz = general_U4() * put(2, 1=>phase(0.0)) # initial values are 0, here, we attach a global phase. + params = parameters(ansatz) + g!(G, x) = (dispatch!(ansatz, x); G .= gradient(ansatz->loss(u, ansatz), ansatz)[1]) + optimize(x->(dispatch!(ansatz, x); loss(u, ansatz)), g!, parameters(ansatz), + LBFGS(), Optim.Options(iterations=niter)) + println("final loss = $(loss(u,ansatz))") + return ansatz +end + +using Random +Random.seed!(2) +u = rand_unitary(4) +c = learn_u4(u) diff --git a/examples/PortZygote/simple_example.jl b/examples/PortZygote/simple_example.jl new file mode 100644 index 000000000..ae10024f0 --- /dev/null +++ b/examples/PortZygote/simple_example.jl @@ -0,0 +1,17 @@ +include("zygote_patch.jl") + +import YaoExtensions, Random + +c = YaoExtensions.variational_circuit(5) +dispatch!(c, :random) + +function loss(reg::AbstractRegister, circuit::AbstractBlock{N}) where N + #copy(reg) |> circuit + reg = apply!(copy(reg), circuit) + st = state(reg) + sum(real(st.*st)) +end + +reg0 = zero_state(5) +paramsδ = gradient(c->loss(reg0, c), c)[1] +regδ = gradient(reg->loss(reg, c), reg0)[1] diff --git a/examples/PortZygote/zygote_patch.jl b/examples/PortZygote/zygote_patch.jl new file mode 100644 index 000000000..f57714604 --- /dev/null +++ b/examples/PortZygote/zygote_patch.jl @@ -0,0 +1,42 @@ +using Zygote +using Zygote: @adjoint +using Yao, Yao.AD + +@adjoint function apply!(reg::ArrayReg, block::AbstractBlock) + out = apply!(reg, block) + out, function (outδ) + (in, inδ), paramsδ = apply_back((out, outδ), block) + return (inδ, paramsδ) + end +end + +@adjoint function Matrix(block::AbstractBlock) + out = Matrix(block) + out, function (outδ) + paramsδ = mat_back(block, outδ) + return (paramsδ,) + end +end + +@adjoint function ArrayReg{B}(raw::AbstractArray) where B + ArrayReg{B}(raw), adjy->(reshape(adjy.state, size(raw)),) +end + +@adjoint function ArrayReg{B}(raw::ArrayReg) where B + ArrayReg{B}(raw), adjy->(adjy,) +end + +@adjoint function ArrayReg(raw::AbstractArray) + ArrayReg(raw), adjy->(reshape(adjy.state, size(raw)),) +end + +@adjoint function copy(reg::ArrayReg) where B + copy(reg), adjy->(adjy,) +end + +@adjoint state(reg::ArrayReg) = state(reg), adjy->(ArrayReg(adjy),) +@adjoint statevec(reg::ArrayReg) = statevec(reg), adjy->(ArrayReg(adjy),) +@adjoint state(reg::AdjointArrayReg) = state(reg), adjy->(ArrayReg(adjy')',) +@adjoint statevec(reg::AdjointArrayReg) = statevec(reg), adjy->(ArrayReg(adjy')',) +@adjoint parent(reg::AdjointArrayReg) = parent(reg), adjy->(adjy',) +@adjoint Base.adjoint(reg::ArrayReg) = Base.adjoint(reg), adjy->(parent(adjy),) From 7d5c2398840cad822a095295e6559c6bafca715e Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Sun, 17 Nov 2019 20:13:29 +0800 Subject: [PATCH 62/85] move QSVD to examples folder --- examples/PortZygote/gate_learning.jl | 3 +- {src => examples/QSVD}/QSVD.jl | 33 +++++++++++++++---- examples/QSVD/README.md | 2 ++ examples/StructureOpt/StructureOpt.jl | 46 +++++++++++++++++++++++++++ src/QuAlgorithmZoo.jl | 1 - test/QSVD.jl | 21 ------------ 6 files changed, 76 insertions(+), 30 deletions(-) rename {src => examples/QSVD}/QSVD.jl (75%) create mode 100644 examples/QSVD/README.md create mode 100644 examples/StructureOpt/StructureOpt.jl delete mode 100644 test/QSVD.jl diff --git a/examples/PortZygote/gate_learning.jl b/examples/PortZygote/gate_learning.jl index ec4e9d596..957655377 100644 --- a/examples/PortZygote/gate_learning.jl +++ b/examples/PortZygote/gate_learning.jl @@ -1,6 +1,7 @@ using YaoExtensions, Yao using Test, Random using Optim: LBFGS, optimize +using Optim # port the `Matrix` function to Yao's AD. include("zygote_patch.jl") @@ -28,4 +29,4 @@ end using Random Random.seed!(2) u = rand_unitary(4) -c = learn_u4(u) +c = learn_u4(u; niter=150) diff --git a/src/QSVD.jl b/examples/QSVD/QSVD.jl similarity index 75% rename from src/QSVD.jl rename to examples/QSVD/QSVD.jl index 29705b3ab..3b2618031 100644 --- a/src/QSVD.jl +++ b/examples/QSVD/QSVD.jl @@ -1,6 +1,7 @@ -# Quantum SVD -# Reference: https://arxiv.org/abs/1905.01353 -export QuantumSVD, circuit_qsvd, train_qsvd!, readout_qsvd +using QuAlgorithmZoo, Yao,YaoExtensions +using BitBasis: log2i +using Test +using Random, LinearAlgebra """ Quantum singular value decomposition algorithm. @@ -14,12 +15,12 @@ Quantum singular value decomposition algorithm. """ function train_qsvd!(reg, circuit_a::AbstractBlock{Na}, circuit_b::AbstractBlock{Nb}, optimizer; Nc::Int=min(Na, Nb), maxiter::Int=100) where {Na, Nb} nbit = Na+Nb - c = circuit_qsvd(circuit_a, circuit_b, Nc) |> autodiff(:QC) # construct a differentiable circuit for training + c = circuit_qsvd(circuit_a, circuit_b, Nc) obs = -mapreduce(i->put(nbit, i=>Z), +, (1:Na..., Na+Nc+1:Na+Nb...)) params = parameters(c) for i = 1:maxiter - grad = opdiff.(() -> copy(reg) |> c, collect_blocks(Diff, c), Ref(obs)) + grad = expect'(obs, reg => c).second QuAlgorithmZoo.update!(params, grad, optimizer) println("Iter $i, Loss = $(Na+expect(obs, copy(reg) |> c))") dispatch!(c, params) @@ -54,8 +55,8 @@ kwargs includes * `optimizer`, default is `Adam(lr=0.1)`. """ function QuantumSVD(M::AbstractMatrix; Nc::Int=log2i(min(size(M)...)), - circuit_a=variational_circuit(log2i(size(M, 1)), 5, pair_ring(log2i(size(M, 1)))), - circuit_b=variational_circuit(log2i(size(M, 2)), 5, pair_ring(log2i(size(M, 2)))), + circuit_a=variational_circuit(log2i(size(M, 1))), + circuit_b=variational_circuit(log2i(size(M, 2))), maxiter=200, optimizer=Adam(lr=0.1)) dispatch!(circuit_a, :random) @@ -64,3 +65,21 @@ function QuantumSVD(M::AbstractMatrix; Nc::Int=log2i(min(size(M)...)), train_qsvd!(reg, circuit_a, circuit_b, optimizer, Nc=Nc, maxiter=maxiter) readout_qsvd(reg, circuit_a, circuit_b, Nc) end + +@testset "QSVD" begin + Random.seed!(2) + # define a matrix of size (2^Na, 2^Nb) + Na = 2 + Nb = 2 + + # the exact result + M = reshape(rand_state(Na+Nb).state, 1< abs2, I, atol=1e-2) + @test isapprox(V'*V_exact .|> abs2, I, atol=1e-2) +end diff --git a/examples/QSVD/README.md b/examples/QSVD/README.md new file mode 100644 index 000000000..8d259f71f --- /dev/null +++ b/examples/QSVD/README.md @@ -0,0 +1,2 @@ +# Quantum SVD +## Reference: https://arxiv.org/abs/1905.01353 diff --git a/examples/StructureOpt/StructureOpt.jl b/examples/StructureOpt/StructureOpt.jl new file mode 100644 index 000000000..7fb5da1fb --- /dev/null +++ b/examples/StructureOpt/StructureOpt.jl @@ -0,0 +1,46 @@ +using Yao, YaoExtensions +using YaoBlocks.Optimise: replace_block +using YaoBlocks.ConstGate +using YaoBlocks +using Test +using LinearAlgebra + +""" + TODO: + * optimize to optimal value, + * if this optimal value's loss is close to `θ = 0.0`, kill it. +""" +performance(params, gradient) = abs.(params .* gradient) + +nbit = 5 +c = variational_circuit(nbit, 50) + +dispatch!(c, :random) +h = heisenberg(nbit) +cc = replace_block(x->x isa RotationGate ? Bag(x) : x, c) + +function train(h, c; kill_rate=0.1, kill_step=10, niter=100) + bags = blockfilter(b->b isa Bag && isenabled(b), c) + for i=1:niter + regδ, paramsδ = expect'(h, zero_state(nbit) => c) + dispatch!(-, c, 0.01 .* paramsδ) + loss = expect(h, zero_state(nbit)=>c) |> real + + Nk = round(Int, kill_rate*length(paramsδ)) + + if i%kill_step == 0 && kill_rate != 0 + ps = performance(parameters(c), paramsδ) + cut = sort(ps)[Nk] + disable_block!.(bags[ps .<= cut]) + bags = bags[ps .> cut] + end + @show i,nparameters(c), loss + end +end + + +train(h, cc; kill_rate=0.01, niter=200) + +cr = variational_circuit(nbit, 5) +dispatch!(cr, :random) +train(h, cr; kill_rate=0.0, niter=200) diff --git a/src/QuAlgorithmZoo.jl b/src/QuAlgorithmZoo.jl index b434d9ebd..168ee3fd7 100644 --- a/src/QuAlgorithmZoo.jl +++ b/src/QuAlgorithmZoo.jl @@ -8,7 +8,6 @@ include("Adam.jl") include("PhaseEstimation.jl") include("hamiltonian_solvers.jl") include("HadamardTest.jl") -include("QSVD.jl") include("number_theory.jl") @deprecate random_diff_circuit variational_circuit diff --git a/test/QSVD.jl b/test/QSVD.jl deleted file mode 100644 index 91aa5de89..000000000 --- a/test/QSVD.jl +++ /dev/null @@ -1,21 +0,0 @@ -using QuAlgorithmZoo, Yao -using Test -using Random, LinearAlgebra - -@testset "QSVD" begin - Random.seed!(2) - # define a matrix of size (2^Na, 2^Nb) - Na = 2 - Nb = 2 - - # the exact result - M = reshape(rand_state(Na+Nb).state, 1< abs2, I, atol=1e-2) - @test isapprox(V'*V_exact .|> abs2, I, atol=1e-2) -end From c8805e03ed0c396d7c01b8651b91452bf423be6a Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Wed, 11 Dec 2019 02:38:10 +0800 Subject: [PATCH 63/85] fix for Yao-0.6.0 --- examples/Grover/Grover.jl | 4 +- examples/HHL/HHLlib.jl | 2 +- examples/QCBM/Kernels.jl | 38 ----------- examples/QCBM/QCBM.jl | 44 ++++++++++--- examples/QCBM/qcbmlib.jl | 102 ------------------------------ examples/QSVD/QSVD.jl | 10 +-- examples/QuGAN/QuGAN.jl | 101 ++++++++++++++++++++--------- examples/QuGAN/QuGANlib.jl | 126 ------------------------------------- examples/Shor/Shor.jl | 2 +- src/PhaseEstimation.jl | 2 +- 10 files changed, 118 insertions(+), 313 deletions(-) delete mode 100644 examples/QCBM/Kernels.jl delete mode 100644 examples/QCBM/qcbmlib.jl delete mode 100644 examples/QuGAN/QuGANlib.jl diff --git a/examples/Grover/Grover.jl b/examples/Grover/Grover.jl index 0bd2d21f4..852597eca 100644 --- a/examples/Grover/Grover.jl +++ b/examples/Grover/Grover.jl @@ -63,7 +63,7 @@ function single_try(oracle, gen::AbstractBlock{N}, nstep::Int; nbatch::Int) wher return r end reg |> checker - res = measure_remove!(reg, (N+1)) + res = measure!(RemoveMeasured(), reg, (N+1)) return res, reg end @@ -76,7 +76,7 @@ checker = control(num_bit+1,ctrl, num_bit+1=>X) # The register is batched, with batch dimension `nshot`. # [`focus!`](@ref Yao.focus!) views the first 1-N qubts as system. -# For a batched register, [`measure_remove!`](@ref Yao.measure_remove!) +# For a batched register, [`measure!`](@ref Yao.measure!) # returns a vector of bitstring as output. # #### Run diff --git a/examples/HHL/HHLlib.jl b/examples/HHL/HHLlib.jl index a4318ff36..297e5bfa9 100644 --- a/examples/HHL/HHLlib.jl +++ b/examples/HHL/HHLlib.jl @@ -56,7 +56,7 @@ function hhlcircuit(UG, n_reg::Int, C_value::Real) n_all = 1 + n_reg + n_b pe = PEBlock(UG, n_reg, n_b) cr = HHLCRot{n_reg+1}([2:n_reg+1...], 1, C_value) - chain(n_all, concentrate(n_all, pe, [2:n_all...,]), concentrate(n_all, cr, [1:(n_reg+1)...,]), concentrate(n_all, pe', [2:n_all...,])) + chain(n_all, subroutine(n_all, pe, [2:n_all...,]), subroutine(n_all, cr, [1:(n_reg+1)...,]), subroutine(n_all, pe', [2:n_all...,])) end """ diff --git a/examples/QCBM/Kernels.jl b/examples/QCBM/Kernels.jl deleted file mode 100644 index 9abad2d2d..000000000 --- a/examples/QCBM/Kernels.jl +++ /dev/null @@ -1,38 +0,0 @@ -export AbstractKernel, RBFKernel, kmat, rbf_kernel, kernel_expect - -abstract type AbstractKernel end - -""" - RBFKernel(σ, matrix) - -RBF Kernel with dense array as kernel matrix. -""" -struct RBFKernel <: AbstractKernel - sigma::Float64 - matrix::Matrix{Float64} -end - -""" - kmat(k::AbstractKernel) -> Matrix - -Returns Kernel Matrix. -""" -function kmat end -kmat(mbf::RBFKernel) = mbf.matrix - -""" - rbf_kernel(basis, σ::Real) -> RBFKernel - -Returns RBF Kernel Matrix. -""" -function rbf_kernel(basis, σ::Real) - dx2 = (basis .- basis').^2 - RBFKernel(σ, exp.(-1/2σ * dx2)) -end - -""" - kernel_expect(kernel::AbstractKernel, px::Vector{Float64}, py::Vector{Float64}) -> Float64 - -Returns the expectation value of kernel on specific distributions. -""" -kernel_expect(kernel::AbstractKernel, px::Vector, py::Vector=px) = px' * kmat(kernel) * py diff --git a/examples/QCBM/QCBM.jl b/examples/QCBM/QCBM.jl index c6ef9ff08..1dbce8ac8 100644 --- a/examples/QCBM/QCBM.jl +++ b/examples/QCBM/QCBM.jl @@ -1,28 +1,54 @@ # # Quantum Circuit Born Machine - using Yao, YaoExtensions -import QuAlgorithmZoo -include("qcbmlib.jl") +import Yao: probs +using QuAlgorithmZoo: Adam, update! + +struct QCBM{BT<:AbstractBlock, MT<:MMD} + circuit::BT + mmd::MT +end + +"""generated output probability distribution""" +function probs(qcbm::QCBM) + zero_state(qcbm.circuit |> nqubits) |> qcbm.circuit |> probs +end + +function loss(qcbm::QCBM) + expect(qcbm.mmd, probs(qcbm) |> as_weights) +end + +function getgrad(qcbm::QCBM) + expect'(qcbm.mmd, zero_state(nqubits(qcbm.circuit))=>qcbm.circuit).second +end # ## DATA: Target Probability Distribution # The gaussian probability disctribution in phase space of 2^6 nbit = 6 N = 1< autodiff(:QC); +kernel = rbf_kernel(0.25) +c = variational_circuit(nbit, depth, pair_ring(nbit)) dispatch!(c, :random) -qcbm = QCBM(c, kernel, pg); +qcbm = QCBM(c, MMD(kernel, pg)) # ## TRAINING: Adam Optimizer # We probide the QCBMGo! iterative interface for training niter = 100 optim = Adam(lr=0.1) -for info in QCBMGo!(qcbm, optim, niter) - curr_loss = loss(qcbm, info["probs"]) - println("Step = ", info["step"], ", Loss = ", curr_loss) + +params = parameters(qcbm.circuit) +for i=1:niter + # initialize the parameters + update!(params, getgrad(qcbm), optim) + dispatch!(qcbm.circuit, params) + println("Step = $i, Loss = $(loss(qcbm))") end diff --git a/examples/QCBM/qcbmlib.jl b/examples/QCBM/qcbmlib.jl deleted file mode 100644 index 79a5620c8..000000000 --- a/examples/QCBM/qcbmlib.jl +++ /dev/null @@ -1,102 +0,0 @@ -import Yao: probs -export QCBM, QCBMGo!, psi, mmdgrad - -include("Kernels.jl") - -##### QCBM methods ##### -struct QCBM{BT<:AbstractBlock, KT<:AbstractKernel} - circuit::BT - kernel::KT - ptrain::Vector{Float64} - - dbs -end -function QCBM(circuit::AbstractBlock, kernel::AbstractKernel, ptrain::Vector) - QCBM(circuit, kernel, ptrain, collect_blocks(Diff, circuit)) -end - -# INTERFACES -circuit(qcbm::QCBM) = qcbm.circuit -diff_blocks(qcbm::QCBM) = qcbm.dbs -""" - loss(qcbm::QCBM, [p=qcbm|>probs]) -> Float64 - -loss function, optional parameter `p` is the probability distribution. -""" -function loss(qcbm::QCBM, p=qcbm|>probs) - p -= qcbm.ptrain - kernel_expect(qcbm.kernel, p, p) -end -""" - gradient(qcbm::QCBM, p0=qcbm |> probs) -> Vector - -gradient of MMD two sample test loss, `db` must be contained in qcbm. -optional parameter `p0` is current probability distribution. -""" -gradient(qcbm::QCBM, p0=qcbm |> probs) = mmdgrad.(Ref(qcbm), qcbm.dbs, p0=p0) - -"""generated wave function""" -psi(qcbm) = zero_state(qcbm.circuit |> nqubits) |> qcbm.circuit -"""generated probability distribution""" -probs(qcbm::QCBM) = qcbm |> psi |> probs -""" - mmdgrad(qcbm::QCBM, db::Diff; p0::Vector) -> Float64 - -gradient of MMD two sample test loss, `db` must be contained in qcbm. -`p0` is current probability distribution. -""" -function mmdgrad(qcbm::QCBM, db::Diff; p0::Vector) - statdiff(()->probs(qcbm) |> as_weights, db, StatFunctional(kmat(qcbm.kernel)), initial=p0 |> as_weights) - - 2*statdiff(()->probs(qcbm) |> as_weights, db, StatFunctional(kmat(qcbm.kernel)*qcbm.ptrain)) -end - -""" -quantum circuit born machine trainer. -""" -struct QCBMGo!{QT<:QCBM, OT} - qcbm::QT - optimizer::OT - niter::Int -end -QCBMGo!(qcbm::QCBM, optimizer) = QCBMGo!(qcbm, optimizer, typemax(Int)) -Base.length(qo::QCBMGo!) = qo.niter - -function Base.iterate(qo::QCBMGo!, state=(1, parameters(qo.qcbm.circuit))) - state[1] > qo.niter && return nothing - - # initialize the parameters - p0 = qo.qcbm |> probs - grad = gradient(qo.qcbm, p0) - QuAlgorithmZoo.update!(state[2], grad, qo.optimizer) - dispatch!(qo.qcbm.circuit, state[2]) - Dict("probs"=>p0, "step"=>state[1], "gradient"=>grad), (state[1]+1, state[2]) -end - -""" - gaussian_pdf(x, μ::Real, σ::Real) - -gaussian probability density function. -""" -function gaussian_pdf(x, μ::Real, σ::Real) - pl = @. 1 / sqrt(2pi * σ^2) * exp(-(x - μ)^2 / (2 * σ^2)) - pl / sum(pl) -end - -@testset "qcbm" begin - # problem setup - n = 6 - depth = 6 - - N = 1< autodiff(:QC) - dispatch!(circuit, :random) - qcbm = QCBM(circuit, kernel, pg) - - # training - niter = 100 - optim = QuAlgorithmZoo.Adam(lr=0.1) - for info in QCBMGo!(qcbm, optim, niter) end - @test qcbm |> loss < 1e-4 -end diff --git a/examples/QSVD/QSVD.jl b/examples/QSVD/QSVD.jl index 3b2618031..e31ed51b0 100644 --- a/examples/QSVD/QSVD.jl +++ b/examples/QSVD/QSVD.jl @@ -31,12 +31,12 @@ end function circuit_qsvd(circuit_a::AbstractBlock{Na}, circuit_b::AbstractBlock{Nb}, Nc::Int) where {Na, Nb} nbit = Na+Nb cnots = chain(control(nbit, i+Na, i=>X) for i=1:Nc) - c = chain(concentrate(nbit, circuit_a, 1:Na), concentrate(nbit, circuit_b, Na+1:nbit), cnots) + c = chain(subroutine(nbit, circuit_a, 1:Na), subroutine(nbit, circuit_b, Na+1:nbit), cnots) end """read QSVD results""" function readout_qsvd(reg::AbstractRegister, circuit_a::AbstractBlock{Na}, circuit_b::AbstractBlock{Nb}, Nc::Int) where {Na, Nb} - reg = copy(reg) |> concentrate(Na+Nb, circuit_a, 1:Na) |> concentrate(Na+Nb, circuit_b, Na+1:Na+Nb) + reg = copy(reg) |> subroutine(Na+Nb, circuit_a, 1:Na) |> subroutine(Na+Nb, circuit_b, Na+1:Na+Nb) _S = [select(reg, b|b<ConstGate.P0) + new{N}(target, gen, dis, zero_state(N), witness_op, c) + end +end + +# INTERFACES +circuit(qg::QuGAN) = qg.circuit +loss(qg::QuGAN) = p0t(qg) - p0g(qg) + +function gradient(qg::QuGAN) + grad_gen = faithful_grad(qg.witness_op, qg.reg0 => qg.circuit) + grad_tar = faithful_grad(qg.witness_op, qg.target => qg.circuit[2:end]) + ngen = nparameters(qg.generator) + [-grad_gen[1:ngen]; grad_tar - grad_gen[ngen+1:end]] +end + +"""probability to get evidense qubit 0 on generation set.""" +p0g(qg::QuGAN) = expect(qg.witness_op, qg.reg0 => qg.circuit) |> real +"""probability to get evidense qubit 0 on target set.""" +p0t(qg::QuGAN) = expect(qg.witness_op, qg.target => qg.circuit[2:end]) |> real +"""generated wave function""" +outputψ(qg::QuGAN) = copy(qg.reg0) |> qg.generator -# ## MODEL: Quantum Circuit and Loss -# using a 4-layer random differential circuit for both generator and discriminator -# we build the qcgan setup. +"""tracedistance between target and generated wave function""" +tracedist(qg::QuGAN) = tracedist(qg.target, outputψ(qg))[] + +using Test, Random +Random.seed!(2) + +nbit = 3 depth_gen = 4 -generator = dispatch!(variational_circuit(nbit, depth_gen, pair_ring(nbit)), :random) |> autodiff(:QC); - -#------------------------------ -depth_disc = 4 -discriminator = dispatch!(variational_circuit(nbit+1, depth_disc, pair_ring(nbit+1)), :random) |> autodiff(:QC) -qg = QuGAN(target_state, generator, discriminator); - -# ## TRAINING: Gradient Descent -# using a proper learning parameters, we perform 1000 steps of training -g_learning_rate=0.2 -d_learning_rate=0.5 -niter=1000 -for info in QuGANGo!(qg, g_learning_rate, d_learning_rate, niter) - i = info["step"] - (i*20)%niter==0 && println("Step = $i, Trace Distance = $(tracedist(qg)), loss = $(qg |> loss)") +depth_dis = 4 + +# define a QuGAN +target = rand_state(nbit) +generator = dispatch!(variational_circuit(nbit, depth_gen), :random) +discriminator = dispatch!(variational_circuit(nbit+1, depth_dis), :random) +qg = QuGAN(target, generator, discriminator) + +# check the gradient +grad = gradient(qg) +numgrad = numdiff(c->loss(qg), qg.circuit) +@test isapprox(grad, numgrad, atol=1e-4) + +# learning rates for the generator and discriminator +g_lr = 0.2 +d_lr = 0.5 +for i=1:300 + ng = nparameters(qg.generator) + grad = gradient(qg) + dispatch!(-, qg.generator, grad[1:ng]*g_lr) + dispatch!(-, qg.discriminator, -grad[ng+1:end]*d_lr) + println("Step $i, trace distance = $(tracedist(qg))") end + +@test qg |> loss < 0.1 diff --git a/examples/QuGAN/QuGANlib.jl b/examples/QuGAN/QuGANlib.jl deleted file mode 100644 index 1d1aaae49..000000000 --- a/examples/QuGAN/QuGANlib.jl +++ /dev/null @@ -1,126 +0,0 @@ -import Yao: tracedist - -export QuGAN, psi, toy_qugan, QuGANGo! - -""" -Quantum GAN. - -Reference: - Benedetti, M., Grant, E., Wossnig, L., & Severini, S. (2018). Adversarial quantum circuit learning for pure state approximation, 1–14. -""" -struct QuGAN{N} - target::ArrayReg - generator::AbstractBlock{N} - discriminator::AbstractBlock - reg0::ArrayReg - witness_op::AbstractBlock - circuit::AbstractBlock - gdiffs - ddiffs - - function QuGAN(target::ArrayReg, gen::AbstractBlock, dis::AbstractBlock) - N = nqubits(target) - c = Sequence([gen, addbits!(1), dis]) - witness_op = put(N+1, (N+1)=>P0) - gdiffs = collect_blocks(Diff, gen) - ddiffs = collect_blocks(Diff, dis) - new{N}(target, gen, dis, zero_state(N), witness_op, c, gdiffs, ddiffs) - end -end - -# INTERFACES -circuit(qg::QuGAN) = qg.circuit -diff_blocks(qg::QuGAN) = [qg.gdiffs...; qg.ddiffs...] -loss(qg::QuGAN) = p0t(qg) - p0g(qg) -function gradient(qg::QuGAN) - ggrad_g = opdiff.(()->psi_discgen(qg), qg.gdiffs, Ref(qg.witness_op)) - dgrad_g = opdiff.(()->psi_discgen(qg), qg.ddiffs, Ref(qg.witness_op)) - dgrad_t = opdiff.(()->psi_disctarget(qg), qg.ddiffs, Ref(qg.witness_op)) - [-ggrad_g; dgrad_t - dgrad_g] -end - -"""probability to get evidense qubit 0 on generation set.""" -p0g(qg::QuGAN) = expect(qg.witness_op, psi_discgen(qg)) |> real -"""probability to get evidense qubit 0 on target set.""" -p0t(qg::QuGAN) = expect(qg.witness_op, psi_disctarget(qg)) |> real -"""generated wave function""" -psi(qg::QuGAN) = copy(qg.reg0) |> qg.generator -"""input |> generator |> discriminator""" -psi_discgen(qg::QuGAN) = copy(qg.reg0) |> qg.circuit -"""target |> discriminator""" -psi_disctarget(qg::QuGAN) = copy(qg.target) |> qg.circuit[2:end] -"""tracedistance between target and generated wave function""" -tracedist(qg::QuGAN) = tracedist(qg.target, psi(qg))[] - -""" - toy_qugan(target::ArrayReg, depth_gen::Int, depth_disc::Int) -> QuGAN - -Construct a toy qugan. -""" -function toy_qugan(target::ArrayReg, depth_gen::Int, depth_disc::Int) - n = nqubits(target) - generator = dispatch!(variational_circuit(n, depth_gen, pair_ring(n)), :random) |> autodiff(:QC) - discriminator = dispatch!(variational_circuit(n+1, depth_disc, pair_ring(n+1)), :random) |> autodiff(:QC) - return QuGAN(target, generator, discriminator) -end - -# Optimization -""" - QuGANGo!{QT<:QuGAN, OT} <: QCOptGo!{QT} - -Iterative training of quantum generative optimization problem, -QT is the type of quantum optimization problem, -OT is the optimizer/learning_rate parameter type. -""" -struct QuGANGo!{QT<:QuGAN, OT} - qop::QT - goptim::OT - doptim::OT - niter::Int -end -QuGANGo!(qg::QuGAN, glr, dlr) = QuGANGo!(qg, glr, dlr, typemax(Int)) -Base.length(qgg::QuGANGo!) = qgg.niter - -function Base.iterate(qgg::QuGANGo!{<:Any, <:Real}, state=1) - state > qgg.niter && return nothing - - qg = qgg.qop - ng = length(qg.gdiffs) - grad = gradient(qg) - dispatch!(+, qg.generator, -grad[1:ng]*qgg.goptim) - dispatch!(-, qg.discriminator, -grad[ng+1:end]*qgg.doptim) - Dict("step"=>state,"gradient"=>grad), state+1 -end - -function Base.iterate(qgg::QuGANGo!{<:Any, <:QuAlgorithmZoo.Adam}, state=(1, parameters(qgg.qop.generator), parameters(qgg.qop.discriminator))) - state[1] > qgg.niter && return nothing - - qg = qgg.qop - ng = length(qg.gdiffs) - grad = gradient(qg) - dispatch!(qg.generator, QuAlgorithmZoo.update!(state[2], grad[1:ng], qgg.goptim)) - dispatch!(qg.discriminator, QuAlgorithmZoo.update!(state[3], -grad[ng+1:end], qgg.doptim)) - Dict("step"=>state[1], "gradient"=>grad), (state[1]+1, state[2], state[3]) -end - -function run_test(nbit::Int, depth_gen::Int, depth_disc::Int; g_lr=0.1, d_lr=0.2, niter=1000) - qg = toy_qugan(rand_state(nbit), depth_gen, depth_disc) - for info in QuGANGo!(qg, g_lr, d_lr, niter) end - qg -end - -num_gradient(qop::QuGAN) = numdiff.(()->loss(qop), qop |> diff_blocks) - -# to fix -@testset "quantum circuit gan - opdiff" begin - Random.seed!(2) - N = 3 - target = rand_state(N) - qcg = toy_qugan(target, 2, 2) - grad = gradient(qcg) - @test isapprox(grad, num_gradient(qcg), atol=1e-4) - qg = run_test(3, 4, 4, g_lr=0.2, d_lr=0.5, niter=300) - @test qg |> loss < 0.1 - qg = run_test(3, 4, 4, g_lr=QuAlgorithmZoo.Adam(lr=0.005), d_lr=QuAlgorithmZoo.Adam(lr=0.5), niter=1000) - @test qg |> loss < 0.1 -end diff --git a/examples/Shor/Shor.jl b/examples/Shor/Shor.jl index bcf8294e7..87bca87d2 100644 --- a/examples/Shor/Shor.jl +++ b/examples/Shor/Shor.jl @@ -108,7 +108,7 @@ feeding input `|1>⊗|0>` will get the resulting quantum register with the desir function order_finding_circuit(x::Int, L::Int; nbit::Int, ncbit::Int) N = nbit+ncbit chain(N, repeat(N, H, 1:ncbit), KMod{N, ncbit}(x, L), - concentrate(N, QFTCircuit(ncbit)', 1:ncbit)) + subroutine(N, QFTCircuit(ncbit)', 1:ncbit)) end # The circuit for order finding is consisted of three parts diff --git a/src/PhaseEstimation.jl b/src/PhaseEstimation.jl index 63ba53b7c..5aef22073 100644 --- a/src/PhaseEstimation.jl +++ b/src/PhaseEstimation.jl @@ -23,7 +23,7 @@ function PEBlock(UG::GeneralMatrixBlock, n_reg::Int, n_b::Int) end # Inverse QFT Block. - iqft = concentrate(nbit, QFTBlock{n_reg}()',[1:n_reg...,]) + iqft = subroutine(nbit, QFTBlock{n_reg}()',[1:n_reg...,]) chain(hs, control_circuit, iqft) end From 6e3b34d38fdb7906a122abb4a0ba0d7b687e25a1 Mon Sep 17 00:00:00 2001 From: Lei Wang Date: Wed, 11 Dec 2019 08:14:23 +0800 Subject: [PATCH 64/85] rm unused codes --- examples/VQE/H2.jl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/VQE/H2.jl b/examples/VQE/H2.jl index 00a40a8e7..708034263 100644 --- a/examples/VQE/H2.jl +++ b/examples/VQE/H2.jl @@ -10,13 +10,6 @@ function hydrogen_hamiltonian() 0.011280*Z1*Z2 + 0.397936*Z1 + 0.397936*Z2 + 0.180931*X1*X2 end -function get_gradient(circ, hamiltonian) - ψ = zero_state(2) |> circ - dψ = copy(ψ) |> hamiltonian - backward!((copy(ψ), dψ), circ) - gradient(circ) -end - using Flux: Optimise function train!(circ, hamiltonian; optimizer, niter::Int=100) params = parameters(circ) From 35c738a5eccecc24be7fc500b47c423fbaa8981c Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 11 Dec 2019 10:10:16 +0800 Subject: [PATCH 65/85] Delete .travis.yml --- .travis.yml | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0835afc40..000000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -# Documentation: http://docs.travis-ci.com/user/languages/julia/ -language: julia -os: - - linux - - osx -julia: - - 1.0 - - 1.1 - - nightly -matrix: - allow_failures: - - julia: nightly - fast_finish: true -notifications: - email: false -after_success: - - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' - - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder())' From cffdaaa91da8949d5d4798eb0efbb0582a204d03 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 11 Dec 2019 10:12:06 +0800 Subject: [PATCH 66/85] Update README.md --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index fb16e2164..4a39db2d6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,5 @@ # QuAlgorithmZoo -[![Build Status](https://travis-ci.org/QuantumBFS/QuAlgorithmZoo.jl.svg?branch=master)](https://travis-ci.org/QuantumBFS/QuAlgorithmZoo.jl) -[![Build status](https://ci.appveyor.com/api/projects/status/wdbroxclvf1nhsen/branch/master?svg=true)](https://ci.appveyor.com/project/Roger-luo/qualgorithmzoo-jl/branch/master) -[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://quantumbfs.github.io/QuAlgorithmZoo.jl/stable/) -[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://quantumbfs.github.io/QuAlgorithmZoo.jl/latest/) - A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) *Note*: part of functionalities has been moved to [YaoExtensions](https://github.com/QuantumBFS/YaoExtensions.jl). From 0fe873e172bcef82ea890bd5a11f3c770349dc19 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 11 Dec 2019 10:13:07 +0800 Subject: [PATCH 67/85] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 4a39db2d6..b3b8560d1 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # QuAlgorithmZoo -A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) - -*Note*: part of functionalities has been moved to [YaoExtensions](https://github.com/QuantumBFS/YaoExtensions.jl). +A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) @0.6 ## Installation From 24e24f83a96f47930caa9030fab802d05cb39ccf Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 11 Dec 2019 10:16:04 +0800 Subject: [PATCH 68/85] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b3b8560d1..b7d6986ec 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ A curated implementation of quantum algorithms with [Yao.jl](https://github.com/ QuAlgorithmZoo.jl is not registered yet, please use the following command: ```julia -pkg> add https://github.com/QuantumBFS/QuAlgorithmZoo.jl.git +pkg> dev https://github.com/QuantumBFS/QuAlgorithmZoo.jl.git ``` -Disclaimer: **this package is still under development and needs further polish.** +Then open directory '~/.julia/dev/QuAlgorithmZoo/examples' to find algorithms. ## Contents From 832affd744d7f2743be04453ff77dc280b92d268 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 11 Dec 2019 10:19:03 +0800 Subject: [PATCH 69/85] Delete appveyor.yml --- appveyor.yml | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index eb4e82b24..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,49 +0,0 @@ -environment: - matrix: - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.7/julia-0.7-latest-win32.exe" - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.7/julia-0.7-latest-win64.exe" - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/1.0/julia-1.0-latest-win32.exe" - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/1.0/julia-1.0-latest-win64.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" - -## uncomment the following lines to allow failures on nightly julia -## (tests will run but not make your overall status red) -#matrix: -# allow_failures: -# - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" -# - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" - -branches: - only: - - master - - /release-.*/ - -notifications: - - provider: Email - on_build_success: false - on_build_failure: false - on_build_status_changed: false - -install: - - ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" -# If there's a newer build queued for the same PR, cancel this one - - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` - https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` - Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` - throw "There are newer queued builds for this pull request, failing early." } -# Download most recent Julia Windows binary - - ps: (new-object net.webclient).DownloadFile( - $env:JULIA_URL, - "C:\projects\julia-binary.exe") -# Run installer silently, output to C:\projects\julia - - C:\projects\julia-binary.exe /S /D=C:\projects\julia - -build_script: -# Need to convert from shallow to complete for Pkg.clone to work - - IF EXIST .git\shallow (git fetch --unshallow) - - C:\projects\julia\bin\julia -e "using InteractiveUtils; versioninfo(); - Pkg.clone(pwd(), \"QuAlgorithmZoo\"); Pkg.build(\"QuAlgorithmZoo\")" - -test_script: - - C:\projects\julia\bin\julia -e "Pkg.test(\"QuAlgorithmZoo\")" From 2ce1fd52944d35a1d470e54114fd577db22d4d19 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 11 Dec 2019 10:19:24 +0800 Subject: [PATCH 70/85] Delete .codecov.yml --- .codecov.yml | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .codecov.yml diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 69cb76019..000000000 --- a/.codecov.yml +++ /dev/null @@ -1 +0,0 @@ -comment: false From ef1b0f153f88bd9c15f2ce6ad797485b95e42687 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Wed, 18 Dec 2019 20:51:57 +0800 Subject: [PATCH 71/85] new gatelearning example --- examples/GateLearning/gate_learning.jl | 27 ++++++++++++++++++++++++++ src/hamiltonian_solvers.jl | 3 +-- test/HadamardTest.jl | 6 +++--- test/hamiltonian_solvers.jl | 4 ++-- test/runtests.jl | 4 ---- 5 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 examples/GateLearning/gate_learning.jl diff --git a/examples/GateLearning/gate_learning.jl b/examples/GateLearning/gate_learning.jl new file mode 100644 index 000000000..bbc76fcb8 --- /dev/null +++ b/examples/GateLearning/gate_learning.jl @@ -0,0 +1,27 @@ +using YaoExtensions, Yao +using Test, Random +using Optim: LBFGS, optimize +using Optim + +""" + learn_u4(u::AbstractMatrix; niter=100) + +Learn a general U4 gate. The optimizer is LBFGS. +""" +function learn_u4(u::AbstractBlock; niter=100) + ansatz = general_U4() + params = parameters(ansatz) + println("initial loss = $(operator_fidelity(u,ansatz))") + optimize(x->-operator_fidelity(u, dispatch!(ansatz, x)), + (G, x) -> (G .= -operator_fidelity'(u, dispatch!(ansatz, x))[2]), + parameters(ansatz), + LBFGS(), + Optim.Options(iterations=niter)) + println("final fidelity = $(operator_fidelity(u,ansatz))") + return ansatz +end + +using Random +Random.seed!(2) +u = matblock(rand_unitary(4)) +c = learn_u4(u; niter=150) diff --git a/src/hamiltonian_solvers.jl b/src/hamiltonian_solvers.jl index 7b5b71dfb..bb33b65ac 100644 --- a/src/hamiltonian_solvers.jl +++ b/src/hamiltonian_solvers.jl @@ -39,10 +39,9 @@ variational quantum eigensolver, faithful simulation with optimizer Adam(lr=0.01 """ function vqe_solve!(circuit::AbstractBlock{N}, hamiltonian::AbstractBlock; niter::Int=100) where N optimizer = Adam(lr=0.01) - dbs = collect_blocks(Diff, circuit) params = parameters(circuit) for i = 1:niter - grad = opdiff.(()->zero_state(N) |> circuit, dbs, Ref(hamiltonian)) + grad = expect'(hamiltonian, zero_state(N) => circuit).second dispatch!(circuit, update!(params, grad, optimizer)) println("Step $i, Energy = $(expect(hamiltonian, zero_state(N) |> circuit))") end diff --git a/test/HadamardTest.jl b/test/HadamardTest.jl index 0a5a43dd5..d04ab038e 100644 --- a/test/HadamardTest.jl +++ b/test/HadamardTest.jl @@ -20,11 +20,11 @@ single_swap_test(reg::AbstractRegister, ϕ::Real) = hadamard_test(SWAP, reg, ϕ) @test desired ≈ res desired = tr(Matrix(rho1)*Matrix(rho2)*Matrix(rho3)) |> real c = swap_test_circuit(2, 3, 0) - res = expect(put(7, 1=>Z), reduce(⊗, [reg3, reg2, reg1, zero_state(1)]) |> c) |> tr |> real + res = expect(put(7, 1=>Z), reduce(join, [reg3, reg2, reg1, zero_state(1)]) |> c) |> tr |> real @test desired ≈ res desired = tr(Matrix(rho1)*Matrix(rho2)*Matrix(rho3)) |> imag c = swap_test_circuit(2, 3, -π/2) - res = expect(put(7, 1=>Z), reduce(⊗, [reg3, reg2, reg1, zero_state(1)]) |> c) |> tr |> real + res = expect(put(7, 1=>Z), reduce(join, [reg3, reg2, reg1, zero_state(1)]) |> c) |> tr |> real @test desired ≈ res end @@ -36,7 +36,7 @@ end @test hadamard_test(U, reg, 0.0) ≈ real(expect(U, reg)) @test hadamard_test(U, reg, -π/2) ≈ imag(expect(U, reg)) - reg = zero_state(2) |> singlet_block() + reg = zero_state(2) |> YaoExtensions.singlet_block() @test single_swap_test(reg, 0) ≈ -1 reg = zero_state(2) diff --git a/test/hamiltonian_solvers.jl b/test/hamiltonian_solvers.jl index c5476c433..740618340 100644 --- a/test/hamiltonian_solvers.jl +++ b/test/hamiltonian_solvers.jl @@ -24,9 +24,9 @@ using YaoBlocks: ConstGate N = 4 h = heisenberg(N) E = eigen(h |> mat |> Matrix).values[1] - c = YaoExtensions.variational_circuit(N, 5, [i=>mod(i,N)+1 for i=1:N], mode=:Merged) |> autodiff(:QC) + c = YaoExtensions.variational_circuit(N, 5) dispatch!(c, :random) vqe_solve!(c, h) E2 = expect(h, zero_state(N) |> c) - @test isapprox(E, E2, atol=1e-1) + @test isapprox(E, E2, rtol=1e-1) end diff --git a/test/runtests.jl b/test/runtests.jl index 1bf9b2928..9634f07f8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,7 +14,3 @@ end @testset "hadamard test" begin include("HadamardTest.jl") end - -@testset "QSVD" begin - include("QSVD.jl") -end From ac5406141d1308f06914e61db7179bc33583b6b4 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Mon, 23 Dec 2019 17:05:32 +0800 Subject: [PATCH 72/85] new port quantum information --- .../PortQuantumInfromation/yao_port_qi.ipynb | 374 ++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 examples/PortQuantumInfromation/yao_port_qi.ipynb diff --git a/examples/PortQuantumInfromation/yao_port_qi.ipynb b/examples/PortQuantumInfromation/yao_port_qi.ipynb new file mode 100644 index 000000000..3c3a438d4 --- /dev/null +++ b/examples/PortQuantumInfromation/yao_port_qi.ipynb @@ -0,0 +1,374 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Porting Yao.jl with QuantumInformation.jl\n", + "### GiggleLiu" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# overview\n", + "\n", + " [`Yao`](https://github.com/QuantumBFS/Yao.jl) is a powerful tool for quantum circuit based simulation, but it does not support many density matrix related operations. This is why we need to port `Yao.jl` with [`QuantumInformation (QI)`](https://github.com/QuantumBFS/QuantumInformation.jl) sometimes (e.g. for computing entanglement entropy).\n", + " \n", + "* `Yao.jl` Documentation: https://quantumbfs.github.io/Yao.jl/latest/ (paper is comming out)\n", + "* `QuantumInformation.jl` paper: https://arxiv.org/abs/1806.11464\n", + " \n", + "### `Yao` provides\n", + "* high performance quantum circuit based simulation\n", + " * parameter management\n", + " * gradients\n", + " * batched regiser\n", + "* operator matrix representation and arithmatics\n", + "* [quantum algorithms](https://github.com/QuantumBFS/QuAlgorithmZoo.jl)\n", + "* [GPU support](https://github.com/QuantumBFS/CuYao.jl)\n", + "\n", + "### `QI` provides\n", + "\n", + "* Compute entropy from density matrices\n", + "* Quantum channels, four types of channel representations\n", + " * Kraus Operator\n", + " * Super operator\n", + " * Dynamic matrices\n", + " * Stinespring representation\n", + "* Compute norm, distance and distingushability between \"states\" (density matrices)\n", + " * Hilbert–Schmidt norm and distance\n", + " * trace norm and *distance*\n", + " * diamond norm\n", + " * Bures distane and Bures angles\n", + " * *fidelity* and superfidelity\n", + " * KL-divergence\n", + " * JS-distance\n", + "* Compute the amount of entanglement\n", + " * negativity\n", + " * positive partial trace\n", + " * concurrence\n", + "* POVM measurements" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "import Yao\n", + "using Yao: ArrayReg, ρ, mat, ConstGate, purify, exchange_sysenv, @bit_str, statevec\n", + "import QuantumInformation; const QI = QuantumInformation\n", + "using QuantumInformation: ket\n", + "using LinearAlgebra\n", + "using Test" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Obtain reduced density matrices in Yao\n", + "-------------------------\n", + "The memory layout of `Yao` register and `QI` ket are similar, their basis are both [little endian](https://en.wikipedia.org/wiki/Endianness), despite they have different representation powers\n", + "\n", + "* `Yao` support batch,\n", + "* `QI` is not limited to qubits.\n", + "\n", + "\n", + "`Yao` does not have much operations defined on density matrices, but purified states with environment,\n", + "On the other side, most operations in `QI` are defined on **(density) matrices**, they can be easily obtained in `Yao`." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[32m\u001b[1mTest Passed\u001b[22m\u001b[39m" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# construct a product state, notice the indexing in `QI` starts from `1`\n", + "@test QI.ket(3, 1<<4) ≈ statevec(ArrayReg(bit\"0010\"))\n", + "\n", + "# join two registers, notice little endian convension is used here.\n", + "reg = join(ArrayReg(bit\"10\"), ArrayReg(bit\"11\"))\n", + "v = QI.:⊗(QI.ket(0b10+1,1<<2), QI.ket(0b11+1,1<<2))\n", + "@test statevec(reg) ≈ v" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "16×16 Array{Complex{Float64},2}:\n", + " 0.0668399+0.0im … 0.0048149+0.00800254im \n", + " -0.00683079+0.00430075im 0.00271044+0.013467im \n", + " -0.00405524-0.00233655im 0.00489161-0.00506099im \n", + " 0.0041184-0.00690317im -0.00724508+0.00433365im \n", + " 0.000248112-0.00614303im -0.00169715-0.0060107im \n", + " -0.00638715+0.00343611im … -0.00346919+0.0104737im \n", + " -0.0032589-0.00594789im -0.00502371+0.00889227im \n", + " 0.0053714-0.00448422im 0.000149836+0.00490488im \n", + " -0.00485418+0.00190183im -0.00707738-0.0117206im \n", + " -0.00185245-0.0113168im -0.00100021+0.00456715im \n", + " 0.000202351+0.00648573im … 9.29962e-5-0.00362312im \n", + " 0.0038004-0.00408768im 0.00290617+0.0109155im \n", + " -0.00488166-0.00699333im -0.00471523+0.000137239im\n", + " 0.00485705+0.00532262im 0.00956895-0.00457732im \n", + " 0.00756613-0.00569826im 0.0032851+0.0014402im \n", + " 0.0048149-0.00800254im … 0.0786938+0.0im " + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# convert a Yao register to density matrix in QI\n", + "reg2dm(reg::ArrayReg{1}) = reg |> ρ |> Matrix\n", + "\n", + "# e.g. obtain a reduced denstiy matrix for subsystem 1,2,3,4\n", + "reg = Yao.rand_state(10)\n", + "freg = Yao.focus!(reg, 1:4) # make qubits 1-4 active\n", + "reg2dm(freg)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One can also convert a density matrix to a a quantum state through **purification**" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[32m\u001b[1mTest Passed\u001b[22m\u001b[39m" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# e.g. purify a state and recover it\n", + "reg = Yao.rand_state(6) |> Yao.focus!(1:4)\n", + "reg_p = purify(reg |> ρ; nbit_env=2)\n", + "@test Yao.fidelity(reg, reg_p)[] ≈ 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "entanglement & state distance\n", + "----------------\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[32m\u001b[1mTest Passed\u001b[22m\u001b[39m" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reg1 = Yao.rand_state(10)\n", + "freg1 = Yao.focus!(reg1, 1:4)\n", + "reg2 = Yao.rand_state(6)\n", + "freg2 = Yao.focus!(reg2, 1:4)\n", + "dm1, dm2 = freg1 |> reg2dm, freg2 |> reg2dm\n", + "\n", + "# trace distance between two registers (different by a factor 2)\n", + "@test Yao.tracedist(freg1, freg2)[]/2 ≈ QI.trace_distance(dm1, dm2)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QI.vonneumann_entropy(dm1) = 2.6568839293081608\n", + "QI.vonneumann_entropy(dm2) = 1.3245543916726097\n" + ] + }, + { + "data": { + "text/plain": [ + "1.3245543916726097" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# get the entanglement entropy between system and env\n", + "@show QI.vonneumann_entropy(dm1)\n", + "@show QI.vonneumann_entropy(dm2)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.5694621854109723" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# KL-divergence (or relative entropy)\n", + "QI.kl_divergence(dm2, dm1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note: you can defined many distances and entropies in a similar way, we don't enumerate it." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Quantum Operations/Quantum Gates\n", + "------------------------\n", + "\n", + "A quantum gate in `Yao` is equivalent to a unitary channel in `QI`, matrix representations of blocks in `Yao` can be used to construct channels." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "QuantumInformation.KrausOperators{Array{Complex{Float64},2}}\n", + " dimensions: (2, 2)\n", + " Complex{Float64}[1.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 0.0 + 0.0im]\n", + " Complex{Float64}[0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im]\n", + " Complex{Float64}[0.0 + 0.0im 1.0 + 0.0im; 0.0 + 0.0im 0.0 + 0.0im]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# construct a Kraus Operator\n", + "QI.KrausOperators([Matrix(ConstGate.P0), Matrix(ConstGate.P1), Matrix(ConstGate.Pu)])" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + ":(#= In[24]:9 =# @test (copy(reg) |> Yao.chain(b1, b2)) |> reg2dm ≈ (c2 ∘ c1)(reg |> reg2dm))" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# applying a rotation gate\n", + "b1 = Yao.put(2,2=>Yao.Rx(0.3π))\n", + "c1 = QI.UnitaryChannel(mat(b1))\n", + "b2 = Yao.put(2,2=>Yao.Ry(0.3π))\n", + "c2 = QI.UnitaryChannel(mat(b2))\n", + "\n", + "reg = Yao.rand_state(2)\n", + "@test copy(reg) |> b1 |> reg2dm ≈ c1(reg |> reg2dm)\n", + ":@test copy(reg) |> Yao.chain(b1,b2) |> reg2dm ≈ (c2∘c1)(reg |> reg2dm)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "@webio": { + "lastCommId": null, + "lastKernelId": null + }, + "kernelspec": { + "display_name": "Julia 1.3.0", + "language": "julia", + "name": "julia-1.3" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.3.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 06759a30367e52f6af477cf654d526e50dfe3579 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Mon, 23 Dec 2019 04:19:08 -0500 Subject: [PATCH 73/85] update project & README --- Project.toml | 9 ++++++--- README.md | 36 ++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/Project.toml b/Project.toml index a5f8bbd44..74a9f419c 100644 --- a/Project.toml +++ b/Project.toml @@ -3,17 +3,20 @@ uuid = "65c24e16-9b0a-11e8-1353-efc5bc5f6586" version = "0.1.0" [deps] -BitBasis = "50ba71b6-fa0f-514d-ae9a-0916efc90dcf" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +BitBasis = "50ba71b6-fa0f-514d-ae9a-0916efc90dcf" Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" YaoArrayRegister = "e600142f-9330-5003-8abb-0ebd767abc51" YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df" YaoExtensions = "7a06699c-c960-11e9-3c98-9f78548b5f0f" [compat] -Yao = ">=0.4.0" -julia = ">=1.0.0" +julia = "1" +Yao = "0.6.0" +BitBasis = "0.6" +YaoArrayRegister = "0.6" +YaoBlocks = "0.8, 0.9, 0.10" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/README.md b/README.md index b7d6986ec..8c9ef9e4a 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,38 @@ # QuAlgorithmZoo -A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) @0.6 +A curated implementation of quantum algorithms with [Yao.jl](https://github.com/QuantumBFS/Yao.jl) ## Installation -QuAlgorithmZoo.jl is not registered yet, please use the following command: +QuAlgorithmZoo.jl is not registered, please use the following command: ```julia pkg> dev https://github.com/QuantumBFS/QuAlgorithmZoo.jl.git ``` -Then open directory '~/.julia/dev/QuAlgorithmZoo/examples' to find algorithms. +Then open directory `.julia/dev/QuAlgorithmZoo/examples` to find algorithms. ## Contents -- [x] [QFT](https://github.com/QuantumBFS/YaoExtensions.jl) -- [x] Phase Estimation -- [x] Imaginary Time Evolution Quantum Eigensolver -- [x] Variational Quantum Eigensolver -- [x] Hadamard Test -- [x] State Overlap Algorithms -- [x] Quantum SVD +- [QFT](https://github.com/QuantumBFS/YaoExtensions.jl) +- Phase Estimation +- Imaginary Time Evolution Quantum Eigensolver +- Variational Quantum Eigensolver +- Hadamard Test +- State Overlap Algorithms +- Quantum SVD In examples folder, you will find -- [x] HHL -- [x] QAOA -- [x] Quantum Circuit Born Machine -- [x] QuGAN -- [x] Shor -- [x] Grover search +- HHL +- QAOA +- Quantum Circuit Born Machine +- QuGAN +- Shor +- Grover search -- [x] [QuODE](https://github.com/QuantumBFS/QuDiffEq.jl) -- [x] [TensorNetwork Inspired Circuits](https://github.com/GiggleLiu/QuantumPEPS.jl) +- [QuODE](https://github.com/QuantumBFS/QuDiffEq.jl) +- [TensorNetwork Inspired Circuits](https://github.com/GiggleLiu/QuantumPEPS.jl) ## License From 068f8d6b02abd02be1aecd452469494f462664b8 Mon Sep 17 00:00:00 2001 From: Roger-luo Date: Mon, 23 Dec 2019 06:54:55 -0500 Subject: [PATCH 74/85] add compat --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 74a9f419c..86fc49a4f 100644 --- a/Project.toml +++ b/Project.toml @@ -17,6 +17,7 @@ Yao = "0.6.0" BitBasis = "0.6" YaoArrayRegister = "0.6" YaoBlocks = "0.8, 0.9, 0.10" +YaoExtensions = "0.2" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From 88fcff41aa66fa1b9b42de571a086704ef48af28 Mon Sep 17 00:00:00 2001 From: Julia TagBot <50554310+JuliaTagBot@users.noreply.github.com> Date: Mon, 24 Feb 2020 04:49:35 +0700 Subject: [PATCH 75/85] Install TagBot as a GitHub Action (#15) --- .github/workflows/TagBot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/workflows/TagBot.yml diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml new file mode 100644 index 000000000..d77d3a0c3 --- /dev/null +++ b/.github/workflows/TagBot.yml @@ -0,0 +1,11 @@ +name: TagBot +on: + schedule: + - cron: 0 * * * * +jobs: + TagBot: + runs-on: ubuntu-latest + steps: + - uses: JuliaRegistries/TagBot@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} From 8fdef4c1d44d4a7fbaeec9ac9304448fa8cf09fa Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Wed, 27 May 2020 23:24:57 -0400 Subject: [PATCH 76/85] update Zygote example --- Project.toml | 6 +++--- examples/PortZygote/shared_parameters.jl | 25 ++++++++++++++++++++++++ examples/PortZygote/zygote_patch.jl | 12 ++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 examples/PortZygote/shared_parameters.jl diff --git a/Project.toml b/Project.toml index 86fc49a4f..93d448e94 100644 --- a/Project.toml +++ b/Project.toml @@ -3,21 +3,21 @@ uuid = "65c24e16-9b0a-11e8-1353-efc5bc5f6586" version = "0.1.0" [deps] +BitBasis = "50ba71b6-fa0f-514d-ae9a-0916efc90dcf" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -BitBasis = "50ba71b6-fa0f-514d-ae9a-0916efc90dcf" Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" YaoArrayRegister = "e600142f-9330-5003-8abb-0ebd767abc51" YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df" YaoExtensions = "7a06699c-c960-11e9-3c98-9f78548b5f0f" [compat] -julia = "1" -Yao = "0.6.0" BitBasis = "0.6" +Yao = "0.6.0" YaoArrayRegister = "0.6" YaoBlocks = "0.8, 0.9, 0.10" YaoExtensions = "0.2" +julia = "1" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/examples/PortZygote/shared_parameters.jl b/examples/PortZygote/shared_parameters.jl new file mode 100644 index 000000000..1643176e5 --- /dev/null +++ b/examples/PortZygote/shared_parameters.jl @@ -0,0 +1,25 @@ +include("zygote_patch.jl") + +import YaoExtensions, Random + +c = YaoExtensions.variational_circuit(5) +h = YaoExtensions.heisenberg(5) + +function loss(h, c, θ) where N + # the assign is nessesary! + c = dispatch!(c, fill(θ, nparameters(c))) + reg = apply!(zero_state(nqubits(c)), c) + real(expect(h, reg)) +end + +reg0 = zero_state(5) +zygote_grad = Zygote.gradient(θ->loss(h, c, θ), 0.5)[1] + + +# check gradients +using Test +dispatch!(c, fill(0.5, nparameters(c))) +greg, gparams = expect'(h, zero_state(5)=>c) +true_grad = sum(gparams) + +@test true_grad ≈ true_grad diff --git a/examples/PortZygote/zygote_patch.jl b/examples/PortZygote/zygote_patch.jl index f57714604..a216a7e3f 100644 --- a/examples/PortZygote/zygote_patch.jl +++ b/examples/PortZygote/zygote_patch.jl @@ -10,6 +10,13 @@ using Yao, Yao.AD end end +@adjoint function Yao.dispatch!(block::AbstractBlock, params) + out = dispatch!(block, params) + out, function (outδ) + (nothing, outδ) + end +end + @adjoint function Matrix(block::AbstractBlock) out = Matrix(block) out, function (outδ) @@ -40,3 +47,8 @@ end @adjoint statevec(reg::AdjointArrayReg) = statevec(reg), adjy->(ArrayReg(adjy')',) @adjoint parent(reg::AdjointArrayReg) = parent(reg), adjy->(adjy',) @adjoint Base.adjoint(reg::ArrayReg) = Base.adjoint(reg), adjy->(parent(adjy),) +Zygote.@nograd Yao.nparameters +Zygote.@nograd Yao.zero_state +Zygote.@nograd Yao.rand_state +Zygote.@nograd Yao.uniform_state +Zygote.@nograd Yao.product_state From f7cd7efe771a00887966e34aecaff712d02d5b4f Mon Sep 17 00:00:00 2001 From: Neuromancer Date: Sun, 31 May 2020 17:07:41 +0800 Subject: [PATCH 77/85] Create VQE_H2_4bit.ipynb (#18) * Create VQE_H2_4bit.ipynb add example for H2 under Jordan-Weigner transformation with OpenFermion * Update and rename VQE_H2_4bit.ipynb to H2_OpenFermion.jl change the notebook to Literate.jl format for better maintenance * Update README.md --- examples/VQE/H2_OpenFermion.jl | 110 +++++++++++++++++++++++++++++++++ examples/VQE/README.md | 18 ++++++ 2 files changed, 128 insertions(+) create mode 100644 examples/VQE/H2_OpenFermion.jl diff --git a/examples/VQE/H2_OpenFermion.jl b/examples/VQE/H2_OpenFermion.jl new file mode 100644 index 000000000..b924fd9b1 --- /dev/null +++ b/examples/VQE/H2_OpenFermion.jl @@ -0,0 +1,110 @@ +# A more precise example of $\text H_2$ with 4 qubits hamiltonian. + +using Yao +using YaoExtensions + +# Here we we invoke [OpenFermion](https://github.com/quantumlib/OpenFermion) in Python +# to get the hamiltonian under [Jordan-Weigner transformation](https://en.wikipedia.org/wiki/Jordan%E2%80%93Wigner_transformation) +# (with 2 electrons and 4 sto-3g basis set orbitals). + +using PyCall + +py""" +import numpy as np +from openfermion.config import * +from openfermion.hamiltonians import MolecularData +from openfermion.transforms import jordan_wigner + +def make_H2_mol(bond_len): + + ## Returns: + ## molecule(openfermion.hamiltonians.MolecularData): + ## MolecularData for H2 with certain bond length + + ## load molecule + atom_1 = 'H' + atom_2 = 'H' + basis = 'sto-3g' + multiplicity = 1 + charge = 0 + coordinate_1 = (0.0, 0.0, 0.0) + coordinate_2 = (0.0, 0.0, bond_len) + geometry = [(atom_1, coordinate_1), (atom_2, coordinate_2)] + molecule = MolecularData(geometry, basis, multiplicity, + charge, description=str(bond_len)) + ## molecule = run_pyscf(molecule,run_scf=1,run_ccsd=1,run_fci=1) + ## can calculate on arbitrary bond length if package openfermionpyscf is available, + ## only existing molecule data can be used + molecule.load() + return molecule + +def get_hamiltonian(bond_len): + ## get the coefficients and pauli terms of Hamiltonian of H_2 with given bond length + ## Returns: + ## (PyArray,PyArray) + molecule = make_H2_mol(bond_len) + qubit_hamiltonian = jordan_wigner(molecule.get_molecular_hamiltonian()) + qubit_hamiltonian.compress() + return list(qubit_hamiltonian.terms.keys()),list(qubit_hamiltonian.terms.values()) + +""" + +# Define the function to transfer OpenFermion hamiltonian to Yao circuit + +function get_fermion_hamiltonian(n::Int,terms::Array,coefs::Vector) + gates=Dict("Z"=>Z,"X"=>X,"Y"=>Y) + to_pauli(t::Tuple{Int,String})=put(n,t[1]+1=>get(gates,t[2],ErrorException("Invalid"))) + return sum(coefs[2:end].*map(x->reduce(*,map(to_pauli,x)),terms[2:end])) +end + +# Use VQE code given in the previous Yao example + +using Flux: Optimise + +function train!(circ, hamiltonian; optimizer, niter::Int=200,verbose::Bool=true) + params = parameters(circ) + dispatch!(circ, :random) + for i=1:niter + _, grad = expect'(hamiltonian, zero_state(nqubits(circ)) => circ) + Optimise.update!(optimizer, params, grad) + dispatch!(circ, params) + if verbose + println("Energy = + $(expect(hamiltonian, zero_state(nqubits(hamiltonian)) |> circ) |> real)") + end + end + return expect(hamiltonian, zero_state(nqubits(hamiltonian)) |> circ) +end + +# Train VQE to search ground energy on various bond lengths. + +bond_lens = Array(0.2:0.1:1.5)#\AA +pes = Vector{Real}() + +for l in bond_lens + terms, coefs = py"get_hamiltonian"(l) + e = coefs[1] + + + h = get_fermion_hamiltonian(4,terms,coefs) + c = variational_circuit(4) + emin_vqe = train!(c, h; optimizer=Optimise.ADAM(0.1),verbose=false) + println("Estimated minimum energy at bond length $(l): $(e+emin_vqe|>real)") + push!(pes,e+emin_vqe|>real) + + using LinearAlgebra + emin = eigvals(Matrix(mat(h)))[1] + @assert isapprox(emin, emin_vqe, atol=1e-2) +end + +# Plot the potential energy surface (PES) + +using Plots + +plot(bond_lens,pes, markershape = :circle, linestyle = :dash, label = "VQE",ylabel = "Ground State Energy (Hartree)", xlabel = "Bond Length (Å) ") + +# Since OpenFermion only generates molecule data at discrete bond length with minimum step to be 0.1 Å, +# +# the VQE gives lowest energy -1.1361862609 Hartree at 0.7 Å. +# +# As a comparison, the ground energy at stable bond length given by FCI (Full Configuration Interaction) is -1.13730688 Hartree at 0.734832 Å. diff --git a/examples/VQE/README.md b/examples/VQE/README.md index 3ab48aece..a0fb31738 100644 --- a/examples/VQE/README.md +++ b/examples/VQE/README.md @@ -12,3 +12,21 @@ pkg> dev git@github.com:QuantumBFS/YaoExtensions.jl.git ```bash julia examples/VQE/H2.jl ``` +## More +4. set your Python environment in Julia +``` +julia> ENV["PYTHON"] = "... path of the python executable ..." +``` +5. install [PyCall](https://github.com/JuliaPy/PyCall.jl) +``` +pkg> add PyCall +pkg> build PyCall +``` +6. install [OpenFermion](https://github.com/quantumlib/OpenFermion) +```bash +pip install openfermion +``` +7. run H2_OpenFermion example +```bash +julia examples/VQE/H2_OpenFermion.jl +``` From 9dfae0e10b7b8cc4fe4413f165adb4907f8c0dff Mon Sep 17 00:00:00 2001 From: Neuromancer Date: Tue, 2 Jun 2020 03:51:59 +0800 Subject: [PATCH 78/85] Update H2.jl (#19) correct reference to global variable h->hamiltonian --- examples/VQE/H2.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/VQE/H2.jl b/examples/VQE/H2.jl index 708034263..5a1e0e1a7 100644 --- a/examples/VQE/H2.jl +++ b/examples/VQE/H2.jl @@ -18,9 +18,9 @@ function train!(circ, hamiltonian; optimizer, niter::Int=100) _, grad = expect'(hamiltonian, zero_state(nqubits(circ)) => circ) Optimise.update!(optimizer, params, grad) dispatch!(circ, params) - println("Energy = $(expect(hamiltonian, zero_state(nqubits(h)) |> circ) |> real)") + println("Energy = $(expect(hamiltonian, zero_state(nqubits(hamiltonian)) |> circ) |> real)") end - return expect(hamiltonian, zero_state(nqubits(h)) |> circ) + return expect(hamiltonian, zero_state(nqubits(hamiltonian)) |> circ) end h = hydrogen_hamiltonian() From ee45962727a738f614d3b7de9563ada9c1da2fff Mon Sep 17 00:00:00 2001 From: Neuromancer Date: Tue, 2 Jun 2020 19:41:34 +0800 Subject: [PATCH 79/85] Update maxcut_gw.jl (#20) the SCSsolver is deprecated in new SCS, shoud be SCS.optimizer now --- examples/QAOA/maxcut_gw.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/QAOA/maxcut_gw.jl b/examples/QAOA/maxcut_gw.jl index ad913b78a..dff4f928b 100644 --- a/examples/QAOA/maxcut_gw.jl +++ b/examples/QAOA/maxcut_gw.jl @@ -38,7 +38,7 @@ function goemansWilliamson(W::Matrix{T}; tol::Real=1e-1, iter::Int=100) where T< expr = dot(W, S) constr = [S[i,i] == 1.0 for i in 1:k] problem = minimize(expr, constr...) - solve!(problem, SCSSolver(verbose=0)) + solve!(problem, SCS.Optimizer(verbose=0)) ### Ensure symmetric positive-definite. A = 0.5 * (S.value + S.value') From 839ee0ec89fcdd7297a6294b21540a9a0f638316 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Fri, 19 Jun 2020 11:29:40 -0400 Subject: [PATCH 80/85] update project.toml --- Project.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 93d448e94..db3d4ec44 100644 --- a/Project.toml +++ b/Project.toml @@ -12,10 +12,10 @@ YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df" YaoExtensions = "7a06699c-c960-11e9-3c98-9f78548b5f0f" [compat] -BitBasis = "0.6" -Yao = "0.6.0" -YaoArrayRegister = "0.6" -YaoBlocks = "0.8, 0.9, 0.10" +BitBasis = "0.6, 0.7" +Yao = "0.6" +YaoArrayRegister = "0.6, 0.7" +YaoBlocks = "0.8, 0.9, 0.10, 0.11" YaoExtensions = "0.2" julia = "1" From 7d9743561e445a6b2044f76337f12dd445c1ddb1 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Mon, 5 Apr 2021 20:04:26 -0400 Subject: [PATCH 81/85] fix hhl --- examples/HHL/HHL.jl | 2 +- examples/HHL/HHLlib.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/HHL/HHL.jl b/examples/HHL/HHL.jl index 27822ef22..2c4901389 100644 --- a/examples/HHL/HHL.jl +++ b/examples/HHL/HHL.jl @@ -1,4 +1,4 @@ -using Yao +using Yao, YaoBlocks using BitBasis using YaoArrayRegister using Test, LinearAlgebra diff --git a/examples/HHL/HHLlib.jl b/examples/HHL/HHLlib.jl index 297e5bfa9..1ff8146a0 100644 --- a/examples/HHL/HHLlib.jl +++ b/examples/HHL/HHLlib.jl @@ -22,7 +22,7 @@ end a, -b, b, a end -function Yao.apply!(reg::ArrayReg, hr::HHLCRot{N, NC, T}) where {N, NC, T} +function YaoBlocks._apply!(reg::ArrayReg, hr::HHLCRot{N, NC, T}) where {N, NC, T} mask = bmask(hr.ibit) step = 1<<(hr.ibit-1) step_2 = step*2 @@ -71,7 +71,7 @@ function hhlsolve(A::Matrix, b::Vector, n_reg::Int, C_value::Real) UG = matblock(exp(2π*im.*A)) # Generating input bits - all_bit = ArrayReg(b) ⊗ zero_state(n_reg) ⊗ zero_state(1) + all_bit = join(ArrayReg(b), zero_state(n_reg), zero_state(1)) # Construct HHL circuit. circuit = hhlcircuit(UG, n_reg, C_value) From 20a0b5970115c0f4018a006f5a177dbff82645a2 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 11 Aug 2021 14:04:06 -0400 Subject: [PATCH 82/85] Switch AD example to chainrules (#22) * switch to chainrules * switch to Zygote --- examples/PortZygote/chainrules_patch.jl | 64 ++++++++++++++++++++++++ examples/PortZygote/gate_learning.jl | 7 +-- examples/PortZygote/shared_parameters.jl | 19 ++++++- examples/PortZygote/simple_example.jl | 3 +- examples/PortZygote/zygote_patch.jl | 54 -------------------- 5 files changed, 87 insertions(+), 60 deletions(-) create mode 100644 examples/PortZygote/chainrules_patch.jl delete mode 100644 examples/PortZygote/zygote_patch.jl diff --git a/examples/PortZygote/chainrules_patch.jl b/examples/PortZygote/chainrules_patch.jl new file mode 100644 index 000000000..89f60b77d --- /dev/null +++ b/examples/PortZygote/chainrules_patch.jl @@ -0,0 +1,64 @@ +import ChainRulesCore: rrule, @non_differentiable, NoTangent +using Yao, Yao.AD + +function rrule(::typeof(apply!), reg::ArrayReg, block::AbstractBlock) + out = apply!(reg, block) + out, function (outδ) + (in, inδ), paramsδ = apply_back((out, outδ), block) + return (NoTangent(), inδ, paramsδ) + end +end + +function rrule(::typeof(dispatch!), block::AbstractBlock, params) + out = dispatch!(block, params) + out, function (outδ) + (NoTangent(), NoTangent(), outδ) + end +end + +function rrule(::typeof(expect), op::AbstractBlock, reg::AbstractRegister{B}) where {B} + out = expect(op, reg) + out, function (outδ) + greg = Yao.AD.expect_g(op, reg) + for b=1:B + viewbatch(greg, b).state .*= 2*outδ[b] + end + return (NoTangent(), NoTangent(), greg) + end +end + +function rrule(::Type{Matrix}, block::AbstractBlock) + out = Matrix(block) + out, function (outδ) + paramsδ = mat_back(block, outδ) + return (NoTangent(), paramsδ) + end +end + +function rrule(::Type{ArrayReg{B}}, raw::AbstractArray) where B + ArrayReg{B}(raw), adjy->(NoTangent(), reshape(adjy.state, size(raw))) +end + +function rrule(::Type{ArrayReg{B}}, raw::ArrayReg) where B + ArrayReg{B}(raw), adjy->(NoTangent(), adjy) +end + +function rrule(::Type{ArrayReg}, raw::AbstractArray) + ArrayReg(raw), adjy->(NoTangent(), reshape(adjy.state, size(raw))) +end + +function rrule(::typeof(copy), reg::ArrayReg) where B + copy(reg), adjy->(NoTangent(), adjy) +end + +rrule(::typeof(state), reg::ArrayReg) = state(reg), adjy->(NoTangent(), ArrayReg(adjy)) +rrule(::typeof(statevec), reg::ArrayReg) = statevec(reg), adjy->(NoTangent(), ArrayReg(adjy)) +rrule(::typeof(state), reg::AdjointArrayReg) = state(reg), adjy->(NoTangent(), ArrayReg(adjy')') +rrule(::typeof(statevec), reg::AdjointArrayReg) = statevec(reg), adjy->(NoTangent(), ArrayReg(adjy')') +rrule(::typeof(parent), reg::AdjointArrayReg) = parent(reg), adjy->(NoTangent(), adjy') +rrule(::typeof(Base.adjoint), reg::ArrayReg) = Base.adjoint(reg), adjy->(NoTangent(), parent(adjy)) +@non_differentiable Yao.nparameters(::Any) +@non_differentiable Yao.zero_state(args...) +@non_differentiable Yao.rand_state(args...) +@non_differentiable Yao.uniform_state(args...) +@non_differentiable Yao.product_state(args...) diff --git a/examples/PortZygote/gate_learning.jl b/examples/PortZygote/gate_learning.jl index 957655377..30af24763 100644 --- a/examples/PortZygote/gate_learning.jl +++ b/examples/PortZygote/gate_learning.jl @@ -4,7 +4,8 @@ using Optim: LBFGS, optimize using Optim # port the `Matrix` function to Yao's AD. -include("zygote_patch.jl") +using Zygote +include("chainrules_patch.jl") function loss(u, ansatz) m = Matrix(ansatz) @@ -19,7 +20,7 @@ Learn a general U4 gate. The optimizer is LBFGS. function learn_u4(u::AbstractMatrix; niter=100) ansatz = general_U4() * put(2, 1=>phase(0.0)) # initial values are 0, here, we attach a global phase. params = parameters(ansatz) - g!(G, x) = (dispatch!(ansatz, x); G .= gradient(ansatz->loss(u, ansatz), ansatz)[1]) + g!(G, x) = (dispatch!(ansatz, x); G .= Zygote.gradient(ansatz->loss(u, ansatz), ansatz)[1]) optimize(x->(dispatch!(ansatz, x); loss(u, ansatz)), g!, parameters(ansatz), LBFGS(), Optim.Options(iterations=niter)) println("final loss = $(loss(u,ansatz))") @@ -27,6 +28,6 @@ function learn_u4(u::AbstractMatrix; niter=100) end using Random -Random.seed!(2) +Random.seed!(3) u = rand_unitary(4) c = learn_u4(u; niter=150) diff --git a/examples/PortZygote/shared_parameters.jl b/examples/PortZygote/shared_parameters.jl index 1643176e5..b4705003d 100644 --- a/examples/PortZygote/shared_parameters.jl +++ b/examples/PortZygote/shared_parameters.jl @@ -1,4 +1,5 @@ -include("zygote_patch.jl") +using Zygote +include("chainrules_patch.jl") import YaoExtensions, Random @@ -22,4 +23,18 @@ dispatch!(c, fill(0.5, nparameters(c))) greg, gparams = expect'(h, zero_state(5)=>c) true_grad = sum(gparams) -@test true_grad ≈ true_grad +@test zygote_grad ≈ true_grad + +# the batched version +function loss2(h, c, θ) where N + # the assign is nessesary! + c = dispatch!(c, fill(θ, nparameters(c))) + reg = zero_state(nqubits(c),nbatch=2) + reg = apply!(reg, c) + sum(real(expect(h, reg))) +end + +reg0 = zero_state(5) +zygote_grad2 = Zygote.gradient(θ->loss2(h, c, θ), 0.5)[1] +true_grad2 = (loss2(h, c, 0.5+1e-5) - loss2(h, c, 0.5-1e-5)) / 2e-5 +@test true_grad2 ≈ zygote_grad2 \ No newline at end of file diff --git a/examples/PortZygote/simple_example.jl b/examples/PortZygote/simple_example.jl index ae10024f0..50bff360f 100644 --- a/examples/PortZygote/simple_example.jl +++ b/examples/PortZygote/simple_example.jl @@ -1,4 +1,5 @@ -include("zygote_patch.jl") +using Zygote +include("chainrules_patch.jl") import YaoExtensions, Random diff --git a/examples/PortZygote/zygote_patch.jl b/examples/PortZygote/zygote_patch.jl deleted file mode 100644 index a216a7e3f..000000000 --- a/examples/PortZygote/zygote_patch.jl +++ /dev/null @@ -1,54 +0,0 @@ -using Zygote -using Zygote: @adjoint -using Yao, Yao.AD - -@adjoint function apply!(reg::ArrayReg, block::AbstractBlock) - out = apply!(reg, block) - out, function (outδ) - (in, inδ), paramsδ = apply_back((out, outδ), block) - return (inδ, paramsδ) - end -end - -@adjoint function Yao.dispatch!(block::AbstractBlock, params) - out = dispatch!(block, params) - out, function (outδ) - (nothing, outδ) - end -end - -@adjoint function Matrix(block::AbstractBlock) - out = Matrix(block) - out, function (outδ) - paramsδ = mat_back(block, outδ) - return (paramsδ,) - end -end - -@adjoint function ArrayReg{B}(raw::AbstractArray) where B - ArrayReg{B}(raw), adjy->(reshape(adjy.state, size(raw)),) -end - -@adjoint function ArrayReg{B}(raw::ArrayReg) where B - ArrayReg{B}(raw), adjy->(adjy,) -end - -@adjoint function ArrayReg(raw::AbstractArray) - ArrayReg(raw), adjy->(reshape(adjy.state, size(raw)),) -end - -@adjoint function copy(reg::ArrayReg) where B - copy(reg), adjy->(adjy,) -end - -@adjoint state(reg::ArrayReg) = state(reg), adjy->(ArrayReg(adjy),) -@adjoint statevec(reg::ArrayReg) = statevec(reg), adjy->(ArrayReg(adjy),) -@adjoint state(reg::AdjointArrayReg) = state(reg), adjy->(ArrayReg(adjy')',) -@adjoint statevec(reg::AdjointArrayReg) = statevec(reg), adjy->(ArrayReg(adjy')',) -@adjoint parent(reg::AdjointArrayReg) = parent(reg), adjy->(adjy',) -@adjoint Base.adjoint(reg::ArrayReg) = Base.adjoint(reg), adjy->(parent(adjy),) -Zygote.@nograd Yao.nparameters -Zygote.@nograd Yao.zero_state -Zygote.@nograd Yao.rand_state -Zygote.@nograd Yao.uniform_state -Zygote.@nograd Yao.product_state From 665225c383719f53e4b078495834a3d1d0971e34 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Fri, 13 Aug 2021 00:31:02 -0400 Subject: [PATCH 83/85] new chainrules patch --- examples/PortZygote/chainrules_patch.jl | 66 +++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 examples/PortZygote/chainrules_patch.jl diff --git a/examples/PortZygote/chainrules_patch.jl b/examples/PortZygote/chainrules_patch.jl new file mode 100644 index 000000000..71807e1f9 --- /dev/null +++ b/examples/PortZygote/chainrules_patch.jl @@ -0,0 +1,66 @@ +import ChainRulesCore: rrule, @non_differentiable, NoTangent +using Yao, Yao.AD + +function rrule(::typeof(apply!), reg::ArrayReg, block::AbstractBlock) + out = apply!(reg, block) + out, function (outδ) + (in, inδ), paramsδ = apply_back((out, outδ), block) + return (NoTangent(), inδ, paramsδ) + end +end + +function rrule(::typeof(dispatch!), block::AbstractBlock, params) + out = dispatch!(block, params) + out, function (outδ) + (NoTangent(), NoTangent(), outδ) + end +end + +function rrule(::typeof(expect), op::AbstractBlock, reg::AbstractRegister{B}) where {B} + out = expect(op, reg) + out, function (outδ) + greg = Yao.AD.expect_g(op, reg) + for b=1:B + viewbatch(greg, b).state .*= 2*outδ[b] + end + return (NoTangent(), NoTangent(), greg) + end +end + +function rrule(::Type{Matrix}, block::AbstractBlock) + out = Matrix(block) + out, function (outδ) + paramsδ = mat_back(block, outδ) + return (NoTangent(), paramsδ) + end +end + +function rrule(::Type{ArrayReg{B}}, raw::AbstractArray) where B + ArrayReg{B}(raw), adjy->(NoTangent(), reshape(adjy.state, size(raw))) +end + +function rrule(::Type{ArrayReg{B}}, raw::ArrayReg) where B + ArrayReg{B}(raw), adjy->(NoTangent(), adjy) +end + +function rrule(::Type{ArrayReg}, raw::AbstractArray) + ArrayReg(raw), adjy->(NoTangent(), reshape(adjy.state, size(raw))) +end + +function rrule(::typeof(copy), reg::ArrayReg) where B + copy(reg), adjy->(NoTangent(), adjy) +end + +_totype(::Type{T}, x::AbstractArray{T}) where T = x +_totype(::Type{T}, x::AbstractArray{T2}) where {T,T2} = convert.(T, x) +rrule(::typeof(state), reg::ArrayReg{B,T}) where {B,T} = state(reg), adjy->(NoTangent(), ArrayReg(_totype(T, adjy))) +rrule(::typeof(statevec), reg::ArrayReg{B,T}) where {B,T} = statevec(reg), adjy->(NoTangent(), ArrayReg(_totype(T, adjy))) +rrule(::typeof(state), reg::AdjointArrayReg{B,T}) where {B,T} = state(reg), adjy->(NoTangent(), ArrayReg(_totype(T, adjy)')') +rrule(::typeof(statevec), reg::AdjointArrayReg{B,T}) where {B,T} = statevec(reg), adjy->(NoTangent(), ArrayReg(_totype(adjy)')') +rrule(::typeof(parent), reg::AdjointArrayReg) = parent(reg), adjy->(NoTangent(), adjy') +rrule(::typeof(Base.adjoint), reg::ArrayReg) = Base.adjoint(reg), adjy->(NoTangent(), parent(adjy)) +@non_differentiable Yao.nparameters(::Any) +@non_differentiable Yao.zero_state(args...) +@non_differentiable Yao.rand_state(args...) +@non_differentiable Yao.uniform_state(args...) +@non_differentiable Yao.product_state(args...) From e5d00043fbff5c4bd1207ac60af7ff00707b7559 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Sat, 14 Aug 2021 02:55:48 -0400 Subject: [PATCH 84/85] fix issue Yao/#299 --- examples/PortZygote/chainrules_patch.jl | 10 +++++----- examples/PortZygote/gate_learning.jl | 4 ++-- examples/PortZygote/shared_parameters.jl | 8 ++++---- examples/PortZygote/simple_example.jl | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/PortZygote/chainrules_patch.jl b/examples/PortZygote/chainrules_patch.jl index 71807e1f9..76b706fc3 100644 --- a/examples/PortZygote/chainrules_patch.jl +++ b/examples/PortZygote/chainrules_patch.jl @@ -1,16 +1,16 @@ import ChainRulesCore: rrule, @non_differentiable, NoTangent using Yao, Yao.AD -function rrule(::typeof(apply!), reg::ArrayReg, block::AbstractBlock) - out = apply!(reg, block) +function rrule(::typeof(apply), reg::ArrayReg, block::AbstractBlock) + out = apply(reg, block) out, function (outδ) - (in, inδ), paramsδ = apply_back((out, outδ), block) + (in, inδ), paramsδ = apply_back((copy(out), outδ), block) return (NoTangent(), inδ, paramsδ) end end -function rrule(::typeof(dispatch!), block::AbstractBlock, params) - out = dispatch!(block, params) +function rrule(::typeof(dispatch), block::AbstractBlock, params) + out = dispatch(block, params) out, function (outδ) (NoTangent(), NoTangent(), outδ) end diff --git a/examples/PortZygote/gate_learning.jl b/examples/PortZygote/gate_learning.jl index 30af24763..2deb7fbf1 100644 --- a/examples/PortZygote/gate_learning.jl +++ b/examples/PortZygote/gate_learning.jl @@ -20,8 +20,8 @@ Learn a general U4 gate. The optimizer is LBFGS. function learn_u4(u::AbstractMatrix; niter=100) ansatz = general_U4() * put(2, 1=>phase(0.0)) # initial values are 0, here, we attach a global phase. params = parameters(ansatz) - g!(G, x) = (dispatch!(ansatz, x); G .= Zygote.gradient(ansatz->loss(u, ansatz), ansatz)[1]) - optimize(x->(dispatch!(ansatz, x); loss(u, ansatz)), g!, parameters(ansatz), + g!(G, x) = (ansatz=dispatch(ansatz, x); G .= Zygote.gradient(ansatz->loss(u, ansatz), ansatz)[1]) + optimize(x->(ansatz=dispatch(ansatz, x); loss(u, ansatz)), g!, parameters(ansatz), LBFGS(), Optim.Options(iterations=niter)) println("final loss = $(loss(u,ansatz))") return ansatz diff --git a/examples/PortZygote/shared_parameters.jl b/examples/PortZygote/shared_parameters.jl index b4705003d..02602d824 100644 --- a/examples/PortZygote/shared_parameters.jl +++ b/examples/PortZygote/shared_parameters.jl @@ -8,8 +8,8 @@ h = YaoExtensions.heisenberg(5) function loss(h, c, θ) where N # the assign is nessesary! - c = dispatch!(c, fill(θ, nparameters(c))) - reg = apply!(zero_state(nqubits(c)), c) + c = dispatch(c, fill(θ, nparameters(c))) + reg = apply(zero_state(nqubits(c)), c) real(expect(h, reg)) end @@ -28,9 +28,9 @@ true_grad = sum(gparams) # the batched version function loss2(h, c, θ) where N # the assign is nessesary! - c = dispatch!(c, fill(θ, nparameters(c))) + c = dispatch(c, fill(θ, nparameters(c))) reg = zero_state(nqubits(c),nbatch=2) - reg = apply!(reg, c) + reg = apply(reg, c) sum(real(expect(h, reg))) end diff --git a/examples/PortZygote/simple_example.jl b/examples/PortZygote/simple_example.jl index 50bff360f..f3993c95f 100644 --- a/examples/PortZygote/simple_example.jl +++ b/examples/PortZygote/simple_example.jl @@ -8,7 +8,7 @@ dispatch!(c, :random) function loss(reg::AbstractRegister, circuit::AbstractBlock{N}) where N #copy(reg) |> circuit - reg = apply!(copy(reg), circuit) + reg = apply(copy(reg), circuit) st = state(reg) sum(real(st.*st)) end From 9bc7dab4be0ca6a9777004e696b8071974cee195 Mon Sep 17 00:00:00 2001 From: Roger-Luo Date: Wed, 8 Dec 2021 17:03:11 -0500 Subject: [PATCH 85/85] update license --- lib/QuAlgorithmZoo/LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/QuAlgorithmZoo/LICENSE.md b/lib/QuAlgorithmZoo/LICENSE.md index a0b4807ab..a714ea550 100644 --- a/lib/QuAlgorithmZoo/LICENSE.md +++ b/lib/QuAlgorithmZoo/LICENSE.md @@ -1,6 +1,6 @@ The QuAlgorithmZoo.jl package is licensed under the Apache License, Version 2.0: -> Copyright (c) 2018: Roger-luo. +> Copyright (c) 2018: QuantumBFS. > > Apache License > Version 2.0, January 2004