├── QPE ├── Basic QPE │ ├── QPE_circ.JPG │ ├── Phase 0.2 estimate.JPG │ └── QPE_circ_optimized.JPG ├── Iterative QPE │ ├── IQPE_circ.JPG │ └── IQPE Plots │ │ ├── Estimate_plot_0.111.jpg │ │ ├── Estimate_plot_0.524.jpg │ │ └── Estimate_plot_0.2351.jpg ├── Statistical QPE │ ├── SPEA notes.pdf │ ├── spea_circuit.PNG │ ├── Plots │ │ ├── SPE_PLOT1.jpg │ │ ├── SPE_PLOT2.jpg │ │ ├── SPE_PLOT1_global_max.jpg │ │ └── SPE_PLOT2_global_max.jpg │ ├── Alternate Approach │ │ ├── Alternate_SPE_PLOT_1.jpg │ │ ├── Alternate_SPE_PLOT_2(simulator).jpg │ │ ├── Alternate_SPE_PLOT_original_algo.jpg │ │ ├── Alternate_SPE_PLOT_1_modified_algo.jpg │ │ ├── Alternate_SPE_PLOT_1_original_algo.jpg │ │ ├── Alternate_SPE_PLOT_2_modified_algo.jpg │ │ └── Alternate_SPE_PLOT_2_original_algo.jpg │ ├── Experiments │ │ ├── Lower Bound Testing │ │ │ ├── Plot for Error in Cost Lower Bound.JPG │ │ │ ├── Plot for verifying Cost Lower Bound.JPG │ │ │ └── normal_SPEA.py │ │ ├── Experiment_1 │ │ │ ├── 1_qubit(random) │ │ │ │ ├── Modified Algorithm (alternate).JPG │ │ │ │ └── Original Algorithm (alternate).JPG │ │ │ ├── 2_qubit(random) │ │ │ │ ├── Modified Algorithm (alternate).JPG │ │ │ │ └── Original Algorithm (alternate).JPG │ │ │ └── 3_qubit(random) │ │ │ │ ├── Modified Algorithm (alternate).JPG │ │ │ │ └── Original Algorithm (alternate).JPG │ │ ├── Experiment_2 │ │ │ ├── 1_qubit(random) │ │ │ │ ├── Modified Algorithm (alternate).JPG │ │ │ │ └── Original Algorithm (alternate).JPG │ │ │ ├── 2_qubit(random) │ │ │ │ ├── Modified Algorithm (alternate).JPG │ │ │ │ └── Original Algorithm (alternate).JPG │ │ │ ├── 3_qubit(random) │ │ │ │ ├── Modified Algorithm (alternate).JPG │ │ │ │ └── Original Algorithm (alternate).JPG │ │ │ └── 4_qubit(random) │ │ │ │ └── Original Algorithm (alternate).JPG │ │ ├── Experiment_3 │ │ │ ├── 1_qubit(random) │ │ │ │ ├── Modified Algorithm (alternate).JPG │ │ │ │ └── Original Algorithm (alternate).JPG │ │ │ ├── 2_qubit(random) │ │ │ │ ├── Modified Algorithm (alternate).JPG │ │ │ │ └── Original Algorithm (alternate).JPG │ │ │ ├── 3_qubit(random) │ │ │ │ ├── Modified Algorithm (alternate).JPG │ │ │ │ └── Original Algorithm (alternate).JPG │ │ │ └── 4_qubit(random) │ │ │ │ └── Original Algorithm (alternate).JPG │ │ └── Experiment 1 - Resolution vs size of Unitary(randomized).ipynb │ ├── Bundling Jobs in Global Max SPEA- alternate approach.ipynb │ └── Bundling Jobs in Global Max SPEA.ipynb ├── Kitaev's Algorithm │ ├── KQPE_circ_1qubit.JPG │ └── Kitaev Multiqubit Estimation Plot.JPG ├── All algorithm Tests │ └── Plots │ │ ├── 1-qubit │ │ ├── x_gate │ │ │ ├── iqpe.JPG │ │ │ ├── stat_qpe.JPG │ │ │ ├── basic_qpe.JPG │ │ │ └── kitaev_qpe.JPG │ │ └── phase_gate │ │ │ ├── iqpe.JPG │ │ │ ├── basic_qpe.JPG │ │ │ ├── stat_qpe.JPG │ │ │ └── kitaev_qpe.JPG │ │ └── 2-qubit │ │ ├── zx_gate │ │ ├── iqpe.JPG │ │ ├── basic_qpe.JPG │ │ ├── kitaev_qpe.JPG │ │ └── stat_qpe.JPG │ │ └── phase_gate │ │ ├── iqpe.JPG │ │ ├── basic_qpe.JPG │ │ ├── stat_qpe.JPG │ │ └── kitaev_qpe.JPG └── Modules │ ├── vanilla_qpe.py │ ├── kitaev_qpe.py │ ├── bundled_global_max_SPEA.py │ ├── faster_basic_qpe.py │ ├── bundled_global_max_alt_SPEA.py │ ├── iterative_qpe.py │ └── normal_SPEA.py ├── requirements.txt ├── .gitignore └── README.md /QPE/Basic QPE/QPE_circ.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Basic QPE/QPE_circ.JPG -------------------------------------------------------------------------------- /QPE/Iterative QPE/IQPE_circ.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Iterative QPE/IQPE_circ.JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/SPEA notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/SPEA notes.pdf -------------------------------------------------------------------------------- /QPE/Basic QPE/Phase 0.2 estimate.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Basic QPE/Phase 0.2 estimate.JPG -------------------------------------------------------------------------------- /QPE/Basic QPE/QPE_circ_optimized.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Basic QPE/QPE_circ_optimized.JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/spea_circuit.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/spea_circuit.PNG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Plots/SPE_PLOT1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Plots/SPE_PLOT1.jpg -------------------------------------------------------------------------------- /QPE/Statistical QPE/Plots/SPE_PLOT2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Plots/SPE_PLOT2.jpg -------------------------------------------------------------------------------- /QPE/Kitaev's Algorithm/KQPE_circ_1qubit.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Kitaev's Algorithm/KQPE_circ_1qubit.JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Plots/SPE_PLOT1_global_max.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Plots/SPE_PLOT1_global_max.jpg -------------------------------------------------------------------------------- /QPE/Statistical QPE/Plots/SPE_PLOT2_global_max.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Plots/SPE_PLOT2_global_max.jpg -------------------------------------------------------------------------------- /QPE/Iterative QPE/IQPE Plots/Estimate_plot_0.111.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Iterative QPE/IQPE Plots/Estimate_plot_0.111.jpg -------------------------------------------------------------------------------- /QPE/Iterative QPE/IQPE Plots/Estimate_plot_0.524.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Iterative QPE/IQPE Plots/Estimate_plot_0.524.jpg -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/1-qubit/x_gate/iqpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/1-qubit/x_gate/iqpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/2-qubit/zx_gate/iqpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/2-qubit/zx_gate/iqpe.JPG -------------------------------------------------------------------------------- /QPE/Iterative QPE/IQPE Plots/Estimate_plot_0.2351.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Iterative QPE/IQPE Plots/Estimate_plot_0.2351.jpg -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/1-qubit/phase_gate/iqpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/1-qubit/phase_gate/iqpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/1-qubit/x_gate/stat_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/1-qubit/x_gate/stat_qpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/2-qubit/phase_gate/iqpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/2-qubit/phase_gate/iqpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/1-qubit/x_gate/basic_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/1-qubit/x_gate/basic_qpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/1-qubit/x_gate/kitaev_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/1-qubit/x_gate/kitaev_qpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/2-qubit/zx_gate/basic_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/2-qubit/zx_gate/basic_qpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/2-qubit/zx_gate/kitaev_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/2-qubit/zx_gate/kitaev_qpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/2-qubit/zx_gate/stat_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/2-qubit/zx_gate/stat_qpe.JPG -------------------------------------------------------------------------------- /QPE/Kitaev's Algorithm/Kitaev Multiqubit Estimation Plot.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Kitaev's Algorithm/Kitaev Multiqubit Estimation Plot.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/1-qubit/phase_gate/basic_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/1-qubit/phase_gate/basic_qpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/1-qubit/phase_gate/stat_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/1-qubit/phase_gate/stat_qpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/2-qubit/phase_gate/basic_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/2-qubit/phase_gate/basic_qpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/2-qubit/phase_gate/stat_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/2-qubit/phase_gate/stat_qpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/1-qubit/phase_gate/kitaev_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/1-qubit/phase_gate/kitaev_qpe.JPG -------------------------------------------------------------------------------- /QPE/All algorithm Tests/Plots/2-qubit/phase_gate/kitaev_qpe.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/All algorithm Tests/Plots/2-qubit/phase_gate/kitaev_qpe.JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_1.jpg -------------------------------------------------------------------------------- /QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_2(simulator).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_2(simulator).jpg -------------------------------------------------------------------------------- /QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_original_algo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_original_algo.jpg -------------------------------------------------------------------------------- /QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_1_modified_algo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_1_modified_algo.jpg -------------------------------------------------------------------------------- /QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_1_original_algo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_1_original_algo.jpg -------------------------------------------------------------------------------- /QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_2_modified_algo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_2_modified_algo.jpg -------------------------------------------------------------------------------- /QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_2_original_algo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Alternate Approach/Alternate_SPE_PLOT_2_original_algo.jpg -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Lower Bound Testing/Plot for Error in Cost Lower Bound.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Lower Bound Testing/Plot for Error in Cost Lower Bound.JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Lower Bound Testing/Plot for verifying Cost Lower Bound.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Lower Bound Testing/Plot for verifying Cost Lower Bound.JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_1/1_qubit(random)/Modified Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_1/1_qubit(random)/Modified Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_1/1_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_1/1_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_1/2_qubit(random)/Modified Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_1/2_qubit(random)/Modified Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_1/2_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_1/2_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_1/3_qubit(random)/Modified Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_1/3_qubit(random)/Modified Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_1/3_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_1/3_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_2/1_qubit(random)/Modified Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_2/1_qubit(random)/Modified Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_2/1_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_2/1_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_2/2_qubit(random)/Modified Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_2/2_qubit(random)/Modified Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_2/2_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_2/2_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_2/3_qubit(random)/Modified Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_2/3_qubit(random)/Modified Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_2/3_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_2/3_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_2/4_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_2/4_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_3/1_qubit(random)/Modified Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_3/1_qubit(random)/Modified Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_3/1_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_3/1_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_3/2_qubit(random)/Modified Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_3/2_qubit(random)/Modified Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_3/2_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_3/2_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_3/3_qubit(random)/Modified Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_3/3_qubit(random)/Modified Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_3/3_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_3/3_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment_3/4_qubit(random)/Original Algorithm (alternate).JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheGupta2012/QPE-Algorithms/HEAD/QPE/Statistical QPE/Experiments/Experiment_3/4_qubit(random)/Original Algorithm (alternate).JPG -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | coverage>=4.4.0 2 | hypothesis>=4.24.3 3 | fastjsonschema>=2.10 4 | ipython<7.22.0 5 | ipykernel<5.5.2 6 | ipywidgets>=7.3.0 7 | jsonschema>=2.6 8 | jupyter 9 | matplotlib>=2.1,<3.4 10 | pillow>=4.2.1 11 | black==21.4b2 12 | pydot 13 | astroid==2.5 14 | pylint==2.7.1 15 | stestr>=2.0.0 16 | PyGithub 17 | wheel 18 | cython>=0.27.1 19 | pylatexenc>=1.4 20 | ddt>=1.2.0,!=1.4.0 21 | seaborn>=0.9.0 22 | reno>=3.2.0 23 | Sphinx>=3.0.0 24 | qiskit==0.26.0 25 | numpy>=1.17 26 | scipy>=1.4 27 | qiskit-sphinx-theme>=1.6 28 | sphinx-autodoc-typehints 29 | jupyter-sphinx 30 | sphinx-panels<0.6.0 31 | pygments>=2.4 32 | tweedledum>=1.0,<2.0 33 | networkx>=2.2 34 | scikit-learn>=0.20.0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | *.ipynb_checkpoints/ 131 | 132 | QPE/"Adding accounts.ipynb" 133 | "Unitary Eigenvalue distribution.ipynb" 134 | Papers/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QPE-Library :atom: 2 | 3 | * [Overview](#overview) 4 | * [Steps to use](#steps-to-use) 5 | * [Algorithms](#algorithms) 6 | - [Basic QPE Algorithm](#basic-qpe-algorithm) 7 | - [Iterative QPE Algorithm](#iterative-qpe-algorithm) 8 | - [Kitaev's QPE Algorithm](#kitaevs-qpe-algorithm) 9 | - [Statistical QPE Algorithm](#statistical-qpe-algorithm) 10 | 11 | ## Overview 12 | This is a *library* containing some `basic` and `novel` Quantum Phase Estimation algorithms. The four primary algorithms implemented are- 13 | - Basic QPE algorithm 14 | - Iterative QPE algorithm 15 | - Kitaev's QPE algorithm 16 | - Statistical QPE algorithm 🆕 17 | 18 | ## Steps to use 19 | 1. Clone the repository using `git clone [url_of_repo.git]`. 20 | 2. If using anaconda distribution, in the anaconda prompt, make a new virtual environment with `conda create -n yourenvname python=3.9`, activate by typing `conda activate yourenvname` and install dependenices using `pip install -r requirements.txt`. 21 | 3. If not, use `pip install -r requirements.txt` in your python environment. 22 | 4. You are now ready to use the modules present in the `modules` directory, inside this enviroment. 23 | 5. If using `conda`, add the current environment to your jupyter notebook by typing `python -m ipykernel install --user --name=yourenvname` followed by `jupyter notebook` to run a jupyter instance in this environment 24 | 6. If you have `jupyter nbextensions` enabled, use [this](https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/install.html) link to install them in your environment. 25 | 26 | 27 | ## Algorithms 28 | All algorithms have been implemented as python classes and have support for running on an `IBMQBackend` and on the simulator provided by `Aer`. For the examples, each subfolder contains a jupyter notebook depicting the usage of the implemented algorithms. 29 | 30 | ### Basic QPE Algorithm 31 | - This basic QPE algorithm is based on the principles of phase kickback and inverse Quantum Fourier Transform. 32 | - A variant called `fast_QPE` has also been included which uses binary exponentiation to reduce the number of unitary applications in simulation. Note that this is only applicable for simulation purposes but not in real scenarios. 33 | - Class Names : `basic_QPE`, `fast_QPE` 34 | - Module Path : `modules/vanilla_qpe.py`, `modules/faster_basic_qpe.py` 35 | - Main folder : `Basic QPE` 36 | - Examples : [Notebook](https://nbviewer.jupyter.org/github/TheGupta2012/QPE-Algorithms/blob/master/QPE/Basic%20QPE/Basic%20QPE.ipynb) 37 | 38 | 39 | 40 | 41 |









42 | 43 | 44 | ### Iterative QPE Algorithm 45 | - The iterative phase estimation algorithm(IQPE) is based on the principle that reducing the width of the circuit in exchange for its depth results in smaller circuits which reduce the *interaction* between qubits thus, reducing errors. 46 | - The module has a distinctive feature called `unknown` which uses binary exponentiation to reduce the number of unitary applications in simulation. This feature is only utilised when `unknown` is set to `False`. 47 | - This algorithm proves as *one of the best* phase estimation routines for the present day *NISQ computers*. 48 | 49 | 50 | - Class Name : `general_IQPE` 51 | - Module Path : `modules/iterative_qpe.py` 52 | - Main folder : `Iterative QPE` 53 | - Examples and Tests: [Notebook](https://nbviewer.jupyter.org/github/TheGupta2012/QPE-Algorithms/blob/master/QPE/Iterative%20QPE/Iterative%20QPE.ipynb) 54 | 55 | 56 | 57 | 58 | ### Kitaev's QPE Algorithm 59 | - Kitaev's algorithm for Phase Estimation is an algorithm with two forms. In this implementation, the algorithm which uses a *single Unitary matrix* for phase estimation is used. 60 | - Kitaev's algorithm is a very efficient algorithm in terms of quantum execution. Involving some classical post processing work and relatively simple circuits for the phase estimation, the only drawback for this algorithm is the number of shots required for a given precision and probability of error scale up very quickly. 61 | - Class Name : `KQPE` 62 | - Module Path : `modules/kitaev_qpe.py` 63 | - Main Folder : `Kitaev's Algorithm` 64 | - Examples : [Notebook](https://nbviewer.jupyter.org/github/TheGupta2012/QPE-Algorithms/blob/master/QPE/Kitaev%27s%20Algorithm/Kitaev%20QPE.ipynb) 65 | 66 | 67 | 68 | 69 | ### Statistical QPE Algorithm 70 | 71 | - Statistical Phase Estimation Algorithm or SPEA is a novel approach for phase etimation based on [this](https://arxiv.org/pdf/2104.10285.pdf) recent paper. SPEA uses a variational approach to solve the phase estimation and **does not** require the eigenvector of the unitary matrix to be prepared beforehand. 72 | - It proposes to give an **eigenstate and eigenvalue** pair of our Unitary in one successful execution of the algorithm which can be extended to find the full *spectral decomposition* of a matrix. 73 | - This library contains the original algorithm proposed by the authors and **a modified algorithm**. The **modified approach** was proposed keeping in mind that greedy choices in the original algorithm may not always propose to be optimal. One advantage of this approach is in terms of *quantum execution* time. 74 | - Since any quantum computer contains a classical controller through which it is accessed, calling the device multiple times incurs overhead in terms of the classical interfacing. While original appraoach uses *exponential* API calls for its execution, the modified approach only requires only a *constant* number of API calls to reach the result. 75 | 76 | 77 | 78 | - **Original Algorithm** 79 | - Class Name - `SPEA` 80 | - Module Path - `modules/normal_SPEA.py` 81 | - Main Folder - `Statistical QPE` 82 | - Examples - [Notebook](https://nbviewer.jupyter.org/github/TheGupta2012/QPE-Algorithms/blob/master/QPE/Statistical%20QPE/Statistical%20QPE.ipynb) 83 | - **Modified Algorithm** 84 | - Class Names - `global_max_SPEA` , `bundled_global_max_SPEA`, `bundled_global_max_alt_SPEA` 85 | - Module Path - `modules/changed_SPEA.py` 86 | - Main Folder - `Statistical QPE` 87 | - Examples - [Notebook](https://nbviewer.jupyter.org/github/TheGupta2012/QPE-Algorithms/blob/master/QPE/Statistical%20QPE/Statistical%20QPE-Changed%20Algorithm%20.ipynb) 88 | 89 | 90 | -------------------------------------------------------------------------------- /QPE/Modules/vanilla_qpe.py: -------------------------------------------------------------------------------- 1 | from qiskit.circuit import QuantumCircuit 2 | from qiskit.extensions import UnitaryGate 3 | import numpy as np 4 | from IPython.display import display 5 | 6 | 7 | class QPE: 8 | """ 9 | Implements the vanilla QPE algorithm utilising the Inverse Fourier Transform 10 | and Phase Kickbacks. 11 | 12 | Attributes : 13 | precision : int([1,...]): precision of the phase estimation ( also equal to number of qubits in scratch register) 14 | unitary(np.ndarray or QuantumCircuit or UnitaryGate): unitary operator for which QPE is being applied. 15 | rotations(dict): contains the rotation angles required for the IQFT procedure 16 | key : stage number, value : angle value 17 | 18 | Methods : 19 | get_rotations() : generate rotation values for the implementation of Inverse QFT 20 | get_QFT(n_qubits,swaps,show) : generate an n_qubit Quantum Fourier Transform 21 | get_QPE(show) : generate the final QPE circuit to be attached to the user's quantum circuit 22 | 23 | """ 24 | 25 | def __init__(self, precision, unitary): 26 | """ 27 | Args : 28 | precision(int) : The precision upto which the phase needs to be estimated. 29 | Interpreted as 2^(-precision). 30 | eg. precision = 4 means the phase is going to be precise 31 | upto 2^(-4). 32 | unitary(np.ndarray or UnitaryGate or QuantumCircuit): 33 | The unitary for which we want to determine the phase. Currently 34 | this class supports 2 x 2 matrices or single qubit gates. 35 | Shall be extended for higher order matrices. 36 | Raises : 37 | TypeError : if precision or unitary are not of a valid type 38 | ValueError : if precision is not valid 39 | 40 | Examples : 41 | from basic_QPE import QPE 42 | # passing as ndarray 43 | theta = 1/5 44 | U1 = np.ndarray([[1,0], 45 | [0, np.exp(2*np.pi*1j*(theta))]]) 46 | qpe1 = QPE(precision = 4, unitary = U1) 47 | 48 | # passing as QuantumCircuit 49 | U2 = QuantumCircuit(1) 50 | U2.rz(np.pi/7,0) 51 | qpe2 = QPE(precision = 5,unitary = U2) 52 | 53 | """ 54 | # handle precision 55 | if type(precision) != int: 56 | raise TypeError("Precision needs to be an integer") 57 | elif precision < 0 or precision == 0: 58 | raise ValueError("Precision needs to be atleast 1") 59 | 60 | self.precision = precision 61 | 62 | # handle unitary 63 | if ( 64 | not isinstance(unitary, np.ndarray) 65 | and not isinstance(unitary, QuantumCircuit) 66 | and not isinstance(unitary, UnitaryGate) 67 | ): 68 | raise TypeError( 69 | "A numpy array, Quantum Circuit or UnitaryGate needs to be passed as the unitary matrix" 70 | ) 71 | 72 | if isinstance(unitary, np.ndarray): 73 | self.unit_qubits = int(np.log2(unitary.shape[0])) 74 | else: 75 | self.unit_qubits = unitary.num_qubits 76 | self.unitary = unitary 77 | self.rotations = self.get_rotations() 78 | 79 | def get_rotations(self): 80 | """Returns a dictionary of the angles associated with 81 | each rotation gate upto precision specified""" 82 | rots = {} 83 | for k in range(1, self.precision): 84 | rots[k] = np.pi / (2 ** k) 85 | return rots 86 | 87 | def get_QFT(self, n_qubits: int, show=False, swaps=True): 88 | """Returns an n_qubits QFT circuit 89 | 90 | Arguments : 91 | n_qubits : the number of qubits 92 | show : whether to draw the circuit 93 | swaps : whether swaps need to be performed at the 94 | end of the procedure 95 | 96 | Returns : 97 | QuantumCircuit : the QuantumCircuit containing the QFT circuit 98 | """ 99 | qc = QuantumCircuit(n_qubits) 100 | for target in range(n_qubits): 101 | # apply hadamard 102 | qc.h(target) 103 | # add rotations 104 | rot = 1 105 | for control in range(target + 1, n_qubits): 106 | qc.cp(self.rotations[rot], control, target) 107 | rot += 1 108 | if swaps: 109 | ## add swap gates 110 | i, j = 0, n_qubits - 1 111 | while i < j: 112 | qc.swap(i, j) 113 | i += 1 114 | j -= 1 115 | 116 | if show: 117 | print("Circuit for QFT of " + str(n_qubits)) 118 | display(qc.draw("mpl")) 119 | return qc 120 | 121 | def get_QPE(self, show=False, save=False): 122 | """Returns the final QPE circuit to the user in form of a 123 | QuantumCircuit 124 | 125 | Arguments : 126 | show(bool) : Whether to draw the circuit or not 127 | 128 | Returns : 129 | QuantumCircuit : The QuantumCircuit which can be attached to the user circuit 130 | 131 | Usage Notes : 132 | NOTE1: The last qubits, equal to the number of qubits required to represent the eigenvector 133 | for the unitary, of the circuit which is returned needs to be attached to the qubits 134 | containing the EIGENVECTOR of the unitary that is passed. 135 | The phase is encoded in the scratch qubits above the target qubit. 136 | 137 | For example :- 138 | If precision is specified as 4 and a 6 qubit QuantumCircuit contains the 139 | eigenvector in the 5th qubit. 140 | 141 | q = QuantumCircuit(6,4) 142 | 143 | q.x(4) # the eigenvector qubit 144 | 145 | qpe = QPE(precision = 4,unitary = unitary) 146 | qpe_circ = qpe.get_QPE(show = True) 147 | q.append(qpe_circ, qargs = [0,1,2,3,4]) # precision qubits and the eigenvector qubits 148 | q.draw('mpl') 149 | 150 | NOTE2: The phase is assumed to be a binary fraction as 0.x1x2x2...xn where n 151 | is the precision specified by the user. 152 | The least significant bit , xn, is saved in the qubit with index 153 | t - precision and the most significant bit, x1, is saved in the 154 | qubit with index t - 1 where t is the index of the first target qubit 155 | containing the EIGENVECTOR of the unitary. 156 | 157 | For example :- 158 | theta = 1/5 # binary representation upto 4 bits : 0.0011 159 | 160 | q = QuantumCircuit(6,4) 161 | q.x(4) # the eigenvector qubit 162 | 163 | qpe = QPE(precision = 4,unitary = unitary) 164 | qpe_circ = qpe.get_QPE(show = True) 165 | q.append(qpe_circ, qargs = [0,1,2,3,4]) 166 | q.draw('mpl') 167 | # let '0011' be the state with highest probability 168 | # q[0] would contain 1 169 | # q[1] would contain 1 170 | # q[2] would contain 0 171 | # q[3] would contain 0 where q refers to the qubit 172 | 173 | """ 174 | qc = QuantumCircuit(self.precision + self.unit_qubits, name="QPE_circ") 175 | 176 | # generate the controlled Unitary gate and start applying it according 177 | # to the orientation specified 178 | if isinstance(self.unitary, np.ndarray): 179 | # if a normal unitary is passed 180 | gate = UnitaryGate(data=self.unitary) 181 | CU_gate = gate.control(num_ctrl_qubits=1, label="CU", ctrl_state="1") 182 | else: 183 | # if a QuantumCircuit is passed, then also works 184 | # if a UnitaryGate is passed, then also works 185 | CU_gate = self.unitary.control( 186 | num_ctrl_qubits=1, label="CU", ctrl_state="1" 187 | ) 188 | 189 | # add hadamard transform 190 | qc.h([i for i in range(self.precision)]) 191 | 192 | # add Controlled unitaries 193 | n_qubits = self.precision 194 | target = [i + self.precision for i in range(self.unit_qubits)] 195 | for i in range(n_qubits - 1, -1, -1): 196 | control = [i] 197 | for _ in range(2 ** i): 198 | # append this CU gate for kickback 199 | qc = qc.compose(CU_gate, qubits=control + target) 200 | qc.barrier() 201 | 202 | # attach IQFT 203 | IQFT = self.get_QFT(n_qubits, show=show, swaps=False).inverse() 204 | IQFT.name = "IQFT circuit" 205 | 206 | ## add swap gates 207 | i, j = 0, n_qubits - 1 208 | while i < j: 209 | IQFT.swap(i, j) 210 | i += 1 211 | j -= 1 212 | 213 | # append circuit 214 | qc.append(IQFT, qargs=[i for i in range(self.precision)]) 215 | 216 | # that's it 217 | if show == True: 218 | if save == True: 219 | display(qc.draw(output="mpl", filename="QPE_circ.JPG", scale=0.8)) 220 | else: 221 | display(qc.draw(output="mpl")) 222 | return qc 223 | -------------------------------------------------------------------------------- /QPE/Statistical QPE/Bundling Jobs in Global Max SPEA- alternate approach.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Global Max SPEA with job bundling\n", 8 | "- This is a variant of the SPEA algorithm in which the jobs are bundled together into 1 big circuit and sent to the backend.\n", 9 | "- NOTE : the **alternate approach** is being used in this particular code \n", 10 | "- Note, the total API calls are bound by just **MAX_ITERS** as the job manager sends the circuits in a bundle and executes according to the backend limit on which we are executing\n", 11 | "- This is much better than the **$O(2^{n})$** calls done otherwise with the normal approach " 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "#### Trying out Job manager \n", 19 | "- This manager is used to **optimize** the api call time that is needed in the backend execution" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "### Imports" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 1, 32 | "metadata": { 33 | "ExecuteTime": { 34 | "end_time": "2021-06-26T08:02:00.984170Z", 35 | "start_time": "2021-06-26T08:01:50.106348Z" 36 | } 37 | }, 38 | "outputs": [], 39 | "source": [ 40 | "from qiskit.providers.ibmq.managed import IBMQJobManager\n", 41 | "from qiskit import IBMQ\n", 42 | "IBMQ.load_account()\n", 43 | "provider = IBMQ.get_provider(hub='ibm-q-education')" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 1, 49 | "metadata": { 50 | "ExecuteTime": { 51 | "end_time": "2021-06-27T07:28:35.302043Z", 52 | "start_time": "2021-06-27T07:28:32.109624Z" 53 | } 54 | }, 55 | "outputs": [], 56 | "source": [ 57 | "from qiskit import QuantumCircuit, execute, transpile, Aer\n", 58 | "from qiskit.extensions import UnitaryGate, Initialize\n", 59 | "from qiskit.quantum_info import Statevector\n", 60 | "from qiskit.compiler import assemble \n", 61 | "from qiskit.tools.visualization import plot_bloch_vector\n", 62 | "from qiskit.tools.visualization import plot_histogram, plot_bloch_multivector\n", 63 | "import numpy as np\n", 64 | "from time import sleep\n", 65 | "import sys\n", 66 | "sys.path.append(\"..\")\n", 67 | "from scipy.stats import unitary_group\n", 68 | "import matplotlib.pyplot as plt\n", 69 | "%matplotlib inline" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "- So what job manager does is that it takes all circuits, executes them in batches of 75 and brings back the results.\n", 77 | "- This means, what I can do is I need (2Bm) jobs each consisting of resolution number of circuits.\n", 78 | "- This means, I can bundle all the (2Bm.resolution) jobs together , get the result \n", 79 | "- Now, take first resolution number of jobs, get the max theta and cost and append to the list \n", 80 | "- At the end you will only have **max_iters** jobs that are sent to the backend instead of **max_iters.2*Bm** jobs, exponentially lesser api calls" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "### Algorithm" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 2, 93 | "metadata": { 94 | "ExecuteTime": { 95 | "end_time": "2021-06-27T07:28:43.943651Z", 96 | "start_time": "2021-06-27T07:28:43.930745Z" 97 | } 98 | }, 99 | "outputs": [], 100 | "source": [ 101 | "from Modules.changed_SPEA import bundled_SPEA_alternate " 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "### Testing\n", 109 | "- This algorithm is tested for a phase gate of $ \\theta = \\frac{1}{4} $ " 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 3, 115 | "metadata": { 116 | "ExecuteTime": { 117 | "end_time": "2021-06-26T08:02:18.934488Z", 118 | "start_time": "2021-06-26T08:02:18.197565Z" 119 | } 120 | }, 121 | "outputs": [ 122 | { 123 | "data": { 124 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKoAAAB7CAYAAADkFBsIAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAIK0lEQVR4nO3dW0xU+R3A8e8MC8KWrcWQYsSIIowKkUHxFmK51OqiL9h4t0yiIdGI2kTb6kOD7Ut5IJPGtMHGpgYTH2iUkE2aGFI2wiBC6rWD2rqjQURdBNSyLlRQYfow67AoF+mGc85v+H2SCXBgwm/CN+fMmXDmb/P7/X6Usji72QMo9SE0VCWChqpE0FCVCBqqEkFDVSJoqEoEDVWJoKEqETRUJYKGqkTQUJUIGqoSQUNVImioSgQNVYmgoSoRNFQlgoaqRNBQlQgaqhJBQ1UiaKhKBA1ViaChKhE0VCWChqpE0FCVCBqqEkFDVSJoqEoEDVWJoKEqETRUJYKGqkTQUJUIGqpAgwPQ3wsDr8yexDiWDnVwcBC3201ycjKRkZE4nU48Hg8LFixgz549Zo9nuL4XcOdzqPsjXPwT1P4BblTB8zazJ5t8H5k9wFgKCwupqqqiuLiYjIwMGhsb2bFjB11dXRw+fNjs8QzV8xSu/RVe9w3f/uw+PGuBRZ9C/GJzZjOCZUOtqKjg9OnT1NXVkZ2dDUBubi7Xr1+nqqqKpUuXmjyhcfx+uPk3eN0/0jcDH/79d4iZDR/HGDqaYSx76C8pKSEvLy8Y6VtJSUmEh4eTlpYGQGtrK9nZ2TgcDhYvXszFixfNGHdSdT+C3mcEoxyRHx57jZrIeJYM9dGjR9y6dYstW7a89722tjZSU1OZNm0aAHv37mXbtm34fD5OnjzJ9u3befVq/LMMm80m5vbzwt+M+3j8fj81n10zfdaJ3j6UZUMFmDlz5rDtL1++xOPxBA/7T58+paGhgcLCQgAyMzOZNWsWtbW1xg48ycJsYYy3EqjNZiPMHmbQRMaz5HPU2NhYAHw+Hxs2bAhuLy0tpb29nYyMDCCwd42LiwvuXQHmzZvHgwcPxv0dkpaA7bwHzZ+N/3M/+jQd/+/lPK6JsGSoiYmJpKWlUVJSwowZM4iPj6eyspLz588DBEOdKmITYVp04LXTsZ6nzk43aiLjWfLQb7fbOXfuHKmpqezbt4/du3cTGxvL/v37CQsLC55IzZkzh46ODvr7h06H79+/T0JCglmjTwq7HVI3gM0GjPK0bu5K+H6coWMZyiZpGXSXy4XX66W5uTm4bd26dWzcuJGioiIaGxvZvHkzra2tREREmDjp5PjqS7h3Ef7zcGhb5HSYtxJmLf4m5BAlKtRFixaxatUqysvLg9taWlrYtWsXT548ISIigrKysvde0go1L7vh0l8Cn6/5RWgH+pYln6OOpKenB5/PR1FR0bDtiYmJ1NfXmzSVOaJ+MPT5VIgUBIUaHR3NwMCA2WMok1jyZEqpd2moSgQNVYmgoSoRNFQlgoaqRNBQlQgaqhJBQ1UiaKhKBA01BNXV1ZGQkEBOTg75+fn09Q2/dNXtdnPjxo1R7//48WPWr19PZmYmq1ev5tChQwB4vV5KS0sndfbRaKghyuVyUVdXR2ZmJpWVlcHtg4ODXLp0iSVLlox635qaGgoKCrhw4QINDQ10dnZy8+ZNnE4nTU1NplwdoaGGuPT09OA1aBDYKyYlJQGBPW9MTAw5OTkkJCSQn58PgMfjIT8/n8jISADCw8MJCwtcj5WcnDzm3niyaKghrr6+HofDEfz67t27zJ07F4CsrCxWrFgRfO+EEydO4Pf76e3tJTo6GoDm5ma6urpISUkBAv9WeefOHcMfh4Yaos6cOUNubi7d3d3BPeW7WlpaSExMBODhw4fEx8fj9XpxOp0APH/+nAMHDnDq1CnD5h6NhhqiXC4XtbW1lJWVBQ/bEDh0t7a2AnD79m1SU1MZGBjAbg+kUFNTw9q1a3nz5g0FBQW43e5hl623tLSwcOFCQx8LaKhTjtPpxOfzAUOh9vf309nZSXt7O1evXmXZsmWcO3eOK1eucOTIEXJycmhqagICl7Cnp6cbPreoa6bUkM/dgY8/+eXE7+t2u1mzZs2IZ/5nz55l69atI97P6/VSXV3N0aNHJ/5LvyMNVajvEqpEeuhXImioSgQNVYmgoSoRNFQlgoaqRNBQlQgaqhJBQ1UiaKhKBA1ViaChKhE0VCWCpUPVRXvf999uuNcw9PXztsASlKHO0u84rYv2DvEPwhcX4NE/h2+/fhY+iYP0nwaW+AlVlv1/1IqKCnbu3Dls0V6ATZs2UVVVxeXLl1m+fLmJExrLVwdtV0f5pg2+NwNWusBu6V3P/8+yh/4PXbT32LFjOBwO7Hb7sOvXQ8mrXnh4fYwf8AcW9e3wGTaS4SwZ6kQW7c3Ly6O6upqsrCyjxzTMky8Ch/4x2aD9tiHjmMKyocL4i/ZCYKHet5f8ToTZqypP5PbbX/9u/Hcn8cO1f9wyfdYptbr0txft/bZ3F+2dKr7qfTruH3VwcIDurzsMmsh4lnzqbcSivRY9hxxR39fQ8GfGXLDXbg/jZwfW8KuTch7XRFhyj/qhi/ZOFZGfwOyxHrINoqZDnPHvC2EYS+5RARwOB7W1tcO2uVwuUlJSiIqKMmkq8zh+DG9ew5N/fbPh7TMBP3wcA0s2QVi4WdNNPsu+jjqSkRbtLS4upry8nK6uLqKjo4mKisLj8TB//nwTJ508Lzrgy1vQ9wI+ioAfLoDYxMBS6aFMTKg9PT1Mnz6d48ePc/DgQbPHUQYTE6qa2kL8gKFChYaqRNBQlQgaqhJBQ1UiaKhKBA1ViaChKhE0VCWChqpE0FCVCBqqEkFDVSJoqEoEDVWJoKEqETRUJYKGqkTQUJUIGqoSQUNVImioSgQNVYmgoSoRNFQlgoaqRNBQlQj/A08ZhshjQJjAAAAAAElFTkSuQmCC\n", 125 | "text/plain": [ 126 | "
" 127 | ] 128 | }, 129 | "execution_count": 3, 130 | "metadata": {}, 131 | "output_type": "execute_result" 132 | } 133 | ], 134 | "source": [ 135 | "q = QuantumCircuit(2)\n", 136 | "q.cp(2*np.pi*(1/4), 0, 1)\n", 137 | "q.draw('mpl')" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 4, 143 | "metadata": { 144 | "ExecuteTime": { 145 | "end_time": "2021-06-26T08:02:19.027240Z", 146 | "start_time": "2021-06-26T08:02:18.999313Z" 147 | } 148 | }, 149 | "outputs": [], 150 | "source": [ 151 | "spe = bundled_SPEA_alternate(q, resolution=30, error=3, max_iters=10)" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": [ 158 | "- Choosing backend as **ibmq_jakarta**" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 6, 164 | "metadata": { 165 | "ExecuteTime": { 166 | "end_time": "2021-06-26T08:02:20.105525Z", 167 | "start_time": "2021-06-26T08:02:20.099507Z" 168 | } 169 | }, 170 | "outputs": [], 171 | "source": [ 172 | "jakarta = provider.get_backend('ibmq_jakarta')\n", 173 | "bogota = provider.get_backend('ibmq_bogota')" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": null, 179 | "metadata": { 180 | "ExecuteTime": { 181 | "end_time": "2021-06-26T08:07:52.037078Z", 182 | "start_time": "2021-06-26T08:02:20.643357Z" 183 | } 184 | }, 185 | "outputs": [], 186 | "source": [ 187 | "thetas = []\n", 188 | "for k in range(5):\n", 189 | " result = spe.get_eigen_pair(\n", 190 | " backend=jakarta, progress=True, randomize=True)\n", 191 | " print(\"Result is :\", result)\n", 192 | " thetas.append(result['theta'])" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": null, 198 | "metadata": { 199 | "ExecuteTime": { 200 | "end_time": "2021-06-26T08:07:52.049009Z", 201 | "start_time": "2021-06-26T08:02:22.353Z" 202 | } 203 | }, 204 | "outputs": [], 205 | "source": [ 206 | "thetas" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "plt.title(\"Plot for returned Eigenvalues\", fontsize=16)\n", 216 | "plt.xlabel(\"Experiment number\")\n", 217 | "plt.ylabel(\"Eigenvalues\")\n", 218 | "plt.plot([0, 6], [0, 0], color='black')\n", 219 | "plt.plot([0, 6], [1, 1], color='black')\n", 220 | "plt.plot([0, 6], [0.25, 0.25], color='black')\n", 221 | "plt.plot(list(range(5)), thetas, label='Estimates',\n", 222 | " color='cyan', linewidth=2, marker='s')\n", 223 | "plt.legend()\n", 224 | "plt.grid()" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": null, 230 | "metadata": {}, 231 | "outputs": [], 232 | "source": [] 233 | } 234 | ], 235 | "metadata": { 236 | "kernelspec": { 237 | "display_name": "qpe_test", 238 | "language": "python", 239 | "name": "qpe_test" 240 | }, 241 | "language_info": { 242 | "codemirror_mode": { 243 | "name": "ipython", 244 | "version": 3 245 | }, 246 | "file_extension": ".py", 247 | "mimetype": "text/x-python", 248 | "name": "python", 249 | "nbconvert_exporter": "python", 250 | "pygments_lexer": "ipython3", 251 | "version": "3.9.5" 252 | }, 253 | "varInspector": { 254 | "cols": { 255 | "lenName": 16, 256 | "lenType": 16, 257 | "lenVar": 40 258 | }, 259 | "kernels_config": { 260 | "python": { 261 | "delete_cmd_postfix": "", 262 | "delete_cmd_prefix": "del ", 263 | "library": "var_list.py", 264 | "varRefreshCmd": "print(var_dic_list())" 265 | }, 266 | "r": { 267 | "delete_cmd_postfix": ") ", 268 | "delete_cmd_prefix": "rm(", 269 | "library": "var_list.r", 270 | "varRefreshCmd": "cat(var_dic_list()) " 271 | } 272 | }, 273 | "types_to_exclude": [ 274 | "module", 275 | "function", 276 | "builtin_function_or_method", 277 | "instance", 278 | "_Feature" 279 | ], 280 | "window_display": false 281 | } 282 | }, 283 | "nbformat": 4, 284 | "nbformat_minor": 2 285 | } 286 | -------------------------------------------------------------------------------- /QPE/Modules/kitaev_qpe.py: -------------------------------------------------------------------------------- 1 | from qiskit import QuantumCircuit, execute 2 | from qiskit.tools.visualization import plot_histogram 3 | from qiskit.extensions import UnitaryGate 4 | import numpy as np 5 | from IPython.display import display 6 | 7 | 8 | class KQPE: 9 | """ 10 | Implements the Kitaev's phase estimation algorithm where a single circuit 11 | is used to estimate the phase of a unitary but the measurements are 12 | exponential. 13 | Attributes : 14 | precision : (int)the precision upto which the phase needs to be estimated 15 | NOTE : precision is estimated as 2^(-precision) 16 | 17 | unitary (np.ndarray or QuantumCircuit or UnitaryGate) : the unitary matrix for which 18 | we want to find the phase, given its eigenvector 19 | qubits (int) : the number of qubits on which the unitary matrix acts 20 | 21 | Methods : 22 | get_phase(QC,ancilla,clbits,backend, show) : generate the resultant phase associated with the given Unitary 23 | and the given eigenvector 24 | get_circuit(show,save_circ, circ_name ) : generate a Kitaev phase estimation circuit which can be attached 25 | to the parent quantum circuit containing eigenvector of the unitary matrix 26 | """ 27 | 28 | def __init__(self, unitary, precision=10): 29 | """ 30 | Args : 31 | precision(int) : The precision upto which the phase is estimated. 32 | Interpreted as 2^(-precision). 33 | eg. precision = 4 means the phase is going to be precise 34 | upto 2^(-4). 35 | 36 | unitary(np.ndarray or UnitaryGate or QuantumCircuit): 37 | The unitary for which we want to determine the phase. 38 | 39 | Raises : 40 | TypeError : if precision or unitary are not of a valid type 41 | ValueError : if precision is not valid 42 | 43 | Examples : 44 | # passing as array 45 | theta = 1/6 46 | U1 = np.ndarray([[1,0], 47 | [0, np.exp(2*np.pi*1j*(theta))]]) 48 | 49 | kqpe1 = KQPE(precision = 8, unitary = U1) 50 | 51 | # passing as QuantumCircuit 52 | U2 = QuantumCircuit(1) 53 | U2.rz(np.pi/7,0) 54 | 55 | kqpe2 = KQPE(precision = 8,unitary = U2) 56 | 57 | """ 58 | # handle precision 59 | if not isinstance(precision, int): 60 | raise TypeError("Precision needs to be an integer") 61 | elif precision <= 0: 62 | raise ValueError("Precision needs to be >=0") 63 | 64 | self.precision = 1 / (2 ** precision) 65 | 66 | # handle unitary 67 | if unitary is None: 68 | raise Exception( 69 | "Unitary needs to be specified for the Kitaev QPE algorithm" 70 | ) 71 | elif ( 72 | not isinstance(unitary, np.ndarray) 73 | and not isinstance(unitary, QuantumCircuit) 74 | and not isinstance(unitary, UnitaryGate) 75 | ): 76 | raise TypeError( 77 | "A numpy array, QuantumCircuit or UnitaryGate needs to be passed as the unitary matrix" 78 | ) 79 | 80 | self.unitary = unitary 81 | 82 | # get the number of qubits in the unitary 83 | if isinstance(unitary, np.ndarray): 84 | self.qubits = int(np.log2(unitary.shape[0])) 85 | else: 86 | self.qubits = int(unitary.num_qubits) 87 | 88 | def get_phase(self, QC, ancilla, clbits, backend, show=False): 89 | """This function is used to determine the final measured phase from the circuit 90 | with the specified precision. 91 | 92 | Args : 93 | QC (QuantumCircuit) : the quantum circuit on which we have attached 94 | the kitaev's estimation circuit. Must contain atleast 2 95 | classical bits for correct running of the algorithm 96 | ancilla(list-like) : the ancilla qubits to be used in the kitaev phase 97 | estimation 98 | clbits(list-like) : the classical bits in which the measurement results 99 | of the given ancilla qubits is stored 100 | backend(ibmq_backend or 'qasm_simulator') : the backend on which the 101 | circuit is executed on 102 | show(bool) : boolean to specify whether the progress and the circuit 103 | need to be shown 104 | 105 | Raises : 106 | Exception : If a QuantumCircuit with less than 2 classical bits or less 107 | than 3 qubits is passed, if the ancilla or the clbits are 108 | not unique, if more than 2 of classical or ancilla bits are 109 | provided 110 | 111 | Returns : 112 | phase(tuple) : (phase_dec, phase_binary) : A 2-tuple representing 113 | the calculated phase in decimal and binary upto the given 114 | precision 115 | 116 | NOTE : for details of the math please refer to section2A of https://arxiv.org/pdf/1910.11696.pdf. 117 | 118 | Examples : 119 | 120 | U = np.array([[1, 0], 121 | [0, np.exp(2*np.pi*1j*(1/3))]]) 122 | 123 | kqpe = KQPE(unitary=U, precision=16) 124 | kq_circ = kqpe.get_circuit(show=True, save_circ=True, 125 | circ_name="KQPE_circ_1qubit.JPG") 126 | # defining parent quantum circuit 127 | q = QuantumCircuit(5, 6) 128 | 129 | #eigenvector of unitary 130 | q.x(3) 131 | 132 | #kitaev circuit is attached on the eigenvector and 133 | # two additional ancilla qubits 134 | q.append(kq_circ, qargs=[1, 2, 3]) 135 | q.draw('mpl') 136 | 137 | # result 138 | phase = kqpe.get_phase(backend=Aer.get_backend( 139 | 'qasm_simulator'), QC=q, ancilla=[1, 2], clbits=[0, 1], show=True) 140 | 141 | """ 142 | # handle circuit 143 | if not isinstance(QC, QuantumCircuit): 144 | raise TypeError( 145 | "A QuantumCircuit must be provided for generating the phase" 146 | ) 147 | 148 | if len(QC.clbits) < 2: 149 | raise Exception("Atleast 2 classical bits needed for measurement") 150 | elif len(QC.qubits) < 3: 151 | raise Exception("Quantum Circuit needs to have atleast 3 qubits") 152 | 153 | # handle bits 154 | elif len(ancilla) != 2 or ancilla is None: 155 | raise Exception("Exactly two ancilla bits need to be specified") 156 | elif len(clbits) != 2 or clbits is None: 157 | raise Exception( 158 | "Exactly two classical bits need to be specified for measurement" 159 | ) 160 | elif len(set(clbits)) != len(clbits) or len(set(ancilla)) != len(ancilla): 161 | raise Exception("Duplicate bits provided in lists") 162 | 163 | # find number of shots -> atleast Big-O(1/precision shots) 164 | 165 | shots = 10 * int(1 / self.precision) 166 | if show == True: 167 | print("Shots :", shots) 168 | 169 | # measure into the given bits 170 | QC.measure([ancilla[0], ancilla[1]], [clbits[0], clbits[1]]) 171 | 172 | if show == True: 173 | display(QC.draw("mpl")) 174 | 175 | # execute the circuit 176 | result = execute( 177 | QC, backend=backend, shots=shots, optimization_level=3 178 | ).result() 179 | counts = result.get_counts() 180 | if show: 181 | print("Measurement results :", counts) 182 | if show: 183 | display(plot_histogram(counts)) 184 | 185 | # now get the results 186 | C0, C1, S0, S1 = 0, 0, 0, 0 187 | first = clbits[0] 188 | second = clbits[1] 189 | for i, j in zip(list(counts.keys()), list(counts.values())): 190 | # get bits 191 | l = len(i) 192 | one = i[l - first - 1] 193 | two = i[l - second - 1] 194 | 195 | # First qubit 0 - C (0,theta) 196 | if one == "0": 197 | C0 += j 198 | # First qubit 1 - C (1,theta) 199 | else: 200 | C1 += j 201 | # Second qubit 0 - S (0,theta) 202 | if two == "0": 203 | S0 += j 204 | # Second qubit 1 - S (1,theta) 205 | else: 206 | S1 += j 207 | 208 | # normalize 209 | C0, C1, S0, S1 = C0 / shots, C1 / shots, S0 / shots, S1 / shots 210 | 211 | # determine theta_0 212 | tan_1 = np.arctan2([(1 - 2 * S0)], [(2 * C0 - 1)])[0] 213 | theta_0 = (1 / (2 * np.pi)) * tan_1 214 | 215 | # determine theta_1 216 | tan_2 = np.arctan2([(2 * S1 - 1)], [(1 - 2 * C1)])[0] 217 | theta_1 = (1 / (2 * np.pi)) * tan_2 218 | 219 | phase_dec = np.average([theta_0, theta_1]) 220 | phase_binary = [] 221 | phase = phase_dec 222 | 223 | # generate the binary representation 224 | for i in range(int(np.log2((1 / self.precision)))): 225 | phase *= 2 226 | if phase < 1: 227 | phase_binary.append(0) 228 | else: 229 | phase -= 1 230 | phase_binary.append(1) 231 | 232 | return (phase_dec, phase_binary) 233 | 234 | def get_circuit(self, show=False, save_circ=False, circ_name="KQPE_circ.JPG"): 235 | """Returns a kitaev phase estimation circuit 236 | with the unitary provided 237 | 238 | Args: 239 | show(bool) : whether to draw the circuit or not 240 | Default - False 241 | save_circ(bool) : whether to save the circuit in 242 | an image or not. Default - False 243 | circ_name(str) : filename with which the circuit 244 | is stored. Default - KQPE_circ.JPG 245 | 246 | Returns : A QuantumCircuit with the controlled unitary matrix 247 | and relevant gates attached to the circuit. 248 | Size of the circuit is (2 + the number of qubits in unitary) 249 | 250 | Examples: 251 | theta = 1/5 252 | unitary = UnitaryGate(np.ndarray([[1,0], 253 | [0, np.exp(2*np.pi*1j*(theta))]])) 254 | kqpe = KQPE(unitary,precision = 10) 255 | kq_circ = kqpe.get_circuit(show = True,save_circ = True, circ_name= "KQPE_circ_1qubit.JPG") 256 | 257 | # attaching the circuit 258 | q = QuantumCircuit(5, 6) 259 | q.x(3) 260 | q.append(kq_circ, qargs=[1, 2, 3]) 261 | q.draw('mpl') 262 | 263 | """ 264 | qc = QuantumCircuit(2 + self.qubits, name="KQPE") 265 | qubits = [i for i in range(2, 2 + self.qubits)] 266 | 267 | # make the unitary 268 | if isinstance(self.unitary, np.ndarray): 269 | U = UnitaryGate(data=self.unitary) 270 | C_U = U.control(num_ctrl_qubits=1, label="CU", ctrl_state="1") 271 | else: 272 | C_U = self.unitary.control(num_ctrl_qubits=1, label="CU", ctrl_state="1") 273 | 274 | # qubit 0 is for the H estimation 275 | qc.h(0) 276 | qc = qc.compose(C_U, qubits=[0] + qubits) 277 | qc.h(0) 278 | qc.barrier() 279 | 280 | # qubit 1 is for the H + S estimation 281 | qc.h(1) 282 | qc.s(1) 283 | qc = qc.compose(C_U, qubits=[1] + qubits) 284 | qc.h(1) 285 | 286 | qc.barrier() 287 | if show == True: 288 | if save_circ: 289 | display(qc.draw("mpl", filename=circ_name)) 290 | else: 291 | display(qc.draw("mpl")) 292 | 293 | return qc 294 | -------------------------------------------------------------------------------- /QPE/Modules/bundled_global_max_SPEA.py: -------------------------------------------------------------------------------- 1 | from qiskit import QuantumCircuit, execute, transpile, Aer 2 | from qiskit.extensions import UnitaryGate, Initialize 3 | from qiskit.providers.ibmq.managed import IBMQJobManager 4 | import numpy as np 5 | from sys import stdout 6 | from scipy.stats import unitary_group 7 | 8 | 9 | class bundled_changed_SPEA: 10 | def __init__(self, unitary, resolution=100, error=3, max_iters=20): 11 | 12 | # handle resolution 13 | if not isinstance(resolution, int): 14 | raise TypeError("Please enter the number of intervals as an integer value") 15 | if resolution < 10 or resolution > 1e6: 16 | raise ValueError( 17 | "Resolution needs to be atleast 0.1 and greater than 0.000001" 18 | ) 19 | 20 | self.resolution = resolution 21 | 22 | # handle unitary 23 | if ( 24 | not isinstance(unitary, np.ndarray) 25 | and not isinstance(unitary, QuantumCircuit) 26 | and not isinstance(unitary, UnitaryGate) 27 | ): 28 | raise TypeError( 29 | "A numpy array or Quantum Circuit or UnitaryGate needs to be passed as the unitary matrix" 30 | ) 31 | 32 | # convert circuit to numpy array for uniformity 33 | if isinstance(unitary, UnitaryGate): 34 | U = unitary.to_matrix() 35 | else: # both QC and ndarray type 36 | U = unitary 37 | 38 | # note - the unitary here is not just a single qubit unitary 39 | if isinstance(U, np.ndarray): 40 | self.dims = U.shape[0] 41 | else: 42 | self.dims = 2 ** (U.num_qubits) 43 | 44 | if isinstance(U, np.ndarray): 45 | self.c_unitary_gate = UnitaryGate(data=U).control( 46 | num_ctrl_qubits=1, label="CU", ctrl_state="1" 47 | ) 48 | else: 49 | self.c_unitary_gate = U.control( 50 | num_ctrl_qubits=1, label="CU", ctrl_state="1" 51 | ) 52 | 53 | # handle error 54 | if not isinstance(error, int): 55 | raise TypeError( 56 | "The allowable error should be provided as an int. Interpreted as 10**(-error)" 57 | ) 58 | if error <= 0: 59 | raise ValueError("The error threshold must be finite and greater than 0.") 60 | 61 | self.error = error 62 | 63 | # handle max_iters 64 | if not isinstance(max_iters, int): 65 | raise TypeError("Max iterations must be of integer type") 66 | if max_iters <= 0 and max_iters > 1e5: 67 | raise ValueError("Max iterations should be atleast 1 and less than 1e5") 68 | 69 | self.iterations = max_iters 70 | self.basis = [] 71 | 72 | def get_basis_vectors(self, randomize=True): 73 | # get the d dimensional basis for the unitary provided 74 | if randomize == True: 75 | UR = unitary_group.rvs(self.dims) 76 | else: 77 | UR = np.identity(self.dims) 78 | 79 | basis = [] 80 | for k in UR: 81 | basis.append(np.array(k, dtype=complex)) 82 | return basis 83 | 84 | def get_circuits(self, angles, state): 85 | """Given an initial state and a set of angles, 86 | return the circuits that are generated with 87 | those angles""" 88 | result = {"cost": -1, "theta": -1} 89 | # all theta values are iterated over for the same state 90 | phi = Initialize(state) 91 | shots = 512 92 | circuits = [] 93 | 94 | for theta in angles: 95 | qc = QuantumCircuit(1 + int(np.log2(self.dims)), 1) 96 | # initialize the circuit 97 | qc = qc.compose(phi, qubits=list(range(1, int(np.log2(self.dims)) + 1))) 98 | # add hadamard 99 | qc.h(0) 100 | # add unitary which produces a phase kickback on control qubit 101 | qc = qc.compose( 102 | self.c_unitary_gate, qubits=range(1 + int(np.log2(self.dims))) 103 | ) 104 | # add the inv rotation 105 | qc.p(-2 * np.pi * theta, 0) 106 | # add hadamard 107 | qc.h(0) 108 | # measure 109 | qc.measure([0], [0]) 110 | # generate all the circuits... 111 | circuits.append(qc) 112 | 113 | return circuits 114 | 115 | def get_cost(self, angles, counts, shots): 116 | """Generate the best cost and theta pair 117 | for the particular state""" 118 | 119 | result = {"cost": -1, "theta": -1} 120 | # get the cost for this theta 121 | 122 | for k, theta in zip(counts, angles): 123 | # for all experiments you ran 124 | try: 125 | C_val = (k["0"]) / shots 126 | except: 127 | C_val = 0 128 | 129 | if C_val > result["cost"]: 130 | # means this is a better theta value 131 | result["theta"] = theta 132 | result["cost"] = C_val 133 | 134 | return result 135 | 136 | def get_eigen_pair(self, backend, progress=False, randomize=True): 137 | """Finding the eigenstate pair for the unitary""" 138 | 139 | if not isinstance(progress, bool): 140 | raise TypeError("Progress must be a boolean variable") 141 | 142 | if not isinstance(randomize, bool): 143 | raise Exception("Randomize must be a boolean variable") 144 | 145 | results = dict() 146 | 147 | # first initialize the state phi 148 | self.basis = self.get_basis_vectors(randomize) 149 | 150 | # choose a random index 151 | ind = np.random.choice(self.dims) 152 | phi = self.basis[ind] 153 | 154 | # doing the method 1 of our algorithm 155 | # define resolution of angles and precision 156 | precision = 1 / 10 ** self.error 157 | samples = self.resolution 158 | 159 | # initialization of range 160 | left, right = 0, 1 161 | shots = 512 162 | 163 | # generate the angles 164 | angles = np.linspace(left, right, samples) 165 | 166 | # First execution can be done without JobManager also... 167 | circs = self.get_circuits(angles, phi) 168 | job = execute(circs, backend=backend, shots=shots) 169 | counts = job.result().get_counts() 170 | result = self.get_cost(angles, counts, shots) 171 | 172 | # get initial estimates 173 | cost = result["cost"] 174 | theta_max = result["theta"] 175 | best_phi = phi 176 | 177 | # the range upto which theta extends iin each iteration 178 | angle_range = 0.5 179 | # a parameter 180 | a = 1 181 | # start algorithm 182 | iters = 0 183 | found = True 184 | plus = (1 / np.sqrt(2)) * np.array([1, 1]) 185 | minus = (1 / np.sqrt(2)) * np.array([1, -1]) 186 | 187 | # define IBMQManager instance 188 | manager = IBMQJobManager() 189 | 190 | while 1 - cost >= precision: 191 | # get angles, note if theta didn't change, then we need to 192 | # again generate the same range again 193 | right = min(1, theta_max + angle_range / 2) 194 | left = max(0, theta_max - angle_range / 2) 195 | if progress: 196 | print("Right :", right) 197 | print("Left :", left) 198 | # generate the angles only if the theta has been updated 199 | if found == True: 200 | angles = np.linspace(left, right, samples) 201 | 202 | found = False # for this iteration 203 | if progress: 204 | print("ITERATION NUMBER", iters + 1, "...") 205 | 206 | # generate a cost dict for each of the iterations 207 | # final result lists 208 | thetas, costs, states = [], [], [] 209 | 210 | # circuit list 211 | circuits = [] 212 | 213 | # list to store intermediate states 214 | phis = [] 215 | 216 | # 1. Circuit generation loop 217 | for i in range((2 * self.dims)): 218 | # everyone is supplied with the same range of theta in one iteration 219 | # define z 220 | # make a list of the circuits 221 | if i < self.dims: 222 | z = 1 223 | else: 224 | z = 1j 225 | 226 | # alter and normalise phi 227 | curr_phi = best_phi + z * a * (1 - cost) * self.basis[i % self.dims] 228 | curr_phi = curr_phi / np.linalg.norm(curr_phi) 229 | phis.append(curr_phi) 230 | 231 | # bundle the circuits together ... 232 | circs = self.get_circuits(angles, curr_phi) 233 | circuits = circuits + circs 234 | 235 | # now each iteration would see the same state as the best phi 236 | # is updated once at the end of the iteration 237 | 238 | # also, the cost is also updated only once at the end of the iteration 239 | 240 | # 2. run the generated circuits 241 | if progress: 242 | print("Transpiling circuits...") 243 | circuits = transpile(circuits=circuits, backend=backend) 244 | job_set = manager.run( 245 | circuits, backend=backend, name="Job_set" + str(iters), shots=shots 246 | ) 247 | if progress: 248 | print("Transpilation Done!\nJob sent...") 249 | job_result = job_set.results() 250 | 251 | # now get the circuits in chunks of resolution each 252 | if progress: 253 | print("Job has returned") 254 | 255 | # 3. Result generation loop 256 | for i in range((2 * self.dims)): 257 | # get the results of this basis state 258 | # it will have resolution number of circuits... 259 | counts = [] 260 | for j in range(i * self.resolution, (i + 1) * self.resolution): 261 | # in this you'll get the counts 262 | counts.append(job_result.get_counts(j)) 263 | 264 | result = self.get_cost(counts, angles, shots) 265 | 266 | # get the estimates for this basis 267 | curr_theta = result["theta"] 268 | curr_cost = result["cost"] 269 | curr_phi = phis[i] # the result was generated pertaining to this phi 270 | 271 | if ( 272 | curr_cost > cost 273 | ): # then only add this cost in the cost and states list 274 | thetas.append(float(curr_theta)) 275 | costs.append(float(curr_cost)) 276 | states.append(curr_phi) 277 | found = True 278 | 279 | if progress: 280 | stdout.write("\r") 281 | stdout.write("%f %%completed" % (100 * (i + 1) / (2 * self.dims))) 282 | stdout.flush() 283 | 284 | if found == False: 285 | # phi was not updated , change a 286 | a = a / 2 287 | if progress: 288 | print("\nNo change, updating a...") 289 | else: 290 | # if found is actually true, then only update 291 | 292 | # O(n) , would update this though 293 | index = np.argmax(costs) 294 | # update the parameters of the model 295 | cost = costs[index] 296 | theta_max = thetas[index] 297 | best_phi = states[index] 298 | if progress: 299 | print("Best Phi is :", best_phi) 300 | print("Theta estimate :", theta_max) 301 | print("Current cost :", cost) 302 | angle_range /= 2 # updated phi and thus theta too -> refine theta range 303 | 304 | # update the iterations 305 | iters += 1 306 | if progress: 307 | print("\nCOST :", cost) 308 | print("THETA :", theta_max) 309 | 310 | if iters >= self.iterations: 311 | print( 312 | "Maximum iterations reached for the estimation.\nTerminating algorithm..." 313 | ) 314 | break 315 | # add cost, eigenvector and theta to the dict 316 | results["cost"] = cost 317 | results["theta"] = theta_max 318 | results["state"] = best_phi 319 | 320 | return results 321 | -------------------------------------------------------------------------------- /QPE/Modules/faster_basic_qpe.py: -------------------------------------------------------------------------------- 1 | from qiskit.circuit import QuantumCircuit 2 | from qiskit.quantum_info import Operator 3 | from qiskit.extensions import UnitaryGate 4 | import numpy as np 5 | from IPython.display import display 6 | 7 | 8 | class fast_QPE: 9 | """ 10 | Implements the vanilla QPE algorithm utilising the Inverse Fourier Transform 11 | and Phase Kickbacks. 12 | 13 | Attributes : 14 | precision : int([1,...]): precision of the phase estimation ( also equal to number of qubits in scratch register) 15 | unitary(np.ndarray or QuantumCircuit or UnitaryGate): unitary operator for which QPE is being applied. 16 | rotations(dict): contains the rotation angles required for the IQFT procedure 17 | key : stage number, value : angle value 18 | powers(dict): contains the powers of the unitary matrix 19 | key : exponent, value : U^(exponent) 20 | controls(dict): contains equivalent controlled U gates to be applied 21 | key : qubit_index, value : controlled U gate 22 | Methods : 23 | get_rotations() : generate rotation values for the implementation of Inverse QFT 24 | binary_exp(U,n) : generates powers of U from U^1 -> U^(2^n) and stores in powers dict 25 | get_controls(unitary,n_qubits,exponent) : generates the U^(2^j) control gates for 26 | faster simulation of the QPE algorithm 27 | get_QFT(n_qubits,swaps,show) : generate an n_qubit Quantum Fourier Transform 28 | get_QPE(show) : generate the final QPE circuit to be attached to the user's quantum circuit 29 | """ 30 | 31 | def __init__(self, precision, unitary): 32 | """ 33 | Args : 34 | precision(int) : The precision upto which the phase needs to be estimated. 35 | Interpreted as 2^(-precision). 36 | eg. precision = 4 means the phase is going to be precise 37 | upto 2^(-4). 38 | unitary(np.ndarray or UnitaryGate or QuantumCircuit): 39 | The unitary for which we want to determine the phase. Currently 40 | this class supports 2 x 2 matrices or single qubit gates. 41 | Shall be extended for higher order matrices. 42 | Raises : 43 | TypeError : if precision or unitary are not of a valid type 44 | ValueError : if precision is not valid 45 | Exception : if unitary is of larger size than 2 x 2 46 | 47 | Examples : 48 | from basic_QPE import fast_QPE 49 | # passing as ndarray 50 | theta = 1/5 51 | U1 = np.ndarray([[1,0], 52 | [0, np.exp(2*np.pi*1j*(theta))]]) 53 | qpe1 = fast_QPE(precision = 4, unitary = U1) 54 | 55 | # passing as QuantumCircuit 56 | U2 = QuantumCircuit(1) 57 | U2.rz(np.pi/7,0) 58 | qpe2 = fast_QPE(precision = 5,unitary = U2) 59 | 60 | """ 61 | # handle precision 62 | if type(precision) != int: 63 | raise TypeError("Precision needs to be an integer") 64 | elif precision < 0 or precision == 0: 65 | raise ValueError("Precision needs to be atleast 1") 66 | 67 | self.precision = precision 68 | self.powers = {} 69 | self.controls = {} 70 | # handle unitary 71 | if ( 72 | not isinstance(unitary, np.ndarray) 73 | and not isinstance(unitary, UnitaryGate) 74 | and not isinstance(unitary, QuantumCircuit) 75 | ): 76 | raise TypeError( 77 | "A numpy array, UnitaryGate or QuantumCircuit needs to be passed as the unitary matrix" 78 | ) 79 | 80 | if isinstance(unitary, np.ndarray): 81 | self.unit_qubits = int(np.log2(unitary.shape[0])) 82 | else: 83 | self.unit_qubits = unitary.num_qubits 84 | 85 | self.unitary = unitary 86 | self.rotations = self.get_rotations() 87 | 88 | def get_rotations(self): 89 | """Returns a dictionary of the angles associated with 90 | each rotation gate upto precision specified""" 91 | rots = {} 92 | for k in range(1, self.precision): 93 | rots[k] = np.pi / (2 ** k) 94 | return rots 95 | 96 | def binary_exp(self, U, n): 97 | """This function returns the matrix U^(n) and saves 98 | other smaller powers 99 | 100 | Arguments: 101 | U(np.ndarray): 102 | The Unitary matrix which needs to be exponentitated 103 | n(int): integer specifying the exponent 104 | 105 | Raises: 106 | ValueError : when n is < 0 107 | Returns: 108 | a dictionary containing the relevant powers of the matrix U""" 109 | if n < 0: 110 | raise ValueError("Power should be atleast 0") 111 | if n == 1: 112 | self.powers[1] = U 113 | return self.powers[1] 114 | if n == 0: 115 | return np.identity(2) 116 | if n % 2 == 1: 117 | # if odd 118 | if (n - 1) not in self.powers: 119 | self.powers[n - 1] = self.binary_exp(U, n - 1) 120 | 121 | self.powers[n] = U @ self.powers[n - 1] 122 | return self.powers[n] 123 | else: 124 | # if even 125 | if n / 2 not in self.powers: 126 | self.powers[n / 2] = self.binary_exp(U, n / 2) 127 | 128 | self.powers[n] = self.powers[n / 2] @ self.powers[n / 2] 129 | return self.powers[n] 130 | 131 | def get_QFT(self, n_qubits: int, show=False, swaps=True): 132 | """Returns an n_qubits QFT circuit 133 | 134 | Arguments : 135 | n_qubits : the number of qubits 136 | show : whether to draw the circuit 137 | swaps : whether swaps need to be performed at the 138 | end of the procedure 139 | 140 | Returns : 141 | QuantumCircuit : the QuantumCircuit containing the QFT circuit 142 | """ 143 | 144 | if n_qubits <= 0: 145 | raise ValueError("Number of qubits must be > 0 ") 146 | 147 | qc = QuantumCircuit(n_qubits) 148 | for target in range(n_qubits): 149 | # apply hadamard 150 | qc.h(target) 151 | # add rotations 152 | rot = 1 153 | for control in range(target + 1, n_qubits): 154 | qc.cp(self.rotations[rot], control, target) 155 | rot += 1 156 | if swaps: 157 | ## add swap gates 158 | i, j = 0, n_qubits - 1 159 | while i < j: 160 | qc.swap(i, j) 161 | i += 1 162 | j -= 1 163 | 164 | if show: 165 | print("Circuit for QFT of " + str(n_qubits)) 166 | display(qc.draw("mpl")) 167 | return qc 168 | 169 | def get_controls(self, unitary, n_qubits, exponent): 170 | """Get the control gates for the circuit 171 | While computing exponent, we also 172 | compute the smaller powers 173 | 174 | Arguments: 175 | unitary : Unitary matrix given by the user 176 | n_qubits : the number of qubits that are needed ( equals the amount of precision) 177 | exponent : the largest exponent value , is equal to 2**(n_qubits - 1) 178 | 179 | Returns: 180 | controls(dict) : dictionary containing the relevant controlled unitary gates 181 | key : qubit_index, value : controlled U gate 182 | """ 183 | 184 | # Qubit i gets the phase kickback of 185 | # 2^i 186 | 187 | # generate the unitaries 188 | self.binary_exp(unitary, exponent) 189 | 190 | # self.controls has 1 based index 191 | controls = {} 192 | power = 1 193 | for qubit in range(n_qubits): 194 | c = UnitaryGate(data=self.powers[power]) 195 | cu = c.control(num_ctrl_qubits=1, label="CU", ctrl_state="1") 196 | controls[qubit] = cu 197 | power *= 2 198 | 199 | return controls 200 | 201 | def get_QPE(self, show=False, save=False): 202 | """Returns the final QPE circuit to the user in form of a 203 | QuantumCircuit 204 | 205 | Arguments : 206 | show(bool) : Whether to draw the circuit or not 207 | save(bool) : Whether to draw the circuit or not 208 | Returns : 209 | QuantumCircuit : The QuantumCircuit which can be attached to the user circuit 210 | 211 | Usage Notes : 212 | NOTE1: The last qubits of the circuit which is returned, need to be attached to the qubits 213 | containing the EIGENVECTOR of the unitary that is passed. 214 | The phase is encoded in the scratch qubits above the target qubit. 215 | 216 | For example :- 217 | If precision is specified as 4 and a 6qubit QuantumCircuit contains the 218 | eigenvector in the 5th qubit. 219 | unitary = np.ndarray([[1,0], 220 | [0, np.exp(2*np.pi*1j*(theta))]]) 221 | q = QuantumCircuit(6,4) 222 | 223 | q.x(4) # the eigenvector qubit 224 | 225 | qpe = fast_QPE(precision = 4,unitary = unitary) 226 | qpe_circ = qpe.get_QPE(show = True) 227 | q.append(qpe_circ, qargs = [0,1,2,3,4]) # last qubit is the eigenvector 228 | q.draw('mpl') 229 | 230 | NOTE2: The phase is assumed to be a binary fraction as 0.x1x2x2...xn where n 231 | is the precision specified by the user. 232 | The least significant bit , xn, is saved in the qubit with index 233 | t - precision and the most significant bit, x1, is saved in the 234 | qubit with index t - 1 where t is the index of the first target qubit 235 | containing the EIGENVECTOR of the unitary. 236 | 237 | For example :- 238 | theta = 1/5 # binary representation upto 4 bits : 0.0011 239 | 240 | unitary = np.ndarray([[1,0], 241 | [0, np.exp(2*np.pi*1j*(theta))]]) 242 | 243 | q = QuantumCircuit(6,4) 244 | q.x(4) # the eigenvector qubit 245 | 246 | qpe = fast_QPE(precision = 4,unitary = unitary) 247 | qpe_circ = qpe.get_QPE(show = True) 248 | q.append(qpe_circ, qargs = [0,1,2,3,4]) 249 | q.draw('mpl') 250 | # let '0011' be the state with highest probability after 251 | # measurement 252 | # q[0] would contain 1 253 | # q[1] would contain 1 254 | # q[2] would contain 0 255 | # q[3] would contain 0 where q refers to the qubit 256 | 257 | """ 258 | qc = QuantumCircuit(self.precision + self.unit_qubits, name="fast_QPE_circ") 259 | 260 | # generate the controlled Unitary gate and start applying it according 261 | # to the orientation specified 262 | if isinstance(self.unitary, QuantumCircuit): 263 | U = Operator(self.unitary).data 264 | elif isinstance(self.unitary, UnitaryGate): 265 | U = self.unitary.to_matrix() 266 | else: 267 | U = self.unitary 268 | 269 | # generate the binary exponent of the matrix 270 | n_qubits = self.precision 271 | exp = 2 ** (n_qubits - 1) 272 | 273 | # updates the self.controls dictionary 274 | self.controls = self.get_controls(U, n_qubits, exp) 275 | 276 | # add hadamard transform 277 | qc.h([i for i in range(self.precision)]) 278 | 279 | ## attach control gates 280 | target = [i + self.precision for i in range(self.unit_qubits)] 281 | for i in range(n_qubits): 282 | control = i 283 | qc = qc.compose(self.controls[i], qubits=[control] + target) 284 | qc.barrier() 285 | 286 | # attach IQFT 287 | IQFT = self.get_QFT(n_qubits, show=show, swaps=False).inverse() 288 | IQFT.name = "IQFT_circuit" 289 | 290 | ## add swap gates 291 | i, j = 0, n_qubits - 1 292 | while i < j: 293 | IQFT.swap(i, j) 294 | i += 1 295 | j -= 1 296 | 297 | # append circuit 298 | qc.append(IQFT, qargs=[i for i in range(self.precision)]) 299 | 300 | # that's it 301 | if show == True: 302 | if save == True: 303 | display(qc.draw("mpl", filename="QPE_circ_optimized.JPG", scale=0.8)) 304 | else: 305 | display(qc.draw("mpl")) 306 | return qc 307 | -------------------------------------------------------------------------------- /QPE/Modules/bundled_global_max_alt_SPEA.py: -------------------------------------------------------------------------------- 1 | from qiskit import QuantumCircuit, execute, transpile, Aer 2 | from qiskit.extensions import UnitaryGate, Initialize 3 | import numpy as np 4 | from qiskit.providers.ibmq.managed import IBMQJobManager 5 | from sys import stdout 6 | from scipy.stats import unitary_group 7 | 8 | 9 | class bundled_SPEA_alternate: 10 | def __init__(self, unitary, resolution=100, error=3, max_iters=20): 11 | 12 | # handle resolution 13 | if not isinstance(resolution, int): 14 | raise TypeError("Please enter the number of intervals as an integer value") 15 | if resolution < 10 or resolution > 1e6: 16 | raise ValueError( 17 | "Resolution needs to be atleast 0.1 and greater than 0.000001" 18 | ) 19 | 20 | self.resolution = resolution 21 | 22 | # handle unitary 23 | if ( 24 | not isinstance(unitary, np.ndarray) 25 | and not isinstance(unitary, QuantumCircuit) 26 | and not isinstance(unitary, UnitaryGate) 27 | ): 28 | raise TypeError( 29 | "A numpy array or Quantum Circuit or UnitaryGate needs to be passed as the unitary matrix" 30 | ) 31 | 32 | # convert circuit to numpy array for uniformity 33 | if isinstance(unitary, UnitaryGate): 34 | U = unitary.to_matrix() 35 | else: # both QC and ndarray type 36 | U = unitary 37 | 38 | # note - the unitary here is not just a single qubit unitary 39 | if isinstance(U, np.ndarray): 40 | self.dims = U.shape[0] 41 | else: 42 | self.dims = 2 ** (U.num_qubits) 43 | 44 | if isinstance(U, np.ndarray): 45 | self.c_unitary_gate = UnitaryGate(data=U).control( 46 | num_ctrl_qubits=1, label="CU", ctrl_state="1" 47 | ) 48 | else: 49 | self.c_unitary_gate = U.control( 50 | num_ctrl_qubits=1, label="CU", ctrl_state="1" 51 | ) 52 | 53 | # handle error 54 | if not isinstance(error, int): 55 | raise TypeError( 56 | "The allowable error should be provided as an int. Interpreted as 10**(-error)" 57 | ) 58 | if error <= 0: 59 | raise ValueError("The error threshold must be finite and greater than 0.") 60 | 61 | self.error = error 62 | 63 | # handle max_iters 64 | if not isinstance(max_iters, int): 65 | raise TypeError("Max iterations must be of integer type") 66 | if max_iters <= 0 and max_iters > 1e5: 67 | raise ValueError("Max iterations should be atleast 1 and less than 1e5") 68 | 69 | self.iterations = max_iters 70 | self.basis = [] 71 | 72 | def get_basis_vectors(self, randomize=True): 73 | # get the d dimensional basis for the unitary provided 74 | if randomize == True: 75 | UR = unitary_group.rvs(self.dims) 76 | else: 77 | UR = np.identity(self.dims) 78 | 79 | basis = [] 80 | for k in UR: 81 | basis.append(np.array(k, dtype=complex)) 82 | return basis 83 | 84 | def get_unitary_circuit(self, backend): 85 | """Return the pretranspiled circuit""" 86 | if backend is None: 87 | backend = Aer.get_backend("qasm_simulator") 88 | 89 | qc = QuantumCircuit(1 + int(np.log2(self.dims))) 90 | 91 | # make the circuit 92 | qc.h(0) 93 | qc = qc.compose(self.c_unitary_gate, qubits=range(1 + int(np.log2(self.dims)))) 94 | 95 | qc.barrier() 96 | 97 | qc = transpile(qc, backend=backend, optimization_level=3) 98 | return qc 99 | 100 | def get_circuit(self, state, backend, shots, angle=None): 101 | """Given an initial state , 102 | return the circuit that is generated with 103 | inverse rotation as 0.""" 104 | # all theta values are iterated over for the same state 105 | phi = Initialize(state) 106 | shots = 512 107 | 108 | qc1 = QuantumCircuit(1 + int(np.log2(self.dims)), 1) 109 | # initialize the circuit 110 | qc1 = qc1.compose(phi, qubits=list(range(1, int(np.log2(self.dims)) + 1))) 111 | qc1 = transpile(qc1, backend=backend) 112 | 113 | # get the circuit2 114 | qc2 = self.unitary_circuit 115 | 116 | qc3 = QuantumCircuit(1 + int(np.log2(self.dims)), 1) 117 | if angle is not None: 118 | # add inverse rotation on the first qubit 119 | qc3.p(-2 * np.pi * angle, 0) 120 | # add hadamard 121 | qc3.h(0) 122 | qc3 = transpile(qc3, backend=backend) 123 | 124 | # make final circuit 125 | qc = qc1 + qc2 + qc3 126 | # qc = assemble(qc,shots = shots) 127 | # measure 128 | qc.measure([0], [0]) 129 | return qc 130 | 131 | def get_optimal_angle(self, p0, p1, angles): 132 | """Return the theta value which minimizes 133 | the S metric""" 134 | min_s = 1e5 135 | best_theta = -1 136 | for theta in angles: 137 | c0 = (np.cos(np.pi * theta)) ** 2 138 | c1 = (np.sin(np.pi * theta)) ** 2 139 | # generate the metric ... 140 | s = (p0 - c0) ** 2 + (p1 - c1) ** 2 141 | if s < min_s: 142 | s = min_s 143 | best_theta = theta 144 | 145 | return best_theta 146 | 147 | def execute_job(self, progress, iteration, backend, shots, circuits): 148 | """Send a job to the backend using IBMQ Job Manager""" 149 | # define IBMQManager instance 150 | manager = IBMQJobManager() 151 | # first run the generated circuits 152 | if progress: 153 | print("Transpiling circuits...") 154 | 155 | # get the job runner instance 156 | job_set = manager.run( 157 | circuits, backend=backend, name="Job_set " + str(iteration), shots=shots 158 | ) 159 | if progress: 160 | print("Transpilation Done!\nJob sent...") 161 | 162 | # send and get job 163 | job_result = job_set.results() 164 | 165 | if progress: 166 | print("Job has returned") 167 | 168 | # return result 169 | return job_result 170 | 171 | def get_eigen_pair( 172 | self, 173 | backend, 174 | theta_left=0, 175 | theta_right=1, 176 | progress=False, 177 | randomize=True, 178 | basis=None, 179 | basis_ind=None, 180 | target_cost=None, 181 | shots=512, 182 | ): 183 | """Finding the eigenstate pair for the unitary""" 184 | self.unitary_circuit = self.get_unitary_circuit(backend) 185 | 186 | if theta_left > theta_right: 187 | raise ValueError( 188 | "Left bound for theta should be smaller than the right bound" 189 | ) 190 | elif (theta_left < 0) or (theta_right > 1): 191 | raise ValueError("Bounds of theta are [0,1].") 192 | 193 | if not isinstance(progress, bool): 194 | raise TypeError("Progress must be a boolean variable") 195 | 196 | if not isinstance(randomize, bool): 197 | raise Exception("Randomize must be a boolean variable") 198 | 199 | results = dict() 200 | 201 | # first initialize the state phi 202 | if basis is None: 203 | self.basis = self.get_basis_vectors(randomize) 204 | else: 205 | self.basis = basis 206 | 207 | # choose a random index 208 | if basis_ind is None: 209 | ind = np.random.choice(self.dims) 210 | else: 211 | ind = basis_ind 212 | 213 | phi = self.basis[ind] 214 | 215 | # doing the method 1 of our algorithm 216 | # define resolution of angles and precision 217 | precision = 1 / 10 ** self.error 218 | samples = self.resolution 219 | 220 | # initialization of range 221 | left, right = theta_left, theta_right 222 | 223 | # generate the angles 224 | angles = np.linspace(left, right, samples) 225 | 226 | # First execution can be done without JobManager also... 227 | circ = self.get_circuit(phi, backend=backend, shots=shots) 228 | job = execute(circ, backend=backend, shots=shots) 229 | counts = job.result().get_counts() 230 | 231 | if "0" in counts: 232 | p0 = counts["0"] 233 | else: 234 | p0 = 0 235 | if "1" in counts: 236 | p1 = counts["1"] 237 | else: 238 | p1 = 0 239 | 240 | # experimental probabilities 241 | p0, p1 = p0 / shots, p1 / shots 242 | 243 | # get initial angle estimate 244 | theta_max = self.get_optimal_angle(p0, p1, angles) 245 | 246 | # get intial cost 247 | circ = self.get_circuit(phi, backend=backend, shots=shots, angle=theta_max) 248 | job = execute(circ, backend=backend, shots=shots) 249 | counts = job.result().get_counts() 250 | if "0" in counts: 251 | cost = counts["0"] / shots 252 | else: 253 | cost = 0 254 | # update best phi 255 | best_phi = phi 256 | 257 | # the range upto which theta extends iin each iteration 258 | angle_range = (right - left) / 2 259 | # a parameter 260 | a = 1 261 | # start algorithm 262 | iters = 0 263 | found = True 264 | 265 | while 1 - cost >= precision: 266 | # get angles, note if theta didn't change, then we need to 267 | # again generate the same range again 268 | right = min(theta_right, theta_max + angle_range / 2) 269 | left = max(theta_left, theta_max - angle_range / 2) 270 | if progress: 271 | print("Right :", right) 272 | print("Left :", left) 273 | # generate the angles only if the theta has been updated 274 | if found == True: 275 | angles = np.linspace(left, right, samples) 276 | 277 | found = False # for this iteration 278 | if progress: 279 | print("ITERATION NUMBER", iters + 1, "...") 280 | 281 | # generate a cost dict for each of the iterations 282 | # final result lists 283 | costs, states = [], [] 284 | 285 | # circuit list 286 | circuits = [] 287 | 288 | # 1. Circuit generation loop 289 | for i in range((2 * self.dims)): 290 | # everyone is supplied with the same range of theta in one iteration 291 | # define z 292 | if i < self.dims: 293 | z = 1 294 | else: 295 | z = 1j 296 | 297 | # alter and normalise phi 298 | curr_phi = best_phi + z * a * (1 - cost) * self.basis[i % self.dims] 299 | curr_phi = curr_phi / np.linalg.norm(curr_phi) 300 | states.append(curr_phi) 301 | 302 | # bundle the circuits together ... 303 | circuits.append( 304 | self.get_circuit(curr_phi, backend=backend, shots=shots) 305 | ) 306 | 307 | job_result = self.execute_job(progress, iters, backend, shots, circuits) 308 | # define lists again 309 | thetas, circuits = [], [] 310 | 311 | # 3. Classical work 312 | for i in range((2 * self.dims)): 313 | # we have that many circuits only 314 | counts = job_result.get_counts(i) 315 | 316 | # get the experimental counts for this state 317 | try: 318 | exp_p0 = counts["0"] 319 | except: 320 | exp_p0 = 0 321 | try: 322 | exp_p1 = counts["1"] 323 | except: 324 | exp_p1 = 0 325 | 326 | # normalize 327 | exp_p0 = exp_p0 / shots 328 | exp_p1 = exp_p1 / shots 329 | theta_val = self.get_optimal_angle(exp_p0, exp_p1, angles) 330 | 331 | # generate the circuit and append 332 | circuits.append( 333 | self.get_circuit( 334 | states[i], backend=backend, shots=shots, angle=theta_val 335 | ) 336 | ) 337 | thetas.append(theta_val) 338 | 339 | # again execute these circuits 340 | 341 | job_result = self.execute_job(progress, iters, backend, shots, circuits) 342 | 343 | # 5. Result generation loop 344 | for i in range((2 * self.dims)): 345 | # cost generation after execution ... 346 | counts = job_result.get_counts(i) 347 | if "0" in counts: 348 | curr_cost = counts["0"] / (shots) 349 | else: 350 | curr_cost = 0 351 | if ( 352 | curr_cost > cost 353 | ): # then only add this cost in the cost and states list 354 | cost = curr_cost 355 | best_phi = states[i] 356 | theta_max = thetas[i] 357 | found = True 358 | 359 | if progress: 360 | stdout.write("\r") 361 | stdout.write("%f %%completed" % (100 * (i + 1) / (2 * self.dims))) 362 | stdout.flush() 363 | 364 | if found == False: 365 | # phi was not updated , change a 366 | a = a / 2 367 | if progress: 368 | print("\nNo change, updating a...") 369 | else: 370 | # if found is actually true, then only print 371 | if progress: 372 | print("Best Phi is :", best_phi) 373 | print("Theta estimate :", theta_max) 374 | print("Current cost :", cost) 375 | angle_range /= 2 # updated phi and thus theta too -> refine theta range 376 | 377 | # update the iterations 378 | iters += 1 379 | if progress: 380 | print("\nCOST :", cost) 381 | print("THETA :", theta_max) 382 | 383 | if iters >= self.iterations: 384 | print( 385 | "Maximum iterations reached for the estimation.\nTerminating algorithm..." 386 | ) 387 | break 388 | # add cost, eigenvector and theta to the dict 389 | results["cost"] = cost 390 | results["theta"] = theta_max 391 | results["state"] = best_phi 392 | 393 | return results 394 | -------------------------------------------------------------------------------- /QPE/Statistical QPE/Bundling Jobs in Global Max SPEA.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Global Max SPEA with job bundling\n", 8 | "- This is a variant of the SPEA algorithm in which the jobs are bundled together into 1 big circuit and sent to the backend.\n", 9 | "- Note, the total API calls are bound by just **MAX_ITERS** as the job manager sends the circuits in a bundle and executes according to the backend limit on which we are executing" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "#### Trying out Job manager \n", 17 | "- This manager is used to **optimize** the api call time that is needed in the backend execution" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": { 24 | "ExecuteTime": { 25 | "end_time": "2021-06-26T08:18:27.761998Z", 26 | "start_time": "2021-06-26T08:18:17.067483Z" 27 | } 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "from qiskit.providers.ibmq.managed import IBMQJobManager\n", 32 | "from qiskit import IBMQ \n", 33 | "IBMQ.load_account()\n", 34 | "provider = IBMQ.get_provider(hub='ibm-q-education')" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 1, 40 | "metadata": { 41 | "ExecuteTime": { 42 | "end_time": "2021-06-27T07:28:12.878772Z", 43 | "start_time": "2021-06-27T07:28:09.643775Z" 44 | } 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "from qiskit import QuantumCircuit, execute, transpile, Aer \n", 49 | "from qiskit.extensions import UnitaryGate,Initialize\n", 50 | "from qiskit.quantum_info import Statevector \n", 51 | "from qiskit.tools.visualization import plot_bloch_vector\n", 52 | "from qiskit.tools.visualization import plot_histogram,plot_bloch_multivector \n", 53 | "import numpy as np \n", 54 | "from time import sleep \n", 55 | "import sys \n", 56 | "sys.path.append(\"..\")\n", 57 | "from scipy.stats import unitary_group \n", 58 | "import matplotlib.pyplot as plt \n", 59 | "%matplotlib inline " 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "- So what job manager does is that it takes all circuits, executes them in batches of 75 and brings back the results.\n", 67 | "- This means, what I can do is I need (2Bm) jobs each consisting of resolution number of circuits.\n", 68 | "- This means, I can bundle all the (2Bm.resolution) jobs together , get the result \n", 69 | "- Now, take first resolution number of jobs, get the max theta and cost and append to the list \n", 70 | "- At the end you will only have **max_iters** jobs that are sent to the backend instead of **max_iters.2*Bm** jobs, exponentially lesser api calls" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 2, 76 | "metadata": { 77 | "ExecuteTime": { 78 | "end_time": "2021-06-27T07:28:14.931057Z", 79 | "start_time": "2021-06-27T07:28:14.494170Z" 80 | } 81 | }, 82 | "outputs": [], 83 | "source": [ 84 | "from Modules.changed_SPEA import bundled_changed_SPEA" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "### Testing\n", 92 | "- This algorithm is tested for a phase gate of $ \\theta = \\frac{1}{9} $ " 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": 3, 98 | "metadata": { 99 | "ExecuteTime": { 100 | "end_time": "2021-06-26T08:18:41.132028Z", 101 | "start_time": "2021-06-26T08:18:40.818826Z" 102 | } 103 | }, 104 | "outputs": [ 105 | { 106 | "data": { 107 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKoAAAB7CAYAAADkFBsIAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAIkklEQVR4nO3dbUxT+x3A8W/by9Mddw5DhhEjilAVIhTxKY3jQaYSE4OLT+hsoiHRiLhEt+GLBbc34wVpFjODi8sMJr5gEUJussSQcbUtIsRHVtTNW40iykVAHdcLA1ToXvRaLlcE2dJz+i+/T0KA04K/E785pyU9/Ru8Xq8XIYKcUe8BhPgYEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqAoaHYHhARh5rfck2gnqUEdHR7Hb7SQnJxMZGUl6ejoul4vFixezf/9+vcfT3NAruPcFOE/C5T+B44/QWgcvO/SeLPA+0XuAyRQVFVFXV0dZWRmZmZk0Nzeza9cuent7OXr0qN7jaar/Odz8K7wZGr/9xSN48RCWboT4ZfrMpoWgDbW6upqzZ8/idDrJzs4GIDc3l1u3blFXV8fy5ct1nlA7Xi/c/hu8GZ7oRt+nf/0dYubBpzGajqaZoD31l5eXk5+f74/0naSkJMLCwkhLSwOgvb2d7OxszGYzy5Yt4/Lly3qMG1B9T2HgBf4oJ+SFTrdWE2kvKEN9+vQpd+7cYfv27e/d1tHRQWpqKhEREQAcOHCAnTt34vF4OH36NIWFhbx+PfWzDIPBoMzHL4p+O+X+eL1eGj6/qfus0/34WEEbKsCcOXPGbR8cHMTlcvlP+8+fP6epqYmioiIArFYrc+fOxeFwaDtwgJkMJqZaCdRgMGAymjSaSHtB+Rg1NjYWAI/Hw6ZNm/zbKyoq6OrqIjMzE/AdXePi4vxHV4CFCxfy+PHjKf8NlZaA7XkAbZ9Pfb+fbLTg/YM6+zUdQRlqYmIiaWlplJeXM3v2bOLj46mtreXChQsA/lBnithEiIj2/e10ssep8yxaTaS9oDz1G41GampqSE1N5eDBg+zbt4/Y2FgOHTqEyWTyP5GaP38+3d3dDA+PPR1+9OgRCQkJeo0eEEYjpG4CgwH4wMO6Bavhh3GajqUpg0rLoNtsNtxuN21tbf5tGzZsYMuWLRQXF9Pc3My2bdtob28nPDxcx0kD4+uv4MFl+PeTsW2Rs2Dhapi77NuQQ5RSoS5dupQ1a9ZQVVXl3/bw4UP27t3Ls2fPCA8Pp7Ky8r0/aYWawT648hff13m/DO1A3wnKx6gT6e/vx+PxUFxcPG57YmIijY2NOk2lj6gfjX09EyIFhUKNjo5mZGRE7zGEToLyyZQQ3yehCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqCHI6XSSkJBATk4OBQUFDA2Nv3TVbrfT2trK1atXsVqtrF27liNHjkz6Ozs7OykpKaGwsJDc3FxKS0sBcLvdVFRUBGxf3pFQQ5TNZsPpdGK1WqmtrfVvHx0d5cqVK2RkZJCQkMClS5doamqip6eH27dvf/D3NTQ0sHLlStLT03E4HAwODuJ2u0lPT6elpSXgV0xIqCHOYrH4r0ED3xEwKSkJ8F2TFhkZCUBYWBgmkwmn00lMTAw5OTkkJCRQUFAAgMvlor293f+idYvFQnNzMwDJycm0trYGdD8k1BDX2NiI2Wz2f3///n0WLFgw7j5tbW309vaSkpJCVlYWq1at8r+fwqlTp/B6vQwMDPjfqQbA4XDQ19cH+F5qee/evYDuh4Qaos6dO0dubi59fX3+o+JEXr58SUlJCWfOnAF8L0RPTEwE4MmTJ8THx/tP8Zs3b2ZwcJC8vDwiIiKIi9Pu2hcJNUTZbDYcDgeVlZWYTGOXUScnJ9Pe3g7A27dv2bNnD3a73X9p+t27d0lNTWVkZASj0ZdHQ0MD69evx2QycfLkSS5evIjJZGLjxo2AL+4lS5YEdH8k1BkmPT0dj8cDQE1NDdevX6e0tJScnBxaWlr8oQ4PD9PT00NXVxc3btxgxYoVdHZ2kpOTw7p167BarcTHxwO+y9otFktA51bqmikx5gu77/NPfzX9n7Xb7eTl5ZGRkfFR9z9//jw7duyY8Da32019fT3Hjh2b/iDTIKEq6v8JVUVy6hdKkFCFEiRUoQQJVShBQhVKkFCFEiRUoQQJVShBQhVKkFCFEiRUoQQJVShBQhVKCOpQZdHe9/2nDx40jX3/ssO3BGWoC+p3nJZFe8d4R+HLS/D0H+O33zoPn8WB5We+JX5CVdC+HrW6uprdu3ePW7QXYOvWrdTV1XHt2jVWrlyp44Ta8jih48YHbjTAD2bDahsYg/rQ878L2lP/xy7ae/z4ccxmM0ajcdz166Hk9QA8uTXJHby+RX27PZqNpLmgDHU6i/bm5+dTX19PVlaW1mNq5tmXvlP/pAzQdVeTcXQRtKHC1Iv2gm+h3neX906H3qsqT+fjd7/5/dTvROKFm1fv6D7rjFpd+ruL9n7X9xftnSm+Hng+5X/q6OgIfd90azSR9oLyobcWi/YG6XPICQ19A01/ZtIFe41GEz8vyePXp9XZr+kIyiPqxy7aO1NEfgbzJttlA0TNgrjAvgeEroLyiApgNptxOBzjttlsNlJSUoiKitJpKv2Y18HbN/Dsn99uePdIwAufxkDGVjCF6TVd4AXt31EnMtGivWVlZVRVVdHb20t0dDRRUVG4XC4WLVqk46SB86obvroDQ6/gk3D48WKITfQtlR7KlAm1v7+fWbNmceLECQ4fPqz3OEJjyoQqZrYQP2GIUCGhCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJEqpQgoQqlCChCiVIqEIJ/wV/nLDd+CqONgAAAABJRU5ErkJggg==\n", 108 | "text/plain": [ 109 | "
" 110 | ] 111 | }, 112 | "execution_count": 3, 113 | "metadata": {}, 114 | "output_type": "execute_result" 115 | } 116 | ], 117 | "source": [ 118 | "q = QuantumCircuit(2)\n", 119 | "q.cp(2*np.pi*(1/9),0,1)\n", 120 | "q.draw('mpl')" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 4, 126 | "metadata": { 127 | "ExecuteTime": { 128 | "end_time": "2021-06-26T08:22:08.787096Z", 129 | "start_time": "2021-06-26T08:22:08.772153Z" 130 | } 131 | }, 132 | "outputs": [], 133 | "source": [ 134 | "spe = bundled_changed_SPEA(q,resolution=20,error = 3,max_iters=10)" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "- Choosing backend as **ibmq_jakarta**" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 10, 147 | "metadata": { 148 | "ExecuteTime": { 149 | "end_time": "2021-06-26T08:22:10.608939Z", 150 | "start_time": "2021-06-26T08:22:10.602955Z" 151 | } 152 | }, 153 | "outputs": [], 154 | "source": [ 155 | "jakarta = provider.get_backend('ibmq_jakarta')" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": null, 161 | "metadata": { 162 | "ExecuteTime": { 163 | "start_time": "2021-06-26T08:22:11.305Z" 164 | } 165 | }, 166 | "outputs": [ 167 | { 168 | "name": "stdout", 169 | "output_type": "stream", 170 | "text": [ 171 | "Right : 0.40789473684210525\n", 172 | "Left : 0\n", 173 | "ITERATION NUMBER 1 ...\n", 174 | "Transpiling circuits...\n", 175 | "Transpilation Done!\n", 176 | "Job sent...\n", 177 | "Job has returned\n", 178 | "100.000000 %completed\n", 179 | "No change, updating a...\n", 180 | "\n", 181 | "COST : 0.76953125\n", 182 | "THETA : 0.15789473684210525\n", 183 | "Right : 0.40789473684210525\n", 184 | "Left : 0\n", 185 | "ITERATION NUMBER 2 ...\n", 186 | "Transpiling circuits...\n", 187 | "Transpilation Done!\n", 188 | "Job sent...\n", 189 | "Job has returned\n", 190 | "100.000000 %completed\n", 191 | "No change, updating a...\n", 192 | "\n", 193 | "COST : 0.76953125\n", 194 | "THETA : 0.15789473684210525\n", 195 | "Right : 0.40789473684210525\n", 196 | "Left : 0\n", 197 | "ITERATION NUMBER 3 ...\n", 198 | "Transpiling circuits...\n", 199 | "Transpilation Done!\n", 200 | "Job sent...\n", 201 | "Job has returned\n", 202 | "100.000000 %completed\n", 203 | "No change, updating a...\n", 204 | "\n", 205 | "COST : 0.76953125\n", 206 | "THETA : 0.15789473684210525\n", 207 | "Right : 0.40789473684210525\n", 208 | "Left : 0\n", 209 | "ITERATION NUMBER 4 ...\n", 210 | "Transpiling circuits...\n", 211 | "Transpilation Done!\n", 212 | "Job sent...\n", 213 | "Job has returned\n", 214 | "100.000000 %completed\n", 215 | "No change, updating a...\n", 216 | "\n", 217 | "COST : 0.76953125\n", 218 | "THETA : 0.15789473684210525\n", 219 | "Right : 0.40789473684210525\n", 220 | "Left : 0\n", 221 | "ITERATION NUMBER 5 ...\n", 222 | "Transpiling circuits...\n", 223 | "Transpilation Done!\n", 224 | "Job sent...\n", 225 | "Job has returned\n", 226 | "100.000000 %completed\n", 227 | "No change, updating a...\n", 228 | "\n", 229 | "COST : 0.76953125\n", 230 | "THETA : 0.15789473684210525\n", 231 | "Right : 0.40789473684210525\n", 232 | "Left : 0\n", 233 | "ITERATION NUMBER 6 ...\n", 234 | "Transpiling circuits...\n", 235 | "Transpilation Done!\n", 236 | "Job sent...\n", 237 | "Job has returned\n", 238 | "100.000000 %completed\n", 239 | "No change, updating a...\n", 240 | "\n", 241 | "COST : 0.76953125\n", 242 | "THETA : 0.15789473684210525\n", 243 | "Right : 0.40789473684210525\n", 244 | "Left : 0\n", 245 | "ITERATION NUMBER 7 ...\n", 246 | "Transpiling circuits...\n", 247 | "Transpilation Done!\n", 248 | "Job sent...\n", 249 | "Job has returned\n", 250 | "100.000000 %completed\n", 251 | "No change, updating a...\n", 252 | "\n", 253 | "COST : 0.76953125\n", 254 | "THETA : 0.15789473684210525\n", 255 | "Right : 0.40789473684210525\n", 256 | "Left : 0\n", 257 | "ITERATION NUMBER 8 ...\n", 258 | "Transpiling circuits...\n", 259 | "Transpilation Done!\n", 260 | "Job sent...\n", 261 | "Job has returned\n", 262 | "100.000000 %completed\n", 263 | "No change, updating a...\n", 264 | "\n", 265 | "COST : 0.76953125\n", 266 | "THETA : 0.15789473684210525\n", 267 | "Right : 0.40789473684210525\n", 268 | "Left : 0\n", 269 | "ITERATION NUMBER 9 ...\n", 270 | "Transpiling circuits...\n", 271 | "Transpilation Done!\n", 272 | "Job sent...\n" 273 | ] 274 | } 275 | ], 276 | "source": [ 277 | "thetas = []\n", 278 | "for k in range(5):\n", 279 | " result = spe.get_eigen_pair(backend=jakarta,progress=True)\n", 280 | " print(\"Result is :\",result)\n", 281 | " thetas.append(result['theta'])" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": null, 287 | "metadata": { 288 | "ExecuteTime": { 289 | "end_time": "2021-06-26T08:21:59.307315Z", 290 | "start_time": "2021-06-26T08:19:31.641Z" 291 | } 292 | }, 293 | "outputs": [], 294 | "source": [ 295 | "thetas" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": null, 301 | "metadata": { 302 | "ExecuteTime": { 303 | "end_time": "2021-06-26T08:21:59.310307Z", 304 | "start_time": "2021-06-26T08:19:35.011Z" 305 | } 306 | }, 307 | "outputs": [], 308 | "source": [ 309 | "plt.title(\"Plot for returned Eigenvalues\",fontsize = 16)\n", 310 | "plt.xlabel(\"Experiment number\")\n", 311 | "plt.ylabel(\"Eigenvalues\")\n", 312 | "plt.plot([0,6],[0,0],color = 'black')\n", 313 | "plt.plot([0,6],[1,1],color = 'black')\n", 314 | "plt.plot([0,6],[0.111,0.111],color = 'black')\n", 315 | "plt.plot(list(range(5)), thetas, label = 'Estimates', color = 'cyan', linewidth = 2, marker = 's')\n", 316 | "plt.legend()\n", 317 | "plt.grid()" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": null, 323 | "metadata": {}, 324 | "outputs": [], 325 | "source": [] 326 | }, 327 | { 328 | "cell_type": "code", 329 | "execution_count": null, 330 | "metadata": {}, 331 | "outputs": [], 332 | "source": [] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": null, 337 | "metadata": {}, 338 | "outputs": [], 339 | "source": [] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": null, 344 | "metadata": {}, 345 | "outputs": [], 346 | "source": [] 347 | } 348 | ], 349 | "metadata": { 350 | "kernelspec": { 351 | "display_name": "qpe_test", 352 | "language": "python", 353 | "name": "qpe_test" 354 | }, 355 | "language_info": { 356 | "codemirror_mode": { 357 | "name": "ipython", 358 | "version": 3 359 | }, 360 | "file_extension": ".py", 361 | "mimetype": "text/x-python", 362 | "name": "python", 363 | "nbconvert_exporter": "python", 364 | "pygments_lexer": "ipython3", 365 | "version": "3.9.5" 366 | }, 367 | "varInspector": { 368 | "cols": { 369 | "lenName": 16, 370 | "lenType": 16, 371 | "lenVar": 40 372 | }, 373 | "kernels_config": { 374 | "python": { 375 | "delete_cmd_postfix": "", 376 | "delete_cmd_prefix": "del ", 377 | "library": "var_list.py", 378 | "varRefreshCmd": "print(var_dic_list())" 379 | }, 380 | "r": { 381 | "delete_cmd_postfix": ") ", 382 | "delete_cmd_prefix": "rm(", 383 | "library": "var_list.r", 384 | "varRefreshCmd": "cat(var_dic_list()) " 385 | } 386 | }, 387 | "types_to_exclude": [ 388 | "module", 389 | "function", 390 | "builtin_function_or_method", 391 | "instance", 392 | "_Feature" 393 | ], 394 | "window_display": false 395 | } 396 | }, 397 | "nbformat": 4, 398 | "nbformat_minor": 2 399 | } 400 | -------------------------------------------------------------------------------- /QPE/Modules/iterative_qpe.py: -------------------------------------------------------------------------------- 1 | from qiskit import QuantumCircuit 2 | from qiskit import execute, Aer 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | from IPython.display import display 6 | from qiskit.tools.monitor import job_monitor 7 | from qiskit.extensions import UnitaryGate 8 | from qiskit.circuit import Gate 9 | 10 | 11 | class IQPE: 12 | """Implements the iterative QPE algorithm upto n bit precision specified by user 13 | 14 | Attributes: 15 | precision : int([1,...]): precision of the phase estimation ( also equal to number of qubits in scratch register) 16 | unitary(np.ndarray or QuantumCircuit or UnitaryGate): unitary operator for which QPE is being applied. 17 | unknown(bool) : a boolean variable used to specify whether exponentiation of unitary needs to be done or not 18 | (True : no exponentiation done) 19 | powers(dict): contains the powers of the unitary matrix 20 | key : exponent, value : U^(exponent) 21 | controls(dict): contains equivalent controlled U gates to be applied 22 | key : iteration number, value : controlled U gate 23 | 24 | Methods : 25 | get_powers(unitary,n) : generates powers of U from U^1 -> U^(2^(n-1)) and stores power in dict 26 | get_controls() : generates the U^(2^j) control gates for faster simulation of the QPE algorithm 27 | 28 | """ 29 | 30 | def __init__(self, precision, unitary, unknown=False): # write docs 31 | """ 32 | Args : 33 | precision(int) : The precision upto which the phase needs to be estimated. 34 | Interpreted as 2^(-precision). 35 | eg. precision = 4 means the phase is going to be precise 36 | upto 2^(-4). 37 | unitary(np.ndarray or UnitaryGate or QuantumCircuit): 38 | The unitary for which we want to determine the phase. Currently 39 | this class supports 2 x 2 matrices or single qubit gates. 40 | Shall be extended for higher order matrices. 41 | unknown(bool) : Whether exponentiation is to be done or not 42 | Raises : 43 | TypeError : if precision or unitary are not of a valid type 44 | ValueError : if precision is not valid 45 | Exception : if unitary is of larger size than 2 x 2 46 | 47 | Examples : 48 | from iter_QPE import IQPE 49 | # passing as ndarray 50 | theta = 1/5 51 | U1 = np.ndarray([[1,0], 52 | [0, np.exp(2*np.pi*1j*(theta))]]) 53 | 54 | qpe1 = IQPE(precision = 4, unitary = U1,unknown = True) 55 | 56 | # passing as QuantumCircuit 57 | U2 = QuantumCircuit(1) 58 | U2.rz(np.pi/7,0) 59 | qpe2 = IQPE(precision = 5,unitary = U2,unknown = True) 60 | 61 | """ 62 | # handle precision 63 | if type(precision) != int: 64 | raise TypeError("Precision needs to be an integer") 65 | elif precision < 0 or precision == 0: 66 | raise ValueError("Precision needs to be atleast 1") 67 | 68 | self.precision = precision 69 | 70 | # handle unitary 71 | if ( 72 | not isinstance(unitary, np.ndarray) 73 | and not isinstance(unitary, QuantumCircuit) 74 | and not isinstance(unitary, UnitaryGate) 75 | and not isinstance(unitary, Gate) 76 | ): 77 | raise TypeError( 78 | "A numpy array, Quantum Circuit or Gate needs to be passed as the unitary matrix" 79 | ) 80 | 81 | if unknown == False: 82 | # means matrix rep needed 83 | if not isinstance(unitary, np.ndarray) and not isinstance( 84 | unitary, UnitaryGate 85 | ): 86 | raise TypeError( 87 | """Unitary needs to be of type ndarray or Unitary Gate if optimization 88 | needs to be done""" 89 | ) 90 | if isinstance(unitary, UnitaryGate): 91 | U = unitary.to_matrix() 92 | else: 93 | U = unitary # already an array 94 | else: 95 | # if it is numpy type array 96 | if isinstance(unitary, np.ndarray): 97 | U = UnitaryGate(data=unitary) 98 | else: 99 | U = unitary 100 | # here we can directly use the .control method in our circuit. 101 | 102 | # the unitary is an ndarray if unknown is False and 103 | # the unitary is not ndarray is unknown is true 104 | self.unitary = U 105 | self.unknown = unknown 106 | self.powers = {} 107 | 108 | # optimization can only be performed when we know the 109 | # matrix representation 110 | if unknown == False: 111 | self.controls = self.get_controls() 112 | 113 | def get_powers(self, unitary, n): 114 | """This function returns the matrix U^(n) and saves 115 | other smaller powers 116 | 117 | Arguments: 118 | unitary(np.ndarray): 119 | The Unitary matrix which needs to be exponentitated 120 | n(int): integer specifying the exponent 121 | 122 | Raises: 123 | ValueError : when n is < 0 124 | Returns: 125 | a dictionary containing the relevant powers of the matrix U""" 126 | if n < 0: 127 | raise ValueError("Power should be atleast 0") 128 | if n == 1: 129 | self.powers[1] = unitary 130 | return unitary 131 | if n % 2 == 1: 132 | if n - 1 not in self.powers: 133 | self.powers[n - 1] = self.get_powers(unitary, n - 1) 134 | 135 | self.powers[n] = unitary @ self.powers[n - 1] 136 | return self.powers[n] 137 | else: 138 | if n / 2 not in self.powers: 139 | self.powers[n / 2] = self.get_powers(unitary, n / 2) 140 | 141 | self.powers[n] = self.powers[n / 2] @ self.powers[n / 2] 142 | return self.powers[n] 143 | 144 | # get the controls, if using optimization 145 | def get_controls(self): 146 | """Get the control gates for the circuit 147 | While computing exponent, we also 148 | compute the smaller powers 149 | 150 | Returns: 151 | controls(dict) : dictionary containing the relevant controlled unitary gates 152 | key : iteration number, value : controlled U gate 153 | """ 154 | 155 | n_iters = self.precision 156 | 157 | exp = 2 ** (n_iters - 1) 158 | self.get_powers(self.unitary, exp) 159 | 160 | # got the powers 161 | controls = {} 162 | 163 | # note that iteration 0 has the highest powered matrix and 164 | # then it goes on to get lesser and lesser 165 | iterations = self.precision 166 | for it in range(iterations): 167 | mat = self.powers[exp] 168 | u_gate = UnitaryGate(data=mat) 169 | cu = u_gate.control(num_ctrl_qubits=1, label="CU", ctrl_state="1") 170 | controls[it] = cu 171 | exp /= 2 172 | 173 | return controls 174 | 175 | def get_circuit_phase( 176 | self, 177 | QC, 178 | clbits, 179 | qubits, 180 | ancilla, 181 | show=False, 182 | backend=None, 183 | save_circ=False, 184 | circ_name="IQPE_circ.JPG", 185 | ): 186 | # QC must be atleast size 2 187 | """Add the experiments pararmeters .., and the shots parameter""" 188 | 189 | """ 190 | Returns the circuit phase as a 2-tuple phase : (binary phase,decimal phase) 191 | Arguments: 192 | QC(QuantumCircuit) : the circuit containing the eigenvector of the unitary matrix 193 | clbits(list-like) : the list of the classical bits in which the phase would be saved 194 | qubits(list-like) : the indices of the qubits containing the eigenvector of unitary 195 | ancilla(int) : the ancilliary qubit which would be used as the control qubit 196 | show(bool) : boolean to specify if circuit should be drawn or not 197 | save(bool) : boolean to specify if circuit should be saved or not 198 | (saved as IQPE_circuit.JPG, if true) 199 | backend(IBMQBackend) : backend for running the circuit 200 | NOTE : IBMQ provider must be enabled for execution of circuits on real backends 201 | 202 | 203 | Raises: 204 | ValueError : if clbits are not equal to precision or non-unique bits specified 205 | or if elements of clbits/qubits are not integer type or ancilla qubit is 206 | same as one of the eigenvector qubits 207 | TypeError : if qubit indices are not integral 208 | Exception : if unitary has less than 2 qubits 209 | 210 | 211 | Returns : 212 | A 2-tuple specifying the phase of unitary matrix : (binary phase,decimal phase) 213 | 214 | Usage Notes : 215 | NOTE : The phase is assumed to be a binary fraction as 0.x1x2x2...xn where n 216 | is the precision specified by the user. 217 | 218 | The least significant bit , xn, is saved in the qubit with index 219 | precision-1 and the most significant bit, x1, is saved in the 220 | qubit with index 0 in the phase[0] of tuple. 221 | 222 | For example :- 223 | theta = 1/5 # binary representation upto 4 bits : 0.0011 224 | unitary = np.ndarray([[1,0], 225 | [0, np.exp(2*np.pi*1j*(theta))]]) 226 | q = QuantumCircuit(6,4) 227 | q.x(4) # the eigenvector qubit 228 | 229 | qpe = get_circuit_phase(precision = 4,unitary = unitary,unknown = True) 230 | athens = provider.get_backend('ibmq_athens') 231 | phase = iqpe.get_circuit_phase( 232 | QC=q, clbits=[0, 1, 2, 3], qubits=[4], ancilla=3, show=True,backend = athens) 233 | 234 | # phase[0] would contain a 4-bit phase representation 235 | # phase[1] would contain the decimal representation of the phase 236 | """ 237 | 238 | # handle qubits in circuit 239 | if len(QC.qubits) < 2: 240 | raise Exception("Quantum Circuit needs to have atleast size 2") 241 | 242 | # handle classical bits 243 | if len(clbits) != self.precision: 244 | raise ValueError( 245 | "Exactly", self.precision, "classical bits needed for measurement" 246 | ) 247 | elif len(set(clbits)) != len(clbits): 248 | raise ValueError("Non-unique classical bits given for measurement") 249 | elif not all(isinstance(i, int) for i in clbits): 250 | raise ValueError("All classical indices must be integer type") 251 | 252 | # qubit and ancilla need to be integers 253 | if type(ancilla) is not int: 254 | raise TypeError("Ancilla indix need to be specified as integer") 255 | elif not all(isinstance(i, int) for i in qubits): 256 | raise TypeError( 257 | "The indices containing the eigenvector must be integer type " 258 | ) 259 | elif len(set(qubits)) != len(qubits): 260 | raise ValueError("Non-unique qubits given for the eigenvector") 261 | elif ancilla in qubits: 262 | raise Exception("Ancilla can not be equal to a qubit index ") 263 | 264 | res = [] 265 | # start with the iteration 266 | phase = -2 * np.pi 267 | factor = 0 268 | iterations = self.precision 269 | # generate the qubit list on which the Unitary is applied 270 | qargs = [ancilla] 271 | for q in qubits: 272 | qargs.append(q) 273 | 274 | if self.unknown == True: 275 | # no matrix repr is available -> means .control method can be applied easily 276 | exponent = 2 ** (iterations - 1) 277 | CU = self.unitary.control(num_ctrl_qubits=1, label="CU", ctrl_state=1) 278 | 279 | for it in range(iterations): 280 | # start 281 | QC.reset(ancilla) 282 | QC.h(ancilla) 283 | # add the inverse rotation 284 | inv_phase = phase * factor 285 | 286 | QC.p(inv_phase, ancilla) 287 | 288 | # add the controlled Unitary of iteration it 289 | if self.unknown == False: 290 | QC = QC.compose(self.controls[it], qubits=qargs) 291 | else: 292 | # need to add exponential amount of matrices 293 | for _ in range(int(exponent)): 294 | QC = QC.compose(CU, qubits=qargs) 295 | exponent /= 2 296 | 297 | # add H gate 298 | QC.h(ancilla) 299 | QC.measure(ancilla, clbits[it]) 300 | 301 | # or, iterate in reverse manner , no of steps -> 302 | # clbits[it] as it is the absolute 303 | # classical register index 304 | if backend == None: # simulating 305 | counts = ( 306 | execute(QC, backend=Aer.get_backend("qasm_simulator"), shots=1) 307 | .result() 308 | .get_counts() 309 | ) 310 | else: 311 | job = execute( 312 | QC, 313 | shots=1, 314 | backend=backend, 315 | job_name="Iter " + str(it + 1), 316 | optimization_level=3, 317 | ) 318 | display(job_monitor(job)) 319 | counts = job.result().get_counts() 320 | 321 | # we will have only one single key in the dict 322 | key = list(counts.keys())[0][::-1] 323 | # try adding x based on clasical 324 | curr_bit = key[clbits[it]] 325 | res.append(int(curr_bit)) 326 | # if bit measured is 1 327 | if curr_bit == "1": 328 | factor += 1 / 2 # add the phase factor 329 | 330 | factor = factor / 2 # shift each towards one weight right 331 | if it + 1 == iterations: 332 | if show == True: 333 | if save_circ == False: 334 | display(QC.draw("mpl")) 335 | else: 336 | display(QC.draw(output="mpl", filename=circ_name, scale=0.8)) 337 | 338 | # phase has now been stored in the clbits 339 | # returning its binary representation 340 | 341 | # need to reverse as LSB is stored at the zeroth index and 342 | # not the last 343 | res = res[::-1] 344 | 345 | # find decimal phase 346 | dec = 0 347 | weight = 1 / 2 348 | for k in res: 349 | dec += (weight) * k 350 | weight /= 2 351 | 352 | return (res, dec) 353 | 354 | 355 | def get_estimate_plot_phase( 356 | theta=None, 357 | unitary=None, 358 | unknown=True, 359 | experiments=1, 360 | iters=9, 361 | show_circ=False, 362 | save=False, 363 | backend=None, 364 | ): 365 | """ 366 | Displays an estimate plot of the phase that is found through the IQPE 367 | algorithm for SINGLE qubit phase unitaries 368 | 369 | theta(float) : contains the actual theta (if some theoretical assumption is known) 370 | unitary(ndarray / QuantumCircuit / UnitaryGate) : 371 | the unitary for which phase needs to be determined 372 | unknown(bool) : boolean variable to specify whether the optimization in unitary 373 | application is to be used 374 | experiments(int) : the number of experiments for which each iteration is to be run 375 | iters(int) : the max number of iterations to be run ( circuit is run from precision 376 | 2 -> iters) 377 | 378 | show_circ(bool) : boolean variable to specify whether circuit needs to be drawn or not 379 | save(bool) : boolean variable specifying whether plot needs to be saved or 380 | not (saved as IQPE Plots/Estimate_plot_theta.jpg) 381 | 382 | backend(IBMBackend) : ibmq backend on which the circuit should be run (if None, 383 | qasm_simulator is used) 384 | 385 | Example :- 386 | provider = IBMQ.get_provider('ibm-q') 387 | casb = provider.get_backend('ibmq-casablanca) 388 | U = np.array([[1,0], 389 | [0, np.exp(2*np.pi*1j(0.524))]]) 390 | 391 | get_estimate_plot(unitary = U, iters = 10,unknown= True, save=True,backend = casb) 392 | 393 | """ 394 | import os 395 | 396 | if theta is None and unitary is None: 397 | raise Exception("Atleast one of theta or unitary is needed.") 398 | 399 | if theta > 1 or theta < 0: 400 | raise ValueError("Theta must be specified as a float val between 0 and 1") 401 | 402 | if unitary is None: 403 | unitary = np.array([[1, 0], [0, np.exp(2 * np.pi * 1j * (theta))]]) 404 | 405 | estimates, errors = [], [] 406 | avg_phase, avg_error = {}, {} 407 | for prec in range(2, iters): 408 | print("\n\nITERATION NUMBER", prec - 1, "...\n\n") 409 | 410 | iqpe = IQPE(precision=prec, unitary=unitary, unknown=unknown) 411 | dec_phase, abs_errors = [], [] 412 | 413 | for exp in range(experiments): 414 | # single qubit rotation matrices 415 | q = QuantumCircuit(2, prec) 416 | q.x(1) 417 | # simulate the matrix on a circuit 418 | phase = iqpe.get_circuit_phase( 419 | QC=q, 420 | clbits=[i for i in range(prec)], 421 | qubits=[1], 422 | ancilla=0, 423 | show=show_circ, 424 | backend=backend, 425 | ) 426 | # add the phase and the error ... 427 | dec_phase.append(phase[1]) 428 | print("Binary Phase in experiment", exp, phase[0]) 429 | 430 | if theta is not None: 431 | ae = np.round(abs(phase[1] - theta), 5) 432 | abs_errors.append(ae) 433 | 434 | # run experiments number of times AND get the avg phase 435 | 436 | avg_phase[prec] = sum(dec_phase) / len(dec_phase) 437 | avg_error[prec] = sum(abs_errors) / len(abs_errors) 438 | 439 | print("Decimal Phase :", avg_phase[prec]) 440 | print("Absolute Error :", avg_error[prec]) 441 | if theta is not None: 442 | print("Percentage error :", avg_error[prec] * 100 / theta, "%") 443 | 444 | # append to graph 445 | estimates.append(avg_phase[prec]) 446 | errors.append(avg_error[prec]) 447 | 448 | # choose color 449 | colors = ["r", "g", "c", "m", "y"] 450 | c1 = np.random.choice(colors)[0] 451 | c2 = np.random.choice(colors)[0] 452 | while c2 == c1: 453 | c2 = np.random.choice(colors)[0] 454 | plt.figure(figsize=(9, 7)) 455 | plt.grid(True) 456 | 457 | # plot 458 | plt.plot( 459 | [i for i in range(2, iters)], 460 | estimates, 461 | alpha=0.6, 462 | marker="o", 463 | color=c1, 464 | label="Estimates", 465 | linestyle="dashed", 466 | linewidth=2, 467 | ) 468 | plt.plot( 469 | [i for i in range(2, iters)], 470 | errors, 471 | alpha=0.6, 472 | marker="s", 473 | color=c2, 474 | label="Absoulte error", 475 | linestyle="dotted", 476 | linewidth=2, 477 | ) 478 | if theta != None: 479 | plt.plot([0, iters], [theta, theta], color="black", label="Actual phase") 480 | plt.title("IQPE estimates for $\\theta =" + str(theta) + "$", fontsize=16) 481 | plt.xlabel("Number of iterations ", fontsize=14) 482 | plt.ylabel("Estimates by IQPE", fontsize=14) 483 | plt.legend() 484 | if save: 485 | os.makedirs("IQPE Plots", exist_ok=True) 486 | plt.savefig("IQPE Plots/Estimate_plot_" + str(theta) + ".jpg", dpi=200) 487 | -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Lower Bound Testing/normal_SPEA.py: -------------------------------------------------------------------------------- 1 | from qiskit import QuantumCircuit, execute, transpile, Aer 2 | from qiskit.extensions import UnitaryGate,Initialize 3 | from qiskit.tools.visualization import plot_histogram 4 | from qiskit.compiler import assemble 5 | import numpy as np 6 | from time import sleep 7 | from qiskit.tools.monitor import job_monitor 8 | from qiskit.extensions import UnitaryGate 9 | from qiskit.quantum_info import Statevector 10 | import sys 11 | from scipy.stats import unitary_group 12 | import matplotlib.pyplot as plt 13 | 14 | class SPEA(): 15 | ''' 16 | This is a class which implements the Statistical Phase Estimation algorithm 17 | paper1 - https://arxiv.org/pdf/2104.10285.pdf 18 | paper2 - https://arxiv.org/pdf/1906.11401.pdf(discussion) 19 | Attributes : 20 | resolution(int) : the number of intervals the angle range has to be divided into 21 | dims(int) : dimensions of the passed unitary matrix 22 | c_unitary_gate(Unitary Gate) : the controlled unitary gate in our algorithm 23 | error(int) : the number of places after decimal, upto which the cost of the algorithm 24 | would be estimated 25 | itrations(int) : the maximum iterations after which the algorithm ends the computation 26 | basis(list of np.ndarray) : the basis generated at the start of the algorithm 27 | unitary_circuit(QuantumCircuit): a pre-transpiled QuantumCircuit which is applied during 28 | the algorithm 29 | 30 | Methods : 31 | __get_basis_vectors(randomize) : Get the d dimensional basis for the initializtion of the algorithm 32 | __get_unitary_circuit(backend) : Get the pre-transpiled circuit for the unitary matrix 33 | __get_alternate_cost(angle,state,backend,shots) : Get the cost through the alternate method specified in the algorithm 34 | __get_standard_cost(angle,state,backend,shots) : Get the cost through the standard method specified in the algorithm 35 | __get_circuit(state,angle,backend,shots) : Get the completed circuit used inside the algorithm to estimate the phase 36 | get_eigen_pair(backend, algo='alternate', theta_left,theta_right,progress, randomize, target_cost, basis, basis_ind,shots) : Get the eigenstate and eigenphase phase for the unitary matrix 37 | 38 | ''' 39 | 40 | 41 | def __init__(self, unitary, resolution=50, error=3, max_iters=20): 42 | # handle resolution 43 | if not isinstance(resolution, int): 44 | raise TypeError( 45 | "Please enter the number of intervals as an integer value") 46 | if resolution < 10 or resolution > 1e6: 47 | raise ValueError( 48 | "Resolution needs to be atleast 0.1 and greater than 0.000001") 49 | 50 | self.resolution = resolution 51 | 52 | # handle unitary 53 | if not isinstance(unitary, np.ndarray) and not isinstance(unitary, QuantumCircuit)\ 54 | and not isinstance(unitary, UnitaryGate): 55 | raise TypeError( 56 | "A numpy array or Quantum Circuit or UnitaryGate needs to be passed as the unitary matrix") 57 | 58 | # convert circuit to numpy array for uniformity 59 | if isinstance(unitary, UnitaryGate): 60 | U = unitary.to_matrix() 61 | else: # both QC and ndarray type 62 | U = unitary 63 | 64 | # note - the unitary here is not just a single qubit unitary 65 | if isinstance(U, np.ndarray): 66 | self.dims = U.shape[0] 67 | else: 68 | self.dims = 2**(U.num_qubits) 69 | 70 | if isinstance(U, np.ndarray): 71 | self.c_unitary_gate = UnitaryGate(data=U).control( 72 | num_ctrl_qubits=1, label='CU', ctrl_state='1') 73 | else: 74 | self.c_unitary_gate = U.control( 75 | num_ctrl_qubits=1, label='CU', ctrl_state='1') 76 | 77 | # handle error 78 | if not isinstance(error, int): 79 | raise TypeError( 80 | "The allowable error should be provided as an int. Interpreted as 10**(-error)") 81 | if error <= 0: 82 | raise ValueError( 83 | "The error threshold must be finite and greater than 0.") 84 | 85 | self.error = error 86 | 87 | # handle max_iters 88 | if not isinstance(max_iters, int): 89 | raise TypeError("Max iterations must be of integer type") 90 | if max_iters <= 0 and max_iters > 1e5: 91 | raise ValueError( 92 | "Max iterations should be atleast 1 and less than 1e5") 93 | 94 | self.iterations = max_iters 95 | self.basis = [] 96 | 97 | def __get_basis_vectors(self, randomize=True): 98 | ''' Get the d dimensional basis for the unitary provided 99 | Args : randomize (bool) : whether to pick a random basis or 100 | not 101 | Returns: 102 | a list of np.ndarrays which are used as the basis 103 | vectors ''' 104 | if randomize == True: 105 | UR = unitary_group.rvs(self.dims) 106 | else: 107 | UR = np.identity(self.dims) 108 | 109 | basis = [] 110 | for k in UR: 111 | basis.append(np.array(k, dtype=complex)) 112 | return basis 113 | 114 | def __get_unitary_circuit(self, backend): 115 | '''Return the pretranspiled circuit 116 | Args: 117 | backend : the IBMQBackend on which we want to transpile. 118 | If None, Default : 'qasm_simulator' 119 | Returns: QuantumCircuit containing the transpiled circuit 120 | for the controlled unitary 121 | ''' 122 | if backend is None: 123 | backend = Aer.get_backend('qasm_simulator') 124 | 125 | qc = QuantumCircuit(1 + int(np.log2(self.dims))) 126 | 127 | # make the circuit 128 | qc.h(0) 129 | qc = qc.compose(self.c_unitary_gate, qubits=range( 130 | 1+int(np.log2(self.dims)))) 131 | 132 | qc.barrier() 133 | # RANDOMNESS 1 134 | qc = transpile(qc,backend=backend,optimization_level = 3) 135 | 136 | return qc 137 | 138 | def __get_circuit(self, state, backend, shots,angle=None): 139 | '''Given an initial state , 140 | return the assembled and transpiled 141 | circuit that is generated with 142 | inverse rotation 143 | 144 | Args: 145 | state(np.ndarray) : The eigenvector guess state for the initialization 146 | backend(IBMQBackend) : the backend on which this circuit is going to be 147 | executed 148 | shots(int) : the number of shots in our experiments 149 | angle(float) : whether the returned circuit contains an inverse rotation 150 | gate or not. If angle is None, no rotation gate attached 151 | Else, cp(angle) is attached on control qubit 0 152 | 153 | Returns: 154 | QuantumCircuit which is pre-transpiled according to the backend provided 155 | ''' 156 | # all theta values are iterated over for the same state 157 | phi = Initialize(state) 158 | 159 | qc1 = QuantumCircuit(1 + int(np.log2(self.dims)), 1) 160 | 161 | # initialize the circuit 162 | qc1 = qc1.compose(phi, qubits=list( 163 | range(1, int(np.log2(self.dims))+1))) 164 | qc1.barrier() 165 | # RANDOMNESS 2 166 | qc1 = transpile(qc1, backend=backend,optimization_level=1) 167 | 168 | # get the circuit2 169 | qc2 = self.unitary_circuit 170 | 171 | qc3 = QuantumCircuit(1 + int(np.log2(self.dims)), 1) 172 | if angle is not None: 173 | # add inverse rotation on the first qubit 174 | qc3.p(-2*np.pi*angle, 0) 175 | # add hadamard 176 | qc3.h(0) 177 | qc3 = transpile(qc3, backend=backend,optimization_level=1) 178 | 179 | # make final circuit 180 | qc = qc1 + qc2 + qc3 181 | # measure 182 | qc.measure([0], [0]) 183 | 184 | return qc 185 | 186 | def __get_standard_cost(self, angles, state, backend,shots): 187 | '''Given an initial state and a set of angles, 188 | return the best cost and the associated angle. 189 | Implements the standard method as specified in the paper. 190 | 191 | Args : 192 | angles(np.ndarray) : the set of angles on which we execute 193 | the circuits 194 | state(np.ndarray) : the initialization state provided 195 | backend(IBMQBackend): the backend on which this circuit 196 | needs to be executed 197 | shots(int) : the number of shots used to execute this circuit 198 | 199 | Returns : 200 | result(dict) : {result : (float), theta : (float)} 201 | result - the best cost given this set of angles 202 | theta - the best theta value amongst this set 203 | of angles''' 204 | result = {'cost': -1, 'theta': -1} 205 | # all theta values are iterated over for the same state 206 | circuits = [] 207 | 208 | for theta in angles: 209 | qc = self.__get_circuit(state,backend,shots,theta) 210 | circuits.append(qc) 211 | 212 | # RANDOMNESS 3 213 | # execute only once... 214 | counts = backend.run(circuits, shots=shots).result().get_counts() 215 | # get the cost for this theta 216 | for k, theta in zip(counts, angles): 217 | # for all experiments you ran 218 | try: 219 | C_val = (k['0'])/shots 220 | except: 221 | C_val = 0 222 | 223 | if C_val > result['cost']: 224 | # means this is a better theta value 225 | result['theta'] = theta 226 | result['cost'] = C_val 227 | return result 228 | 229 | def __get_alternate_cost(self, angles, state, backend,shots): 230 | '''Given an initial state and a set of angles, 231 | return the best cost and the associated angle. 232 | Implements the alternate method as specified in the paper1 233 | and discussion of paper2. 234 | 235 | Args : 236 | angles(np.ndarray) : the set of angles on which we execute 237 | the circuits 238 | state(np.ndarray) : the initialization state provided 239 | backend(IBMQBackend): the backend on which this circuit 240 | needs to be executed 241 | shots(int) : the number of shots used to execute this circuit 242 | 243 | Returns : 244 | result(dict) : {result : (float), theta : (float)} 245 | result - the best cost given this set of angles 246 | theta - the best theta value amongst this set 247 | of angles''' 248 | result = {'cost': -1, 'theta': -1} 249 | # all theta values are iterated over for the same state 250 | 251 | qc = self.__get_circuit(state,backend,shots) 252 | 253 | # execute only once... 254 | counts = backend.run(qc, shots=shots).result().get_counts() 255 | 256 | # generate experimental probabilities 257 | try: 258 | p0 = counts['0']/shots 259 | except: 260 | p0 = 0 261 | try: 262 | p1 = counts['1']/shots 263 | except: 264 | p1 = 0 265 | 266 | # now, find the best theta as specified by the 267 | # alternate method classically 268 | min_s = 1e5 269 | for theta in angles: 270 | # generate theoretical probabilities 271 | c0 = (np.cos(np.pi*theta))**2 272 | c1 = 1 - c0 273 | 274 | # generate s value 275 | s = (p0-c0)**2 + (p1-c1)**2 276 | if s < min_s: 277 | result['theta'] = theta 278 | min_s = s 279 | 280 | # now , we have the best theta stored in phi 281 | # run circuit once again to get the value of C* 282 | 283 | # RANDOMNESS 4 284 | qc = self.__get_circuit(state, backend, shots, result['theta']) 285 | counts = backend.run(qc, shots=shots).result().get_counts() 286 | 287 | try: 288 | result['cost'] = counts['0']/shots 289 | except: 290 | result['cost'] = 0 291 | # no 0 counts present 292 | 293 | # return the result 294 | return result 295 | 296 | def get_eigen_pair(self, backend, algo='alternate', theta_left = 0,theta_right = 1,progress=False, randomize=True, target_cost=None, basis = None, basis_ind = None,shots=512): 297 | '''Finding the eigenstate pair for the unitary 298 | Args : 299 | backend(IBMQBackend) : the backend on which the circuit needs to be executed 300 | algo(str) : ['alternate','standard'] the algorithm to use as specified in the 301 | paper1 section 3. 302 | theta_left(float): the left bound for the search of eigenvalue. Default : 0 303 | theta_right(float): the right bound for the search of eigenvalue. Default : 1 304 | progress(bool) : Whether to show the progress as the algorithm runs 305 | randomize(bool): Whether to choose random initialization of basis states or not 306 | If False, computational basis is chosen. 307 | target_cost(float) : the min cost required to be achieved by the algorithm 308 | basis(list of np.ndarray) : The basis to be used in the algorithm. 309 | Note, if basis is specified, randomize value is ignored 310 | basis_ind(int) : the index of the basis vector to be used as the initial state 311 | vector 312 | 313 | Returns : 314 | result(dict) : {cost :(float), theta :(float), state : (np.ndarray) 315 | cost - the cost with which the algorithm terminates 316 | theta - the eigenvalue estimated by SPEA 317 | state - the eigenvector estimated by SPEA 318 | 319 | ''' 320 | # handle algorithm... 321 | self.unitary_circuit = self.__get_unitary_circuit(backend) 322 | 323 | if(theta_left > theta_right): 324 | raise ValueError("Left bound for theta should be smaller than the right bound") 325 | elif (theta_left<0) or (theta_right>1): 326 | raise ValueError("Bounds of theta are [0,1].") 327 | 328 | if not isinstance(algo, str): 329 | raise TypeError( 330 | "Algorithm must be mentioned as a string from the values {alternate,standard}") 331 | elif algo not in ['alternate', 'standard']: 332 | raise ValueError( 333 | "Algorithm must be specified as 'alternate' or 'standard' ") 334 | 335 | if not isinstance(progress, bool): 336 | raise TypeError("Progress must be a boolean variable") 337 | if not isinstance(randomize, bool): 338 | raise Exception("Randomize must be a boolean variable") 339 | 340 | if target_cost is not None: 341 | if not isinstance(target_cost, float): 342 | raise TypeError("Target cost must be a float") 343 | if (target_cost <= 0 or target_cost >= 1): 344 | raise ValueError( 345 | "Target cost must be a float value between 0 and 1") 346 | 347 | results = dict() 348 | 349 | # first initialize the state phi 350 | if basis is None: 351 | self.basis = self.__get_basis_vectors(randomize) 352 | else: 353 | # is basis is specified, given as array of vectors... 354 | self.basis = basis 355 | 356 | # choose a random index 357 | if basis_ind is None: 358 | ind = np.random.choice(self.dims) 359 | else: 360 | # choose the index given in that basis 361 | ind = basis_ind 362 | 363 | phi = self.basis[ind] 364 | 365 | # doing the method 1 of our algorithm 366 | # define resolution of angles and precision 367 | if target_cost == None: 368 | precision = 1/10**self.error 369 | else: 370 | precision = 1 - target_cost 371 | 372 | samples = self.resolution 373 | 374 | # initialization of range 375 | left, right = theta_left, theta_right 376 | # generate the angles 377 | angles = np.linspace(left, right, samples) 378 | 379 | # iterate once 380 | if algo == 'alternate': 381 | result = self.__get_alternate_cost(angles, phi, backend,shots) 382 | else: 383 | result = self.__get_standard_cost(angles, phi, backend,shots) 384 | # get initial estimates 385 | cost = min(1,result['cost']) 386 | theta_max = result['theta'] 387 | best_phi = phi 388 | 389 | # the range upto which theta extends iin each iteration 390 | angle_range = (right - left)/2 391 | # a parameter 392 | a = 1 393 | # start algorithm 394 | iters = 0 395 | found = True 396 | 397 | while 1 - cost >= precision: 398 | # get angles, note if theta didn't change, then we need to 399 | # again generate the same range again 400 | right = min(theta_right, theta_max + angle_range/2) 401 | left = max(theta_left, theta_max - angle_range/2) 402 | if progress: 403 | print("Right :", right) 404 | print("Left :", left) 405 | # generate the angles only if the theta has been updated 406 | if found == True: 407 | angles = np.linspace(left, right, samples) 408 | 409 | found = False # for this iteration 410 | if progress: 411 | print("ITERATION NUMBER", iters+1, "...") 412 | for i in range((2*self.dims)): 413 | # everyone is supplied with the same range of theta in one iteration 414 | # define z 415 | if i < self.dims: 416 | z = 1 417 | else: 418 | z = 1j 419 | 420 | # alter and normalise phi 421 | curr_phi = best_phi + z*a*(1 - cost)*self.basis[i % self.dims] 422 | curr_phi = curr_phi / np.linalg.norm(curr_phi) 423 | 424 | # iterate (angles would be same until theta is changed) 425 | if algo == 'alternate': 426 | res = self.__get_alternate_cost(angles, curr_phi, backend,shots) 427 | else: 428 | res = self.__get_standard_cost(angles, curr_phi, backend,shots) 429 | curr_cost = res['cost'] 430 | curr_theta = res['theta'] 431 | 432 | # at this point I have the best Cost for the state PHI and the 433 | 434 | 435 | if curr_cost > cost: 436 | theta_max = float(curr_theta) 437 | cost = min(1.0,float(curr_cost)) 438 | best_phi = curr_phi 439 | found = True 440 | if progress: 441 | sys.stdout.write('\r') 442 | sys.stdout.write("%f %%completed" % 443 | (100*(i+1)/(2*self.dims))) 444 | sys.stdout.flush() 445 | 446 | # iteration completes 447 | 448 | if found == False: 449 | # phi was not updated , change a 450 | a = a/2 451 | if progress: 452 | print("\nNo change, updating a...") 453 | else: 454 | angle_range /= 2 # updated phi and thus theta too -> refine theta range 455 | 456 | iters += 1 457 | if progress: 458 | print("\nCOST :", cost) 459 | print("THETA :", theta_max) 460 | 461 | if iters >= self.iterations: 462 | print( 463 | "Maximum iterations reached for the estimation.\nTerminating algorithm...") 464 | break 465 | # add the warning that iters maxed out 466 | 467 | # add cost, eigenvector and theta to the dict 468 | results['cost'] = cost 469 | results['theta'] = theta_max 470 | results['state'] = best_phi 471 | return results -------------------------------------------------------------------------------- /QPE/Modules/normal_SPEA.py: -------------------------------------------------------------------------------- 1 | from qiskit import QuantumCircuit, transpile, Aer 2 | from qiskit.extensions import UnitaryGate, Initialize 3 | import numpy as np 4 | from qiskit.extensions import UnitaryGate 5 | import sys 6 | from scipy.stats import unitary_group 7 | 8 | 9 | class SPEA: 10 | """ 11 | This is a class which implements the Statistical Phase Estimation algorithm 12 | paper1 - https://arxiv.org/pdf/2104.10285.pdf 13 | paper2 - https://arxiv.org/pdf/1906.11401.pdf(discussion) 14 | Attributes : 15 | resolution(int) : the number of intervals the angle range has to be divided into 16 | dims(int) : dimensions of the passed unitary matrix 17 | c_unitary_gate(Unitary Gate) : the controlled unitary gate in our algorithm 18 | error(int) : the number of places after decimal, upto which the cost of the algorithm 19 | would be estimated 20 | itrations(int) : the maximum iterations after which the algorithm ends the computation 21 | basis(list of np.ndarray) : the basis generated at the start of the algorithm 22 | unitary_circuit(QuantumCircuit): a pre-transpiled QuantumCircuit which is applied during 23 | the algorithm 24 | 25 | Methods : 26 | __get_basis_vectors(randomize) : Get the d dimensional basis for the initializtion of the algorithm 27 | __get_unitary_circuit(backend) : Get the pre-transpiled circuit for the unitary matrix 28 | __get_alternate_cost(angle,state,backend,shots) : Get the cost through the alternate method specified in the algorithm 29 | __get_standard_cost(angle,state,backend,shots) : Get the cost through the standard method specified in the algorithm 30 | __get_circuit(state,angle,backend,shots) : Get the completed circuit used inside the algorithm to estimate the phase 31 | get_eigen_pair(backend, algo='alternate', theta_left,theta_right,progress, randomize, target_cost, basis, basis_ind,shots) : Get the eigenstate and eigenphase phase for the unitary matrix 32 | 33 | """ 34 | 35 | def __init__(self, unitary, resolution=50, error=3, max_iters=20): 36 | # handle resolution 37 | if not isinstance(resolution, int): 38 | raise TypeError("Please enter the number of intervals as an integer value") 39 | if resolution < 10 or resolution > 1e6: 40 | raise ValueError( 41 | "Resolution needs to be atleast 0.1 and greater than 0.000001" 42 | ) 43 | 44 | self.resolution = resolution 45 | 46 | # handle unitary 47 | if ( 48 | not isinstance(unitary, np.ndarray) 49 | and not isinstance(unitary, QuantumCircuit) 50 | and not isinstance(unitary, UnitaryGate) 51 | ): 52 | raise TypeError( 53 | "A numpy array or Quantum Circuit or UnitaryGate needs to be passed as the unitary matrix" 54 | ) 55 | 56 | # convert circuit to numpy array for uniformity 57 | if isinstance(unitary, UnitaryGate): 58 | U = unitary.to_matrix() 59 | else: # both QC and ndarray type 60 | U = unitary 61 | 62 | # note - the unitary here is not just a single qubit unitary 63 | if isinstance(U, np.ndarray): 64 | self.dims = U.shape[0] 65 | else: 66 | self.dims = 2 ** (U.num_qubits) 67 | 68 | if isinstance(U, np.ndarray): 69 | self.c_unitary_gate = UnitaryGate(data=U).control( 70 | num_ctrl_qubits=1, label="CU", ctrl_state="1" 71 | ) 72 | else: 73 | self.c_unitary_gate = U.control( 74 | num_ctrl_qubits=1, label="CU", ctrl_state="1" 75 | ) 76 | 77 | # handle error 78 | if not isinstance(error, int): 79 | raise TypeError( 80 | "The allowable error should be provided as an int. Interpreted as 10**(-error)" 81 | ) 82 | if error <= 0: 83 | raise ValueError("The error threshold must be finite and greater than 0.") 84 | 85 | self.error = error 86 | 87 | # handle max_iters 88 | if not isinstance(max_iters, int): 89 | raise TypeError("Max iterations must be of integer type") 90 | if max_iters <= 0 and max_iters > 1e5: 91 | raise ValueError("Max iterations should be atleast 1 and less than 1e5") 92 | 93 | self.iterations = max_iters 94 | self.basis = [] 95 | 96 | def __get_basis_vectors(self, randomize=True): 97 | """Get the d dimensional basis for the unitary provided 98 | Args : randomize (bool) : whether to pick a random basis or 99 | not 100 | Returns: 101 | a list of np.ndarrays which are used as the basis 102 | vectors""" 103 | if randomize == True: 104 | UR = unitary_group.rvs(self.dims) 105 | else: 106 | UR = np.identity(self.dims) 107 | 108 | basis = [] 109 | for k in UR: 110 | basis.append(np.array(k, dtype=complex)) 111 | return basis 112 | 113 | def __get_unitary_circuit(self, backend): 114 | """Return the pretranspiled circuit 115 | Args: 116 | backend : the IBMQBackend on which we want to transpile. 117 | If None, Default : 'qasm_simulator' 118 | Returns: QuantumCircuit containing the transpiled circuit 119 | for the controlled unitary 120 | """ 121 | if backend is None: 122 | backend = Aer.get_backend("qasm_simulator") 123 | 124 | qc = QuantumCircuit(1 + int(np.log2(self.dims))) 125 | 126 | # make the circuit 127 | qc.h(0) 128 | qc = qc.compose(self.c_unitary_gate, qubits=range(1 + int(np.log2(self.dims)))) 129 | 130 | qc.barrier() 131 | # RANDOMNESS 1 132 | qc = transpile(qc, backend=backend, optimization_level=3) 133 | 134 | return qc 135 | 136 | def __get_circuit(self, state, backend, shots, angle=None): 137 | """Given an initial state , 138 | return the assembled and transpiled 139 | circuit that is generated with 140 | inverse rotation 141 | 142 | Args: 143 | state(np.ndarray) : The eigenvector guess state for the initialization 144 | backend(IBMQBackend) : the backend on which this circuit is going to be 145 | executed 146 | shots(int) : the number of shots in our experiments 147 | angle(float) : whether the returned circuit contains an inverse rotation 148 | gate or not. If angle is None, no rotation gate attached 149 | Else, cp(angle) is attached on control qubit 0 150 | 151 | Returns: 152 | QuantumCircuit which is pre-transpiled according to the backend provided 153 | """ 154 | # all theta values are iterated over for the same state 155 | phi = Initialize(state) 156 | 157 | qc1 = QuantumCircuit(1 + int(np.log2(self.dims)), 1) 158 | 159 | # initialize the circuit 160 | qc1 = qc1.compose(phi, qubits=list(range(1, int(np.log2(self.dims)) + 1))) 161 | qc1.barrier() 162 | 163 | # RANDOMNESS 2 164 | qc1 = transpile(qc1, backend=backend, optimization_level=1) 165 | 166 | # get the circuit2 167 | qc2 = self.unitary_circuit 168 | 169 | qc3 = QuantumCircuit(1 + int(np.log2(self.dims)), 1) 170 | 171 | if angle is not None: 172 | # add inverse rotation on the first qubit 173 | qc3.p(-2 * np.pi * angle, 0) 174 | # add hadamard 175 | qc3.h(0) 176 | qc3 = transpile(qc3, backend=backend, optimization_level=1) 177 | 178 | # make final circuit 179 | qc = qc1 + qc2 + qc3 180 | 181 | # measure 182 | qc.measure([0], [0]) 183 | 184 | return qc 185 | 186 | def __get_standard_cost(self, angles, state, backend, shots): 187 | """Given an initial state and a set of angles, 188 | return the best cost and the associated angle. 189 | Implements the standard method as specified in the paper. 190 | 191 | Args : 192 | angles(np.ndarray) : the set of angles on which we execute 193 | the circuits 194 | state(np.ndarray) : the initialization state provided 195 | backend(IBMQBackend): the backend on which this circuit 196 | needs to be executed 197 | shots(int) : the number of shots used to execute this circuit 198 | 199 | Returns : 200 | result(dict) : {result : (float), theta : (float)} 201 | result - the best cost given this set of angles 202 | theta - the best theta value amongst this set 203 | of angles""" 204 | result = {"cost": -1, "theta": -1} 205 | # all theta values are iterated over for the same state 206 | circuits = [] 207 | 208 | for theta in angles: 209 | qc = self.__get_circuit(state, backend, shots, theta) 210 | circuits.append(qc) 211 | 212 | # RANDOMNESS 3 213 | # execute only once... 214 | 215 | counts = backend.run(circuits, shots=shots).result().get_counts() 216 | # get the cost for this theta 217 | 218 | for k, theta in zip(counts, angles): 219 | # for all experiments you ran 220 | try: 221 | C_val = (k["0"]) / shots 222 | except: 223 | C_val = 0 224 | 225 | if C_val > result["cost"]: 226 | # means this is a better theta value 227 | result["theta"] = theta 228 | result["cost"] = C_val 229 | return result 230 | 231 | def __get_alternate_cost(self, angles, state, backend, shots): 232 | """Given an initial state and a set of angles, 233 | return the best cost and the associated angle. 234 | Implements the alternate method as specified in the paper1 235 | and discussion of paper2. 236 | 237 | Args : 238 | angles(np.ndarray) : the set of angles on which we execute 239 | the circuits 240 | state(np.ndarray) : the initialization state provided 241 | backend(IBMQBackend): the backend on which this circuit 242 | needs to be executed 243 | shots(int) : the number of shots used to execute this circuit 244 | 245 | Returns : 246 | result(dict) : {result : (float), theta : (float)} 247 | result - the best cost given this set of angles 248 | theta - the best theta value amongst this set 249 | of angles""" 250 | result = {"cost": -1, "theta": -1} 251 | # all theta values are iterated over for the same state 252 | 253 | qc = self.__get_circuit(state, backend, shots) 254 | 255 | # execute only once... 256 | counts = backend.run(qc, shots=shots).result().get_counts() 257 | 258 | # generate experimental probabilities 259 | try: 260 | p0 = counts["0"] / shots 261 | except: 262 | p0 = 0 263 | try: 264 | p1 = counts["1"] / shots 265 | except: 266 | p1 = 0 267 | 268 | # now, find the best theta as specified by the 269 | # alternate method classically 270 | min_s = 1e5 271 | for theta in angles: 272 | # generate theoretical probabilities 273 | c0 = (np.cos(np.pi * theta)) ** 2 274 | c1 = 1 - c0 275 | 276 | # generate s value 277 | s = (p0 - c0) ** 2 + (p1 - c1) ** 2 278 | if s < min_s: 279 | result["theta"] = theta 280 | min_s = s 281 | 282 | # now , we have the best theta stored in phi 283 | # run circuit once again to get the value of C* 284 | 285 | # RANDOMNESS 4 286 | qc = self.__get_circuit(state, backend, shots, result["theta"]) 287 | counts = backend.run(qc, shots=shots).result().get_counts() 288 | 289 | try: 290 | result["cost"] = counts["0"] / shots 291 | except: 292 | result["cost"] = 0 293 | # no 0 counts present 294 | 295 | # return the result 296 | return result 297 | 298 | def __validate_params( 299 | self, algorithm, progress, randomize, target_cost, bounds 300 | ) -> None: 301 | 302 | # validate algorithm 303 | if not isinstance(algorithm, str): 304 | raise TypeError( 305 | "Algorithm must be mentioned as a string from the values {alternate,standard}" 306 | ) 307 | elif algorithm not in ["alternate", "standard"]: 308 | raise ValueError( 309 | "Algorithm must be specified as 'alternate' or 'standard' " 310 | ) 311 | 312 | # validate progress 313 | if not isinstance(progress, bool): 314 | raise TypeError("Progress must be a boolean variable") 315 | if not isinstance(randomize, bool): 316 | raise Exception("Randomize must be a boolean variable") 317 | 318 | # validate target_cost 319 | if target_cost is not None: 320 | if not isinstance(target_cost, float): 321 | raise TypeError("Target cost must be a float") 322 | if target_cost <= 0 or target_cost >= 1: 323 | raise ValueError("Target cost must be a float value between 0 and 1") 324 | 325 | # validate bounds 326 | 327 | theta_left, theta_right = bounds[0], bounds[1] 328 | if theta_left > theta_right: 329 | raise ValueError( 330 | "Left bound for theta should be smaller than the right bound" 331 | ) 332 | elif (theta_left < 0) or (theta_right > 1): 333 | raise ValueError("Bounds of theta are [0,1].") 334 | 335 | def get_eigen_pair( 336 | self, 337 | backend, 338 | algo="alternate", 339 | theta_left=0, 340 | theta_right=1, 341 | progress=False, 342 | randomize=True, 343 | target_cost=None, 344 | basis=None, 345 | basis_ind=None, 346 | shots=512, 347 | ): 348 | """Finding the eigenstate pair for the unitary 349 | Args : 350 | backend(IBMQBackend) : the backend on which the circuit needs to be executed 351 | algo(str) : ['alternate','standard'] the algorithm to use as specified in the 352 | paper1 section 3. 353 | theta_left(float): the left bound for the search of eigenvalue. Default : 0 354 | theta_right(float): the right bound for the search of eigenvalue. Default : 1 355 | progress(bool) : Whether to show the progress as the algorithm runs 356 | randomize(bool): Whether to choose random initialization of basis states or not 357 | If False, computational basis is chosen. 358 | target_cost(float) : the min cost required to be achieved by the algorithm 359 | basis(list of np.ndarray) : The basis to be used in the algorithm. 360 | Note, if basis is specified, randomize value is ignored 361 | basis_ind(int) : the index of the basis vector to be used as the initial state 362 | vector 363 | 364 | Returns : 365 | result(dict) : {cost :(float), theta :(float), state : (np.ndarray) 366 | cost - the cost with which the algorithm terminates 367 | theta - the eigenvalue estimated by SPEA 368 | state - the eigenvector estimated by SPEA 369 | 370 | """ 371 | self.unitary_circuit = self.__get_unitary_circuit(backend) 372 | 373 | self.__validate_params( 374 | algo, progress, randomize, target_cost, [theta_left, theta_right] 375 | ) 376 | 377 | results = dict() 378 | 379 | # first initialize the state phi 380 | if basis is None: 381 | self.basis = self.__get_basis_vectors(randomize) 382 | else: 383 | # is basis is specified, given as array of vectors... 384 | self.basis = basis 385 | 386 | # choose a random index 387 | if basis_ind is None: 388 | ind = np.random.choice(self.dims) 389 | else: 390 | # choose the index given in that basis 391 | ind = basis_ind 392 | 393 | phi = self.basis[ind] 394 | 395 | # doing the method 1 of our algorithm 396 | # define resolution of angles and precision 397 | if target_cost == None: 398 | precision = 1 / 10 ** self.error 399 | else: 400 | precision = 1 - target_cost 401 | 402 | samples = self.resolution 403 | 404 | # initialization of range 405 | left, right = theta_left, theta_right 406 | # generate the angles 407 | angles = np.linspace(left, right, samples) 408 | 409 | # iterate once 410 | if algo == "alternate": 411 | result = self.__get_alternate_cost(angles, phi, backend, shots) 412 | else: 413 | result = self.__get_standard_cost(angles, phi, backend, shots) 414 | 415 | # get initial estimates 416 | cost = min(1, result["cost"]) 417 | theta_max = result["theta"] 418 | best_phi = phi 419 | 420 | # the range upto which theta extends in each iteration 421 | angle_range = (right - left) / 2 422 | # a parameter 423 | a = 1 424 | # start algorithm 425 | iters = 0 426 | found = True 427 | 428 | while 1 - cost >= precision: 429 | # get angles, note if theta didn't change, then we need to 430 | # again generate the same range again 431 | right = min(theta_right, theta_max + angle_range / 2) 432 | left = max(theta_left, theta_max - angle_range / 2) 433 | if progress: 434 | print("Right :", right) 435 | print("Left :", left) 436 | 437 | # generate the angles only if the theta has been updated 438 | if found == True: 439 | angles = np.linspace(left, right, samples) 440 | 441 | found = False # for this iteration 442 | 443 | if progress: 444 | print("ITERATION NUMBER", iters + 1, "...") 445 | for i in range((2 * self.dims)): 446 | # everyone is supplied with the same range of theta in one iteration 447 | # define z 448 | if i < self.dims: 449 | z = 1 450 | else: 451 | z = 1j 452 | 453 | # alter and normalise phi 454 | curr_phi = best_phi + z * a * (1 - cost) * self.basis[i % self.dims] 455 | curr_phi = curr_phi / np.linalg.norm(curr_phi) 456 | 457 | # iterate (angles would be same until theta is changed) 458 | if algo == "alternate": 459 | res = self.__get_alternate_cost(angles, curr_phi, backend, shots) 460 | else: 461 | res = self.__get_standard_cost(angles, curr_phi, backend, shots) 462 | 463 | curr_cost = res["cost"] 464 | curr_theta = res["theta"] 465 | 466 | # at this point I have the best Cost for the state PHI and the 467 | 468 | if curr_cost > cost: 469 | theta_max = float(curr_theta) 470 | cost = min(1.0, float(curr_cost)) 471 | best_phi = curr_phi 472 | found = True 473 | if progress: 474 | sys.stdout.write("\r") 475 | sys.stdout.write( 476 | "%f %%completed" % (100 * (i + 1) / (2 * self.dims)) 477 | ) 478 | sys.stdout.flush() 479 | 480 | # iteration completes 481 | 482 | if found == False: 483 | # phi was not updated , change a 484 | a = a / 2 485 | if progress: 486 | print("\nNo change, updating a...") 487 | else: 488 | angle_range /= 2 # updated phi and thus theta too -> refine theta range 489 | 490 | iters += 1 491 | if progress: 492 | print("\nCOST :", cost) 493 | print("THETA :", theta_max) 494 | 495 | if iters >= self.iterations: 496 | print( 497 | "Maximum iterations reached for the estimation.\nTerminating algorithm..." 498 | ) 499 | break 500 | # add the warning that iters maxed out 501 | 502 | # add cost, eigenvector and theta to the dict 503 | results["cost"] = cost 504 | results["theta"] = theta_max 505 | results["state"] = best_phi 506 | return results 507 | -------------------------------------------------------------------------------- /QPE/Statistical QPE/Experiments/Experiment 1 - Resolution vs size of Unitary(randomized).ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Experiment 1 \n", 8 | "- Seeing which algorithm converges faster given a particular unitary size and given resolution \n", 9 | "- Resolution ranges from [10,50] with a changed of 5 in between \n", 10 | "- Max iterations is 10 and error threshold is $10^{-3}$" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "metadata": { 17 | "ExecuteTime": { 18 | "end_time": "2021-06-18T09:10:28.334524Z", 19 | "start_time": "2021-06-18T09:10:28.312582Z" 20 | }, 21 | "scrolled": false 22 | }, 23 | "outputs": [ 24 | { 25 | "data": { 26 | "text/plain": [ 27 | "[10, 15, 20, 25, 30, 35, 40, 45, 50]" 28 | ] 29 | }, 30 | "execution_count": 1, 31 | "metadata": {}, 32 | "output_type": "execute_result" 33 | } 34 | ], 35 | "source": [ 36 | "resolutions = [i for i in range(10, 55, 5)]\n", 37 | "resolutions" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 2, 43 | "metadata": { 44 | "ExecuteTime": { 45 | "end_time": "2021-06-27T07:38:38.445639Z", 46 | "start_time": "2021-06-27T07:38:38.423648Z" 47 | } 48 | }, 49 | "outputs": [], 50 | "source": [ 51 | "from qiskit import IBMQ\n", 52 | "from qiskit import QuantumCircuit, execute, transpile, Aer\n", 53 | "from qiskit.extensions import UnitaryGate, Initialize\n", 54 | "from qiskit.quantum_info import Statevector\n", 55 | "from qiskit.tools.visualization import plot_bloch_vector\n", 56 | "from qiskit.tools.visualization import plot_histogram, plot_bloch_multivector\n", 57 | "import numpy as np\n", 58 | "from time import sleep\n", 59 | "import sys\n", 60 | "sys.path.append(\"../..\")\n", 61 | "import os\n", 62 | "from scipy.stats import unitary_group\n", 63 | "import matplotlib.pyplot as plt\n", 64 | "%matplotlib inline\n", 65 | "\n", 66 | "# IBMQ.load_account()\n", 67 | "# provider = IBMQ.get_provider(hub='ibm-q-education')\n", 68 | "# santiago = provider.get_backend('ibmq_santiago')\n", 69 | "# casablanca = provider.get_backend('ibmq_casablanca')\n", 70 | "# bogota = provider.get_backend('ibmq_bogota')\n", 71 | "sim = Aer.get_backend('qasm_simulator')\n", 72 | "# athens = provider.get_backend('ibmq_athens')" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 3, 78 | "metadata": { 79 | "ExecuteTime": { 80 | "end_time": "2021-06-27T07:38:48.434132Z", 81 | "start_time": "2021-06-27T07:38:48.426423Z" 82 | } 83 | }, 84 | "outputs": [], 85 | "source": [ 86 | "from Modules.normal_SPEA import SPEA\n", 87 | "from Modules.changed_SPEA import global_max_SPEA" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "### Utils" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 4, 100 | "metadata": { 101 | "ExecuteTime": { 102 | "end_time": "2021-06-18T09:10:54.090876Z", 103 | "start_time": "2021-06-18T09:10:53.826955Z" 104 | } 105 | }, 106 | "outputs": [], 107 | "source": [ 108 | "def generate_plots(unitary_size, costs, errors, overlaps, algorithm):\n", 109 | " import random\n", 110 | " colors = ['red', 'brown', 'cyan', 'green',\n", 111 | " 'grey', 'blue', 'purple', 'black', 'orange']\n", 112 | " c1, c2, c3 = random.sample(colors, 3)\n", 113 | "\n", 114 | " # plot\n", 115 | " os.makedirs(\"Experiment_1/\"+str(unitary_size) +\n", 116 | " \"_qubit(random)/\", exist_ok=True)\n", 117 | " # plot 1\n", 118 | " fig = plt.figure(figsize=(13, 6))\n", 119 | " ax1 = fig.add_subplot(1, 2, 1)\n", 120 | " ax1.set_title(str(unitary_size)+\" qubit \"+algorithm +\n", 121 | " \" Cost v/s Max iters\", fontsize=16)\n", 122 | " ax1.set_xlabel(\"Number of Resolutions \", fontsize=15)\n", 123 | " ax1.set_ylabel(\"Metrics Returned for unitary \", fontsize=15)\n", 124 | " ax1.plot(resolutions, costs, label='Costs of Unitary',\n", 125 | " marker='o', color=c1, alpha=0.7)\n", 126 | " ax1.plot(resolutions, overlaps, label='Average overlap from nearest eigenvector',\n", 127 | " marker='s', color=c2, alpha=0.6)\n", 128 | " ax1.legend(loc='best')\n", 129 | " ax1.grid()\n", 130 | " # plot 2\n", 131 | " ax2 = fig.add_subplot(1, 2, 2)\n", 132 | " ax2.set_title(str(unitary_size)+\" qubit \"+algorithm +\n", 133 | " \" % error v/s Max iters\", fontsize=16)\n", 134 | " ax2.set_xlabel(\"Number of resolutions \", fontsize=15)\n", 135 | " ax2.set_ylabel(\"% error for nearest eigenvalue\", fontsize=15)\n", 136 | " ax2.plot(resolutions, errors, label='Average error from nearest eigenvalue',\n", 137 | " marker='o', color=c3, alpha=0.6)\n", 138 | " ax2.legend(loc='best')\n", 139 | " ax2.grid()\n", 140 | " # save axure\n", 141 | " fig.savefig(\"Experiment_1/\"+str(unitary_size)+\"_qubit(random)/\" +\n", 142 | " algorithm+\" Algorithm (alternate).JPG\", dpi=200)" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 5, 148 | "metadata": { 149 | "ExecuteTime": { 150 | "end_time": "2021-06-18T09:10:54.217441Z", 151 | "start_time": "2021-06-18T09:10:54.141742Z" 152 | } 153 | }, 154 | "outputs": [], 155 | "source": [ 156 | "def get_results(eig_vals, eig_vect, bases, basis_indices, unitary, algorithm, experiments):\n", 157 | " '''Return the results of running the algorithm for this particular unitary matrix'''\n", 158 | " costs_g = []\n", 159 | " errors_g = []\n", 160 | " max_overlaps_g = []\n", 161 | " # find how the cost converges with increasing iterations\n", 162 | " for reso in resolutions:\n", 163 | " costs = []\n", 164 | " errors = []\n", 165 | " overlaps = []\n", 166 | " i = 0\n", 167 | " # run the experiments ...\n", 168 | " while len(costs) < experiments:\n", 169 | " if algorithm == 'original':\n", 170 | " spea = SPEA(unitary, resolution=reso, error=3, max_iters=10)\n", 171 | " else:\n", 172 | " spea = global_max_SPEA(\n", 173 | " unitary, resolution=reso, error=3, max_iters=10)\n", 174 | "\n", 175 | " result = spea.get_eigen_pair(\n", 176 | " progress=False, backend=sim, algo='alternate', basis=bases[i], basis_ind=basis_indices[i],\n", 177 | " randomize=False,shots = 2**12)\n", 178 | "\n", 179 | "# if result['cost'] < 0.65:\n", 180 | "# continue\n", 181 | " \n", 182 | " # increment the basis index \n", 183 | " i+=1 # in exp 1 -> basis[0], in exp 2 -> basis[1] and so on....\n", 184 | " \n", 185 | " # find the costs\n", 186 | " costs.append(result['cost'])\n", 187 | " theta = result['theta']\n", 188 | " res_state = result['state']\n", 189 | "\n", 190 | " # find the abs difference in this theta with the closest eigenvalue\n", 191 | " # and append that to the errors ...\n", 192 | " min_error = 1e5\n", 193 | " for e in eig_vals:\n", 194 | " error = abs(e - theta)\n", 195 | " if error < min_error:\n", 196 | " min_error = error\n", 197 | " perc_error = ((error)/e)*100\n", 198 | " errors.append(perc_error)\n", 199 | "\n", 200 | " # find overlaps\n", 201 | " max_overlap = -1\n", 202 | " for k in eig_vect:\n", 203 | " dot = np.linalg.norm(np.dot(k, res_state.conjugate().T))**2\n", 204 | " max_overlap = max(max_overlap, dot)\n", 205 | " overlaps.append(max_overlap)\n", 206 | " \n", 207 | " print(\"Result with\", reso, \" resolutions :\")\n", 208 | " print(\"AVG. COST :\", np.average(costs),\n", 209 | " \"AVG. ERROR :\", np.average(errors))\n", 210 | " # append the average result of your algorithm ...\n", 211 | " costs_g.append(np.average(costs))\n", 212 | " errors_g.append(np.average(errors))\n", 213 | " max_overlaps_g.append(np.average(overlaps))\n", 214 | "\n", 215 | " return costs_g, errors_g, max_overlaps_g" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "## 2 - qubit unitary\n" 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": 10, 228 | "metadata": { 229 | "ExecuteTime": { 230 | "end_time": "2021-06-18T05:57:18.630172Z", 231 | "start_time": "2021-06-18T05:57:18.621195Z" 232 | }, 233 | "scrolled": false 234 | }, 235 | "outputs": [ 236 | { 237 | "data": { 238 | "text/plain": [ 239 | "array([[ 0.42301886+1.21660658e-01j, -0.65963338+3.73554408e-01j,\n", 240 | " -0.18525662-3.36202564e-04j, -0.42228448-1.37660205e-01j],\n", 241 | " [-0.64298263-7.13600650e-05j, 0.00808872+3.00568579e-01j,\n", 242 | " 0.26424576-4.30078301e-01j, -0.42757007-2.41985764e-01j],\n", 243 | " [ 0.09090023-4.01928229e-01j, -0.19042309-5.09608118e-01j,\n", 244 | " 0.17662822-2.54866545e-01j, -0.42167745+5.10159877e-01j],\n", 245 | " [ 0.0320143 +4.71161329e-01j, -0.15710225+1.19547105e-01j,\n", 246 | " 0.7512439 +2.24421083e-01j, 0.07758884+3.42428398e-01j]])" 247 | ] 248 | }, 249 | "execution_count": 10, 250 | "metadata": {}, 251 | "output_type": "execute_result" 252 | } 253 | ], 254 | "source": [ 255 | "unit_2 = unitary_group.rvs(4)\n", 256 | "unit_2" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": 11, 262 | "metadata": { 263 | "ExecuteTime": { 264 | "end_time": "2021-06-18T05:57:18.991831Z", 265 | "start_time": "2021-06-18T05:57:18.978867Z" 266 | } 267 | }, 268 | "outputs": [ 269 | { 270 | "name": "stdout", 271 | "output_type": "stream", 272 | "text": [ 273 | "Eigenstates : [[ 0.52650795-0.06310447j 0.21056612-0.14875779j 0.79640627+0.j\n", 274 | " -0.0981045 +0.09193908j]\n", 275 | " [ 0.66274237+0.j 0.28132978+0.19922114j -0.47395219-0.22650573j\n", 276 | " -0.27267107-0.30274082j]\n", 277 | " [-0.09738967+0.15331384j 0.76540519+0.j -0.1221906 -0.13716539j\n", 278 | " 0.41854012+0.41502325j]\n", 279 | " [ 0.31454626-0.3842532j -0.21768008+0.42599039j -0.01695622+0.23615488j\n", 280 | " 0.68447138+0.j ]]\n", 281 | "Eigenvalues : [0.39374559 0.75352756 0.99946376 0.17598498]\n" 282 | ] 283 | } 284 | ], 285 | "source": [ 286 | "eig_vals2, eig_vect2 = np.linalg.eig(unit_2)\n", 287 | "eig_vals2 = np.angle(eig_vals2)\n", 288 | "e = []\n", 289 | "for k in eig_vals2:\n", 290 | " if k < 0:\n", 291 | " v = (k + 2*np.pi)/(2*np.pi)\n", 292 | " else:\n", 293 | " v = (k)/(2*np.pi)\n", 294 | " e.append(v)\n", 295 | "eig_vals2 = np.array(e)\n", 296 | "print(\"Eigenstates :\", eig_vect2)\n", 297 | "print(\"Eigenvalues :\", eig_vals2)" 298 | ] 299 | }, 300 | { 301 | "cell_type": "markdown", 302 | "metadata": {}, 303 | "source": [ 304 | "### Generate Basis set" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": 12, 310 | "metadata": { 311 | "ExecuteTime": { 312 | "end_time": "2021-06-18T05:57:20.418486Z", 313 | "start_time": "2021-06-18T05:57:20.407491Z" 314 | } 315 | }, 316 | "outputs": [ 317 | { 318 | "name": "stdout", 319 | "output_type": "stream", 320 | "text": [ 321 | "Basis set : [[array([ 0.48981882+0.15298201j, -0.26666321+0.51410535j,\n", 322 | " 0.11562013+0.19072544j, -0.4984273 -0.32107082j]), array([ 0.07760449-0.07568072j, -0.29461687-0.28498114j,\n", 323 | " 0.60698511-0.6521419j , -0.12415838-0.10536281j]), array([-0.17083986+0.78881862j, 0.32737969-0.07448516j,\n", 324 | " 0.0180167 -0.17376859j, -0.38937447+0.23177755j]), array([ 0.27086009+0.01173636j, 0.61932291+0.01693214j,\n", 325 | " -0.14139571-0.32561163j, 0.19489482-0.61534529j])], [array([-0.6065911 +0.49551241j, -0.12611167+0.29228041j,\n", 326 | " 0.33866586+0.09231937j, 0.3853081 +0.11620213j]), array([ 0.43630048+0.2064912j , 0.09552736+0.05507663j,\n", 327 | " 0.35342332+0.6175565j , -0.18716143+0.46209403j]), array([-0.32474628-0.03985988j, 0.68916108+0.13652098j,\n", 328 | " 0.16158226-0.29181325j, -0.48157728+0.23704343j]), array([ 0.16517718+0.13851172j, -0.54007379+0.31819559j,\n", 329 | " -0.06845901-0.50452757j, -0.29482859+0.46308128j])], [array([-0.16637693+0.07437035j, -0.27177586+0.28326414j,\n", 330 | " -0.82024684-0.08340147j, -0.34426411+0.12003586j]), array([-0.00656605+0.21250192j, -0.56146361-0.17133726j,\n", 331 | " 0.23927092+0.45464501j, -0.45779195-0.36969697j]), array([ 0.08169706-0.80289828j, -0.08138814+0.40023988j,\n", 332 | " -0.02387865+0.19632033j, 0.05990353-0.37304644j]), array([ 0.39228631+0.34114363j, 0.50420852+0.28330062j,\n", 333 | " -0.07279836-0.10897332j, -0.27306791-0.5509139j ])], [array([-0.25885366+0.68954978j, -0.20520953-0.01486998j,\n", 334 | " -0.24209936-0.05223903j, 0.53439675+0.26127173j]), array([-0.03949677+0.1594949j , 0.41953507+0.2688496j ,\n", 335 | " 0.29489632+0.69853549j, -0.0425462 +0.38468917j]), array([-0.22441294-0.60052465j, 0.11774864+0.12148871j,\n", 336 | " -0.18731147+0.1816182j , 0.69495289-0.09671842j]), array([-0.13860503+0.01774525j, -0.11329497+0.8172626j ,\n", 337 | " 0.37842992-0.39046557j, 0.04463515-0.04533197j])]]\n", 338 | "Basis indices : [2, 1, 3, 2]\n" 339 | ] 340 | } 341 | ], 342 | "source": [ 343 | "bases2 , basis_indices2 = [], []\n", 344 | "for _ in range(4):\n", 345 | " sample = unitary_group.rvs(4)\n", 346 | " basis = []\n", 347 | " for k in sample:\n", 348 | " basis.append(np.array(k, dtype=complex))\n", 349 | " ind = np.random.choice(range(4))\n", 350 | " bases2.append(basis)\n", 351 | " basis_indices2.append(ind)\n", 352 | "print(\"Basis set :\",bases2)\n", 353 | "print(\"Basis indices :\",basis_indices2)" 354 | ] 355 | }, 356 | { 357 | "cell_type": "markdown", 358 | "metadata": {}, 359 | "source": [ 360 | "### Algorithm 1" 361 | ] 362 | }, 363 | { 364 | "cell_type": "code", 365 | "execution_count": null, 366 | "metadata": { 367 | "ExecuteTime": { 368 | "end_time": "2021-06-18T05:58:48.956190Z", 369 | "start_time": "2021-06-18T05:57:21.648560Z" 370 | } 371 | }, 372 | "outputs": [], 373 | "source": [ 374 | "costs_2qubit_b, errors_eig_2qubit_b, max_overlaps_2qubit_b = get_results(\n", 375 | " eig_vals2, eig_vect2, bases2, basis_indices2, unit_2, 'original', 4)\n", 376 | "generate_plots(2, costs_2qubit_b, errors_eig_2qubit_b,\n", 377 | " max_overlaps_2qubit_b, \"Original\")" 378 | ] 379 | }, 380 | { 381 | "cell_type": "markdown", 382 | "metadata": {}, 383 | "source": [ 384 | "### Algorithm 2" 385 | ] 386 | }, 387 | { 388 | "cell_type": "code", 389 | "execution_count": null, 390 | "metadata": { 391 | "ExecuteTime": { 392 | "end_time": "2021-06-18T05:58:48.968123Z", 393 | "start_time": "2021-06-18T05:57:24.374Z" 394 | } 395 | }, 396 | "outputs": [], 397 | "source": [ 398 | "costs_2qubit_c, errors_eig_2qubit_c, max_overlaps_2qubit_c = get_results(\n", 399 | " eig_vals2, eig_vect2, bases2, basis_indices2, unit_2, 'modified', 4)\n", 400 | "generate_plots(2, costs_2qubit_c, errors_eig_2qubit_c,\n", 401 | " max_overlaps_2qubit_c, \"Modified\")" 402 | ] 403 | }, 404 | { 405 | "cell_type": "markdown", 406 | "metadata": {}, 407 | "source": [ 408 | "## 3 - qubit unitary" 409 | ] 410 | }, 411 | { 412 | "cell_type": "code", 413 | "execution_count": null, 414 | "metadata": { 415 | "ExecuteTime": { 416 | "end_time": "2021-06-18T05:58:48.973110Z", 417 | "start_time": "2021-06-18T05:57:24.859Z" 418 | } 419 | }, 420 | "outputs": [], 421 | "source": [ 422 | "unit_3 = unitary_group.rvs(8)\n", 423 | "unit_3" 424 | ] 425 | }, 426 | { 427 | "cell_type": "code", 428 | "execution_count": null, 429 | "metadata": { 430 | "ExecuteTime": { 431 | "end_time": "2021-06-18T05:58:48.979094Z", 432 | "start_time": "2021-06-18T05:57:25.028Z" 433 | } 434 | }, 435 | "outputs": [], 436 | "source": [ 437 | "eig_vals3, eig_vect3 = np.linalg.eig(unit_3)\n", 438 | "eig_vals3 = np.angle(eig_vals3)\n", 439 | "e = []\n", 440 | "for k in eig_vals3:\n", 441 | " if k < 0:\n", 442 | " v = (k + 2*np.pi)/(2*np.pi)\n", 443 | " else:\n", 444 | " v = (k)/(2*np.pi)\n", 445 | " e.append(v)\n", 446 | "eig_vals3 = np.array(e)\n", 447 | "print(\"Eigenstates :\", eig_vect3)\n", 448 | "print(\"Eigenvalues :\", eig_vals3)" 449 | ] 450 | }, 451 | { 452 | "cell_type": "markdown", 453 | "metadata": {}, 454 | "source": [ 455 | "### Generate Basis" 456 | ] 457 | }, 458 | { 459 | "cell_type": "code", 460 | "execution_count": null, 461 | "metadata": { 462 | "ExecuteTime": { 463 | "end_time": "2021-06-18T05:58:48.986078Z", 464 | "start_time": "2021-06-18T05:57:25.325Z" 465 | } 466 | }, 467 | "outputs": [], 468 | "source": [ 469 | "bases3 , basis_indices3 = [], []\n", 470 | "for _ in range(4):\n", 471 | " sample = unitary_group.rvs(8)\n", 472 | " basis = []\n", 473 | " for k in sample:\n", 474 | " basis.append(np.array(k, dtype=complex))\n", 475 | " ind = np.random.choice(range(8))\n", 476 | " bases3.append(basis)\n", 477 | " basis_indices3.append(ind)\n", 478 | "print(\"Basis indices :\",basis_indices3)" 479 | ] 480 | }, 481 | { 482 | "cell_type": "markdown", 483 | "metadata": {}, 484 | "source": [ 485 | "- Algorithm 1" 486 | ] 487 | }, 488 | { 489 | "cell_type": "code", 490 | "execution_count": null, 491 | "metadata": { 492 | "ExecuteTime": { 493 | "end_time": "2021-06-18T05:58:48.992059Z", 494 | "start_time": "2021-06-18T05:57:25.629Z" 495 | } 496 | }, 497 | "outputs": [], 498 | "source": [ 499 | "costs_3qubit_b, errors_eig_3qubit_b, max_overlaps_3qubit_b = get_results(\n", 500 | " eig_vals3, eig_vect3, bases3, basis_indices3, unit_3, 'original', 4)" 501 | ] 502 | }, 503 | { 504 | "cell_type": "code", 505 | "execution_count": null, 506 | "metadata": { 507 | "ExecuteTime": { 508 | "end_time": "2021-06-18T05:58:48.998043Z", 509 | "start_time": "2021-06-18T05:57:25.790Z" 510 | } 511 | }, 512 | "outputs": [], 513 | "source": [ 514 | "generate_plots(3, costs_3qubit_b, errors_eig_3qubit_b,\n", 515 | " max_overlaps_3qubit_b, \"Original\")" 516 | ] 517 | }, 518 | { 519 | "cell_type": "markdown", 520 | "metadata": {}, 521 | "source": [ 522 | "- Algorithm 2" 523 | ] 524 | }, 525 | { 526 | "cell_type": "code", 527 | "execution_count": null, 528 | "metadata": { 529 | "ExecuteTime": { 530 | "end_time": "2021-06-18T05:58:49.006022Z", 531 | "start_time": "2021-06-18T05:57:26.123Z" 532 | } 533 | }, 534 | "outputs": [], 535 | "source": [ 536 | "costs_3qubit_c, errors_eig_3qubit_c, max_overlaps_3qubit_c = get_results(\n", 537 | " eig_vals3, eig_vect3, bases3, basis_indices3, unit_3, 'modified', 4)\n", 538 | "generate_plots(3, costs_3qubit_c, errors_eig_3qubit_c,\n", 539 | " max_overlaps_3qubit_c, \"Modified\")" 540 | ] 541 | }, 542 | { 543 | "cell_type": "markdown", 544 | "metadata": {}, 545 | "source": [ 546 | "## 4 - qubit unitary" 547 | ] 548 | }, 549 | { 550 | "cell_type": "code", 551 | "execution_count": null, 552 | "metadata": { 553 | "ExecuteTime": { 554 | "end_time": "2021-06-18T05:58:49.014000Z", 555 | "start_time": "2021-06-18T05:57:26.504Z" 556 | } 557 | }, 558 | "outputs": [], 559 | "source": [ 560 | "unit_4 = unitary_group.rvs(16)\n", 561 | "# unit_4" 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "execution_count": null, 567 | "metadata": { 568 | "ExecuteTime": { 569 | "end_time": "2021-06-18T05:58:49.021979Z", 570 | "start_time": "2021-06-18T05:57:26.689Z" 571 | } 572 | }, 573 | "outputs": [], 574 | "source": [ 575 | "eig_vals4, eig_vect4 = np.linalg.eig(unit_4)\n", 576 | "eig_vals4 = np.angle(eig_vals4)\n", 577 | "e = []\n", 578 | "for k in eig_vals4:\n", 579 | " if k < 0:\n", 580 | " v = (k + 2*np.pi)/(2*np.pi)\n", 581 | " else:\n", 582 | " v = (k)/(2*np.pi)\n", 583 | " e.append(v)\n", 584 | "eig_vals4 = np.array(e)\n", 585 | "print(\"Eigenstates :\", eig_vect4)\n", 586 | "print(\"Eigenvalues :\", eig_vals4)" 587 | ] 588 | }, 589 | { 590 | "cell_type": "markdown", 591 | "metadata": {}, 592 | "source": [ 593 | "### Generate basis set" 594 | ] 595 | }, 596 | { 597 | "cell_type": "code", 598 | "execution_count": null, 599 | "metadata": { 600 | "ExecuteTime": { 601 | "end_time": "2021-06-18T05:58:49.031952Z", 602 | "start_time": "2021-06-18T05:57:27.005Z" 603 | } 604 | }, 605 | "outputs": [], 606 | "source": [ 607 | "bases4 , basis_indices4 = [], []\n", 608 | "for _ in range(4):\n", 609 | " sample = unitary_group.rvs(16)\n", 610 | " basis = []\n", 611 | " for k in sample:\n", 612 | " basis.append(np.array(k, dtype=complex))\n", 613 | " ind = np.random.choice(range(16))\n", 614 | " bases4.append(basis)\n", 615 | " basis_indices4.append(ind)\n", 616 | "print(\"Basis indices :\",basis_indices4)" 617 | ] 618 | }, 619 | { 620 | "cell_type": "markdown", 621 | "metadata": {}, 622 | "source": [ 623 | "- Algorithm 1" 624 | ] 625 | }, 626 | { 627 | "cell_type": "code", 628 | "execution_count": null, 629 | "metadata": { 630 | "ExecuteTime": { 631 | "end_time": "2021-06-18T05:58:49.039930Z", 632 | "start_time": "2021-06-18T05:57:27.725Z" 633 | } 634 | }, 635 | "outputs": [], 636 | "source": [ 637 | "costs_4qubit_b, errors_eig_4qubit_b, max_overlaps_4qubit_b = get_results(\n", 638 | " eig_vals4, eig_vect4, bases4, basis_indices4, unit_4, 'original', 4)\n", 639 | "generate_plots(4, costs_4qubit_b, \n", 640 | " errors_eig_4qubit_b, max_overlaps_4qubit_b, \"Original\")" 641 | ] 642 | }, 643 | { 644 | "cell_type": "markdown", 645 | "metadata": {}, 646 | "source": [ 647 | "- Algorithm 2" 648 | ] 649 | }, 650 | { 651 | "cell_type": "code", 652 | "execution_count": null, 653 | "metadata": { 654 | "ExecuteTime": { 655 | "end_time": "2021-06-18T05:58:49.048907Z", 656 | "start_time": "2021-06-18T05:57:28.911Z" 657 | } 658 | }, 659 | "outputs": [], 660 | "source": [ 661 | "costs_4qubit_c, errors_eig_4qubit_c, max_overlaps_4qubit_c = get_results(\n", 662 | " eig_vals4, eig_vect4, bases4, basis_indices4, unit_4, 'modified', 4)\n", 663 | "generate_plots(4, costs_4qubit_c, \n", 664 | " errors_eig_4qubit_c,max_overlaps_4qubit_c, \"Modified\")" 665 | ] 666 | }, 667 | { 668 | "cell_type": "code", 669 | "execution_count": null, 670 | "metadata": {}, 671 | "outputs": [], 672 | "source": [] 673 | }, 674 | { 675 | "cell_type": "code", 676 | "execution_count": null, 677 | "metadata": {}, 678 | "outputs": [], 679 | "source": [] 680 | }, 681 | { 682 | "cell_type": "code", 683 | "execution_count": null, 684 | "metadata": {}, 685 | "outputs": [], 686 | "source": [] 687 | } 688 | ], 689 | "metadata": { 690 | "kernelspec": { 691 | "display_name": "Python 3", 692 | "language": "python", 693 | "name": "python3" 694 | }, 695 | "language_info": { 696 | "codemirror_mode": { 697 | "name": "ipython", 698 | "version": 3 699 | }, 700 | "file_extension": ".py", 701 | "mimetype": "text/x-python", 702 | "name": "python", 703 | "nbconvert_exporter": "python", 704 | "pygments_lexer": "ipython3", 705 | "version": "3.7.4" 706 | }, 707 | "varInspector": { 708 | "cols": { 709 | "lenName": 16, 710 | "lenType": 16, 711 | "lenVar": 40 712 | }, 713 | "kernels_config": { 714 | "python": { 715 | "delete_cmd_postfix": "", 716 | "delete_cmd_prefix": "del ", 717 | "library": "var_list.py", 718 | "varRefreshCmd": "print(var_dic_list())" 719 | }, 720 | "r": { 721 | "delete_cmd_postfix": ") ", 722 | "delete_cmd_prefix": "rm(", 723 | "library": "var_list.r", 724 | "varRefreshCmd": "cat(var_dic_list()) " 725 | } 726 | }, 727 | "types_to_exclude": [ 728 | "module", 729 | "function", 730 | "builtin_function_or_method", 731 | "instance", 732 | "_Feature" 733 | ], 734 | "window_display": false 735 | } 736 | }, 737 | "nbformat": 4, 738 | "nbformat_minor": 2 739 | } 740 | --------------------------------------------------------------------------------