diff --git a/.gitignore b/.gitignore index f7d913e..4f7e523 100644 --- a/.gitignore +++ b/.gitignore @@ -47,9 +47,6 @@ coverage.xml .hypothesis/ .pytest_cache/ -# Sphinx stuff -docs/build/ - # Translations *.mo *.pot @@ -68,13 +65,13 @@ instance/ # Sphinx documentation docs/_build/ -docs/_test/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints +*.nbconvert.ipynb # pyenv .python-version diff --git a/.readthedocs.yml b/.readthedocs.yml index 6023c4b..88a2c8f 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -2,7 +2,7 @@ build: image: latest python: - version: 3.7 + version: 3.9 setup_py_install: true pip_install: true extra_requirements: diff --git a/.travis.yml b/.travis.yml index ab75916..883bf80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ matrix: include: - name: python 3.7 python: 3.7 + if: branch = dev sudo: true dist: xenial - name: python 3.8 @@ -39,7 +40,7 @@ deploy: provider: pypi user: qinspire password: - secure: LN2iXxsoffIVmLKBuX6z273D1PpJt6zYLLF3snfkM4lzQZhNl4nLn/D0CdMb0OiMAKINzQELiKZFlH0RRXI9xsG3ErdJU3M9PcKpbGKndJ1tz92hFwy6rP1H5gwPXsx03PuiiXGGp6xZiK/BYuyzkZBjS9LRt8Z3cmjINwJSMiF8jTBfQo1sVe5eaqpDzofrCAe4VThuUsZGOpPjQsjT7xL93k8tr6soQIL8ruE6VUxscW4dAaqG+1CyRUkP8oC2SQ9TJVkYE1iI90LgwHzQEYxOzB6wwmpNZ9RM2nQ3p7eOBcn+/rbr/sc87C+exQN+lWLL32plesdVnctBxjNcfupCip66tEtx/JPqWdJvQUbRz4L2Zi2YY9wTWILn0UzIjULid6hqhe5sEoVCpjBGbCLV5E6dSRpZwsF44flLWGX5PY0nHNtE+oFReygcupxKkeOEXoFwk5GlhOzxNUGDlqWUKQuxxiGMWqmJe73hzdjUlP+VEO5dFrlQdRt4clKCZV+0kNvzxhW1GT/nxBBVermpKPIbPR019686J+pv0RFsLhZYGNGTXNc/IKCfvwhMSsF6169ue0P70AomXQXsXYNcQNAypdUGiVTIfiU6B6xVfKYKQVXu1ZqV4UxxOcSORtXAolgmGLZkc7+k8Jy8QRRY/iK3BDmsdRF2YY2p3A4= + secure: Hhiv2J3i5bU25M1QlSknneMN9ZQ0KLOf1hSrS9FkmXNQFS5EIWKbSBJYP/5DeWLsezR7SxFNql1aPF3G2QLSfztELqd3avbS5qNC5ZmooDGD/PdR5XEjnUlYrn5YtBgNq+IR+91uWaBI5TZfD9bQl7lYW5LVxH2GpI9MZrx27gytxwjfqfNOTcnn/fKDn0artCQ3V+Bnu5clEGlQagNWajuYKqSCMY2gk3r5xqHPgyt7OMWk7L2ZDLiEsN8NDx3leRPWPhwVX1CCO5CR5PHSCAdc0C7Uxy8l44DgCpx8+jeLnADh7RXsHGQfj7pBX3CtbjwERBrysNtKUB4zTzkgzBpBXoI1ffCLUOmPaMdYhXveNeAWnem6aZ2lLl9+MVikPS8laPttWcRAqzDjbdFBB6D6WHvsXAFUpEllsD3AILYpD7nOEsx9uvwKlCfcgf6fmq0z5Q6Y0Kux3/oDs4AQpAPVXmWsfnbrQkaJ0Knlho+ncgrLFymvIGmMxOee8tuj6bTeOtazvCjfoDcwjT86n6qkSUZ8g/dPKMkJox8SILFpVQzOWlIwrJMzs7X65vP25nBcHYZwI7db/e+fwOwxECHvKTHf4Bubgc7FNAK7kGcobCSeC2djA83Or3LAVen58m6YCBG+AJfRHUIEkqtN2KDIuDBZfn3r8OvT3Hd2IQA= on: tags: true branch: master diff --git a/README.md b/README.md index 44b500d..e3ffb86 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ # Quantum Inspire SDK -*Note: this SDK is made available as a public beta, please report any -issues or bugs in the github issue tracker.* +[![License](https://img.shields.io/github/license/qutech-delft/quantuminspire.svg?)](https://opensource.org/licenses/Apache-2.0) +[![Coverage Status](https://coveralls.io/repos/github/QuTech-Delft/quantuminspire/badge.svg?branch=dev)](https://coveralls.io/github/QuTech-Delft/quantuminspire?branch=dev) +[![Documentation Status](https://readthedocs.org/projects/quantum-inspire/badge/?version=latest)](https://quantum-inspire.readthedocs.io/en/latest/?badge=latest) +[![](https://img.shields.io/github/release/qutech-delft/quantuminspire.svg)](https://github.com/qutech-delft/quantuminspire/releases) +[![Downloads](https://pepy.tech/badge/quantuminspire)](https://pypi.org/project/quantuminspire/) The Quantum Inspire platform allows to execute quantum algorithms using the cQASM language. @@ -17,6 +20,7 @@ For more information on Quantum Inspire see on cQASM can be found in the Quantum Inspire [knowledge base](https://www.quantum-inspire.com/kbase/advanced-guide/). +Examples of more complex algorithms that make use of Quantum Inspire SDK can be found in [Quantum Inspire Examples](https://github.com/QuTech-Delft/quantum-inspire-examples). ## Installation @@ -62,15 +66,20 @@ pip install .[qiskit,projectq] ### Installing for generating documentation To install the necessary packages to perform documentation activities for SDK do: + ``` pip install -e .[rtd] ``` + To build the 'readthedocs' documentation do: + ``` cd docs make html ``` +The documentation is then build in 'docs/_build/html' and can be viewed [here](docs/_build/html/index.html). + ## Running For example usage see the python scripts and Jupyter notebooks in the [docs/examples](docs/examples) directory @@ -92,7 +101,7 @@ cd docs/examples python example_projectq_grover.py ``` -[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/QuTech-Delft/quantuminspire/master?filepath=docs) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/QuTech-Delft/quantuminspire/master?filepath=docs/examples) Another way to browse and run the available notebooks is by clicking the 'launch binder' button above. @@ -204,8 +213,8 @@ qi = QuantumInspireAPI() ``` To create a token authentication object yourself using the stored token you do: ```python -from quantuminspire.credentials import get_token_authentication -auth = get_token_authentication() +from quantuminspire.credentials import get_authentication +auth = get_authentication() ``` This `auth` can then be used to initialize the Quantum Inspire API object. @@ -220,19 +229,9 @@ coverage report -m ## Known issues -* Some test-cases call protected methods * Known issues and common questions regarding the Quantum Inspire platform can be found in the [FAQ](https://www.quantum-inspire.com/faq/). ## Bug reports Please submit bug-reports [on the github issue tracker](https://github.com/QuTech-Delft/quantuminspire/issues). - -## Note - -If you are getting import errors related to `tests.quantuminspire` when running -the above commands after a `pip install -e .`, as a workaround you should remove -the package `tests` installed by older versions of `marshmallow-polyfield` (a Qiskit -dependency): - -`rm -Rf env/lib/python3.7/site-packages/tests` diff --git a/docs/conf.py b/docs/conf.py index 852d8fb..8b06839 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,7 +30,10 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ + 'nbsphinx', 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', # google style docstrings + 'sphinx.ext.viewcode', 'sphinx_rtd_theme', 'recommonmark' ] @@ -46,13 +49,14 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'examples/*nbconvert.ipynb'] + +nbsphinx_execute = 'never' # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -# html_theme = 'sphinx_rtd_theme' # Add any paths that contain custom static files (such as style sheets) here, diff --git a/docs/examplenotebooks.rst b/docs/examplenotebooks.rst new file mode 100644 index 0000000..e3c78cc --- /dev/null +++ b/docs/examplenotebooks.rst @@ -0,0 +1,26 @@ +Example notebooks +================= + +ProjectQ examples +----------------- + +.. toctree:: + :maxdepth: 3 + :titlesonly: + :glob: + + examples/example_projectq.ipynb + +Qiskit examples +--------------- + +.. toctree:: + :maxdepth: 3 + :titlesonly: + :glob: + + examples/grover_algorithm_qi.ipynb + examples/qi-performance-test.ipynb + + +Back to the :doc:`main page `. diff --git a/docs/examples.rst b/docs/examples.rst index a567d98..c5f2c0e 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,38 +1,29 @@ -Examples -======== +Python Examples +=============== -ProjectQ --------- -.. toctree:: +ProjectQ examples +----------------- -.. - examples/example_projectq - examples/example_projectq_entangle - examples/example_projectq_grover +A simple example that demonstrates how to use the SDK to create a circuit to create a Bell state, and simulate the +circuit on Quantum Inspire. -Qiskit ------- -.. toctree:: +.. literalinclude:: examples/example_projectq_entangle.py -.. - examples/example_qiskit_conditional - examples/example_qiskit_entangle +An example that demonstrates how to use the SDK to create a more complex circuit to run Grover's algorithm and +simulate the circuit on Quantum Inspire. -Classifier ----------- +.. literalinclude:: examples/example_projectq_grover.py -.. toctree:: +Qiskit examples +--------------- -.. - examples/classifier_example/classification_example1_2_data_points - examples/classifier_example/classification_example2_4_data_points - examples/classifier_example/classification_example3_4_features +A simple example that demonstrates how to use the SDK to create a circuit to create a Bell state, and simulate the +circuit on Quantum Inspire. -General -------- +.. literalinclude:: examples/example_qiskit_entangle.py -.. toctree:: +A simple example that demonstrates how to use the SDK to create a circuit to demonstrate conditional gate execution. -.. - examples/grover_algorithm_qi - examples/qi-performance-test +.. literalinclude:: examples/example_qiskit_conditional.py + +Back to the :doc:`main page `. diff --git a/docs/examples/classifier_example/classification_example1_2_data_points.ipynb b/docs/examples/classifier_example/classification_example1_2_data_points.ipynb deleted file mode 100644 index 1f7513c..0000000 --- a/docs/examples/classifier_example/classification_example1_2_data_points.ipynb +++ /dev/null @@ -1,1004 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#
A Quantum distance-based classifier
#" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##
Robert Wezeman, TNO
##" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Table of Contents\n", - "* [Introduction](#introduction)\n", - "* [Problem](#problem)\n", - "* [Amplitude Encoding](#amplitude)\n", - "* [Data preprocessing](#dataset)\n", - "* [Quantum algorithm](#algorithm)\n", - "* [Conclusion and further work](#conclusion)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "## Import external python file\n", - "import nbimporter\n", - "import numpy as np\n", - "from data_plotter import get_bin, DataPlotter # for easier plotting\n", - "DataPlotter = DataPlotter()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "$$ \\newcommand{\\ket}[1]{\\left|{#1}\\right\\rangle} $$\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Introduction #\n", - "\n", - "\n", - "Consider the following scatter plot of the first two flowers in [the famous Iris flower data set](https://en.wikipedia.org/wiki/Iris_flower_data_set)\n", - "\n", - "\n", - "\n", - "\n", - "Notice that just two features, the sepal width and the sepal length, divide the two different Iris species into different regions in the plot. This gives rise to the question: given only the sepal length and sepal width of a flower can we classify the flower by their correct species? This type of problem, also known as [statistical classification](https://en.wikipedia.org/wiki/Statistical_classification), is a common problem in machine learning. In general, a classifier is constructed by letting it learn a function which gives the desired output based on a sufficient amount of data. This is called supervised learning, as the desired output (the labels of the data points) are known. After learning, the classifier can classify an unlabeled data point based on the learned function. The quality of a classifier improves if it has a larger training dataset it can learn on. The true power of this quantum classifier becomes clear when using extremely large data sets. \n", - "In this notebook we will describe how to build a distance-based classifier on the Quantum Inspire using amplitude encoding. It turns out that, once the system is initialized in the desired state, regardless of the size of training data, the actual algorithm consists of only 3 actions, one Hadamard gate and two measurements. This has huge implications for the scalability of this problem for large data sets. Using only 4 qubits we show how to encode two data points, both of a different class, to predict the label for a third data point. In this notebook we will demonstrate how to use the Quantum Inspire SDK using QASM-code, we will also provide the code to obtain the same results for the ProjectQ framework.\n", - "\n", - "\n", - "\n", - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Problem #\n", - "We define the following binary classification problem: Given the data set \n", - "$$\\mathcal{D} = \\Big\\{ ({\\bf x}_1, y_1), \\ldots ({\\bf x}_M , y_M) \\Big\\},$$\n", - "consisting of $M$ data points $x_i\\in\\mathbb{R}^n$ and corresponding labels $y_i\\in \\{-1, 1\\}$, give a prediction for the label $\\tilde{y}$ corresponding to an unlabeled data point $\\bf\\tilde{x}$. The classifier we shall implement with our quantum circuit is a distance-based classifier and is given by\n", - "\\begin{equation}\\newcommand{\\sgn}{{\\rm sgn}}\\newcommand{\\abs}[1]{\\left\\lvert#1\\right\\rvert}\\label{eq:classifier} \\tilde{y} = \\sgn\\left(\\sum_{m=0}^{M-1} y_m \\left[1-\\frac{1}{4M}\\abs{{\\bf\\tilde{x}}-{\\bf x}_m}^2\\right]\\right). \\hspace{3cm} (1)\\end{equation}\n", - "\n", - "This is a typical $M$-nearest-neighbor model, where each data point is given a weight related to the distance measure. To implement this classifier on a quantum computer, we need a way to encode the information of the training data set in a quantum state. We do this by first encoding the training data in the amplitudes of a quantum system, and then manipulate the amplitudes of then the amplitudes will be manipulated by quantum gates such that we obtain a result representing the above classifier. Encoding input features in the amplitude of a quantum system is known as amplitude encoding.\n", - "\n", - "\n", - "[Back to Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Amplitude encoding #\n", - "Suppose we want to encode a classical vector $\\bf{x}\\in\\mathbb{R}^N$ by some amplitudes of a quantum system. We assume $N=2^n$ and that $\\bf{x}$ is normalised to unit length, meaning ${\\bf{x}^T{x}}=1$. We can encode $\\bf{x}$ in the amplitudes of a $n$-qubit system in the following way\n", - "\\begin{equation}\n", - " {\\bf x} = \\begin{pmatrix}x^1 \\\\ \\vdots \\\\ x^N\\end{pmatrix} \\Longleftrightarrow{} \\ket{\\psi_{{\\bf x}}} = \\sum_{i=0}^{N-1}x^i\\ket{i},\n", - "\\end{equation}\n", - "where $\\ket{i}$ is the $i^{th}$ entry of the computational basis $\\left\\{\\ket{0\\ldots0},\\ldots,\\ket{1\\ldots1}\\right\\}$. By applying an efficient quantum algorithm (resources growing polynomially in the number of qubits $n$), one can manipulate the $2^n$ amplitudes super efficiently, that is $\\mathcal{O}\\left(\\log N\\right)$. This follows as manipulating all amplitudes requires an operation on each of the $n = \\mathcal{O}\\left(\\log N\\right)$ qubits. For algorithms to be truly super-efficient, the phase where the data is encoded must also be at most polynomial in the number of qubits. The idea of quantum memory, sometimes referred as quantum RAM (QRAM), is a particular interesting one. Suppose we first run some quantum algorithm, for example in quantum chemistry, with as output some resulting quantum states. If these states could be fed into a quantum classifier, the encoding phase is not needed anymore. Finding efficient data encoding systems is still a topic of active research. We will restrict ourselves here to the implementation of the algorithm, more details can be found in the references.\n", - "\n", - "\n", - "The algorithm requires the $n$-qubit quantum system to be in the following state \n", - "\\begin{equation}\\label{eq:prepstate}\n", - " \\ket{\\mathcal{D}} = \\frac{1}{\\sqrt{2M}} \\sum_{m=0}^{M-1} \\ket{m}\\Big(\\ket{0}\\ket{\\psi_{\\bf\\tilde{{x}}}} + \\ket{1}\\ket{\\psi_{\\bf{x}_m}}\\Big)\\ket{y_m}.\\hspace{3cm} (2)\n", - "\\end{equation}\n", - "Here $\\ket{m}$ is the $m^{th}$ state of the computational basis used to keep track of the $m^{th}$ training input. The second register is a single ancillary qubit entangled with the third register. The excited state of the ancillary qubit is entangled with the $m^{th}$ training state $\\ket{\\psi_{{x}_m}}$, while the ground state is entangled with the new input state $\\ket{\\psi_{\\tilde{x}}}$. The last register encodes the label of the $m^{th}$ training data point by\n", - "\\begin{equation}\n", - "\\begin{split}\n", - " y_m = -1 \\Longleftrightarrow& \\ket{y_m} = \\ket{0},\\\\\n", - " y_m = 1 \\Longleftrightarrow& \\ket{y_m} = \\ket{1}.\n", - "\\end{split}\n", - "\\end{equation}\n", - "Once in this state the algorithm only consists of the following three operations:\n", - "\n", - "1. Apply a Hadamard gate on the second register to obtain \n", - "\n", - " $$\\frac{1}{2\\sqrt{M}} \\sum_{m=0}^{M-1} \\ket{m}\\Big(\\ket{0}\\ket{\\psi_{\\bf\\tilde{x}+x_m}} + \\ket{1}\\ket{\\psi_{\\bf\\tilde{x}-x_m}}\\Big)\\ket{y_m},$$\n", - "\n", - " where $\\ket{\\psi_{\\bf\\tilde{{x}}\\pm{x}_m}} = \\ket{\\psi_{\\tilde{\\bf{x}}}}\\pm \\ket{\\psi_{\\bf{x}_m}}$. \n", - " \n", - "2. Measure the second qubit. We restart the algorithm if we measure a $\\ket{1}$ and only continue if we are in the $\\ket{0}$ branch. We continue the algorithm with a probability $p_{acc} = \\frac{1}{4M}\\sum_M\\abs{{\\bf\\tilde{x}}+{\\bf x}_m}^2$, for standardised random data this is usually around $0.5$. The resulting state is given by\n", - "\n", - "\\begin{equation}\n", - " \\frac{1}{2\\sqrt{Mp_{acc}}}\\sum_{m=0}^{M-1}\\sum_{i=0}^{N-1} \\ket{m}\\ket{0}\\left({\\tilde{x}}^i + x_m^i\\right)\\ket{i}\\ket{y_m}.\n", - "\\end{equation} \n", - "\n", - "3. Measure the last qubit $\\ket{y_m}$. The probability that we measure outcome zero is given by\n", - "\\begin{equation}\n", - " p(q_4=0) = \\frac{1}{4Mp_{acc}}\\sum_{m|y_m=0}\\abs{\\bf{\\tilde{{x}}+{x}_m}}^2.\n", - "\\end{equation}\n", - "\n", - "In the special case where the amount of training data for both labels is equal, this last measurement relates to the classifier as described in previous section by\n", - "\\begin{equation}\n", - "\\tilde{y} = \\left\\{\n", - " \\begin{array}{lr}\n", - " -1 & : p(q_4 = 0 ) > p(q_4 = 1)\\\\\n", - " +1 & : p(q_4 = 0 ) < p(q_4 = 1)\n", - " \\end{array}\n", - "\\right. \n", - "\\end{equation}\n", - "By setting $\\tilde{y}$ to be the most likely outcome of many measurement shots, we obtain the desired distance-based classifier.\n", - "\n", - "\n", - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Data preprocessing#\n", - "In the previous section we saw that for amplitude encoding we need a data set which is normalised. Luckily, it is always possible to bring data to this desired form with some data transformations. Firstly, we standardise the data to have zero mean and unit variance, then we normalise the data to have unit length. Both these steps are common methods in machine learning. Effectively, we only have to consider the angle between different data features.\n", - "\n", - "To illustrate this procedure we apply it to the first two features of the famous Iris data set:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Plot the data\n", - "from sklearn.datasets import load_iris\n", - "\n", - "iris = load_iris()\n", - "features = iris.data.T\n", - "data = [el[0:101] for el in features][0:2] # Select only the first two features of the dataset\n", - "\n", - "half_len_data = len(data[0]) // 2\n", - "iris_setosa = [el[0:half_len_data] for el in data[0:2]]\n", - "iris_versicolor = [el[half_len_data:-1] for el in data[0:2]]\n", - "\n", - "DataPlotter.plot_original_data(iris_setosa, iris_versicolor); # Function to plot the data\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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EzQREZEM8T7obNDQwJU8esBu6Y59omx8/9SFI2i1i1eTWL8+tmXJJwDslp6wT5SN75/6MEGrHatJ9Ijd0h37RNn4/qkPE7TasZpEj9gt3bFPlI3vn/rwHLTasZpEj9gt3bFPlI3vn/owQWcDVpPoEbulO/aJsvH9UxdOcRMREckQEzQREZEMMUETERHJEBM0ERGRDDFBExERyRATNBERkQwxQRMREckQEzQREZEMcaESIqJ+iF0lilWpCOAImoioT2JXiWJVKopjgiYi6oPYVaJYlYrimKCJiPogdpUoVqWiOJ6DJiLqg9hVoliViuJES9AejwcLFixAR0cHQqEQ7r77bowZM0as5omIUiZ2lShWpSJAxAT9xBNP4MILL8SsWbPw5Zdf4q677sKrr74qVvNERESKIlqCnjVrFoxGIwAgEonAZDKJ1TQREZHiZCRBv/jii9iyZUuXx1auXImzzz4bTqcTCxYswOLFizPRNBERkSpoBEEQxGrs888/R2VlJRYuXIgJEyZ0+3ljYyMsFotY4aSF3++H2WyWOgxVYx9nHvtYHOznzFNiH3u9XowbN67b46JNcR84cADz5s3Dr3/9a4wcObLX3xs1apRYIaXF/v37FRez0rCPM499LA72c+YpsY8bGxt7fFy0BL1mzRoEg0GsWLECAGCz2VBbWytW80RERIoiWoJmMiYiIho4riRGREQkQ0zQREREMsQETUREJENM0ERERDLEBE1ERCRDTNBEREQyxARNREQkQ0zQREREMsQETUREJENM0ERERDLEBE1ERCRDTNBEREQyxARNREQkQ0zQREREMsQETUREJEOi1YMmkpuGBmDXLmD8eKCkROpoiIi64giaslJDAzB7NrB+fWzb0CB1REREXTFBU1batQuIRIDCwth21y6pIyIi6ooJmrLS+PGATge0tMS248dLHRERUVc8B01ZqaQE2LyZ56CJSL6YoClrlZQwMRORfHGKm4iISIaYoImIiGSICZqIiEiGmKCJiIhkiAmaiIhIhpigiYiIZIgJmoiISIY0giAIUgcR19jYKHUIREREohs3bly3x2SVoImIiCiGU9xEREQyxARNREQkQ0zQg+TxePDLX/4SN9xwA8rKyvDhhx9KHZJqbd++HXfddZfUYahKNBpFVVUVysrKUF5ejkOHDkkdkmrt3bsX5eXlUoehSqFQ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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Rescale the data\n", - "from sklearn import preprocessing # Module contains method to rescale data to have zero mean and unit variance\n", - "\n", - "# Rescale whole data-set to have zero mean and unit variance\n", - "features_scaled = [preprocessing.scale(el) for el in data[0:2]]\n", - "iris_setosa_scaled = [el[0:half_len_data] for el in features_scaled]\n", - "iris_versicolor_scaled = [el[half_len_data:-1] for el in features_scaled]\n", - " \n", - "DataPlotter.plot_standardised_data(iris_setosa_scaled, iris_versicolor_scaled); # Function to plot the data\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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Z1BAi4jIEezfzOqpp+q+N3VI/9huVpMnTyaRSBzynAEDBvDmm28SDofZtGkTl1xyCX/729/4/vvvGT58OBMnTiQQCPDNN98cdIzCwkI6derEzTffzOOPP47V+kP8lZeXk5mZSW5uLgAjR44kIyMDn89Hu3btALjgggv4+9//Xv+c7777jp/+9Kf4fL5D7v/393WsFNAiInJYzbyZ1VHvZgWQk5PDmWeeydy5c+nduzd2u50zzjiDn/70pzz77LNMmzaNX/7yl/z85z8/6BgfffQRbdu2ZdGiRdx00008+uij9a+Zm5tLZWUlgUAAgKlTp/Ldd98RDAbZt28fAJs2beK0006rf87JJ5/Mzp0761vimzZtqg/mA8P/eCStDzoajTJhwgT++c9/EolEuOmmm7j00kuTdXgRETlKSd7MqsHdrPa7+uqrGTlyJG+99RYAHTp0oHv37vzf//t/qayspGvXrvUt3/06dOjAHXfcwfLly4nFYtxyyy3191mtVh544AFuvPFGrFYrZ599Nueeey5Tp05l9OjRWCwW2rRpw0MPPVTfSs7JyWH06NFcf/31WK1W2rdvz1133cWqVaua7HwkbTerl156ib/+9a/ce++9BAIBBg4cyHvvvXfQY7SblRyOznHz0zlOjnQ8z2m2mVVanuOU72bVt29fLr/8cgAMw6gfFCAiIualzaxSJ2kB7fV6AQgGg9x2223cfvvtyTq0iIhI2knaJW6A3bt3c8stt3DttdcyaNCgQ+7fvHkzHo8nWeU0iVAohNvtTnUZLZrOcfPTOU4Onefml47nuKamJrWXuEtKShgxYgQTJ0780blh6dZ3kI79HelG57j56Rwnh85z80vHc7x58+bD3p60aVZPPvkklZWVzJ07l2HDhjFs2DBCoVCyDi8iIpJWktaCvu+++7jvvvuSdTgREZG0poVKRERETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkJJD+gtW7YwbNiwZB9WREQkrdiTebAFCxawcuVKMjIyknlYkdatqAg2bIDdu2H7dujfH0aPPvLjvF6oroYePaCw8MiPO9L9ItIkkhrQ7du3Z86cOdx9993JPKxIy1RUBL//PezZA8OGHTZ0E++/T2LkSBIVFSQqKjBsNhIbN2IkEhgjRtQ/zvjwQ6oefRRqarCUlkJODpYlS7A+/jjWiy7CarVitVqxrFuH9Te/gXgcFi6ERYsU0iLNJKkBffnll7Nr164ffcy2bduSVE3TCIVCaVdzutE5PlTW0qXkP/ooRjRKzG4nPmECu/fsoXzAABKJBIZhYBgGbV5/nSyXC7vLhc3rxWK1Yo3HqV25kt0XXYTFYgHA/9lnRMJhLIkEVouFOJCIxQisXk25349hGCQSCTJffx2/14vh8+GoqCC4ciVVXi9WqxWbzVb/PePjj8n49FMSGRlYa2upLSigtkuX1J40E9Bnufm1pHOc1IBujI4dO6a6hKOybdu2tKs53egcQzweJxKJEI1GiW3YQOz55ynOzsYaj2OPxbDH45y4ZQun3H8/dru9rrVrsWCJxeDVV+suWZeVgdUKVivZAwdyYufO9a//jy++IHfVKgiFIBgEtxusVvIHDICuXX8oJBrFePVVElVVJBwOcq64gtjPf04sFiMWixGPx0l8/DH2P/wBWzCIY98+HJmZ5L/+Otann271rW19lptfOp7jzZs3H/Z20wW0SGtnGAbRaLQ+kCORCIZh4HQ6cTqdeD79FHttLbbi4roABrDZ4PLL64L1QIWFdZehG+iDru3S5YfH/VgfdGEhlkWLsG3YgK1HDxyHCdzE1q3EKyqIJRLEDINqn4+A24113Tqc556Lw+HA6XRit9vrW/AicigFtEgqjRkDb75J9IorCE2dSjgcJhqNYrfbcTqduN1u/H4/dvsB/6v27AlPPw15eVBZCe3bw803H37g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RMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWERExIQW0iIiICdmTdaCqqirGjRtHMBgkGo1yzz33UFBQkKzDi4iIpJWkBfSf/vQnLrzwQoYPH85XX33FnXfeySuvvJKsw4uIiKSVpAX08OHDcTqdAMTjcVwuV7IOLSIiknaaJaBfeOEFFi9efNBt06dP59xzz6W4uJhx48YxYcKE5ji0iIhIi2AxDMNI1sG2b9/OHXfcwd13302vXr0OuX/z5s14PJ5kldMkQqEQbrc71WW0aDrHzU/nODl0nptfOp7jmpoaOnfufMjtSbvEvWPHDsaMGcMf/vAHOnTocMTHdezYMVklNYlt27alXc3pRue4+ekcJ4fOc/NLx3O8efPmw96etICeNWsWkUiEadOmAeDz+Zg3b16yDi8iIpJWkhbQCmMREZHG00IlIiIiJqSAFhERMSEFtIiIiAkpoEVERExIAS0iImJCCmgRERETUkCLiIiYkAJaRETEhBTQIiIiJqSAFhERMSEFtIiIiAkpoEVERExIAS0iImJCCmgRERETUkCLiIiYkAJaRETEhBTQIiIiJqSAFhERMSEFtIiIiAkpoEVERExIAS0iImJCCmgRERETUkCLiIiYkAJaRETEhBTQIiIiJqSAFhERMSEFtIiIiAkpoEVEREzIYhiGkeoi9tu8eXOqSxAREUm6zp07H3KbqQJaRERE6ugSt4iIiAkpoEVERExIAX2cqqqq+O1vf8t1113HkCFD+PTTT1NdUou1Zs0a7rzzzlSX0aIkEgkmTpzIkCFDGDZsGN98802qS2qxtmzZwrBhw1JdRosUjUYZN24c1157LYMGDeKdd95JdUlNwp7qAtLdn/70Jy688EKGDx/OV199xZ133skrr7yS6rJanKlTp7Ju3To6duyY6lJalLfffptIJMKKFSv47LPPmDFjBvPmzUt1WS3OggULWLlyJRkZGakupUVauXIlWVlZzJw5k0AgwMCBA7n00ktTXdZxUwv6OA0fPpxrrrkGgHg8jsvlSnFFLVOnTp2YNGlSqstocTZv3szFF18MwPnnn89f/vKXFFfUMrVv3545c+akuowWq2/fvowZMwYAwzCw2WwprqhpqAV9FF544QUWL1580G3Tp0/n3HPPpbi4mHHjxjFhwoQUVdcyHOkc9+vXj48++ihFVbVcwWAQn89X/7vNZiMWi2G365+GpnT55Zeza9euVJfRYnm9XqDu83zbbbdx++23p7agJqL/C4/C4MGDGTx48CG3b9++nTvuuIO7776brl27pqCyluNI51iah8/no7q6uv73RCKhcJa0tHv3bm655RauvfZarrrqqlSX0yR0ifs47dixgzFjxjBr1ix69eqV6nJEjkqnTp0oKioC4LPPPuNnP/tZiisSOXolJSWMGDGCcePGMWjQoFSX02T0p/JxmjVrFpFIhGnTpgF1LRINspF00bt3b9avX88111yDYRhMnz491SWJHLUnn3ySyspK5s6dy9y5c4G6gXlutzvFlR0frSQmIiJiQrrELSIiYkIKaBERERNSQIuIiJiQAlpERMSEFNAiIiImpIAWSaL58+czfPhwrrvuOoYNG9ZkS2vu2rWLq6++usHbjseKFSuIRqN89NFHjB07tsHHT506lT179hzTsUKhEOPHj0eTTKQ1U0CLJMmOHTt49913+dOf/sTSpUuZMGFCWi0N+9RTT5FIJBr12M8++wy73c4JJ5xwTMdyu90UFBTw6quvHtPzRVoCLVQikiR+v5/vv/+eF198kcLCQjp27MiLL74I1C0XO3XqVACysrKYPn06X375JU8++SRWq5Xi4mKGDBnC0KFD2bRpE0888QSGYVBdXc2sWbNwOBw/euxNmzYxe/ZsbDYbp5xyCg8++CCvv/4677//PqFQiG+//ZaRI0fyy1/+kq1btzJ58mS8Xi+5ubm4XC46d+5McXExY8eO5Ve/+hXffPMNN9xwA2VlZVxyySWMHj36oOMtWbKEX//61wD84x//4L777iMajeJ2u5k9ezaPPPIIdrud77//nkgkQr9+/Vi7di27d+9m7ty5tG/fniuuuIIbbriB//qv/2qG/xoi5qcWtEiStGvXjnnz5vHJJ58wZMgQ+vbty9q1awG4//77eeCBB1iyZAmFhYUsXLgQgL179zJv3jyef/55nnnmGUpLS/n73//OzJkzWbJkCX369OGtt9760eMahsH999/PE088wdKlS2nXrl39lqjBYJCnnnqKefPmMX/+fAAeeOABZsyYwbPPPkv79u2BujXS8/PzmT17NgDhcJi5c+eybNkyli5desgxN23aVL9s6MMPP8yoUaNYsWIF119/PV9++SUAJ510EosWLeKMM85g165dLFiwgD59+vDuu+8C0KZNG8rLy6mqqjqu8y6SrtSCFkmSb775Bp/Px0MPPQTA559/zsiRI+nWrRs7d+5k8uTJQN3m86eddhoABQUFOJ1OAM466yy+/fZb2rVrx7Rp0/B4POzdu5dOnTr96HHLysrYt29f/Q4/oVCIHj16cOqpp9KhQwcAfvKTnxCJRADYt28fZ511FgCdO3dm9erVh7zmWWedVV/X4TbXSCQS9fd//fXXFBQUANTv0fvGG29w9tlnA5CZmckZZ5xR//P+OgDy8vIIBAL4/f4ffY8iLZECWiRJtm/fzooVK5g3bx5Op5PTTz+dzMxMbDYbp59+Og8//DAnnngimzdvpri4GIBt27YRj8eJRCLs2LGDU089lZtvvpk1a9bg8/kaNZAqOzubE044gblz5+L3+3nnnXfweDzs3r0bi8VyyONPOOEEduzYwU9/+lO2bNlSf7vFYqnvgz7c8w7kcrmIx+PYbDbOPPNMPv/8c3r06MHKlSupqKho1GsAVFZWkpOT0+DjRFoiBbRIkvTp04edO3cyaNAgPB4PhmFw99134/f7mTRpEuPHjycWi2GxWJg2bRr79u0jFosxcuRIAoEAN910Ezk5Ofznf/4nQ4cOJSMjg7y8PPbt2/ejx7Vardx7772MGjUKwzDwer088sgj7N69+7CPf+CBB5gwYQIejweHw0G7du0A6NKlC6NGjeKWW25p8L126tSJL774gnPPPZe7776biRMnMm/ePNxuNzNnzuSLL75o8DUqKyvJzMys3+tXpLXRZhkiJvXRRx/x3HPP1ff7JsuyZcu44ooryMnJYfbs2TgcDm699dajeo1PP/2UVatWcd999x1XHT6fjwEDBhzza4ikMw0SE5GD5ObmMmLECK699lr++te/MnTo0KN+jYKCAuLx+HHNg/7kk0+46qqrjun5Ii2BWtAiIiImpBa0iIiICSmgRURETEgBLSIiYkIKaBERERNSQIuIiJiQAlpERMSE/j+P3EspkLwySAAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Normalise the data\n", - "def normalise_data(arr1, arr2):\n", - " \"\"\"Normalise data to unit length\n", - " input: two array same length\n", - " output: normalised arrays\n", - " \"\"\"\n", - " for idx in range(len(arr1)):\n", - " norm = (arr1[idx]**2 + arr2[idx]**2)**(1 / 2)\n", - " arr1[idx] = arr1[idx] / norm\n", - " arr2[idx] = arr2[idx] / norm\n", - " return [arr1, arr2]\n", - "\n", - "\n", - "iris_setosa_normalised = normalise_data(iris_setosa_scaled[0], iris_setosa_scaled[1])\n", - "iris_versicolor_normalised = normalise_data(iris_versicolor_scaled[0], iris_versicolor_scaled[1])\n", - "# Function to plot the data\n", - "DataPlotter.plot_normalised_data(iris_setosa_normalised, iris_versicolor_normalised);\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# Quantum algorithm #\n", - "Now we can start with our quantum algorithm on the Quantum Inspire. We describe how to build the algorithm for the simplest case with only two data points, each with two features, that is $M=N=2$. For this algorithm we need 4 qubits:\n", - "* One qubit for the index register $\\ket{m}$\n", - "* One ancillary qubit\n", - "* One qubit to store the information of the two features of the data points \n", - "* One qubit to store the information of the classes of the data points\n", - "\n", - "From the data set described in previous section we pick the following data set $\\mathcal{D} = \\big\\{({\\bf x}_1,y_1), ({\\bf x}_2, y_2) \\big\\}$ where: \n", - "* ${\\bf x}_1 = (0.9193, 0.3937)$, $y_1 = -1$,\n", - "* ${\\bf x}_2 = (0.1411, 0.9899)$, $y_2 = 1$.\n", - "\n", - "We are interested in the label $\\tilde{y}$ for the data point ${\\bf \\tilde{x}} = (0.8670, 0.4984)$.\n", - "\n", - "\n", - "The amplitude encoding of these data points look like\n", - "\\begin{equation}\n", - " \\begin{split}\n", - " \\ket{\\psi_{\\bf\\tilde{x}}} & = 0.8670 \\ket{0} + 0.4984\\ket{1}, \\\\\n", - " \\ket{\\psi_{\\bf x_1}} & = 0.9193 \\ket{0} + 0.3937\\ket{1},\\\\\n", - " \\ket{\\psi_{\\bf x_2}} & = 0.1411 \\ket{0} + 0.9899\\ket{1}.\n", - " \\end{split}\n", - "\\end{equation}\n", - "\n", - "Before we can run the actual algorithm we need to bring the system in the desired [initial state (equation 2)](#state) which can be obtain by applying the following combination of gates starting on $\\ket{0000}$. \n", - "\n", - "\n", - "\n", - "* **Part A:** In this part the index register is initialized and the ancilla qubit is brought in the desired state. For this we use the plain QASM language of the Quantum Inspire. Part A consists of two Hadamard gates:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def part_a():\n", - " qasm_a = \"\"\"version 1.0\n", - "qubits 4\n", - "prep_z q[0:3]\n", - ".part_a\n", - "H q[0:1] #execute Hadamard gate on qubit 0, 1\n", - "\"\"\"\n", - " return qasm_a" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " After this step the system is in the state\n", - "$$\\ket{\\mathcal{D}_A} = \\frac{1}{2}\\Big(\\ket{0}+\\ket{1}\\Big)\\Big(\\ket{0}+\\ket{1}\\Big)\\ket{0}\\ket{0} $$ " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "* **Part B:** In this part we encode the unlabeled data point $\\tilde{x}$ by making use of a controlled rotation. We entangle the third qubit with the ancillary qubit. The angle $\\theta$ of the rotation should be chosen such that $\\tilde{x}=R_y(\\theta)\\ket{0}$. By the definition of $R_y$ we have\n", - "$$ R_y(\\theta)\\ket{0} = \\cos\\left(\\frac{\\theta}{2}\\right)\\ket{0} + \\sin\\left(\\frac{\\theta}{2}\\right)\\ket{1}.$$ \n", - "Therefore, the angle needed to rotate to the state $\\psi=a\\ket{0} + b\\ket{1}$ is given by $\\theta = 2\\cos^{-1}(a)\\cdot sign(b)$.\n", - "Quantum Inspire does not directly support controlled-$R_y$ gates, however we can construct it from other gates as shown in the figure below. In these pictures $k$ stand for the angle used in the $R_y$ rotation. " - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def part_b(angle):\n", - " half_angle = angle / 2\n", - " qasm_b = \"\"\".part_b # encode test value x^tilde\n", - "CNOT q[1], q[2]\n", - "Ry q[2], -{0}\n", - "CNOT q[1], q[2]\n", - "Ry q[2], {0}\n", - "X q[1]\n", - "\"\"\".format(half_angle)\n", - " return qasm_b" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After this step the system is in the state\n", - "$$\\ket{\\mathcal{D}_B} = \\frac{1}{2} \\Big(\\ket{0}+\\ket{1}\\Big)\\Big(\\ket{0}\\ket{\\tilde{{x}}}+\\ket{1}\\ket{0}\\Big)\\ket{0}$$\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* **Part C:** In this part we encode the first data point $x_1$. The rotation angle $\\theta$ is such that $\\ket{x_1} = R_y(\\theta)\\ket{0}$. Now a double controlled-$R_y$ rotation is needed, and similar to Part B, we construct it from other gates as shown in the figure below. \n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def part_c(angle):\n", - " quarter_angle = angle / 4\n", - " qasm_c = \"\"\".part_c # encode training x^0 value\n", - "toffoli q[0],q[1],q[2]\n", - "CNOT q[0],q[2]\n", - "Ry q[2], {0}\n", - "CNOT q[0],q[2]\n", - "Ry q[2], -{0}\n", - "toffoli q[0],q[1],q[2]\n", - "CNOT q[0],q[2]\n", - "Ry q[2], -{0}\n", - "CNOT q[0],q[2]\n", - "Ry q[2], {0}\n", - "X q[0]\n", - "\"\"\".format(quarter_angle)\n", - " return qasm_c" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After this step the system is in the state\n", - "$$\\ket{\\mathcal{D}_C} = \\frac{1}{2}\\Bigg(\\ket{0}\\Big(\\ket{0}\\ket{\\tilde{{x}}} + \\ket{1}\\ket{{x_1}}\\Big) + \\ket{1}\\Big(\\ket{0}\\ket{\\tilde{{x}}} + \\ket{1}\\ket{0}\\Big)\\Bigg) \\ket{0}$$" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* **Part D:** This part is almost an exact copy of part C, however now with $\\theta$ chosen such that $\\ket{{x}_2} = R_y(\\theta)\\ket{0}$. \n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def part_d(angle):\n", - " quarter_angle = angle / 4\n", - " qasm_d = \"\"\".part_d # encode training x^1 value\n", - "toffoli q[0],q[1],q[2]\n", - "CNOT q[0],q[2]\n", - "Ry q[2], {0}\n", - "CNOT q[0],q[2]\n", - "Ry q[2], -{0}\n", - "toffoli q[0],q[1],q[2]\n", - "CNOT q[0],q[2]\n", - "Ry q[2], -{0}\n", - "CNOT q[0],q[2]\n", - "Ry q[2], {0}\n", - "\"\"\".format(quarter_angle)\n", - " return qasm_d" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After this step the system is in the state\n", - "$$\\ket{\\mathcal{D}_D} = \\frac{1}{2}\\Bigg(\\ket{0}\\Big(\\ket{0}\\ket{\\tilde{{x}}} + \\ket{1}\\ket{{x_1}}\\Big) + \\ket{1}\\Big(\\ket{0}\\ket{\\tilde{{x}}} + \\ket{1}\\ket{{x}_2}\\Big)\\Bigg) \\ket{0}$$" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* **Part E:** The last step is to label the last qubit with the correct class, this can be done using a simple CNOT gate between the first and last qubit to obtain the desired initial state\n", - "$$\\ket{\\mathcal{D}_E} = \\frac{1}{2}\\ket{0}\\Big(\\ket{0}\\ket{\\tilde{{x}}} + \\ket{1}\\ket{{x_1}}\\Big)\\ket{0} + \\ket{1}\\Big(\\ket{0}\\ket{\\tilde{{x}}} + \\ket{1}\\ket{{x}_2}\\Big)\\ket{1}.\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def part_e():\n", - " qasm_e = \"\"\".part_e # encode the labels\n", - "CNOT q[0], q[3]\n", - "\"\"\"\n", - " return qasm_e" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The actual algorithm\n", - "Once the system is in this initial state, the algorithm itself only consists of one Hadamard gate and two measurements. If the first measurement gives the result $\\ket{1}$, we have to abort the algorithm and start over again. However, these results can also easily be filtered out in a post-proecessing step. " - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def part_f():\n", - " qasm_f = \"\"\"\n", - ".part_f\n", - "H q[1]\n", - "\"\"\"\n", - " return qasm_f" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The circuit for the whole algorithm now looks like: \n", - "\n", - "We can send our QASM code to the Quantum Inspire with the following data points\n", - "\n", - "\n", - "\\begin{equation}\n", - " \\begin{split}\n", - " \\ket{\\psi_{\\tilde{x}}} & = 0.8670 \\ket{0} + 0.4984\\ket{1}, \\\\\n", - " \\ket{\\psi_{x_1}} & = 0.9193 \\ket{0} + 0.3937\\ket{1},\\\\\n", - " \\ket{\\psi_{x_2}} & = 0.1411 \\ket{0} + 0.9899\\ket{1}.\n", - " \\end{split}\n", - "\\end{equation}\n" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "OrderedDict([('9', 0.3988584), ('4', 0.2768809), ('0', 0.1270332), ('13', 0.099428), ('2', 0.0658663), ('6', 0.0302195), ('15', 0.0013716), ('11', 0.0003419)])\n" - ] - } - ], - "source": [ - "import os\n", - "from coreapi.auth import BasicAuthentication\n", - "from quantuminspire.credentials import get_authentication\n", - "from quantuminspire.api import QuantumInspireAPI\n", - "from math import acos\n", - "from math import pi\n", - "\n", - "QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/')\n", - "\n", - "## input data points:\n", - "angle_x_tilde = 2 * acos(0.8670)\n", - "angle_x0 = 2 * acos(0.1411)\n", - "angle_x1 = 2 * acos(0.9193)\n", - "\n", - "\n", - "authentication = get_authentication()\n", - "qi = QuantumInspireAPI(QI_URL, authentication)\n", - "\n", - "## Build final QASM\n", - "final_qasm = part_a() + part_b(angle_x_tilde) + part_c(angle_x0) + part_d(angle_x1) + part_e() + part_f()\n", - "\n", - "backend_type = qi.get_backend_type_by_name('QX single-node simulator')\n", - "result = qi.execute_qasm(final_qasm, backend_type=backend_type, number_of_shots=1, full_state_projection=True)\n", - "\n", - "print(result['histogram'])\n" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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lixR06ZNNXPmTO3fv9/pvgAAwHG1es06JydHX331lfr3768///nPmjlzptN9AQCA42oV1t9++60WLFgQXm6U16wBADCnVpfBJ0yYoMTERI0bN06tWrXShAkTnO4LAAAcV6uZdWVlpUaMGCFJ6tSpk/7617862hQAAPhOjWG9c+dOSdK5556rd955Rz179tTGjRvVpk0bI80BAIDvCespU6aEv164cKEKCgoUCoXk8XgcbwwAABxTY1i/9NJL4a/37dun3bt3q02bNkpISHC8MQAAcEytbjB75513lJmZqblz5yojI0PLli1zui8AAHBcrW4we+GFF7R06VLFxcXJ5/Pp9ttv16BBg5zuDQAAqJYza4/Ho7i4OEmS1+tV48aNHW0KAAB8p1Yz67Zt2yo/P189e/bUunXrlJiY6HRfAADguFrNrB999FG1bdtWq1evVtu2bfXb3/7W6b4AAMBxtZpZ33XXXZo3b57TvQAAgGrUKqzPOeccFRUVqX379oqKOjYZb9++vaONAQCAY743rH0+n3bv3q0XX3wxvM3j8WjBggWONgYAAI6pMaxffvllzZs3T9HR0br//vuVlpZmqi8AAHBcjTeYvfnmm1q+fLkKCwuZSQMAcJbUGNaxsbGKjY1VQkKCjhw5YqonAABwklq9dUuSQqGQk30AAIAzqPE163/+85/KyspSKBQKf33CzJkzHW8OAAB8T1g/9dRT4a8zMzOd7gUAAFSjxrD++c9/bqoPAABwBrV+zRoAAJwdhDUAAJYjrAEAsJwjYR0MBjVlyhRlZGRo5MiR2rVrV7XfM2bMGBUUFDjRAgAAruFIWBcVFcnv96uwsFBZWVnKz88/7Xueeuop7d+/34nyAAC4iiNhXVpaqtTUVElScnKyysrKquxfvny5PB5P+HsAAMCZORLWPp9PXq83/Dg6OlqBQECStG3bNr355pu6//77nSgNAIDreEIOrCOal5enbt266Ve/+pUkKS0tTcXFxZKkGTNmaO3atWrSpIn+/e9/q1GjRpo0adJpn+hVWlqqZs2aRbq173X48GE1adKEOpbVcFsdN42lrnW6dOkc4W6kzZvLf/SfrQ8/M5tquK2OqbFU5+DBg+rRo0f1O0MOWL58eWjChAmhUCgU+vjjj0OjR4+u9vueeeaZ0MKFC6vdt27dOida+16bN2+mjoU13FbHTWOp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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "from collections import OrderedDict\n", - "\n", - "\n", - "def bar_plot(result_data):\n", - " res = [get_bin(el, 4) for el in range(16)]\n", - " prob = [0] * 16\n", - "\n", - " for key, value in result_data['histogram'].items(): \n", - " prob[int(key)] = value\n", - "\n", - " # Set color=light grey when 2nd qubit = 1\n", - " # Set color=blue when 2nd qubit = 0, and last qubit = 1\n", - " # Set color=red when 2nd qubit = 0, and last qubit = 0\n", - " color_list = [\n", - " 'red', 'red', (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1),\n", - " 'red', 'red', (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1),\n", - " 'blue', 'blue', (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1),\n", - " 'blue', 'blue', (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1)\n", - " ]\n", - " plt.bar(res, prob, color=color_list)\n", - " plt.ylabel('Probability')\n", - " plt.title('Results')\n", - " plt.ylim(0, 1)\n", - " plt.xticks(rotation='vertical')\n", - " plt.show()\n", - " return prob\n", - "\n", - "\n", - "prob = bar_plot(result)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We only consider the events where the second qubit equals 0, that is, we only consider the events in the set $$\\{0000, 0001, 0100, 0101, 1000, 1001, 1100, 1101\\}$$\n", - "\n", - "The label $\\tilde{y}$ is now given by\n", - "\n", - "\\begin{equation}\n", - "\\tilde{y} = \\left\\{\n", - " \\begin{array}{lr}\n", - " -1 & : \\#\\{0000, 0001, 0100, 0101\\} > \\#\\{1000, 1001, 1100, 1101\\}\\\\\n", - " +1 & : \\#\\{1000, 1001, 1100, 1101\\} > \\#\\{0000, 0001, 0100, 0101\\}\n", - " \\end{array}\n", - "\\right. \n", - "\\end{equation}" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The sum of the events with label 0 is: 0.4039141\n", - "The sum of the events with label 1 is: 0.4982864\n", - "The label for y_tilde is: 1 because sum_label0 < sum_label1\n" - ] - } - ], - "source": [ - "def summarize_results(prob, display=1):\n", - " sum_label0 = prob[0] + prob[1] + prob[4] + prob[5] \n", - " sum_label1 = prob[8] + prob[9] + prob[12] + prob[13]\n", - "\n", - " def y_tilde():\n", - " if sum_label0 > sum_label1:\n", - " return 0, \">\"\n", - " elif sum_label0 < sum_label1:\n", - " return 1, \"<\"\n", - " else:\n", - " return \"undefined\", \"=\"\n", - " y_tilde_res, sign = y_tilde()\n", - " if display:\n", - " print(\"The sum of the events with label 0 is: {}\".format(sum_label0))\n", - " print(\"The sum of the events with label 1 is: {}\".format(sum_label1))\n", - " print(\"The label for y_tilde is: {} because sum_label0 {} sum_label1\".format(y_tilde_res, sign))\n", - " return y_tilde_res\n", - "\n", - "\n", - "summarize_results(prob);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following code will randomly pick two training data points and a random test point for the algorithm. We can compare the prediction for the label by the Quantum Inspire with the true label. " - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Data point [-0.9804333429271337, -0.19685136544287737] from label 0\n", - "Data point [0.9999994003027372, -0.0010951685559561092] from label 1\n", - "Test point [0.2554805690275516, 0.9668141904468295] from label 0 \n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6cAAAF8CAYAAAAzes21AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAABkb0lEQVR4nO3deVxU9f7H8dcsDMsMoIBLZWqZptZ1Ics0w8osl8yya1pm+Stt1SytzCU1cyHNrDRttUXNUK+VpVnZxlUsi66VxbU0c8kNBYQZlmGY+f3BdYoUN5g5DLyfPXzAnDPzOZ85TsL7fM/5HpPP5/MhIiIiIiIiYiCz0Q2IiIiIiIiIKJyKiIiIiIiI4RRORURERERExHAKpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGM5qdAMiIiIiIkY499xzadasGWazGZPJREFBAQ6Hg4kTJ/KPf/wjINtbv349YWFh3Hfffbz55puVvg2RUKZwKiIiIiI11htvvEFcXJz/8auvvsrkyZNJSUkJ2DYPHTrEjz/+GLD6IqFKp/WKiIiIiAAej4c9e/YQGxvrXzZv3jyuv/56evfuzb333su+ffsA+Pjjj7n++uvp06cPffv25ZtvvgFg4MCBrF692v/6vz8GGD16NIWFhfTu3ZuSkhKee+45evXqRZ8+fbjjjjvYv39/EN6tSNWjkVMRERERqbFuu+02TCYTWVlZhIeHc/nllzNt2jQA3n33XX755ReWLl2K1WolJSWFcePG8fLLLzN9+nSeeuop2rRpw9q1a/n666+58MILT2ib06ZNo1evXrz33nvs2bOHN954g/Xr12Oz2Zg/fz4//PADV155ZSDftkiVpHAqIiIiIjXW4dN6f/75Z4YMGULbtm2Jj48H4PPPP+fHH3/khhtuAMDr9VJQUABAz549GTp0KJ07d+aSSy5hyJAhp7T9evXq0bx5c66//nqSkpJISkqiQ4cOlfPmREKMwqmIiIiI1HgtW7Zk9OjRjBs3jtatW9OgQQO8Xi+DBw/m5ptvBsDtdnPo0CEAHnzwQf75z3+ydu1ali9fzksvvcTy5csB8Pl8/rrFxcXH3K7ZbGbhwoX8+OOPrF+/nqlTp9K+fXvGjRsXoHcqUnXpmlMREREREeCaa66hTZs2TJ06FYBOnTqxbNkynE4nAM8++yyPPPIIHo+HK664gvz8fG666SYmTJjA1q1b8Xg8xMXFsWnTJgB27NjB5s2bj9iO1WqlpKQEn8/Hf//7X6655hqaNGnCXXfdxaBBg476GpGaQCOnIiIiIiL/89hjj3Httdfy73//m759+7Jv3z5uvPFGTCYTp512GsnJyVitVsaMGcNDDz2E1WrFZDIxdepUbDYb99xzD48++ihffvklZ599Nu3atTtiG3Xq1KFly5Z0796dxYsX0717d2644QaioqKIiIjQqKnUWCbfX887EBERERERETGATusVERERERERwymcioiIiIiIiOEUTkVERKq577//noEDBx6x/LPPPuOGG26gX79+LFmyxIDORERE/qQJkURERKqxl19+mRUrVhAZGVlmeXFxMdOmTWPZsmVERkZy0003ccUVV5CQkGBQpyIiUtNp5FRERKQaa9iwIbNnzz5i+datW2nYsCGxsbHYbDYuuOACvvnmGwM6FBERKVWlRk7T09ONbkFERKqZCy64wOgWDHX11Veza9euI5Y7nU6io6P9j+12u/9ejn+ln80iIlLZyvvZXKXCKVT9XyIyMjJo0aKF0W2ENO3DitM+rDjtw4oLhX2oYFU+h8OBy+XyP3a5XGXC6l8F82dzID9Xgf7Mhmrv2i/Brx3o+qFaO9D11Xvwax/NsX4267ReERGRGqhJkyZs376dnJwc3G433377LW3btjW6LRERqcGq3MipiIiIBM77779Pfn4+/fr149FHH+WOO+7A5/Nxww03UK9ePaPbExGRGkzhVEREpJpr0KCB/1YxvXr18i+/4ooruOKKK4xqS0REpAyd1isiIiIiIiKGUzgVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIidh165d3HjjjUcsT01NJSUl5YRqvPPOO9x6660MHDiQ/v37s3bt2mM+PyUlheLi4lPq91h8Ph+PPvooLpeLgQMHsnXr1nKfe8UVV1BUVHRCdY/23I0bN9K3b1/69+/PnDlzADhw4ACTJk069TcgIiIi1Ypm6xURqQRJSUkn9Ly8vDzmzp3LypUrsdls7Nu3j759+/LFF19gNh/9eOGLL77IddddV4ndlvrwww8577zzsNvtlV777yZMmMDs2bM588wzufPOO/n5559p2bIldrudDRs2cNFFFwW8BxEREanaFE5FpPpLTYW0NOjYEU4wRJ6IgQMH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4v333+df//oXXq+X+++/nw4dOgDg8/lYsWIF77zzTpkav/zyC8nJyZSUlJCdnc3EiRNJTEzE7Xbz4IMPsmfPHs4991wmTpyI0+lk7NixZGdnAzBu3DjOPffco+5ft9vN888/zyOPPFJm+TXXXMPs2bO56KKLKvx3KCIiIlWHwqmIVGupqXD77VBSAq+8AvPnV25Aveaaa+jatSvLly8HYMeOHeTk5PDKK69w8OBBfv/99yNe889//pMVK1Zwzz33sHz5cvr378+WLVtYtWoVb731FgD/93//5w+1F198MYMGDeKLL76gVq1aTJ8+nS1btpCfn090dDQAHo+HJ554gqVLlxIfH8/LL7/Mnj17eOyxx1i8eDH16tXjjTfeYN68eVx22WUAZGdnM3v2bN555x0cDgdTp04lJSWFqKgoYmJimDdvXpm+f//9dxwOB2FhYWWWb9myhVGjRnHuuefy/vvvs3z5chITEyksLOShhx7ijDPOYPjw4Xz22Wd89913XHzxxdx88838/vvvjB49msWLFx91315wwQVHXX7OOeeQnp5+Yn9BIiIiEjIUTkWkWktLKw2m8fFw8GDp48oMp2eddVaZx02bNqVfv36MGDECj8fDwIEDj3hN7969GTRoELfffjsbNmxg3LhxfPzxx+zevZtBgwYBcOjQIbZv315mG0lJSfz+++/ce++9WK1W7rnnHn/N7OxsYmJiiI+PB2DIkCFkZWXhcDioV68eABdeeCFPP/20P5zu3LmTc845B4fD4V+/du1aWrdufcT7OryNhISEI5bXrVuXuXPnEhERgcvl8tc7/fTTOeOMMwBo27Yt27Zt45dffuGrr77iww8/9L/Pk2WxWLBarXi93qCcXiwiIiLBoZ/qIlKtdewIFktpMLVYSh9XJpPJVObx5s2bcblcvPTSSyQnJ/PEE08c8Zq4uDiaNGnC3Llz6dq1K1arlbPPPptzzjmHN998kwULFtCnTx//6a6Ht/H1119Tt25d5s+fzz333MPTTz/trxkfH09ubi45OTkATJ48mZ07d+J0Otm/fz8AGzZsoHHjxv7XNGjQgK1bt5Kfn+9ffziUHi30Hd7G302ZMoX777+fJ598kmbNmuHz+QDYu3evf9vfffcdTZs25eyzz2bQoEEsWLCAZ555hmuvvfb4O/lvfD4fVqtVwVRERKSaCdrIaXFxMWPGjOGPP/7A7XZzzz330KVLl2BtXkRqqKSk0lN5A3HN6dE0btyY559/ng8//NB/zebR3HjjjQwZMoTVq1cD0Lx5czp06MBNN92E2+2mVatW/hHPw5o3b86IESNYvHgxHo+H++67z7/ObDYzYcIE7rrrLsxmMy1btqRVq1ZMnjyZYcOGYTKZiI2NZdq0afz6669AaUgeNmwYt956K2azmYYNG/LQQw+xcuXKo/bcqFEjsrKy8Hg8WK1//vi49tprGT58ODExMdSvX99/PWmtWrWYPHky+/bto23btnTu3JlWrVoxduxYlixZgtPpZOjQoSe9jzdv3kybNm1O+nUiIiJSxfmCZNmyZb7Jkyf7fD6fLzs729e5c+cjnvPtt98Gq51T9vPPPxvdQsjTPqw47cOK0z48NS+88ILv448/9vl8xu3DJ5980vfNN9+c0HND4edKVRfsfRjIz1WgP7Oh2rv2S/BrB7p+qNYOdH31HvzaR3OsnytBOyeqW7duDB8+/HAgxmKxBGvTIiJSSW677TZWr16N1+s1ZPuZmZk4nU7atWtnyPZFREQkcIJ2Wq/dbgfA6XRy//3388ADDwRr0yIiUkkiIiKYOXOmYduvU6cOkyZNMmz7IiIiEjhBna13z5493Hfffdx888306tXrqM/JyMgIZksnrbCwsMr3WNVpH1ac9mHFaR9WnPahiIiIVKaghdMDBw5w++23M378eP8N3Y+mRYsWwWrplGRkZFT5Hqs67cOK0z6sOO3DiguFfaj7oYqIiISOoF1z+sILL5Cbm8vcuXMZOHAgAwcOpLCwMFibFxERERERkSosaCOn48aNY9y4ccHanIiIiIiIiISQoF5zKiISbHlFeaT8lMKvB3+laXxT+p3Xj+jw6FOul5yczE8//URmZiaFhYWceeaZ1K5dm+eee+6EXr9582Zyc3O58MILT7mHoUOHMmfOnHLXp6Sk0KdPH8LCwk55GyIiIiLBpnAqItXW2h1r6bGoB16fF1exC3uYnREfjWDVgFXEE39KNR999FEAli9fzm+//cZDDz10Uq//+OOPSUhIqFA4PVYwBXjxxRe57rrrTrm+iIiIiBEUTkWkWsoryqPHoh7kufP8y1zFLgB6LOrBZz0/q7RtFRcXM2HCBLZv347X6+WBBx6gffv2zJo1i6+//hqPx8NVV11F7969eeeddwgLC+O8886jVatWAOzatYvhw4dTp04d9u3bR1JSEg8++CC7du1izJgxlJSUYDKZGDduHM2bN+eSSy5h3bp1DBw4kObNm/Prr7/idDp59tlnSUtLIzMzkwcffJC5c+dW2nsUERERCbSgTYgkIhJMKT+l4PV5j7rO6/OyeufqStvW0qVLqV27NosWLWLu3Ln++3C+//77PPXUU7z11lvExMRQr149rr/+egYNGuQPpof98ccfJCcns2zZMr766it++uknpk+fzq233sqiRYsYO3YsY8aMOWLbrVq14vXXX+eSSy5h5cqV9O3blzp16jBr1qxKe38iIiIiwaCRUxGpln49+Kt/pPTvXMUutju3V9q2fvnlF9LT0/nhhx8A8Hg8ZGVlMWPGDGbOnMmBAwe49NJLj1mjefPm1KpVCygNnNu2bWPr1q3+039btGjB3r17j3hdy5YtAahfvz4HDhyotPckIiIiEmwKpyJSLTWNb4o9zH7UgGoPs9PI0ajStnX22WdTv3597r77bgoLC5k3bx4Oh4PVq1fz9NNPA9CjRw969uyJyWTC6z1yRHfr1q0UFBRgs9n44YcfuOGGG2jSpAnffvstXbp0ISMjg4SEhBPqp7xtiIiIiFRlOq1XRKqlfuf1w2w6+j9xZpOZbmd2q7Rt9e/fn99++41bbrmF/v37c8YZZ2Cz2YiNjeXGG2/k1ltv5ZJLLuH000/n/PPPZ9GiRXz11VdlaoSFhTF8+HD69u1Lly5daN68OY888ggLFy5kwIABTJw4kSlTppxQP+3atePOO+/E5/NV2nsUERERCTSNnIpItRQdHs2qAauOmK3XbDKzasAq7C57her36dPH/73NZmP69OlHPGfo0KEMHTq0zLLLLruMyy677IjnJiQk8NJLL5VZ1qBBA1577bUjnrtu3ToAFixY4F920003+b9/8sknT+xNiIiIiFQhCqciUm11atiJ3SN3k7IphS1ZWzgn7hz6nd8Ph81BRkaG0e2JiIiIyF8onIpIteawObgj8Q6j2zimBg0asGTJEqPbEBERETGUrjkVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiIiIiJiOIVTEaneivNgyyvwn1GlX4vzKlTu66+/pkOHDgwcONB/X9NVq1Yd8zW7d+/ms88+q9B2jyYjI4M5c+aUu76oqIilS5dW+nZFREREAkGz9YpI9bV/LXzRA3xeKHGBxQ7fjYDLVgHxp1z24osvZtasWQC4XC4GDhzIWWedRYsWLY76/K+++orffvuNK6644pS3eTQtWrQod5sAmZmZLF26lL59+1bqdkVEREQCQeFURKqn4rzSYOr5y0hpiav06xc9MLWsnJFMu91Ov379WL16Nc2aNWP8+PHs3buX/fv3c8UVV3D//ffz0ksvUVhYSNu2bYmOjmbOnDn4fD5cLhczZ87krLPO8tdbvnw5a9asweVykZ2dzX333cfVV1/NunXreOaZZwgPD6dWrVpMnTqVjIwM3n77bWbNmsVVV11FYmIi27ZtIz4+ntmzZ/PCCy+wZcsW5syZQ4cOHXjyySexWq1ERkby7LPP4nA4KmUfiIiIiFQGndYrItXT9pTSEdOj8XmJyVldaZuKj48nOzubPXv20KZNG1599VWWLVvG22+/jcVi4c477+Saa66hS5cu/Prrr8yYMYMFCxZw1VVXsXr1kX0UFBTw2muvMX/+fJKTkykuLuaxxx5jzpw5LFy4kAsvvJB58+aVec3OnTsZPnw4KSkpZGVl8eOPP3L33XdzzjnnMHToUNasWUP37t1ZuHAhN910E7m5uZX2/kVEREQqg0ZORaR6yvv1z5HSvytxYSvaXmmb2r17N/Xr16dWrVr8+OOPfPXVVzgcDtxu9xHPrVevHlOmTCEqKop9+/aRmJh4xHMuvPBCzGYzCQkJxMTEcODAARwOB/Xq1fOvf/rpp7nsssv8r6lduzannXYaAKeddhpFRUVlat5999288MIL3HbbbdSrV49WrVpV2vsXERERqQwaORWR6im6aek1pkdjseMOb1Qpm3E6nSxdupRu3bqxfPlyoqOjmTlzJrfffjuFhYX4fD7MZjNeb+ko7mOPPcbUqVNJTk6mbt26+Hy+I2r+9NNPABw4cACn00ndunVxOp3s378fgA0bNtC4ceMyrzGZTEfU+et2V6xYwfXXX8+CBQto2rQpS5YsqZT3LyIiIlJZNHIqItVTo36lkx8djclMbq1unH6Kpb/66isGDhyI2WympKSEYcOGcfbZZ1NSUsLIkSPZuHEjNpuNRo0asX//fpo1a8a8efM477zzuPbaaxkwYACRkZEkJCT4A+dfHThwgNtuu428vDwmTJiAxWJh8uTJDBs2DJPJRGxsLNOmTePXX389Zp/x8fEUFxczY8YMrrrqKsaNG0dkZCRms5lJkyad4rsXERERCQyFUxGpnsKiS2fl/ftsvSYzXLYK38FyRlWPo3379qxfv/6o65o2bcqKFSuOWF6vXj0++ugjAHr27HncbVx44YU89NBDZZZ17NiRjh07HtFL+/btAVi3bp1/+eGZhAHee+89//caLRUREZGqTOFURKqvup3g+t2wIwXytkD0OdCwH4Q54GCG0d2JiIiIyF8onIpI9RbmgCZ3GN3FCevTp4/RLYiIiIgYQhMiiYiIiIiIiOEUTkVERKopr9fL+PHj6devHwMHDmT79rK3UJo/fz59+vThhhtu4JNPPjGoSxERkVI6rVdERKSaWrNmDW63m5SUFDZu3EhycjLz5s0DIDc3lzfffJOPP/6YgoICrrvuOrp27WpwxyIiUpNp5FRERKSaSk9P59JLLwWgTZs2bNq0yb8uMjKS008/nYKCAgoKCo56r1wREZFg0sipiIhINeV0OnE4HP7HFosFj8eD1Vr64/+0006jZ8+elJSUcNddd5VbJyMjeLNbFxYWBmx7gawd6PqhWjvQ9UO1dqDrh2rtQNdX78GvfbIUTkVERKoph8OBy+XyP/Z6vf5gmpqayv79+/n0008BuOOOO0hMTKRVq1ZH1GnRokVwGqY0CAdqe4GsHej6oVo70PVDtXag64dq7UDXV+/Br3006enp5a7Tab0iIiLVVGJiIqmpqQBs3LiRZs2a+dfFxsYSERGBzWYjPDyc6OhocnNzjWpVREREI6ciIiLVVdeuXVm3bh39+/fH5/MxdepUXnvtNRo2bEiXLl1IS0vjxhtvxGw2k5iYyCWXXGJ0yyIiUoMpnIqIiFRTZrOZSZMmlVnWpEkT//f3338/999/f7DbEhEROSqd1isiIiIiIiKGUzgVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiIiIiJiOIVTERERERERMZzCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDKZyKiIiIiIiI4RRORURERERExHAKpyIiIiIiImI4hVMRERERERExnMKpiIiI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3ODjrbe8tG/vxev988/atV5iY73ExvrIyrLyzTdhXHyxlf/8J4qcHCu1alk4eNDEf/4DV19d3vsI8o4UERERkRopIOF06dKlvPHGG2WWTZ06lR49evD1118f87UZGRmBaKnSFBYWVvkeq7rqvA/r1IHevUu/P5m36PP58Hq9Zb7+/fu/PvfnnxsRGekCSsOqxwOrV2cTH38Is9mMyWTCbDbToEEUBw6cwb59ZsxmaNBgNzt3FtCgQSRu9+ns3evFbIYzzthNRkZBhd9HKKnOn8Ng0T4UERGRyhSQcNq3b1/69u17Sq+t6qf8ZmRkVPkeqzrtw4o799xDfPJJrP+x3Q69e8eRmFj2ef/4BzRu/NdR0MYAtGgBjRodubwm0eew4kJhH6anpxvdgoiIiJwgzdYrEoKefXY3jz0We9xrTqH8a0R17aiIiIiIVCUKpyIhatkyozsQEREREak8QQ+n7du3p3379sHerIiIiIiIiFRhwb/pooiIiIiIiMjfKJyKiIiIiIiI4RRORURERERExHAKpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGE7hVERERERERAyncCoiIiIiIiKGUzgVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiIiIiJiOIVTERERERERMZzCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDWY1uQERERALD6/UyceJENm/ejM1mY/LkyTRq1Mi//vXXX2flypUAdO7cmaFDhxrVqoiIiEZORUREqqs1a9bgdrtJSUlh5MiRJCcn+9ft3LmTFStW8Pbbb7NkyRLWrl3Lf//7XwO7FRGRmk4jpyIiItVUeno6l156KQBt2rRh06ZN/nX169fnlVdewWKxAODxeAgPDzekTxEREVA4FRERqbacTicOh8P/2GKx4PF4sFqthIWFERcXh8/nY/r06bRs2ZKzzjrrqHUyMjKC1TKFhYUB214gawe6fqjWDnT9UK0d6PqhWjvQ9dV78GufLIVTERGRasrhcOByufyPvV4vVuufP/qLiooYM2YMdrudCRMmlFunRYsWAe3zrzIyMgK2vUDWDnT9UK0d6PqhWrsi9U2mytm+z3dqr6uq+8Xo2oGuH6q1jyY9Pb3cdbrmVEREpJpKTEwkNTUVgI0bN9KsWTP/Op/Px7333su5557LpEmT/Kf3ioiIGEUjpyIiItVU165dWbduHf3798fn8zF16lRee+01GjZsiNfrZcOGDbjdbv79738DMGLECNq2bWtw11XP3r17T+h5Bw4cOO5z69evXxktiYhUSwqnIiIi1ZTZbGbSpEllljVp0sT//Y8//hjslkRERMql03pFRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIiIiIiIgYTuFUREREREREDKfZekVERESkyjmRW/jo9j0i1YtGTkVERERERMRwCqciIiIiIiJiOIVTERERERERMZzCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDKZyKiIiIiIiI4axGNyAiIiIiIlJVmUwn+swWx32Gz1ehVk7a3r17j/ucAwcOnNDz6tevXxktHZNGTkVERERERMRwQRs5zcvL4+GHH8bpdFJcXMyjjz5K27Ztg7V5ERERERERqcKCFk5fe+01Lr74YgYNGsRvv/3GyJEjeeedd4K1eREREREREanCghZOBw0ahM1mA6CkpITw8PBgbVpERERERESquICE06VLl/LGG2+UWTZ16lRatWpFZmYmDz/8MGPGjDnqazMyMgLRUqUpLCys8j1WddqHFad9WHHahxWnfSgiIiKVKSDhtG/fvvTt2/eI5Zs3b2bEiBE88sgjXHTRRUd9bYsWx5/lykgZGRlVvseqTvuw4rQPK077sOJCYR+mp6cb3YKIiIicoKCd1rtlyxaGDx/OM888Q/PmzYO1WREREREREQkBQQunM2fOxO12M2XKFAAcDgfz5s0L1uZFRERERESkCgtaOFUQFRERERERkfKYjW5AREREREREROFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDBW1CJBEREREJMpPpuE854bsV+3wVakVE5HgUTkVEREREJKD27t17Qs87cODAcZ9bv379ymhJqiCd1isiIiIiIiKG08ipiIiIBEdlnWKq00tFRKoljZyKiIiIiIiI4RRORURERERExHAKpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGE7hVERERERERAyncCoiIiIiIiKGUzgVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiIiIiJiOIVTERERERERMZzCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhFE5FRESqKa/Xy/jx4+nXrx8DBw5k+/btZdYvWbKEPn36cOONN/L5558b1KWIiEgpq9ENiIiISGCsWbMGt9tNSkoKGzduJDk5mXnz5gGQmZnJggUL+Ne//kVRURE333wzl1xyCTabzeCuRUSkptLIqYiISDWVnp7OpZdeCkCbNm3YtGmTf90PP/xA27ZtsdlsREdH07BhQ/773/8a1aqIiAgmn8/nM7qJw9LT041uQUREqpkLLrjA6BYMM3bsWK666io6d+4MwGWXXcaaNWuwWq289957/PLLLzz88MMAPPLII1x33XV07NixTA39bBYRkcpW3s/mKnVab03+BUJERKSyORwOXC6X/7HX68VqtR51ncvlIjo6+oga+tksIiLBotN6RUREqqnExERSU1MB2LhxI82aNfOva9WqFenp6RQVFZGXl8fWrVvLrBcREQm2KnVar4iIiFQer9fLxIkT+eWXX/D5fEydOpXU1FQaNmxIly5dWLJkCSkpKfh8Pu666y6uvvpqo1sWEZEaTOH0JOXl5fHwww/jdDopLi7m0UcfpW3btka3FZI++eQTVq9ezcyZM41uJWQc/kVz8+bN2Gw2Jk+eTKNGjYxuKyR9//33PPXUUyxYsMDoVkJOcXExY8aM4Y8//sDtdnPPPffQpUsXo9sSERGREFelrjkNBa+99hoXX3wxgwYN4rfffmPkyJG88847RrcVciZPnszatWtp0aKF0a2ElGPdFkJO3Msvv8yKFSuIjIw0upWQtGLFCmrVqsWMGTPIycnhuuuuUzgVkaDyer2Yzbo6TaS6UTg9SYMGDfLfA66kpITw8HCDOwpNiYmJXHnllaSkpBjdSkg51m0h5MQ1bNiQ2bNn88gjjxjdSkjq1q2b//RPn8+HxWIxuCMJZf/9739JS0sjLy+PmJgYLrjgAlq1amV0WydkzZo1rF+/vkzv3bp1w2QyGd3aMYVq3zt37mTatGls2rQJq9WK1+ulWbNmjB49mrPOOsvo9qSK0wGN0KBwegxLly7ljTfeKLNs6tSptGrViszMTB5++GHGjBljUHehobx92KNHD77++muDugpdTqcTh8Phf2yxWPB4PP7ZN+XEXH311ezatcvoNkKW3W4HSj+P999/Pw888ICxDUnImjNnDj/88AOdOnWiQYMGuFwu5syZQ8uWLSv8uVq7dm256zp16lSh2gCPP/44Xq+XpKQk7HY7LpeL1NRU1q5dy5QpUypUe+TIkeWuq+ilMIHsGzjmQed+/fpVqPbYsWMZOXIkrVu39i/buHEjo0eP5u23365QbbfbXe66w4MSFRWqBwWKiopYvHgxX331FXl5eURHR9OuXTtuueUWIiIijG7vmEL5gEZWVhbffPON//PSpk0b6tata3RbAaffaI+hb9++9O3b94jlmzdvZsSIETzyyCNcdNFFBnQWOsrbh3JqjnVbCJFg2rNnD/fddx8333wzvXr1MrodCVFpaWm89dZbZZYNHDiQG2+8scLhdMmSJWzatIn27dsfsa4ywumvv/7KwoULyyzr0qUL/fv3r3Dtbt26MWvWLCZOnFjhWn8XyL4BfvvtNz7//HOuvfbaSqn3V263u0wwhdKziCpDr169OHjwILGxsfh8Pkwmk//rp59+WuH6oXxQYPTo0TRv3pwHHnigTO8jR47k+eefr1DtgQMHUlxcXGbZ4f1e0QMOENgDGoHsfenSpaSkpHDBBRdgt9v59ddfeeGFF+jbty833XRThWpDYD8vFaXfak/Sli1bGD58OM888wzNmzc3uh2pYRITE/n888/p0aPHEbeFEAmWAwcOcPvttzN+/Hg6dOhgdDsSwjweD7t27aJBgwb+Zbt27aqUU+9mzZrFLbfcwpAhQzj77LMrXO/vvF4v3377Le3atfMv++abbwgLC6tw7a5du7JhwwYOHjxI9+7dK1zvrwLZN5QGmd9++42kpKRKPz373HPPZfTo0Vx66aVER0fjcrn48ssvOffccytce/Hixdxxxx28/vrrxMbGVkK3ZYXyQYH9+/fz9NNPl1nWvHlzbr755grXfuihhxg3bhzPP/98QC4RCeQBjUD2/q9//YvFixeX+f/S7XZz0003VUo4DeTnpaIUTk/SzJkzcbvd/qNcDodDE9JI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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The sum of the events with label 0 is: 0.1397999\n", - "The sum of the events with label 1 is: 0.3136053\n", - "The label for y_tilde is: 1 because sum_label0 < sum_label1\n" - ] - } - ], - "source": [ - "from random import sample, randint\n", - "from numpy import sign\n", - "\n", - "\n", - "def grab_random_data():\n", - " one_random_index = sample(range(50), 1) \n", - " two_random_index = sample(range(50), 2) \n", - " random_label = sample([1,0], 1) # random label\n", - "\n", - " ## iris_setosa_normalised # Label 0\n", - " ## iris_versicolor_normalised # Label 1\n", - " if random_label[0]:\n", - " # Test data has label = 1, iris_versicolor\n", - " data_label0 = [iris_setosa_normalised[0][one_random_index[0]],\n", - " iris_setosa_normalised[1][one_random_index[0]]]\n", - " data_label1 = [iris_versicolor_normalised[0][two_random_index[0]],\n", - " iris_versicolor_normalised[1][two_random_index[0]]]\n", - " test_data = [iris_versicolor_normalised[0][two_random_index[1]],\n", - " iris_versicolor_normalised[1][two_random_index[1]]] \n", - " else:\n", - " # Test data has label = 0, iris_setosa\n", - " data_label0 = [iris_setosa_normalised[0][two_random_index[0]],\n", - " iris_setosa_normalised[1][two_random_index[0]]]\n", - " data_label1 = [iris_versicolor_normalised[0][one_random_index[0]],\n", - " iris_versicolor_normalised[1][one_random_index[0]]]\n", - " test_data = [iris_setosa_normalised[0][two_random_index[1]],\n", - " iris_setosa_normalised[1][two_random_index[1]]] \n", - " return data_label0, data_label1, test_data, random_label\n", - "\n", - "\n", - "data_label0, data_label1, test_data, random_label = grab_random_data()\n", - "\n", - "print(\"Data point {} from label 0\".format(data_label0))\n", - "print(\"Data point {} from label 1\".format(data_label1))\n", - "print(\"Test point {} from label {} \".format(test_data, random_label[0]))\n", - "\n", - "\n", - "def run_random_data(data_label0, data_label1, test_data):\n", - " angle_x_tilde = 2 * acos(test_data[0]) * sign(test_data[1]) % (4 * pi)\n", - " angle_x0 = 2 * acos(data_label0[0]) * sign(data_label0[1]) % (4 * pi)\n", - " angle_x1 = 2 * acos(data_label1[0])* sign(data_label1[1]) % (4 * pi)\n", - "\n", - " ## Build final QASM\n", - " final_qasm = part_a() + part_b(angle_x_tilde) + part_c(angle_x0) + part_d(angle_x1) + part_e() + part_f()\n", - " result_random_data = qi.execute_qasm(final_qasm, backend_type=backend_type, number_of_shots=1, full_state_projection=True)\n", - " return result_random_data\n", - "\n", - "\n", - "result_random_data = run_random_data(data_label0, data_label1, test_data);\n", - "\n", - "# Plot data points:\n", - "plt.rcParams['figure.figsize'] = [16, 6] # Plot size\n", - "plt.subplot(1, 2, 1)\n", - "DataPlotter.plot_normalised_data(iris_setosa_normalised, iris_versicolor_normalised);\n", - "plt.scatter(test_data[0], test_data[1], s=50, c='green'); # Scatter plot data class ?\n", - "plt.scatter(data_label0[0], data_label0[1], s=50, c='orange'); # Scatter plot data class 0\n", - "plt.scatter(data_label1[0], data_label1[1], s=50, c='orange'); # Scatter plot data class 1\n", - "plt.legend([\"Iris Setosa (label 0)\", \"Iris Versicolor (label 1)\", \"Test point\", \"Data points\"])\n", - "plt.subplot(1, 2, 2)\n", - "prob_random_points = bar_plot(result_random_data);\n", - "summarize_results(prob_random_points);\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To get a better idea how well this quantum classifier works we can compare the predicted label to the true label of the test datapoint. Errors in the prediction can have two causes. The quantum classifier does not give the right classifier prediction or the quantum classifier gives the right classifier prediction which for the selected data gives the wrong label. in general, the first type of errors can be reduced by increasing the number of times we run the algorithm. In our case, as we work with the simulator and our gates are deterministic ([no conditional gates](https://www.quantum-inspire.com/kbase/optimization-of-simulations/)), we do not have to deal with this first error if we use the true probability distribution. This can be done by using only a single shot without measurements." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "In this sample of 100 data points:\n", - "the classifier predicted the true label correct 97 % of the times\n", - "the quantum classifier predicted the true label correct 97 % of the times\n", - "the quantum classifier predicted the classifier label correct 100 % of the times\n", - "Could not assign a label 0 times\n" - ] - } - ], - "source": [ - "quantum_score = 0\n", - "error_prediction = 0\n", - "classifier_is_quantum_prediction = 0\n", - "classifier_score = 0\n", - "no_label = 0\n", - "\n", - "\n", - "def true_classifier(data_label0, data_label1, test_data):\n", - " if np.linalg.norm(np.array(data_label1) - np.array(test_data)) < np.linalg.norm(np.array(data_label0) -\n", - " np.array(test_data)):\n", - " return 1\n", - " else:\n", - " return 0\n", - "\n", - "\n", - "for idx in range(100):\n", - " data_label0, data_label1, test_data, random_label = grab_random_data()\n", - " result_random_data = run_random_data(data_label0, data_label1, test_data)\n", - " classifier = true_classifier(data_label0, data_label1, test_data)\n", - " \n", - " sum_label0 = 0\n", - " sum_label1 = 0\n", - " for key, value in result_random_data['histogram'].items():\n", - " if int(key) in [0, 1, 4, 5]:\n", - " sum_label0 += value\n", - " if int(key) in [8, 9, 12, 13]:\n", - " sum_label1 += value\n", - " if sum_label0 > sum_label1:\n", - " quantum_prediction = 0\n", - " elif sum_label1 > sum_label0:\n", - " quantum_prediction = 1\n", - " else:\n", - " no_label += 1\n", - " continue\n", - " \n", - "\n", - " if quantum_prediction == classifier:\n", - " classifier_is_quantum_prediction += 1\n", - " \n", - " if random_label[0] == classifier:\n", - " classifier_score += 1\n", - "\n", - " if quantum_prediction == random_label[0]:\n", - " quantum_score += 1\n", - " else:\n", - " error_prediction += 1\n", - "\n", - "print(\"In this sample of 100 data points:\")\n", - "print(\"the classifier predicted the true label correct\", classifier_score, \"% of the times\")\n", - "print(\"the quantum classifier predicted the true label correct\", quantum_score, \"% of the times\")\n", - "print(\"the quantum classifier predicted the classifier label correct\",\n", - " classifier_is_quantum_prediction, \"% of the times\")\n", - "print(\"Could not assign a label \", no_label, \"times\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Conclusion and further work #\n", - "\n", - "\n", - "How well the quantum classifier performs, hugely depends on the chosen data points. In case the test data point is significantly closer to one of the two training data points the classifier will result in a one-sided prediction. The other case, where the test data point has a similar distance to both training points, the classifier struggles to give an one-sided prediction. Repeating the algorithm on the same data points, might sometimes give different measurement outcomes. This type of error can be improved by running the algorithm using more shots. In the examples above we only used the true probability distribution (as if we had used an infinite number of shots). By running the algorithm instead with 512 or 1024 shots this erroneous behavior can be observed. In case of an infinite number of shots, we see that the quantum classifier gives the same prediction as classically expected.\n", - "\n", - "The results of this toy example already shows the potential of a quantum computer in machine learning. Because the actual algorithm consists of only three operations, independent of the size of the data set, it can become extremely useful for tasks such as pattern recognition on large data sets. The next step is to extend this toy model to contain more data features and a larger training data set to improve the prediction. As not all data sets are best classified by a distance-based classifier, implementations of other types of classifiers might also be interesting. For more information on this particular classifier see the reference [ref](https://arxiv.org/abs/1703.10793).\n", - "\n", - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### References ###\n", - "* Book: [Schuld and Petruccione, Supervised learning with Quantum computers, 2018](https://www.springer.com/us/book/9783319964232) \n", - "* Article: [Schuld, Fingerhuth and Petruccione, Implementing a distance-based classifier with a quantum interference circuit, 2017](https://arxiv.org/abs/1703.10793)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# The same algorithm for the projectQ framework#" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Results:\n", - "{'0100': 0.0634765625, '0000': 0.1357421875, '0010': 0.294921875, '1001': 0.3828125, '1011': 0.0966796875, '0110': 0.0234375, '1111': 0.001953125, '1101': 0.0009765625}\n" - ] - } - ], - "source": [ - "from math import acos\n", - "import os\n", - "\n", - "from quantuminspire.credentials import get_authentication\n", - "from quantuminspire.api import QuantumInspireAPI\n", - "from quantuminspire.projectq.backend_qx import QIBackend\n", - "\n", - "from projectq import MainEngine\n", - "from projectq.backends import ResourceCounter\n", - "from projectq.meta import Compute, Control, Loop, Uncompute\n", - "from projectq.ops import CNOT, CZ, All, H, Measure, Toffoli, X, Z, Ry, C\n", - "from projectq.setups import restrictedgateset\n", - "\n", - "QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/')\n", - "\n", - "\n", - "# Remote Quantum Inspire backend #\n", - "authentication = get_authentication()\n", - "qi_api = QuantumInspireAPI(QI_URL, authentication)\n", - "\n", - "compiler_engines = restrictedgateset.get_engine_list(one_qubit_gates=\"any\",\n", - " two_qubit_gates=(CNOT, CZ, Toffoli))\n", - "compiler_engines.extend([ResourceCounter()])\n", - "\n", - "qi_backend = QIBackend(quantum_inspire_api=qi_api)\n", - "qi_engine = MainEngine(backend=qi_backend, engine_list=compiler_engines)\n", - "\n", - "# angles data points:\n", - "angle_x_tilde = 2 * acos(0.8670)\n", - "angle_x0 = 2 * acos(0.1411)\n", - "angle_x1 = 2 * acos(0.9193)\n", - "\n", - "qubits = qi_engine.allocate_qureg(4)\n", - "\n", - "# part_a\n", - "for qubit in qubits[0:2]:\n", - " H | qubit\n", - "\n", - "# part_b\n", - "C(Ry(angle_x_tilde), 1) | (qubits[1], qubits[2]) # Alternatively build own CRy gate as done above\n", - "X | qubits[1]\n", - "\n", - "# part_c\n", - "C(Ry(angle_x0), 2) | (qubits[0], qubits[1], qubits[2]) # Alternatively build own CCRy gate as done above\n", - "X | qubits[0]\n", - "\n", - "# part_d\n", - "C(Ry(angle_x1), 2) | (qubits[0], qubits[1], qubits[2]) # Alternatively build own CCRy gate as done above\n", - "\n", - "# part_e\n", - "CNOT | (qubits[0], qubits[3])\n", - "\n", - "# part_f\n", - "H | qubits[1]\n", - "\n", - "qi_engine.flush()\n", - "\n", - "# Results:\n", - "temp_results = qi_backend.get_probabilities(qubits)\n", - "\n", - "res = [get_bin(el, 4) for el in range(16)]\n", - "prob = [0] * 16\n", - "for key, value in temp_results.items(): \n", - " prob[int(key[::-1], 2)] = value # Reverse as projectQ has a different qubit ordering\n", - "\n", - "color_list = [\n", - " 'red', 'red', (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1),\n", - " 'red', 'red', (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1),\n", - " 'blue', 'blue', (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1),\n", - " 'blue', 'blue', (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1)\n", - " ]\n", - "plt.bar(res, prob, color=color_list)\n", - "plt.ylabel('Probability')\n", - "plt.title('Results')\n", - "plt.ylim(0, 1)\n", - "plt.xticks(rotation='vertical')\n", - "plt.show()\n", - "print(\"Results:\")\n", - "print(temp_results)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/examples/classifier_example/classification_example2_4_data_points.ipynb b/docs/examples/classifier_example/classification_example2_4_data_points.ipynb deleted file mode 100644 index c85660b..0000000 --- a/docs/examples/classifier_example/classification_example2_4_data_points.ipynb +++ /dev/null @@ -1,733 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#
A Quantum distance-based classifier
#\n", - "##
Robert Wezeman, TNO
##" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Table of Contents\n", - "* [Introduction](#introduction)\n", - "* [Problem](#problem)\n", - "* [Theory](#theory)\n", - "* [Algorithm](#algorithm)\n", - "* [Implementation](#implementation)\n", - "* [Conclusion and further work](#conclusion)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "$$ \\newcommand{\\ket}[1]{\\left|{#1}\\right\\rangle} $$" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "## Import external python file\n", - "import nbimporter\n", - "from data_plotter import DataPlotter # for easier plotting \n", - "DataPlotter = DataPlotter()\n", - "\n", - "# Import math functions \n", - "from math import acos, pi\n", - "from numpy import sign\n", - "\n", - "# Import plotting tools\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Introduction #\n", - "\n", - "\n", - "In the first part of this notebook series on a distance-based classifier, we looked at how to implement a distance-based classifier on the quantum inspire using QASM code. We looked at the simplest possible case, that is: using two data points, each with two features, to assign a label (classify) to a random test point, see image. In this notebook we will extend the previous classifier, by increasing the number of data points to four. In this notebook we demonstrate how to use the projectQ framework to do this. \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Problem #\n", - "Again we have the following binary classification problem: Given the data set \n", - "$$\\mathcal{D} = \\Big\\{ ({\\bf x}_1, y_1), \\ldots ({\\bf x}_M , y_M) \\Big\\},$$\n", - "consisting of $M$ data points $x_i\\in\\mathbb{R}^n$ and corresponding labels $y_i\\in \\{-1, 1\\}$, give a prediction for the label $\\tilde{y}$ corresponding to an unlabeled data point $\\bf\\tilde{x}$. The classifier we shall implement with our quantum circuit is a distance-based classifier and is given by\n", - "\\begin{equation}\\newcommand{\\sgn}{{\\rm sgn}}\\newcommand{\\abs}[1]{\\left\\lvert#1\\right\\rvert}\\label{eq:classifier} \\tilde{y} = \\sgn\\left(\\sum_{m=0}^{M-1} y_m \\left[1-\\frac{1}{4M}\\abs{{\\bf\\tilde{x}}-{\\bf x}_m}^2\\right]\\right). \\hspace{3cm} (1)\\end{equation}\n", - "\n", - "This is a typical $M$-nearest-neighbor model where each data point is given a weight related to the distance measure. To implement this classifier on a quantum computer we use amplitude encoding. For the details see the previous notebook.\n", - "\n", - "[Back to Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Theory #\n", - "\n", - "\n", - "The algorithm requires a $n$-qubit quantum system to be in the following state initially \n", - "\\begin{equation}\\label{eq:prepstate}\n", - " \\ket{\\mathcal{D}} = \\frac{1}{\\sqrt{2M}} \\sum_{m=0}^{M-1} \\ket{m}\\Big(\\ket{0}\\ket{\\psi_{\\bf\\tilde{{x}}}} + \\ket{1}\\ket{\\psi_{\\bf{x}_m}}\\Big)\\ket{y_m}.\\hspace{3cm} (2)\n", - "\\end{equation}\n", - "\n", - "Here $\\ket{m}$ is the $m^{th}$ state of the computational basis used to keep track of the $m^{th}$ training input. The second register is a single ancillary qubit entangled with the third register. The excited state of the ancillary qubit is entangled with the $m^{th}$ training state $\\ket{\\psi_{{x}_m}}$, while the ground state is entangled with the new input state $\\ket{\\psi_{\\tilde{x}}}$. The last register encodes the label of the $m^{th}$ training data point by\n", - "\\begin{equation}\n", - "\\begin{split}\n", - " y_m = -1 \\Longleftrightarrow& \\ket{y_m} = \\ket{0},\\\\\n", - " y_m = 1 \\Longleftrightarrow& \\ket{y_m} = \\ket{1}.\n", - "\\end{split}\n", - "\\end{equation}\n", - "Once in this state the algorithm only consists of the following three operations:\n", - "\n", - "1. Apply a Hadamard gate on the second register.\n", - " \n", - "2. Measure the second register. We restart the algorithm if we measure a $\\ket{1}$ and only continue when we are in the $\\ket{0}$ branch.\n", - "\n", - "3. Measure the last qubit $\\ket{y_m}$.\n", - "\n", - "In the special case where the amount of training data for both labels is the same, this last measurement relates to the classifier as described in previous section by\n", - "\\begin{equation}\n", - "\\tilde{y} = \\left\\{\n", - " \\begin{array}{lr}\n", - " -1 & : p(q_4 = 0 ) > p(q_4 = 1)\\\\\n", - " +1 & : p(q_4 = 0 ) < p(q_4 = 1)\n", - " \\end{array}\n", - "\\right. \n", - "\\end{equation}\n", - "By setting $\\tilde{y}$ to be the most likely outcome of many measurement shots, we obtain the desired distance-based classifier.\n", - "\n", - "In the previous notebook we saw the implementation for $N=2$ data points, each with $M=2$ features. Now we will consider the case for two datapoints with $M=4$ features.\n", - "\n", - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Algorithm #\n", - "\n", - "To describe the desired initial state for $M = 4$ and $N = 2$ we need 5 qubits:\n", - "* Two qubits for the index register $\\ket{m}$\n", - "* One ancillary qubit\n", - "* One qubit to store the information of the two features of the data points \n", - "* One qubit to store the information of the classes of the data points\n", - "\n", - "Furthermore, these $4$ require us to implement a triple controlled-$R_y$ gate, or $CCCR_y$. ProjectQ does this automatically for us, at the cost of two extra ancillary qubits, resulting in a total of 7 qubits. The algorithm is divided in different parts:\n", - "\n", - "\n", - "* **Part A:** In this part the index register is initialized, as is the ancillary qubit. Part A consists of three Hadamard gates. After this step the system is in the state\n", - "$$\\ket{\\mathcal{D}_A} =\\frac{1}{\\sqrt{8}} \\sum_{m=0}^{3}\\ket{m}\\Big(\\ket{0}+\\ket{1}\\Big)\\ket{0}\\ket{0} $$\n", - "\n", - "* **Part B:** In this part we encode the unlabeled data point $\\bf\\tilde{x}$ by means of a controlled rotation. The encoding of the data will first require some preprocessing on the data, see the previous notebook for the details. We entangle the fourth qubit with the ancillary qubit. The angle $\\theta$ of the rotation should be chosen such that ${\\bf\\tilde{x}}=R_y(\\theta)\\ket{0}$. After this step the system is in the state\n", - "$$\\ket{\\mathcal{D}_B} =\\frac{1}{\\sqrt{8}} \\sum_{m=0}^{3}\\ket{m}\\Big(\\ket{0}\\ket{\\tilde{{\\bf x}}}+\\ket{1}\\ket{0}\\Big)\\ket{0}$$\n", - "\n", - "* **Part C:** In this step the encoding of the data points $\\bf x_m$ is done. So far it has been almost analogous to the $M =2$ case, however, this step is a bit more involved. First, use a $CCCR_y$-rotation so that the datapoint $\\bf x_m$ is connected to $\\ket{m} = \\ket{11}$ and entangled with the ancillary qubit $\\ket{1}$. Next, we rotate $\\ket{m}$ cyclic around such that we can reuse this $CCCR_y$-rotation for next data point, see the figure\n", - "\\begin{equation}\n", - "C2 : \\hspace{1cm}\\ldots \\rightarrow \\ket{00} \\rightarrow \\ket{01} \\rightarrow \\ket{10}\\rightarrow \\ket{11} \\rightarrow \\ldots\n", - "\\end{equation}\n", - "\n", - "After doing step $C1$ four times with the right angles(${\\bf x_m}=R_y\\ket{0}$) and in the right order alternated with step $C2$ the system will be in the state\n", - "$$\\ket{\\mathcal{D}_C} =\\frac{1}{\\sqrt{8}} \\sum_{m=0}^{3}\\ket{m}\\Big(\\ket{0}\\ket{\\tilde{{\\bf x}}}+\\ket{1}\\ket{\\bf x_m}\\Big)\\ket{0}$$\n", - "* **Part D:** In this part we encode the known labels of the data in the last qubit. This can be done easily using a Toffoli-gate with the controls on the first two qubits and the target on the fifth. Note that this requires only one rotation through $\\ket{m}$ of the labels if we choose the data points such that the two points labeled with $y_m = 1$ are $\\ket{00}$ and $\\ket{11}$. The combination of twice circuit $D1$ with circuit $D2$ inbetween does the job. The desired state is now produced:\n", - "\\begin{equation}\n", - " \\ket{\\mathcal{D}_D} = \\frac{1}{\\sqrt{8}} \\sum_{m=0}^{3} \\ket{m}\\Big(\\ket{0}\\ket{\\bf\\tilde{{x}}} + \\ket{1}\\ket{\\bf{x}_m}\\Big)\\ket{y_m}.\n", - "\\end{equation}\n", - "\n", - "\n", - "* **Part E:** In this part the actual distance-based classifier part of the algorithm is done. This part is independent of number of data points, which is precisely the strength of this quantum classifier. This step consists of a simple Hadamard-gate. Results are then obtained by post-processing the measurement results.\n", - "\n", - "\n", - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Implementation #\n", - "We will implement the above algorithm using the projectQ framework. First we need to import some modules and set up the authentication for connecting to the Quantum Inspire API." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# Imports for authentication with Quantum Inspire API\n", - "import os\n", - "from coreapi.auth import BasicAuthentication\n", - "\n", - "# Import the projectQ backend from the Quantum Inspire\n", - "from quantuminspire.credentials import get_authentication\n", - "from quantuminspire.api import QuantumInspireAPI\n", - "from quantuminspire.projectq.backend_qx import QIBackend\n", - "\n", - "# Import projectQ \n", - "from projectq import MainEngine\n", - "from projectq.backends import ResourceCounter\n", - "from projectq.meta import Compute, Control, Loop, Uncompute\n", - "from projectq.ops import CNOT, All, H, Measure, Toffoli, X, Z, CZ, Ry, C\n", - "from projectq.setups import restrictedgateset\n", - "\n", - "QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/')\n", - "\n", - "\n", - "# Remote Quantum-Inspire backend #\n", - "authentication = get_authentication()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before we consider the four point classifier, let us first review the 2 point classifier again. Consider the following data points from the Iris flower dataset:\n", - "\\begin{equation}\n", - " \\begin{split}\n", - " &\\ket{\\psi_{\\tilde{x}}} = \\;\\;\\;0.9999 \\ket{0} -0.0011\\ket{1}, \\hspace{2cm}y = 1,\\\\\n", - " &\\ket{\\psi_{x_0}} = -0.4583 \\ket{0} - 0.8889\\ket{1}, \\hspace{2cm}y = 1,\\\\ \n", - " &\\ket{\\psi_{x_1}} = -0.3728 \\ket{0} +0.9279\\ket{1}, \\hspace{2cm}y = 0.\n", - " \\end{split}\n", - "\\end{equation}\n", - "\n", - "The code for this implementation is shown below and treated in detail in the previous notebook. " - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'1101': 0.2657933, '0100': 0.2355381, '0110': 0.1109331, '0010': 0.1043719, '1011': 0.1019124, '1111': 0.0956278, '0000': 0.0491568, '1001': 0.0366663}\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6cAAAF8CAYAAAAzes21AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAABlDUlEQVR4nO3dd3wUdf7H8deWbNomgSQUFUFFENBDqiglqIgiiCh3CJYop4ANREVBioiIEEFEBcGCoAeIAQ4VFTnFlqOJxkMFI9IEUUpCEpLdlM2W3x/5sRohtOzupLyfjwcPsjOzn/nssCF573fmOyafz+dDRERERERExEBmoxsQERERERERUTgVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiIiIiJiOIVTERERERERMZzV6AZERERERIxwwQUX0LRpU8xmMyaTicLCQux2OxMmTOBvf/tbUPa3fv16wsLCuP/++/nXv/4V8H2IVGUKpyIiIiJSY7355pvEx8f7H7/++utMmjSJ1NTUoO3z8OHD/PDDD0GrL1JV6bReERERERHA7Xazb98+4uLi/MvmzJnDjTfeSJ8+fbjvvvs4cOAAAB9//DE33ngjffv2pV+/fnz99dcAJCcns2rVKv/z//oYYPTo0RQVFdGnTx88Hg8vvvgivXv3pm/fvtx1110cPHgwBK9WpPLRyKmIiIiI1Fh33HEHJpOJ7OxswsPDueKKK5gyZQoA7777Lj///DNLly7FarWSmprKuHHjeO2115g6dSrPPvssrVq1Ys2aNXz11Ve0b9/+pPY5ZcoUevfuzXvvvce+fft48803Wb9+PTabjXnz5vH9999z1VVXBfNli1RKCqciIiIiUmMdOa33xx9/ZPDgwbRu3ZqEhAQAPv/8c3744Qf+/ve/A+D1eiksLASgV69eDB06lK5du9KpUycGDx58WvuvV68ezZo148YbbyQpKYmkpCQuu+yywLw4kSpG4VREREREarwWLVowevRoxo0bx8UXX0yDBg3wer0MGjSIW265BQCXy8Xhw4cBeOihh/jHP/7BmjVrWL58Oa+++irLly8HwOfz+euWlJQcd79ms5mFCxfyww8/sH79eiZPnkyHDh0YN25ckF6pSOWla05FRERERIDrrruOVq1aMXnyZAA6d+7MsmXLcDgcALzwwguMHDkSt9vNlVde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2bNn8+CDD+Lz+SguLubJJ58kJiaGhx9+mCVLlvD5558za9YsfD4fF154IU8++STr16/n+eefJzw8nFq1ajF58uQyr2HFihW8+eab2Gw2zjnnHCZOnMj777/Pv//9b7xeLw888ACXXXYZAD6fjxUrVvDOO++UqfHzzz+TkpKCx+MhJyeHCRMm0KZNG1wuFw899BD79u3jggsuYMKECTgcDsaOHUtOTg4A48aN44ILLjjm8XW5XLz00kuMHDmyzPLrrruOmTNncskll1T431BEREQqD4VTEanW0tLgzjvB44G5c2HevMAG1Ouuu47u3buzfPlyAPbs2UNubi5z587l0KFD/PLLL0c95x//+AcrVqzg3nvvZfny5QwYMIDt27ezcuVK3nrrLQD++c9/+kPtpZdeysCBA/niiy+oVasWU6dOZfv27RQUFBATEwOA2+3mqaeeYunSpSQkJPDaa6+xb98+Hn/8cRYvXky9evV48803mTNnDpdffjkAOTk5zJw5k3feeQe73c7kyZNJTU0lKiqK2NhY5syZU6bvX375BbvdTlhYWJnl27dvZ9SoUVxwwQW8//77LF++nDZt2lBUVMQjjzzCWWedxfDhw/nss8/49ttvufTSS7nlllv45ZdfGD16NIsXLz7msW3btu0xl59//vmkp6ef3D+QiIiIVBkKpyJSra1bVxpMExLg0KHSx4EMp+eee26Zx02aNKF///48/PDDuN1ukpOTj3pOnz59GDhwIHfeeScbN25k3LhxfPzxx/z+++8MHDgQgMOHD7N79+4y+0hKSuKXX37hvvvuw2q1cu+99/pr5uTkEBsbS0JCAgCDBw8mOzsbu91OvXr1AGjfvj3PPfecP5z++uuvnH/++djtdv/6NWvWcPHFFx/1uo7sIzEx8ajldevWZfbs2UREROB0Ov31zjzzTM466ywAWrduza5du/j555/ZsGEDH330kf91niqLxYLVasXr9Ybk9GIREREJDf1UF5FqrWNHsFhKg6nFUvo4kEwmU5nHW7duxel08uqrr5KSksJTTz111HPi4+Np3Lgxs2fPpnv37litVs477zzOP/98/vWvf7FgwQL69u3rP931yD6++uor6taty7x587j33nt57rnn/DUTEhLIy8sjNzcXgEmTJvHrr7/icDg4ePAgABs3buScc87xP6dBgwbs2LGDgoIC//ojofRYoe/IPv7q6aef5oEHHuCZZ56hadOm+Hw+APbv3+/f97fffkuTJk0477zzGDhwIAsWLOD555/n+uuvP/FB/gufz4fValUwFRERqWZCNnJaUlLCmDFj+O2333C5XNx7771069YtVLsXkRoqKan0VN5gXHN6LOeccw4vvfQSH330kf+azWO56aabGDx4MKtWrQKgWbNmXHbZZdx88824XC5atmzpH/E8olmzZjz88MMsXrwYt9vN/fff719nNpt54oknuPvuuzGbzbRo0YKWLVsyadIkhg0bhslkIi4ujilTprBt2zagNCQPGzaM22+/HbPZTMOGDXnkkUf48MMPj9lzo0aNyM7Oxu12Y7X+8ePj+uuvZ/jw4cTGxlK/fn3/9aS1atVi0qRJHDhwgNatW9O1a1datmzJ2LFjWbJkCQ6Hg6FDh57yMd66dSutWrU65eeJiIhIJecLkWXLlvkmTZrk8/l8vpycHF/Xrl2P2uabb74JVTun7ccffzS6hSpPx7DidAwrTsfw9Lz88su+jz/+2OfzGXcMn3nmGd/XX399UttWhZ8rlV2oj2Ew31fBfs9W1d51XEJfO9j1q2rtYNdX76GvfSzH+7kSsnOievTowfDhw48EYiwWS6h2LSIiAXLHHXewatUqvF6vIfvPzMzE4XDQrl07Q/YvIiIiwROy03qjo6MBcDgcPPDAAzz44IOh2rWIiARIREQE06dPN2z/derUYeLEiYbtX0RERIInpLP17tu3j/vvv59bbrmF3r17H3ObjIyMULZ0yoqKiip9j5WdjmHF6RhWnI5hxekYioiISCCFLJxmZWVx5513Mn78eP8N3Y+lefPmoWrptGRkZFT6His7HcOK0zGsOB3DiqsKx1D3QxUREak6QnbN6csvv0xeXh6zZ88mOTmZ5ORkioqKQrV7ERERERERqcRCNnI6btw4xo0bF6rdiYiIiIiISBUS0mtORURCLb84n9QtqWw7tI0mCU3of2F/YsJjTrteSkoKW7ZsITMzk6KiIs4++2xq167Niy++eFLP37p1K3l5ebRv3/60exg6dCizZs0qd31qaip9+/YlLCzstPchIiIiEmoKpyJSba3Zs4aei3ri9XlxljiJDovm4f88zMpbV5JAwmnVfOyxxwBYvnw5O3fu5JFHHjml53/88cckJiZWKJweL5gCvPLKK9xwww2nXV9ERETECAqnIlIt5Rfn03NRT/Jd+f5lzhInAD0X9eSzXp8FbF8lJSU88cQT7N69G6/Xy4MPPkiHDh2YMWMGX331FW63m6uvvpo+ffrwzjvvEBYWxoUXXkjLli0B2Lt3L8OHD6dOnTocOHCApKQkHnroIfbu3cuYMWPweDyYTCbGjRtHs2bN6NSpE2vXriU5OZlmzZqxbds2HA4HL7zwAuvWrSMzM5OHHnqI2bNnB+w1ioiIiARbyCZEEhEJpdQtqXh93mOu8/q8rPp1VcD2tXTpUmrXrs2iRYuYPXu2/z6c77//Ps8++yxvvfUWsbGx1KtXjxtvvJGBAwf6g+kRv/32GykpKSxbtowNGzawZcsWpk6dyu23386iRYsYO3YsY8aMOWrfLVu25I033qBTp058+OGH9OvXjzp16jBjxoyAvT4RERGRUNDIqYhUS9sObfOPlP6Vs8TJbsfugO3r559/Jj09ne+//x4At9tNdnY206ZNY/r06WRlZdGlS5fj1mjWrBm1atUCSgPnrl272LFjh//03+bNm7N///6jnteiRQsA6tevT1ZWVsBek4iIiEioKZyKSLXUJKEJ0WHRxwyo0WHRNLI3Cti+zjvvPOrXr88999xDUVERc+bMwW63s2rVKp577jkAevbsSa9evTCZTHi9R4/o7tixg8LCQmw2G99//z1///vfady4Md988w3dunUjIyODxMTEk+qnvH2IiIiIVGY6rVdEqqX+F/bHbDr2f3Fmk5keZ/cI2L4GDBjAzp07ue222xgwYABnnXUWNpuNuLg4brrpJm6//XY6derEmWeeyUUXXcSiRYvYsGFDmRphYWEMHz6cfv360a1bN5o1a8bIkSNZuHAht956KxMmTODpp58+qX7atWvHkCFD8Pl8AXuNIiIiIsGmkVMRqZZiwmNYeevKo2brNZvMrLx1JdHO6ArV79u3r/9rm83G1KlTj9pm6NChDB06tMyyyy+/nMsvv/yobRMTE3n11VfLLGvQoAHz588/atu1a9cCsGDBAv+ym2++2f/1M888c3IvQkRERKQSUTgVkWqrc8PO/D7id1I3p7I9ezvnx59P/4v6Y7fZycjIMLo9EREREfkThVMRqdbsNjt3tbnL6DaOq0GDBixZssToNkREREQMpWtORURERERExHAKpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGE7hVESqt5J82D4X/jeq9O+S/AqV++qrr7jssstITk7239d05cqVx33O77//zmeffVah/R5LRkYGs2bNKnd9cXExS5cuDfh+RURERIJBs/WKSPV1cA180RN8XvA4wRIN3z4Ml68EEk677KWXXsqMGTMAcDqdJCcnc+6559K8efNjbr9hwwZ27tzJlVdeedr7PJbmzZuXu0+AzMxMli5dSr9+/QK6XxEREZFgUDgVkeqpJL80mLr/NFLqcZb+/UVPTC0CM5IZHR1N//79WbVqFU2bNmX8+PHs37+fgwcPcuWVV/LAAw/w6quvUlRUROvWrYmJiWHWrFn4fD6cTifTp0/n3HPP9ddbvnw5q1evxul0kpOTw/33388111zD2rVref755wkPD6dWrVpMnjyZjIwM3n77bWbMmMHVV19NmzZt2LVrFwkJCcycOZOXX36Z7du3M2vWLC677DKeeeYZrFYrkZGRvPDCC9jt9oAcAxEREZFA0Gm9IlI97U4tHTE9Fp+X2NxVAdtVQkICOTk57Nu3j1atWvH666+zbNky3n77bSwWC0OGDOG6666jW7dubNu2jWnTprFgwQKuvvpqVq06uo/CwkLmz5/PvHnzSElJoaSkhMcff5xZs2axcOFC2rdvz5w5c8o859dff2X48OGkpqaSnZ3NDz/8wD333MP555/P0KFDWb16Nddeey0LFy7k5ptvJi8vL2CvX0RERCQQNHIqItVT/rY/Rkr/yuPEVrw7YLv6/fffqV+/PrVq1eKHH35gw4YN2O12XC7XUdvWq1ePp59+mqioKA4cOECbNm2O2qZ9+/aYzWYSExOJjY0lKysLu91OvXr1/Oufe+45Lr/8cv9zateuzRlnnAHAGWecQXFxcZma99xzDy+//DJ33HEH9erVo2XLlgF7/SIiIiKBoJFTEameYpqUXmN6LJZoXOGNArIbh8PB0qVL6dGjB8uXLycmJobp06dz5513UlRUhM/nw2w24/WWjuI+/vjjTJ48mZSUFOrWrYvP5zuq5pYtWwDIysrC4XBQt25dHA4HBw8eBGDjxo2cc845ZZ5jMpmOqvPn/a5YsYIbb7yRBQsW0KRJE5YsWRKQ1y8iIiISKBo5FZHqqVH/0smPjsVkJq9WD848zdIbNmwgOTkZs9mMx+Nh2LBhnHfeeXg8HkaMGMGmTZuw2Ww0atSIgwcP0rRpU+bMmcOFF17I9ddfz6233kpkZCSJiYn+wPlnWVlZ3HHHHeTn5/PEE09gsViYNGkSw4YNw2QyERcXx5QpU9i2bdtx+0xISKCkpIRp06Zx9dVXM27cOCIjIzGbzUycOPE0X72IiIhIcCicikj1FBZTOivvX2frNZnh8pX4DpUzqnoCHTp0YP369cdc16RJE1asWHHU8nr16vGf//wHgF69ep1wH+3bt+eRRx4ps6xjx4507NjxqF46dOgAwNq1a/3Lj8wkDPDee+/5v9ZoqYiIiFRmCqciUn3V7Qw3/g57UiF/O8ScDw37Q5gdDmUY3Z2IiIiI/InCqYhUb2F2aHyX0V2ctL59+xrdgoiIiIghNCGSiIiIiIiIGE7hVEREpJryer2MHz+e/v37k5yczO7dR99Cyev1MmjQIBYvXmxAhyIiIn9QOBUREammVq9ejcvlIjU1lREjRpCSknLUNs8//zx5eXkGdCciIlKWwqmIiEg1lZ6eTpcuXQBo1aoVmzdvLrN+1apVmEwm/zYiIiJG0oRIIiIi1ZTD4cBut/sfWywW3G43VquVn3/+mQ8++IAXX3yRl1566bh1MjJCN7t1UVFR0PYXzNrBrl9Vawe7flWtHez6VbV2sOur99DXPlUKpyIiItWU3W7H6XT6H3u9XqzW0h/97777LgcOHOCOO+7gt99+IywsjLPOOoukpKSj6jRv3jxkPWdkZARtf8GsHez6VbV2sOtX1drBrl9Vawe7vnoPfe1jSU9PL3edwqmIiEg11aZNGz7//HN69uzJpk2baNq0qX/dyJEj/V/PnDmTxMTEYwZTERGRUFE4FRERqaa6d+/O2rVrGTBgAD6fj8mTJzN//nwaNmxIt27djG5PRESkDIVTERGRaspsNjNx4sQyyxo3bnzUdsOGDQtVSyIiIuXSbL0iIiIiIiJiOIVTERERERERMZzCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDKZyKiIiIiIiI4RRORURERERExHAKpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGE7hVERERERE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JrFmzRqaN28eyt2KnJ4wOzS+q8yi492bs1MnC6+/HsnevZFYLNCxo4fISBcul4vDhw/jdrsxm81Yrdaj/lgsllNuLy0NRo+GbdugSROYMuXYvaWlwV13eSkNoW5mzHDTrp2bNWvcxMV5iImxcOiQjW+/DadbtxisViudOsHrr0NmJv//WsrW1D1KRURERCTQQhpO27Rpw1VXXUVqqm7FIdXP0SOKFiCSyMhI/zYejwe3243b7aakpPSUYLfbjc/nw2w2H/cPgMlUeprwV19ZGDmyiPx8HyYTbNrkY8AAL2+84eWSS7x4vX/8WbPGS2ysj5gYK4cOhZGebiUpKYrLLrMyb56F7GwTFgt06gRWa3mvJdRHU0RERERqmqCE06VLl/Lmm2+WWTZ58mR69uzJV199ddznZmRkBKOlgCkqKqr0PVZ21fkY1qkDffqUfn0qL9Hn8+H1esv8/dev/7ztpk12vF4HkZHm/19morDQxH/+k0PduocxmUyYzWZMJhMNGkSRmdmAAwdKJy5q0OB3du0qpH59eOKJSP73v0haty6kTp3CMj2f7mupKqrz+zBUdAxFREQkkIISTvv160e/fv1O67mV/ZTfjIyMSt9jZadjWHFbt/7C/PmJ5OX9sSwuDvr0qUWrVmW3vegiOOecP4+CnuNf17w5JCeHouPKR+/DiqsKxzA9Pd3oFkREROQkabZekSqoXbtC3n//5K45BV0jKiIiIiKVn8KpSBWVlARr1xrdhYiIiIhIYIQ8nHbo0IEOHTqEerciIiIiIiJSiRlz00URERERERGRP1E4FREREREREcMpnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDKZyKiIiIiIiI4RRORURERERExHAKpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGE7hVERERERERAyncCoiIiIiIiKGUzgVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiIiIiJiOKvRDYiIiIiI/NX+/ftPuE1WVtYJt6tfv36gWhKRINPIqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FRERqaa8Xi/jx4+nf//+JCcns3v37qO2yc7O5pprrqG4uNiADqsIk+mk/jRv0eLE24mISLkUTkVERKqp1atX43K5SE1NZcSIEaSkpJRZ/9///pc777yTzMxMgzoUERH5g8KpiIhINZWenk6XLl0AaNWqFZs3by6z3mw2M3/+fGrVqmVAdyJSk5zkCQi0aNFcJyDUYLqVjIiISDXlcDiw2+3+xxaLBbfbjdVa+uO/U6dOJ1UnIyMjKP0dS1FRUdD2d7q1mwewh9N9bafbe1ZW1gm3KS4uZtu2bSfcLicn55T3D8b3Huq+K0P9ylk7cN9JlfH7CKru+zHY7/VToXAqIiJSTdntdpxOp/+x1+v1B9NT0bx5IOPZ8WVkZARtf8GsfbJOd/+n2/vJ3Ct027ZtNGnS5ITbne79Qo3uPdR9V4b6VbX2yaqM30dQdd+Pof43TU9PL3edTusVERGpptq0aUNaWhoAmzZtomnTpgZ3JCIiUj6NnIqIiFRT3bt3Z+3atQwYMACfz8fkyZOZP38+DRs2pFu3bka3JyIiQXYyo75ZWVkntd3pjvqeCoVTERGRaspsNjNx4sQyyxo3bnzUdp999lmoWhIRESmXTusVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiIiIiJiOIVTERERERERMZzCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDKZyKiIiIiIiI4RRORURERERExHAKpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGE7hVERERERERAyncCoiIiIiIiKGs4ZqR/n5+Tz66KM4HA5KSkp47LHHaN26dah2LyIiIiIiIpVYyMLp/PnzufTSSxk4cCA7d+5kxIgRvPPOO6HavYiIiIiIiFRiIQunAwcOxGazAeDxeAgPDw/VrkVERERERKSSC0o4Xbp0KW+++WaZZZMnT6Zly5ZkZmby6KOPMmbMmGM+NyMjIxgtBUxRUVGl77Gy0zGsOB3DitMxrDgdQxEREQmkoITTfv360a9fv6OWb926lYcffpiRI0dyySWXHPO5zZs3D0ZLAZORkVHpe6zsdAwrTsew4nQMK64qHMP09HSjW5A/M5lOuMlJvaN8vgq3IiIilU/ITuvdvn07w4cP5/nnn6dZs2ah2q2IiIiIiIhUASELp9OnT8flcvH0008DYLfbmTNnTqh2LyIiIiIiIpVYyMKpgqiIiIiIiIiUx2x0AyIiIiIiIiIKpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGC5kEyKJiIiIiEjFnMTtgjmZOwbrdsFSGWnkVERERERERAyncCoiIiIiIiKGUzgVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiIiIiJiOIVTERERERERMZzCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDKZyKiIiIiIiI4RRORUREqimv18v48ePp378/ycnJ7N69u8z6JUuW0LdvX2666SY+//xzg7oUEREpZTW6AREREQmO1atX43K5SE1NZdOmTaSkpDBnzhwAMjMzWbBgAf/+978pLi7mlltuoVOnTthsNoO7FhGRmkojpyIiItVUeno6Xbp0AaBVq1Zs3rzZv+7777+ndevW2Gw2YmJiaNiwIT/99JNRrYqIiGDy+Xw+o5s4Ij093egWRESkmmnbtq3RLRhm7NixXH311XTt2hWAyy+/nNWrV2O1Wnnvvff4+eefefTRRwEYOXIkN9xwAx07dixTQz+bRUQk0Mr72VypTuutyb9AiIiIBJrdbsfpdPofe71erFbrMdc5nU5iYmKOqqGfzSIiEio6rVdERKSaatOmDWlpaQBs2rSJpk2b+te1bNmS9PR0iouLyc/PZ8eOHWXWi4iIhFqlOq1XREREAsfr9TJhwgR+/vlnfD4fkydPJi0tjYYNG9KtWzeWLFlCamoqPp+Pu+++m2uuucbolkVEpAZTOD1F+fn5PProozgcDkpKSnjsscdo3bq10W1VSZ988gmrVq1i+vTpRrdSZRz5RXPr1q3YbDYmTZpEo0aNjG6rSvruu+949tlnWbBggdGtVDklJSWMGTOG3377DZfLxb333ku3bt2MbktERESquEp1zWlVMH/+fC699FIGDhzIzp07GTFiBO+8847RbVU5kyZNYs2aNTRv3tzoVqqU490WQk7e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FouFxo0b88MPP9CxY0dWrFjB4cOHT6oGQF5eHvHx8SfcTkQEghvCglk72PXHjh3LI488UmZypU2bNjF69GjefvvtCtUOdggLZv2xY8cyYsSIMvevDdRxCWZtCH4IC2b9YNZu2LAhc+bMqXCd4wlmCKuqtZOTkykpKSmzzOfzYTKZAvJ+rwiFU5FyXH311ezYsYN//OMfREVF4fP5GDlyJDExMUyYMIFRo0b5Z/F9+umnOXjwIG63m8GDB5Obm8u9995LfHw8119/PbfeeiuRkZEkJiZy8ODB4+7XbDYzduxYhgwZgs/nIzo6mqlTp7Jv375jbv/EE08wZswYoqKiCAsL89+frV27dgwZMoT777//hK+1TZs2bNmyhZYtWzJy5EjGjx/PnDlziIiIYNq0aWzZsuWENfLy8oiNjSU6OvqE24qIQHBDWDBrB7u+y+U6atbfVq1aVajmEcEOYcGs73K5ytSFwB2XYNaG4IewYNYPZu1jhaQjAvF+DGYIq6q1ofQ2NePGjeOll17CYrFUuF4g6VYyIgHy1VdfndQtWwJt0aJFXHvttcTHxzNjxgzCwsJO+Z5m//vf//jwww8ZN25chfqw2+306dPntGuISM0yYMCAY/6iVd7yylI72PWfeOIJXC4XXbp0ISYmBqfTyZdffum/h2JF6LiEvjYYE8ICVT+Ytb/77rtyQ9JZZ51VodrBrl9Vax8xd+5cGjVqRPfu3QNSL1A0cipSxSUkJHDnnXcSFRVFTEwMKSkpp1yjdevWrFixgv3795/2fU6//fZbpk2bdsrPFZGa64ILLmD06NFHBYILLrigUtcOdv0JEyawevVq0tPTcTgc2O12rrjiioD8EqnjEvraEPyRqmDWD2btiy++mD59+rB169aghKRg1q+qtY8YNGhQUOpWlEZORURExBA+n++oQNCmTRu6d+9e4fsnBrN2KOoHi46LcYI9UhXM+pV1lE2qH4VTERERERERMVxg5w8XEREREREROQ0KpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGE7hVERERERERAz3f1+Laopar/e8AAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "## 2 points distance-based classifier ##\n", - "\n", - "# Set-up a new connection with qi_backend:\n", - "def initialize_qi_backend(project_name = \"distance_based_classifier\"):\n", - " compiler_engines = restrictedgateset.get_engine_list(one_qubit_gates=\"any\",\n", - " two_qubit_gates=(CNOT, CZ, Toffoli))\n", - " compiler_engines.extend([ResourceCounter()])\n", - " qi = QuantumInspireAPI(\n", - " r'https://api.quantum-inspire.com/',\n", - " authentication,\n", - " project_name = project_name # Set project name to save projects\n", - " ) \n", - " qi_backend = QIBackend(num_runs=1, quantum_inspire_api=qi)# set num_runs = 1 for true probability distribution\n", - " qi_engine = MainEngine(backend=qi_backend, engine_list=compiler_engines)\n", - " return qi_backend, qi_engine\n", - "\n", - "\n", - "qi_backend, qi_engine = initialize_qi_backend(\"distance_based_classifier_2_points\")\n", - "# Data points:\n", - "x_tilde = [0.9999, -0.0011] # Label 1\n", - "x0 = [-0.4583, -0.8889] # Label 1\n", - "x1 = [-0.3728, 0.9279] # Label 0\n", - "\n", - "# Angles data points:\n", - "angle_x_tilde = 2 * acos(x_tilde[0]) * sign(x_tilde[1]) # Label 1\n", - "angle_x0 = 2 * acos(x0[0]) * sign(x0[1]) # Label 1\n", - "angle_x1 = 2 * acos(x1[0]) * sign(x1[1]) # Label 0\n", - "\n", - "# Quantum circuit:\n", - "qubits = qi_engine.allocate_qureg(4)\n", - "\n", - "# part_a\n", - "for qubit in qubits[0:2]:\n", - " H | qubit\n", - "\n", - "# part_b\n", - "C(Ry(angle_x_tilde), 1) | (qubits[1], qubits[2])\n", - "X | qubits[1]\n", - "\n", - "# part_c\n", - "C(Ry(angle_x1), 2) | (qubits[0], qubits[1], qubits[2])\n", - "X | qubits[0]\n", - "\n", - "# part_d\n", - "C(Ry(angle_x0), 2) | (qubits[0], qubits[1], qubits[2])\n", - "\n", - "# part_e\n", - "CNOT | (qubits[0], qubits[3])\n", - "\n", - "# part_f\n", - "H | qubits[1]\n", - "\n", - "qi_engine.flush()\n", - "\n", - "# Results:\n", - "temp_results = qi_backend.get_probabilities(qubits)\n", - "print(temp_results) # Print the results in a dictionary\n", - "prob = DataPlotter.plot_data_points(x_tilde,[x0], [x1], temp_results) # Function to plot the data\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The classifier predicts the wrong label 0 for the test point with this combination of data points as:\n", - "\\begin{equation}\n", - "0.04 + 0.1043719 > 0.0366663 + 0.1019124 \\rightarrow \\text{Assign label 0 to } \\tilde{y}\n", - "\\end{equation}\n", - "The left figure gives intuition why the classifier fails to predict the correct label. For this specific combination of data points, the test point is closer to the data point with label 0 than to the data point with label 1. As the prediction is based on only these two data points, it is expected to give this wrong prediction. Note, this problem has nothing to do with the quantum computer used for the calculation. The same results would be obtained with a classical distance-based classifier. \n", - "\n", - "By adding two more data points, one for each label, we improve the classifier so that it is better able to give the right label as prediction. Add the following two points to the calculation:\n", - "\\begin{equation}\n", - " \\begin{split}\n", - " &\\ket{\\psi_{x_2}} = -0.3728 \\ket{0} +0.9279\\ket{1}, \\hspace{2cm}y = 1,\\\\\n", - " &\\ket{\\psi_{x_3}} = -0.4583 \\ket{0} - 0.8889\\ket{1}, \\hspace{2cm}y = 0.\n", - " \\end{split}\n", - "\\end{equation}\n", - "\n", - "Consider the quantum circuit for the four point classifier below." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'01110': 0.2476628, '00011': 0.2373197, '11111': 0.1306251, '10110': 0.1200422, '10000': 0.0531932, '10100': 0.0531932, '11001': 0.0500852, '11101': 0.0500852, '10010': 0.023571, '11011': 0.019204, '00001': 0.0062575, '00101': 0.0062575, '01000': 0.0011657, '01100': 0.0011657, '00111': 0.000165, '01010': 5.5e-06} \n", - "\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6cAAAGBCAYAAACadsdyAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAABuM0lEQVR4nO3dd3hUVf7H8fe01EkCSSgqgoog4C7SFGkBRRRBRF0pUpRVsIIoqAgqIFIiLKKCYMUCiAFXd1GRXbFFArYoIphFQASUlhACmUmZTGZ+f+SXkZgEuJDJTfm8noeHzJ0z537nJjD53HPPuRa/3+9HRERERERExERWswsQERERERERUTgVERERERER0ymcioiIiIiIiOkUTkVERERERMR0CqciIiIiIiJiOoVTERERERERMZ3d7AJERERERMxwwQUX0Lx5c6xWKxaLhdzcXJxOJ1OnTuWvf/1rUPa3YcMGHA4H99xzD2+88UaF70OkOlM4FREREZFa6/XXXyc2Njbw+JVXXmH69OkkJSUFbZ9Hjhzhxx9/DFr/ItWVLusVEREREQG8Xi/79u0jJiYmsG3RokVcf/319O/fn7vvvpsDBw4A8N///pfrr7+eG264gQEDBvDNN98AMHz4cNasWRN4/Z8fA0ycOJG8vDz69+9PYWEhzz77LP369eOGG27gtttu4+DBg5XwbkWqHo2cioiIiEitdcstt2CxWMjMzCQ0NJTLLruMWbNmAfCvf/2Ln3/+mZUrV2K320lKSuLRRx/lpZdeYvbs2fzjH/+gTZs2rFu3jq+++oqLL774pPY5a9Ys+vXrx7///W/27dvH66+/zoYNGwgJCWHx4sVs2rSJK664IphvW6RKUjgVERERkVqr+LLen376iVGjRtG2bVvi4uIA+PTTT/nxxx/529/+BoDP5yM3NxeAvn37Mnr0aLp3706XLl0YNWrUKe2/QYMGtGjRguuvv56EhAQSEhLo1KlTxbw5kWpG4VREREREar1WrVoxceJEHn30US666CIaNWqEz+dj5MiRDBkyBACPx8ORI0cAuP/++7nxxhtZt24d77zzDi+++CLvvPMOAH6/P9BvQUHBcfdrtVpZunQpP/74Ixs2bGDmzJl07NiRRx99NEjvVKTq0pxTERERERHgmmuuoU2bNsycOROArl278vbbb+NyuQB45plneOihh/B6vVx++eXk5ORw0003MWXKFHbs2IHX6yU2NpbNmzcDsHv3brZu3VpqP3a7ncLCQvx+P//73/+45ppraNq0KXfccQcjRowo8zUitYFGTkVERERE/t9jjz3GtddeyxdffMGAAQM4cOAAAwcOxGKxcMYZZ5CYmIjdbmfSpEk88MAD2O12LBYLM2fOJCQkhLvuuouHH36Yzz//nPPOO48OHTqU2ke9evVo1aoVV199NcuXL+fqq6/mb3/7GxEREYSFhWnUVGoti//Y6w5ERERERERETKDLekVERERERMR0CqciIiIiIiJiOs05FRERqaF8Ph9Tp05l69athISEMH36dJo0aRJ4fvr06Xz33XdERkYCsHDhQqKioswqV0REajmFUxERkRpq7dq1eDwekpKS2LhxI4mJiSxatCjw/JYtW3j55ZeJjY01sUoREZEiVSqcpqamml2CiIjUMO3btze7BNOkpqbSrVs3ANq0aRO4vQUUjaru2rWLyZMnk5GRwY033siNN95YZh8iIiIVqdzPZn8V8u2335pdwgn99NNPZpdQ7ekYnj4dw9OnY3j6qsMxrA6fK8E0adIk/2effRZ43L17d39BQYHf7/f7s7Oz/c8995w/JyfHn52d7b/++uv9aWlppfoI5jE0+jNkpH0w+65Kteh91qxaasv7rEq16H1WTHsjjve5ogWRREREaiin04nb7Q489vl82O1FF02Fh4dz8803Ex4ejtPp5NJLL+V///ufWaWKiIhotV4REZGaql27diQnJwOwceNGmjdvHnju119/5aabbqKwsJCCggK+++47LrzwQrNKFRERqVpzTkVERKTi9OrVi5SUFAYPHozf72fmzJm8+uqrNG7cmJ49e9K/f38GDhyIw+Ggf//+NGvWzOySRUSkFlM4FRERqaGsVivTpk0rsa1p06aBr0eOHMnIkSMruywREZEy6bJeERERERERMZ3CqYiIiIiIiJhO4VRERERERERMp3AqIiIiIiIiplM4FREx4LfffmPgwIGlticnJ5OUlHRSfbz77rvcfPPNDB8+nMGDB7Nu3brjtk9KSqKgoOCU6j0ev9/Pww8/jNvtZvjw4ezYsaPctpdffjn5+fkn1W9ZbTdu3MiAAQMYPHgwCxYsACAjI6PUYj0iIiJSe2m1XhGRCpCQkHBS7bKzs1m4cCEffPABISEhHDhwgAEDBvDZZ59htZZ9vvCFF17guuuuq8Bqi3z44YdceOGFREZGVnjffzZlyhTmz5/P2Wefze23385PP/1Eq1atiIyM5Ouvv+aSSy4Jeg0iIiJStSmcikjNl5wM69dD585wkiHyZAwfPpzY2FiOHDlC37592bVrF2PGjGHs2LG4XC5yc3O5//776dq1a+A1ISEhFBQUsHz5ci677DIaN27M2rVrsVqt7Nu3j8cee4z8/HxCQ0N54oknWLduHenp6dx///0sXLiQxMREUlNTAbjmmmu45ZZb+O9//8tLL72E3W6nfv36zJs3j4MHDzJ16lTy8/NJT0/nvvvu44orrihR/5IlS3juuedKbNu/f3+5r5s8eTK///47cXFxPPnkk3i9XiZNmsSuXbvw+Xzcd999dOzYsdRxcrlceDweGjduDEDXrl1Zv349rVq14pprrmH+/PkKpyIiIqLLekWkhktOhltvhRdeKPo7OblCu7/mmmt47bXXsNlsAOzevZusrCyef/55nnrqKQoLC0u0Dw0N5fXXX2fXrl2MHDmSyy67jLfffhuAJ598kuHDh7NkyRJuu+02/vGPfzBgwADq1avHvHnz+PTTT/ntt99YsWIFb775Ju+//z5bt27l/fff57bbbgsEXpfLxS+//MLf//53Xn31VaZNm8ayZctK1JGXl8e+ffuIjY0tsf14r7vppptYunQpZ511FitWrOCjjz6ibt26LFu2jIULF5Z7ia7L5cLpdAYeR0ZGkp2dDcD5558fCNsiIiJSu2nkVERqtvXrobAQ4uLg0KGixxU4enruueeWeNysWTMGDRrEuHHj8Hq9DB8+vMTzBw4cIC8vj8mTJwOwc+dORo4cSfv27fn555954YUXePnll/H7/djtJf+L3rFjBx06dMBiseBwOLjooovYsWMHEydO5IUXXmDp0qWcd955XHHFFdSrV49Fixbx9ttvY7FY8Hq9Jfo6cuQIdevWLfV+ynudw+GgTZs2ALRr146UlBQyMzP55Zdf2LRpEwBer5fMzMxSfTqdTtxud+Cx2+0mOjoaAJvNht1ux+fzlXtZs4iIiNQO+k1ARGq2zp3BZisKpjZb0eMKZLFYSjzeunUrbrebF198kcTERJ544okSz2dkZPDggw/icrkAOOuss6hbty4Oh4PzzjuPBx54gCVLlvD444/Tu3fvwD58Ph9NmzYNjDIWFBTw/fff06RJE5KSkhgzZgxLly4F4KOPPuKZZ56hf//+zJkzh44dO+L3+0vUUbdu3RKBsVh5rysoKCAtLQ2Ab7/9lmbNmtGoUSP69u3LkiVLeOmll+jduzd16tQp1afT6cThcLB79278fj/r1q2jQ4cOAIEQrmAqIiIiGjkVkZotIQEWLw7KnNOynHPOOTz33HN8+OGH+Hw+7r333hLPX3jhhQwfPpxhw4YRFhZGYWEhAwYM4LzzzmPChAmB+Z55eXk88sgjAHTo0IHbb7+dN954g6+//ppBgwZRUFBA7969ufDCCzlw4AB33HEHkZGRRERE0KNHDxwOB7Nnz+bFF1+kYcOGHD58uEQdISEhxMfHc+jQIeLi4gLbe/fuXebrHA4HS5YsYdeuXZx55pmMHz+etLQ0li5dyrBhw3C5XAwZMqTckPn444/zwAMPUFhYSNeuXbnooouAojBfPCIrIiIitZvCqYjUfAkJFRZKGzVqxIoVK4CiBYWK3XDDDYGvn3322eP2MWDAAAYMGFBq+9lnn80rr7xSavuTTz4Z+HrChAmlnr/88su5/PLLS2y75ppruOaaa45bx5AhQ3jvvfcYMWJE4L00bdq0zNf95z//KbWtOAD/2SeffFJqW5s2bQLH7VirVq1i6NChx61TREREagddRyUiUkv17duXLVu2lHl5b2VIT0/H5XIFLvEVERGR2k0jpyIitZTFYmHOnDmm7b9evXrlrvArIiIitY9GTkVERERERMR0CqciIiIiIiJiOoVTERERERERMZ3CqYiIiIiIiJhO4VRExIDffvuNgQMHltqenJxMUlLSCV8/YcIE3n777RLbXnvtNebNm3dK9aSnpzN16lRDr/nqq6+4//77T2l/Ho+HBx98EJ/Px6hRo8jPzy+3bZcuXU663/La5ubmMnjwYHbs2AFARkaGFlESERGpoRRORUQqQEJCAoMGDTphuwEDBvDvf/+7xLZ33323zPuenox69eoZDqen47XXXuPqq6/Gag3+x8ePP/7I0KFD2bNnT2BbfHw8kZGRfP3110Hfv4iIiFQu3UpGRGq85GRYvx46d4aEhIrrd/jw4cTGxnLkyBH69u3Lrl27GDNmDGPHjsXlcpGbm8v9999P165dA6/p0KEDmZmZ/P7775x11lls2rSJ+Ph4GjVqxJIlS3j//fexWCz06dOHm2++mYcffpisrCyysrJYuHAh9913H36/n/z8fB5//HGioqIYN24cK1as4NNPP2XBggX4/X4uvPBCHn/8cTZs2MDTTz9NaGgoderUYebMmSXew6pVq3j99dcJCQnhnHPOYdq0abz33nv885//xOfzce+999KpUycA/H4/q1at4t133y3Rx88//0xiYiKFhYUcPnyYqVOn0q5dOzweD/fffz/79u3jggsuYOrUqbhcLh555BEOHz4MwKOPPsoFF1xQ5vH1eDw899xzPPTQQyW2X3PNNcyfP59LLrnktL+HIiIiUnUonIpIjZacDLfeCoWF8PLLsHhxxQbUa665hl69evHOO+8AsHv3brKysnj55Zc5dOgQv/76a6nX3HjjjaxatYq77rqLd955h8GDB7N9+3ZWr17Nm2++CcDf//73QKi99NJLGTFiBJ999hl16tRh9uzZbN++nZycHKKiogDwer088cQTrFy5kri4OF566SX27dvHY489xvLly2nQoAGvv/46ixYtokePHgAcPnyY+fPn8+677+J0Opk5cyZJSUlEREQQHR3NokWLStT966+/4nQ6cTgcJbZv376dCRMmcMEFF/Dee+/xzjvv0K5dO/Ly8njggQc466yzGDt2LJ988gnfffcdl156KUOGDOHXX39l4sSJLF++vMxj2759+zK3n3/++aSmpp7cN0hERESqDYVTEanR1q8vCqZxcXDoUNHjigyn5557bonHzZo1Y9CgQYwbNw6v18vw4cNLvaZ///6MGDGCW2+9la+//ppHH32U//73v+zdu5cRI0YAcOTIEXbt2lViHwkJCfz666/cfffd2O127rrrrkCfhw8fJjo6mri4OABGjRpFZmYmTqeTBg0aAHDxxRfz1FNPBcLpnj17OP/883E6nYHn161bx0UXXVTqfRXvIz4+vtT2+vXrs3DhQsLCwnC73YH+zjzzTM466ywA2rZty86dO/n555/58ssv+fDDDwPv0yibzYbdbsfn81XK5cUiIiJSOfSpLiI1WufOYLMVBVObrehxRbJYLCUeb926FbfbzYsvvkhiYiJPPPFEqdfExsbStGlTFi5cSK9evbDb7Zx33nmcf/75vPHGGyxZsoQbbrghcLlr8T6++uor6tevz+LFi7nrrrt46qmnAn3GxcVx9OhRsrKyAJg+fTp79uzB5XJx8OBBAL7++mvOOeecwGsaNWrEjh07yMnJCTxfHErLCn3F+/izGTNmcO+99/Lkk0/SvHlz/H4/APv37w/s+7vvvqNZs2acd955jBgxgiVLlvD0009z7bXXnvgg/4nf78dutyuYioiI1DCVNnJaUFDApEmT+P333/F4PNx111307NmzsnYvIrVUQkLRpbzBmHNalnPOOYfnnnuODz/8MDBnsywDBw5k1KhRrFmzBoAWLVrQqVMnbrrpJjweD61btw6MeBZr0aIF48aNY/ny5Xi9Xu65557Ac1arlSlTpnDHHXdgtVpp1aoVrVu3Zvr06YwZMwaLxUJMTAyzZs1i27ZtQFFIHjNmDDfffDNWq5XGjRvzwAMP8MEHH5RZc5MmTcjMzMTr9WK3//Hxce211zJ27Fiio6Np2LBhYD5pnTp1mD59OgcOHKBt27Z0796d1q1b88gjj7BixQpcLhejR482fIy3bt1KmzZtDL9OREREqrZKC6erVq2iTp06zJkzh6ysLK677jqFUxGpFAkJFRdKGzVqxIoVKwBYsmRJYPsNN9wQ+PrZZ589YT+dOnVi8+bNJbaNHDmSkSNHltiWmJgY+Lpu3bq8+uqrpfoqrqd79+507969xHOdO3em85+Gizt27EjHjh0B6NevH/369Svx/LHv5c/69evHp59+Sq9evXjppZcIDQ3l73//O3//+99Ltf38889Lbatbty4LFy4stT0lJaXcfR57nKHo82To0KHlthcREZHqqdKuierduzdjx44Fii7JstlslbVrERGpILfccgtr1qzB5/OZsv/09HRcLhcdOnQwZf8iIiISPJU2choZGQmAy+Xi3nvv5b777iuzXVpaWmWVdEry8vKqfI1VnY7h6dMxPH06hqdu5MiRbN261bRjeNNNN+l7JyIiUgNV6mq9+/bt45577mHIkCGlLiMr1rJly8osybC0tLQqX2NVp2N4+nQMT5+O4emrDsdQt5wRERGpPiotnGZkZHDrrbcyefLkwA3dRURERERERKAS55w+//zzHD16lIULFzJ8+HCGDx9OXl5eZe1eREREREREqrBKGzl99NFHefTRRytrdyIiIiIiIlKNVOqcUxGRypadn03SliS2HdpGs7hmDLpwEFGhUafcX2JiIlu2bCE9PZ28vDzOPvts6tate1K3j4Gie3QePXqUiy+++JRrGD16NAsWLCj3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plM4FREREREREdMpnIqIiIiIiIjpFE5FRERERETEdAqnIiIiIiIiYjqFUxERERERETGdwqmIiIiIiIiYTuFURERERERETKdwKiIiIiIiIqZTOBURERERERHTKZyKiIiIiIiI6RRORURERERExHQKpyIiIiIiImI6hVMRERERERExncKpiIiIiIiImE7hVEREREREREyncCoiIiIiIiKmUzgVERERERER0ymcioiIiIiIiOkUTkVERERERMR0CqciIiIiIiJiOoVTERERERERMZ29snf4ww8/8I9//IMlS5ZU9q5Fqo/kZFi/Hvbtg61boW9fGDOm7DaRkeB2Q+fOkJBgTr0iIiIiIqepUsPpSy+9xKpVqwgPD6/M3YpULScKnsnJ+G67Dd+RI/iysvDZ7fhTUsDnwz9qFACWr74iZ/ZsLDk5cOgQlthYrK+9hnXRIqzdu2O1WkvuS8FVRERERKq4Sg2njRs3Zv78+Tz00EPltklLS6vEiozLy8ur8jVWdbX5GNZZupR6CxZQ6Pfj83goCAmh8JtvOLB/P1n9+uH3+4latYroiAgcBQXY/H4sFgtWn4/c995jf5cuADg3bsTl82Gx27GGhuINCcFns5H1wQccDg0FIOynn2j47LNYCguxvfEGGQ8+SEG7dthsNqxWKxaLhfBvvyX8++/xhYdjzc0lt21bcjt0MPMQVZra/HNYUXQMRUREpCJVaji96qqr+O23347bpmXLlpVUzalJS0ur8jVWdTXiGBZkw64kyN4GUc2gySBwRJVq5vf78Xg8eDwevOvX401KIj0qCqvXi93hwA6EFBRw7saNWKdOxWq1YvV4sPzrX5CdDZmZYLUW/enfn0b/Hxx/TUuj/vvvF13Oe+QIOBwQGckZ114LnTvj9/vxff45vpwcfHFxFB45Qtj+/XjPPhuv10thYSG2jRuxz52L3eUiZP9+QqKjsa5aBYsX14pR1hrxc2iy6nAMU1NTzS5BRERETlKlzzkVqfYOroPP+oDfB4VusEXCd+Ogx2p88Z0DYdTj8VBQUIDD4SAkJITQ774jMj8f+6FDWPPzwe//I3j27l0UMAG6dy8KiMe59De3Q4c/2pQx59RisWDr0gXbK6/AwYNgs0GXLhAbCxSFZu/GjXhzc/FarbgjIsiKj8dmtRKSkkLIxRcTEhKCzWar9MMrIiIiIrWTwqmIEQXZRcHUmx3Y5CnwkFcYTv5/huHt8TEhEXUICQkhKiqKkJAQLBZLUcMuXeCVV6BOHfB44LLLwOUqe7GjhIQTj16eqE1Cwh8B9k9zTi0WC44uXXC88kpRsD18GL/NRkFMDJ6LLyY3N5cjR45gtVoJCQkhLCyM0NDQP96LiIiIiEgFUzgVOVnJyfDDMxBfgMfnIK8wnFxfGBb8hNvyiLEfwZH/KZazR5b9+uOExaA5XoA9tp7ISCxuNyGdOxNyTPuCggLy8/NxuVxkZWURFhamoCoiIiIiQVHp4bRRo0asWLGisncrcuqSk2HZMjxr1pDXx0vuJdGBQBrryMRh9Ra18wOuHcfv62RGRCvTCepxOBw4HA6cTieFhYXk5uYqqIqIiIhIUGjkVOQ4/J9/Tu5dd+HOz8dfUEB4dhixvnQc4QWlG9siIer8yi+ykthsNpxOZyCo5uXl4XK5OHLkCJGRkURERPxxCxsREREREYMUTkXKUFhYiNvtJiclhRC7nWirldDdu2FtJPT0lv0iixUaD6rcQk1is9mIjIwkMjKSgoIC3G43Bw8eJCwsjMjISBzFizuJiIiIiJwkDXOIHMPj8XD48GHS09Px+/3Ed+pEbE4OoW431K8P/QdBo2fBHlU0UgpFf9ujoMdqcDjNfQMmcDgc1KlTh/r162O328nMzCQjI4Pc3Fz8fr/Z5YmIiIhINaGRU6ndkpNh/XoKOnbkaOvWFBYWEhkZSUxMTNElqpddVvYiRgUjYHcSZG8vupS38aBaGUyPZbVacTqdREZGkpeXh9vtJjs7m+joaMLCwswuT0RERESqOIVTqb1uvBHvBx+QHR+P5+23cSYmEtGzZ+nFfcpaNMjhhKa3VV6t1YjFYiE8PJzw8HDy8vLIzs7G5XIRHR1NSEiI2eWJiIiISBWly3qlViocO5Yja9eSERODIyuL+j//TOS332rV2QoWFhZGfHw8ERERHD58mMzMTAoKylhMSkRERERqPY2cSq3i9/txuVy4N2wgwu+nfno6Vp8PHI6iy3alwlksFiIiIggPDycnJ4dDhw4RFhZGdHS0VvcVERERkQD9Zii1hsfjISMjg4KCAup16kT00aNFwRTg2mur1v1HayCLxUJkZCT169fHarWSnp5Obm6u2WWJiIiISBWhkVOp2ebPx790KdmNGpF7221EX3YZ4eHh8MwzRc9/+CFcffUfjyXorFZrYJGkrKwscnNziYmJwWazmV2aiIiIiJhI4VRqpuRk+Mc/8Pz3v2RFR+PYto16yclY//nPP0ZIn3lGodREISEh1KtXj+zsbNLT04mJiSk6cSAiIiIitZIu65WaJzkZ/623cvTLLzlcpw5R2dnUPXwYq8tVdEsYqTIsFgvR0dHExcXhcrnIzMzEV3yptYiIiIjUKgqnUuMUpqRwyOnEGx9PvfR0wvPyip6IjNSiR1WUw+EgPj4eu91Oeno6Ho/H7JJEREREpJIpnEqNkp+fT8ZFFxHm9RKbm4u1bl1o0QL69YN33tGiR1VY8ShqTEwMmZmZuN1us0sSERERkUqkOadSY7jdblwuF3V69iR04cKiS3g7d1YgrWaK7416+PBhCgoKiImJ0f1nRURERGoBhVOp9vx+P0ePHsXj8RAXF4fdbi8KpAql1ZbdbicuLo6srCwyMzOpW7eu7okqIiIiUsPptz2pvubPx9+7N5nPPENhYeEfwVRqBKvVSt26dXE4HGRkZFBYWGh2SSIiIiISRPpNXqqn+fPxjxtHZt26WFNTqWO1Yrn3XrOrkgpWPA/VarVy6NAh4uLidD9UERERkRpKI6dSLfk/+IDMunWx2WzUOXwYy+rVZpckQeR0OomMjCQjIwOv12t2OSIiIiISBBo5lWrH9/nnZEZFYS8spE5mJlgs0Lev2WVJkEVGRgJw6NAh0tLi+PJLu9a7EhEREalBFE6l+khOxrdsGZlffIHDaiXGYoH27WHYMBgzxuzqpBJERkayYYOFhx8+RFZWHC+/bGfxYgVUERERkZpAl/VK9ZCcjP/WW8n86CMcBw4QU1AAUVFw/fUKprXMt99G4HJFcfbZh/D7C1m/3uyKRERERKQiKJxK9bB+PUfCw7HGxhJz9CgcPAg2W9F9TKVW6dwZvN4IDhyIJCYmk06d/GaXJCIiIiIVQJf1SrXg6tCBgpUriT90COrXh2uugaFDdT1nLZSQAIsXw/r1Ttq2LaB16yygrtlliYiIiMhpUjiVKi8vLw/3X/9K/Ny5WL78Eq2CIwkJRX/8/jocOnSI7OxsoqKizC5LRERERE7DSYXTn3/+maysLOLi4mjatGmwaxIJKHj2WbI++4zYHj2w3Xsv9OhhdklShVgsFurWrUtGRgZ2u53w8HCzSxKpUnw+H1OnTmXr1q2EhIQwffp0mjRpEnh+2bJlvPPOO1gsFm699Vb69OljYrUiIlLblRtOPR4PL774ImvWrCEuLo74+HiOHj3KwYMHufrqqxkxYgRhYWGVWavUMr6xYzn81ltEu92EvPde0S1jtPiR/InNZiM2NpZDhw5ht9txOBxmlyRSZaxduxaPx0NSUhIbN24kMTGRRYsWAZCZmcny5ct59913yc/Pp2/fvlx99dVYLBaTqxYRkdqq3HA6efJk+vXrx913343V+se6SX6/n+TkZCZPnszs2bMrpUiphZKTOZqURGh+PhE5OUXB9IMPFE6lTA6Hg+joaLKysoiPj9cv1yL/LzU1lW7dugHQpk0bNm/eHHguNjaWf/3rX9jtdn7//XdCQ0P1b0dERExVbjhNTEwsc7vFYqF79+507949aEWJ5KekkB8dTb2DB8H//6ux9u1rblFSpUVERJCbm4vL5dL8U5H/53K5cDqdgcc2mw2v14vdXvTxb7fbWbp0KfPnz2f48OHl9pOWlhaU+vLy8gz1baR9MPuuSrXofdasWmrL+6xKteh9Vkz7inLCOaebNm3igw8+ID8/P7Bt6tSpwaxJajmfz0fWRRdRx2bDWrcu5ObCqFEaNZUTqlOnDunp6YSFhenyXhHA6XTidrsDj30+XyCYFhs2bBgDBw5k1KhRfPnll1x66aWl+mnZsmVQ6ktLSzPUt5H2wey7KtWi91mzaqkt77Mq1aL3WTHtjUhNTS33uRPe53TChAlccMEFdO3aNfBHJJiOHj1KaOfOhL7wAjz4IKxZA888Y3ZZUg3YbLbA5b1+v+5/KtKuXTuSk5MB2LhxI82bNw8898svvzB69Gj8fj8Oh4OQkJAS03hEREQq2wlHTps0acINN9xQGbVIbZecXHQ570UXUa937z/uFyJigC7vFflDr169SElJYfDgwfj9fmbOnMmrr75K48aN6dmzJy1atGDQoEFYLBa6devGJZdcYnbJIiJSi50wnF511VXcf//9JW4hM3r06KAWJbVQcjL+W2/lSFQUdd54A6vTqWAqp6z48t7w8PBSlzCK1CZWq5Vp06aV2Pbnz3N9pouISFVxwut3li1bRsuWLYmPjw/8Ealw69eTY7dji44mNC8P1q83uyKpxmw2G5GRkbhcLrNLEREREZGTdMIhhTp16nD77bdXRi1Si/k7dcL19tvU3bsXbDbo3NnskqSai4yM5ODBgyVWJhURERGRquuEv7HVrVuXyZMn06pVq8D9zwYNGhT0wqR2yenQAceMGYR8/31RMNUlvXKarFYrkZGRZGdnU7duXbPLEREREZETOKkFkQAyMjKCXozUTn6/H5fLRezll8NVV5ldjtQgxaOnBQUFurWMiIiISBV3wjmn/fr145xzzmH06NHk5eVx3XXXVUJZUpu43W5CQkIUHqTCWa1WnKGQvfkV+H4CbH8ZCrLNLktEREREynBS9zlt1KgRAN27d+eRRx4JelFSe/j9ftxuN06n0+xSpCY6uI7I/zYn74cn8W55isJv7oN3z4KD68yuTERERET+5KTutt2mTRsALr74Ynw+XzDrkVomPz8fm82mUVOpeAXZ8FkfLIXZOC2Z5PjCsfnd4C3e7ja7QhERERE5xgnDaXR0NElJSWzdupWVK1cSGRlZGXVJbZCcTM68eURs3Gh2JVIT7UoCf9HJtHBbDrmFEfj9RU8VFvqIzlpjYnEip27atGmkpaWZXYaIiEiFO2E4TUxMZPv27cyZM4cdO3Ywc+bMyqhLarrkZHy33Ybn7bcJu+suSE42uyKpabK3wf+PjjqsXqyWQjy+UABsfjch+bvMrE7klPXo0YPnn3+ewYMH8+abb+p+viIiUmOUu1rvli1buPDCC4mNjS1znmnx8yKnZP16cux2wiIisGZmwvr1un2MVKyoZmCLDATUCGsOOb4IQm35FFoi8YQ2MblAkVOTkJBAQkICmZmZzJgxgzlz5nDVVVdx991307hxY7PLExEROWXljpympKQwfvx4PvroI3bv3o3b7Wb//v385z//YezYsSRrpEtOR+fO5EZEEL5/P9hsRfc2FalITQaB5Y//4sJtueT7QvH5LdhsVo7W6W1icSKnbseOHcyZM4dhw4YRHR3NsmXLGDJkCPfdd5/ZpYmIiJyWckdOb7/9dvbv38/y5ct55ZVXOHz4MPHx8VxyySVMmDCBM888szLrlBrG27kzvieeIHTTpqJgqlFTqWiOKOixGj7rA34f1kI3oXYreZZ6RPT4J/5Dmj8v1dOjjz7KgAEDGD16NOHh4YHtf/vb30ysSkRE5PSVG04BGjZsyP33319ZtUgtkp+fT2jnztCnj9mlSE1Wvytcvxd2J0H2dkId5+Kp35eIemfBIS0oI9VTt27duOGGGwKP586dy/jx4xk6dKiJVYmIiJy+44ZTkWDxeDyEhoaaXYbUBg4nNL0NgBCvl+xDh0wuSOTUrFy5krfffpsdO3YEptYUFhbi9XoZP368ydWJiIicPoVTqVwF2Zzx/gPkHd5HdJML4NYXiy6/FKkEdnvRf3ler9fkSkSM69+/P506deKFF17gzjvvBMBqtRIXF2dyZSIiIhXjpMJpZmYmeXl5gceabyqn5OA6+O8VRNQtxBMfi83/PSS9C1euLbr8UqQShISE4PF4zC5DxLCtW7fy17/+lSuvvJKdO3cGtu/YsYOuXfV/qIiIVH8nDKePPfYYGzZsID4+Hr/fj8Vi4a233qqM2qQmKcguWpjGmk9BSAQhfg84APKLtl+/t+jyS5EgS00N4ZtvPDRqFE7LlmZXI3LyNmzYwF//+ldWr15d6jmFUxERqQlOGE63bt3KRx99hMViqYx6pKbalQR+HwAefyih1j9G4vH7ihas+f95gSLBkpwMd98dSmSki0OHzqRJEy0ULdXHiBEj8Hg8PP7442aXIiIiEhQnDKf169fH7XbjdGpUS05D9jYodAPg9duItBwz56/QDdnbTSpMapP166GgwEZsrI/0dD/r1yucSvXRu3fvUieKi69o+vjjj02qSkREpOKUG04HDRqExWLh0KFDXHnllZx99tkAuqxXTk1UM7BFQqGbQr8Nu6Xwj+dskRB1vnm1Sa3RuTO8/LKFzEwrdnshnTubXZHIyfvkk0/MLkFERCSoyg2nTz31FAAFBQU4HI7A9iNHjgS/Kql5mgyC78bh81vw+y1YLb4/nrNYofEg82qTWiMhARYvhrfespOZeRSIMLskkZM2bdo0Jk+eHDh5fCydNBYRkZqg3HAaEhKCy+ViwoQJzJ49G7/fj8/nY/Lkybz99tuVWaPUBI4o6LGawk+uxYKtaJslDGwO6LFaiyFJpUpJseFy2bj11qKwqkt7pTq4++67gT9OHouIiNQ05YbTH374gddff52dO3fy2GOPAUX3U9OKgHLK6nelsO82Dm9YSMP6OUWX8jYepGAqlap43mndurkcOoTmnUq1ER8fD4DP52P27Nn8+uuvNGvWjAcffNDkykRERCpGueH0iiuu4IorruDzzz+ne/fulVmT1GCF1jDcsVdDmw5mlyK1VJtLsnlu3ZscidxBQXxz2lwyCIgyuyyRkzZp0iRGjhxJu3bt+Oabb5g0aRKvvvqq2WWJiIictnLD6cSJEwNfr1mzpsRzs2bNCl5FUqP5fD6sVqvZZUgttW73OgZ+1QfrJXZsfgs51nwGfjWO1eevpmtjXRUi1YPNZgucNL788st5/fXXTa5IRESkYpQbTvv06QPA8uXLadu2Le3atePHH3/kxx9/POWd+Xw+pk6dytatWwkJCWH69Ok0adLklPuT6sc/ahTnfvEFdOoEmrsslSg7P5s+y/qQ7ckmlFCcOMnzucnzQJ9lfdg7fi/OEF1iLlXXunXrAAgPD+ell17i4osvZtOmTYHLfUVERKq7coewunXrRrdu3cjLy2PUqFG0b9+eESNGkJmZeco7W7t2LR6Ph6SkJMaPH09iYuIp9yXV0I03wocfYj96FP75z6LHIpUkaUsSPn/RKtF+/CWe8/l9JG1OMqMskZP2wQcf8MEHH1CnTh1++eUXkpKSSEtLIyQkxOzSREREKkS5I6fFcnJy2LBhA3/961/5/vvvyc/PP+Wdpaam0q1bNwDatGnD5s2bS7VJS0s75f4rQ15eXpWvsao6/+OP8QMWf1E08H78Mdt1LE+Jfg6N+/LnL3EXuMt8zl3g5sttX9I5XDc+NUI/h5WrvCk1Bw8erORKREREguOE4XTGjBnMmTOHnTt30qxZM5588slT3pnL5cLp/OOyOZvNhtfrxW7/o4yWLVuecv+VIS0trcrXWGX17Inlv/8FiwUL4OjZU8fyFOnn0LhLcy/lrV/eKjOgRjoiubTZpTqmBlWHn8PU1FSzS6hwzzzzDMuXL6egoIC8vDzOOeccPvjgA7PLEhEROW3lhtPi0Hj22Wfz7LPPVsjOnE4nbvcfvxj6fL4SwVRquLffxjJ4MN4vvoCrrtKcU6lUgy4cxLj/jAPAgqXEpb1Wi5VBfxlkVmkihnzyySckJyczc+ZM/v73v/P444+bXZJItbJ///5S2zIyMkpsb9iwYWWWJCL/r9xkOGHCBObOnUvv3r2xWCwA+P1+LBYLH3/88SntrF27dnz66af06dOHjRs30rx581OrWqqvl17il59/pn379mZXIrVMVGgUq4eups+yPjh8DvAWjZhaLVZWD12txZCk2qhXrx4hISG43W6aNGlCQUGB2SWJiIhUiHLD6dy5cwN/t23btkJ21qtXL1JSUhg8eDB+v5+ZM2dWSL9SfdhsNvx+/4kbigRB18Zd2Tt+L2+mvsl3O77j4lYXM+gvgxRMpVpp2LAhb7/9NuHh4cydO5ejR4+aXZKIiEiFOOE1te+88w5PPPEEbdu25corr+Tiiy8+5ftUWq1Wpk2bdkqvlZrBZrNRWFhodhlSizlDnJyTPZit3/eiWeNzcWqhU6lmpk2bxv79++nduzfvvvtu4GSyiIhIdXfCcPrEE08A8O233zJnzhx2797Nhg0bgl6Y1Ey2DRuo+8orsHgxDB0KCQlmlyS1THIy3HtvIZmZ8bz1Fixfrh9DqV6OHDnCG2+8wa+//kqzZs1o0KCB2SWJiIhUiBOG09dee40vv/ySzMxM2rVrx5gxYyqjLqmJkpOxDR2K02LBf+AAlvffVzKQSrdsGWRlFeJ2h+PxFD3Wj6BUJxMmTKBHjx5cd911fPvtt0yYMIGFCxeaXZaIiMhpO2E4XbduHUePHuXKK6+ka9eutGjRojLqkppo/XosOTlYIiLw2e3YcnNh/XolA6l0NpuXwkKb2WWInJL8/HyGDBkCQIsWLfjPf/5jckUiIiIV44Th9OWXXyY/P58vv/ySGTNmsHPnTtatW1cZtUlN07kzRERg93rxWq3YwsOLtolUoiFD/GzY4Cc720JUVNHV5SLVwc6dOwGoW7cuH374IR06dGDTpk00atTI5Mqqn/+/CcGflL5nr9bvExGpXCcMp//9739JTk5my5Yt/OUvf2HUqFGVUZfURAkJsHw5OS+8QHh0NKE33aRRU6l0nToV8OSTdj766BDXXddAP4JSbUyePDnw9Ztvvsny5csDt3gTERGpCU4YTlNTU7nuuut44okn9AEopy8hAf9//oNnyxb44QeFU6l0+fn5dOkSyjnn/E7LllpIRqqPJUuWBL4+fPgwe/bsoVGjRsTGxppYlYiISMU5YTidOHFiZdQhtcX8+TSaM4eMevXwf/ABFgAtsiWVKCXFw/ffR9KoUTgtS1/FJ1Llffjhhzz99NM0bdqUbdu2MXr0aPr37292WSIiIqfthOFUpEJ98AFWnw+rxYLXYsGxdKnCqVSaZ5/1s3Chh6NH62K3n0mTJhq8l+rntdde45133iEyMhKXy8Utt9yicCoiIjWC1ewCpJbp2xeL30+oy0V+SAj8739FN54UCbLkZJg5s4CsLDuZmVZycy2sX292VSLGWSwWIiMjAXA6nYSGhppckYiISMUod+R00KBBpeaYFi+88NZbbwW9MKmhxowhZ/FiQv73P3KdTsjK0o0mpVKsXw8RER6ys0PxeqGgwKLFoqVaOvvss0lMTKRDhw58++23NG7c2OySREREKkS54fSpp56qzDqkFslv1ow6W7ZwJDQUf2Ehln37zC5JaoHISLBa87DbncTEwJ13ppOQcIbZZYkYNmPGDFauXMn69etp2rQp48ePN7skERGRClFuOD3rrLMA2LVrF2vWrKGgoACAgwcPMm3atMqpTmqko/36EfvJJ4Tk5ZEbGUlEamrRNZcaPZUgSU6GOXO82GyFuN2hzJgBV1yRBSicSvVz5513snjxYrPLEBERqXAnnHNafEb2u+++47fffiMrKyvYNUkNl9uhA/ztb4Q7HOS2aAEhIWjynwTTsmWQnZ1DVlY4OTkWNm82uyKRUxcdHc3atWvZsWMHO3fuZOfOnWaXJCIiUiFOuFpvREQEd9xxB7/++iuzZs1iyJAhlVGX1HRDhxL28cccKSzE63Bg1+Q/CaJ9+/yEhuZy6FAchYWgK8mlunK5XOzZs4fXX389sM1isfDGG2+YWJWIiEjFOGE4tVgspKen43a7ycnJIScnpzLqkpouIQHL4sWEp6SQ26EDUbqkV4LozDPz8ftt+P12bDY4Q1fzSjW0dOlSFi9ejM1mY+zYsSTo/00REalhThhOR48ezUcffUT//v254oordC81qTgJCUR06kRmZiZRZtciNVZyMlituTgc4URFQUQEDB1qdlUixr3//vusWbMGl8vFQw89pHAqIiI1zgnD6cUXX0zTpk3Zs2cPq1evpk6dOpVQltQWDocDq9VKXl4eYWFhZpcjNUxyMgwdWkh4eD4eTwwDBxYF04QESEszuzoRY0JCQggJCSE2NjawSKGIiEhNcsJwumzZMl5//XWaNWvG9u3bufvuuzV6KhUqKiqK7OxshVOpcMuWQW5uNjk5ERw9WrT+mwabpCbw+/1mlyAiIlLhThhOV65cyXvvvUdoaCi5ubkMGzZM4VQqVFhYGC6Xi9zcXMLDw80uR2qQ/fu9hITkkZFR3+xSRE7b9u3bGT9+PH6/P/B1sblz55pYmYiISMU4YTiNi4vDZrMBRSFCl/VKMERFRXHkyBHCwsKwWCxmlyM1QHIybN2aTU5OJF6vldhYzTWV6u3pp58OfD148GDzChEREQmSE4ZTv9/PddddR9u2bfnpp5/wer2Bs7U6UysVJTQ0FKvVSm5uLhEREWaXIzVASkoBERH5nHFGDFYr/O1vuqRXqrdLLrnE7BJERESC6oTh9M477wx83a9fv6AWI7VbdHQ0WVlZhIeHa/RUTlu7di6WLXOSl6dRUxEREZHq4IThtFWrVrz00kscPHiQyy67jAsuuIAmTZpURm1Sy4SEhGC323G73TidTrPLkWrsk0/ySU31cPvtdcjJgc6dNWoqIiIiUtVZT9Rg0qRJnH322ezatYv4+HgeeeSRyqhLaqmYmBhcLpdukyCn7PPPfTz0UBavvVaHZ5+1KJiKiIiIVBMnDKdZWVnceOON2O122rVrh8/nq4y6pJay2WyBy3t1qwQ5FevXZ5OXF0p0dCiFhbB+vdkViZjH5/MxefJkBg0axPDhw9m1a1eJ51977TUGDBjAgAEDWLBggUlVioiIFDlhOAXYsWMHAPv37w+s3CsSLBEREVitVtxut9mlSDWSnAyzZuUTFpZHfn40hw6BzVZ0Sa9IbbV27Vo8Hg9JSUmMHz+exMTEwHN79uxh1apVvPXWW6xYsYJ169bxv//9z8RqRUSktjvhnNNHH32USZMmsWPHDu69916mTJlSGXVJLVenTh3S09MJCwvDbj/hj6nUcsnJcNttPpzOLPLyYrj3Xitut+aaiqSmptKtWzcA2rRpw+bNmwPPNWzYkJdffjlw0tnr9RIaGmpKnSIiInAS4bR58+YkJSVx5MgRbDabFqqRSmGz2YiKiuLw4cPEx8dr9V45rn/8A44ePYrDEUpeXhhuNzz8sNlViZjP5XKV+Ny22Wx4vV7sdjsOh4PY2Fj8fj+zZ8+mVatWnHvuuWX2k5aWFpT68vLyDPVtpP3x27Y8qT6Ot6+Kq6Xq9l2VaqnIvjMyMkpty8/PZ9u2bYHHhw8frpRaKrNv1VL5fVelWoL9PitKueF0y5YtPPLII6xcuZLPPvuMyZMnEx0dzYQJE7j88ssrs0appSIjI8nPz+fIkSPUqVPH7HKkipo/Hz77zE1YmIf//S+euDhdyitSzOl0lpgi4fP5SlyNkp+fz6RJk4iMjDzulVEtW55cmDMqLS3NUN9G2hvtuyzHe30wa6kqfVelWiqy7/3795fatm3bNpo1axZ43LBhw0qppTL7Vi2V33dVqiXY79OI1NTUcp8rd87p7NmzSUxMxOFwMG/ePF566SX++c9/8uKLLwalSJGy1K1bl4KCAlwul9mlSBX14Yf5OJ0u3O5YwMq55+pSXpFi7dq1Izk5GYCNGzfSvHnzwHN+v5+7776bCy64gGnTpmlNCRERMV25I6c+n48WLVpw4MABcnNz+ctf/gKA1XpSayiJVAiLxUJsbCwZGRnY7XbCwsLMLkmqEK/XS69eWXz+eR3y8+3YbDB8uNlViVQdvXr1IiUlhcGDB+P3+5k5cyavvvoqjRs3xufz8fXXX+PxePjiiy8AGDduHG3btjW5ahERqa3KDafFl/188cUXdOrUCYCCggKtoCqVzmazUbduXTIzM4mLi8PhcJhdklQBPp+PzMxMbr/did0eygcfQN++MGaM2ZWJVB1Wq5Vp06aV2Na0adPA1z/++GNllyQiIlKucsNpp06dGDx4MPv372fRokXs3r2badOm0adPn8qsTwSAkJAQoqOjAwskaQS/dvP7/Rw+fJjQ0FAiIyMZM0ahVERERKS6Kzec3n777fTs2ROn00mDBg3YvXs3gwYNolevXpVZn0hAREQEhYWFHDp0iLi4OAXUWsrv95OVlYXFYiE6OtrsckRERESkghz3VjLHXvrTuHFjGjduHPSCRI4nKioKgEOHDhEbG6sFPGqZ4hFTKFosS7cYEhEREak5NPQk1U5UVBRhYWEcOnSIwsJCs8uRSqJgKiIiIlKzKZxKtRQVFUVERIQCai1RHEwtFouCqYiIiEgNpXAq1ZbT6VRArQX8fj+ZmZlYLBbq1KmjYCoiIiJSQx13zqlIVed0OrFYLGRkZFC3bl1CQkLMLkkqSHIypKQU0qZNJp06OYiJiVEwFREREanBFE6l2ouMjMRms5GZmUl0dDQRERFmlySn6cYbYfVqDw0bHiYiIpKFC50kJJhdlYiIiIgEky7rlRohLCyMuLg4XC4XR44cwe/3m12SnKKxY+HDD3OIjs5k794Ydu92sn692VWJiIiISLBp5FRqDIfDQXx8PFlZWRw6dIi6mzdj+/JL6NwZDbtVD36/n+TkI0RGejh0KB6v105hYdG3UERERERqNo2cSo1itVqJjY0l9PvvyXjgAfIWL4Zbby2awChVVnIyzJpVwJo1GfTo4Scjox5eb9G5s/79dW5BREREpDbQyKnUSFGpqYQcOcKRRo3IzcggJiUFqxJOlfT5535Gj3bhcLhZujSaRYsi8Pngww/h6qvhmWfMrlBEREREKoPCqdRMnTsT+vLL1Pv1V45GRZF+0UXE5OURFhZmdmXy/4pW4y3gt9+ysFhs2Gz1yMmxsX59USBVKBURERGpXRROpWZKSIDFi7GsX09M586EX3opWVlZ5ObmEhMTg9WqK9rNVDRamo3DkUNWVjT5+RGEhIDNpvmlIiIiIrWVwqnUXAkJgcmKIUC9evXIzs4mPT2dqKgowsPDdd9ME+Tl5ZGSchSLxY7NVg+/38Y118C552rtKhEREZHaTOFUag2LxUJ0dDTh4eEcPXoUl8sVCKkSfB6Ph+zsbAoLC+nYMZrFi8PIzi4aLR06VKFUREREpLZTOJVax+FwEBcXR35+PkePHsXtdhMVFcVXX4Wyfv0xo3cF2bArCbK3QVQzaDIIHFFml19tJCfDP/4B6eleBg06yk03FQROBvTsaWHxYkoebxERERGp1RROpdYKDQ2lXr165Obm8vHHR3jkERvZ2dG8/LKDtxeto83hPuD3QaEbbJHw3TjosRrqdzW79Cpt/nxYuhTS0gqxWrMJC8tjyhQnVmtd7r33j8uoj7nqWkRERERE9zkVCQ8P54cf6pGbG8bZZ2cSX2c35+69Hn9BdlEwhaK/vdnwWR8ocJlbcBU2fz5MmOBh27bDREam4/NZSU+vT3a2k9WrNb9XRERERMqncCoCdOliweuN5Ndf63N5szXk+MI56KmPyxuJz39MqPL7YHeSeYVWMcnJcMcdcMcdfj7+OJePP84gJiYLcHDgQH2ys6Px+axYrdC3r9nVioiIiEhVpst6RQjceYb16y0MabWDM1x78PgcuAsjOehpQLg1lwibGwduPlyxncgutfuS1ORkWLYM3n23kIKCHCIickhOtnPVVU4++CAUv9+C1QotWkBkJAwbBmPGmF21iIiIiFRlCqci/y8wB3J7Mwq/iSTE6ibEmkWh30pOYQSZBXHkeUL5d/K5rF7kZelSe60MqJ99Vsg99+SRk5OHzVZAYWE4hw/HkZtrp2FDeOop+OCDopFSBVIREREROVkKpyJ/1mQQtu/Ggbfooc3iI8ruIsruIsMby+pN1xAacYh166y0bRtGeHg4dnvRP6Xk5JqzAu2x76VLl0Ly8vLIzc1l/XovVmsokZGR7Nnzxyhp3bp/vG+FUhERERExSuFU5M8cUUWr8n7Wh8JCHza/G48/kvx8KzfO+zf7Dp5J/fpw6aUefL5cDh06hNVq5fvvQxk7NoT8/BBeftnK4sXVN6B+/rmfO+7wYLV6WLEinyee8NK5cyhOp5MuXUJZvNhCYSE0aADt28MZZ+hepSIiIiJyehRORcpSvytcvxfb7iTI3k5I1Pl8vWcQF3RzckG34iAWAoQQExODx+MhNTUfh8PNGWdkkZlpIyUlhIsvDiE0NBSrtWquPVY8Otqpk59LL/WQn5+Px+MhJaWAsDAHTmcIBw862bQplL59ixaG6t4d3aNURERERCpcpYfTjz76iDVr1jB37tzK3rWIMQ4nNL0t8LBrU+jao+ymISEhdOkSwiuvwM6dfkJDC+jQwUNubi5HjhzBarVit9sDfxwOB3a7HYulcm6vcmwI7dzZi9frZd06Lw8+6AW8/POfXp54wkGXLqFERUXRpUsIixdbOHIEbDbo0qVkf7pHqYiIiIhUtEoNp9OnT2fdunW0bNmyMncrUimOXfG3c+eQ/x9ZBb/fj9frDfzJz8/H7Xbj9XpLhFar1Vrun7JCbHIyTJwI27ZBs2Z+Zszw0aWLD5+v5J8NG3w89FAhFouXd94p5PHHbXTsaOfbb+3k5YUSExPJwYMONm600Lt3Ud8aHRURERGRylap4bRdu3ZcccUVJCXpPpFSM5U1omixWHA4HDgcjhLb/X4/hYWFeL1eCgsL8fl8eL3eUuHS5/OV6AsgJSWXxMT9uN1+7HbYuRNGjrQwf76Viy/+I9TabDZSU+3k5oYQE2MnPd3ODz9YuPrqotHQV16BgweLRkc7dz7xexERERERCZaghNOVK1fy+uuvl9g2c+ZM+vTpw1dffXXc16alpQWjpAqTl5dX5Wus6nQMjfP7/SX+3rSpHr/+Go/PZ/3/7RZsNj8ffZTOOeekl3hto0bh5Oaeidvtx2ot4Kyz9pKWlku9ejBlSjjffx9O27a51KuXS236tujn8PTpGIqIiEhFCko4HTBgAAMGDDil11b1S37T0tKqfI1VnY7h6bv44l+JjLRz9Ogf25xOC9dd14CWLRuUaNuyJTRpcuwluueUeG748EoquorRz+Hpqw7HMDU11ewSRERE5CRptV6RaqhDh1zee+/YOacwa1b5l+HqEl0RERERqeoUTkWqqYQESEkxuwoRERERkYpR6eG0Y8eOdOzYsbJ3KyIiIiIiIlWY1ewCRERERERERBRORURERERExHQKpyIiIiIiImI6hVMRERERERExncKpiIiIiIiImE7hVEREREREREyncCoiIiIiIiKmq/T7nIqIiIiIiEjVsH///lLbMjIySm1v2LBh0GvRyKmIiIiIiIiYTuFURERERERETKdwKiIiIiIiIqZTOBURERERERHTKZyKiIiIiIiI6RRORURERERExHS6lYyIiIiIiATNydyqpDJuUyJVn0ZORURERERExHQKpyIiIiIiImI6hVMRERERERExncKpiIiIiIiImE7hVEREREREREyncCoiIiIiIiKmUzgVERERERER0ymcioiIiIiIiOkUTkVERERERMR0CqciIiIiIiJiOoVTERGRGsrn8zF58mQGDRrE8OHD2bVrV6k2mZmZXHXVVeTn55tQoYiIyB8UTkVERGqotWvX4vF4SEpKYvz48SQmJpZ4/osvvuDWW28lPT3dpApFRET+YPH7/X6ziyiWmppK+/btzS7juNLS0mjZsqXZZVRrOoanT8fw9OkYnr7qcAyrw+dKMM2aNYvWrVvTt29fALp168YXX3wReD4lJYVWrVrxt7/9jQ8//JDQ0NBSfaSmphIRERGU+vLy8ggLCwtK++O1bdXq5H5uf/opLei1VOW+q1ItFdl3RkZGqW35+fklfv7j4+MrpZbK7NvMWnTMza/ldL8/cPzvkRE5OTnlfjbbK2QPIiIiUuW4XC6cTmfgsc1mw+v1YrcXffx36dLlpPoJ1kkIoyc4jLSviJMnx3t9MGupKn1XpVoqsu/9+/eX2rZt2zaaNWsWeNywYcNKqaUy+zazFh1z82s53e8PHP97ZERqamq5z+myXhERkRrK6XTidrsDj30+XyCYioiIVDUKpyIiIjVUu3btSE5OBmDjxo00b97c5IpERETKp9OnIiIiNVSvXr1ISUlh8ODB+P1+Zs6cyauvvkrjxo3p2bOn2eWJiIiUoHAqIiJSQ1mtVqZNm1ZiW9OmTUu1++STTyqrJBERkXLpsl4RERERERExncKpiIiIiIiImE7hVEREREREREyncCoiIiIiIiKmUzgVERERERER0ymcioiIiIiIiOkUTkVERERERMR0CqciIiIiIiJiOoVTERERERERMZ3CqYiIiIiIiJhO4VRERERERERMp3AqIiIiIiIiplM4FREREREREdMpnIqIiIiIiIjpFE5FRERERETEdAqnIiIiIiIiYjqFUxERERERETGdwqmIiIiIiIiYTuFURERERERETKdwKiIiIiIiIqZTOBURERERERHTKZyKiIiIiIiI6RRORURERERExHQKpyIiIiIiImI6e2XtKDs7mwcffBCXy0VBQQEPP/wwbdu2razdi4iIiIiISBVWaeH01Vdf5dJLL2XEiBH88ssvjB8/nnfffbeydi8iIiIiElwWS6lNLctq5/cHvRSj9u/fX+JxRkZGqW0NGzaszJKkFqq0cDpixAhCQkIAKCwsJDQ0tLJ2LSIiIiIiIlVcUMLpypUref3110tsmzlzJq1btyY9PZ0HH3yQSZMmlfnatLS0YJRUYfLy8qp8jVWdjuHp0zE8fTqGp0/HUEREqpvSg9ulx7ar4MB2qVFsKD26XRNGtoMSTgcMGMCAAQNKbd+6dSvjxo3joYce4pJLLinztS1blnnxQ5WRlpZW5Wus6nQMT5+O4enTMTx91eEYpqamml2CiIiInKRKu6x3+/btjB07lqeffpoWLVpU1m5FRERERESkGqi0cDp37lw8Hg8zZswAwOl0smjRosravYiIiIiIiFRhlRZOFURFRERERESkPFazCxARERERERFROBURERERERHTKZyKiIiIiIiI6RRORURERERExHQKpyIiIiIiImI6hVMRERERERExncKpiIiIiIiImE7hVEREREREREyncCoiIiIiIiKmUzgVERERERER0ymcioiIiIiIiOkUTkVERERERMR0drMLEBERERGRE7BYSm1qWVY7vz/opYgEi0ZORURERERExHQKpyIiIiIiImI6XdYrIiIiIiJymsq48po/X3ytq66PTyOnIiIiIiIiYjqFUxERERERETGdwqmIiIiIiIiYTuFURERERERETKdwKiIiIiIiIqZTOBURERERERHTKZyKiIiIiIiI6XSfUxEREalyTuZ+gVA17xm4f//+Eo8zMjJKbWvYsGFlliQiUi1o5FRERERERERMp3AqIiJSQ/l8PiZPnsygQYMYPnw4u3btKvH8ihUruOGGGxg4cCCffvqpSVWKiIgU0WW9IiIiNdTatWvxeDwkJSWxceNGEhMTWbRoEQDp6eksWbKEf/7zn+Tn5zNkyBC6dOlCSEiIyVWLiEhtZfH7q85sjdTUVLNL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yNAJgdjYWCwWC3v27Dlu4DT6Papbty6PPvrocfd9Ou2N6NevH4cPHy7zuT+vtm2xWDj77LPZvXs3YWFhxMTE0LBhw3KDptH2cPKB89hVxhs1aoTb7WbBggXlrkoOYLfbT/rWLUbaQtHPSmxs7Em1/fMl2scqnhpQGRRORcoRFRXF3r17efvtt0lISKBly5a8/fbbAGzdupXp06cDUKdOHWbOnMlPP/3E888/j9VqJT09nUGDBjF06FC+/vprFixYgN/vx+12M3fu3HLnHhX7+uuvmTdvHjabjbPPPptp06bx3nvv8fnnn5OXl8fu3bsZNWoUN9xwA5s2beLxxx8nMjKSuLg4QkNDad++Penp6dx///3ccsst7Nq1i5EjR5KZmclll13GmDFjSuxvyZIl/P3vfwfg119/5dFHHw3cvHnevHnMnj0bu93O3r178Xg89OnTh08//ZR9+/axcOFCGjduzNVXX83IkSMVTkWkTMGcF1aV5pwFc+GXU5nPdmwYBDjjjDPK/SXcSIA0GnyN1GI0EBptv3jxYoYNG8acOXNo0KBBWYfilNoWM3JsjI5uGwmcRr9HwWQkbEJRyBs3bhzLli074cq/xwbCs88++4SB8HTanyhwrl+/vtQq48OHD2fgwIFl9m0kEBoNj0bbx8bGsnz5cu666y7MvLBW4VSkHA0aNGDRokUsXbqU5557jrCwMO6//36uuuoqHnvsMWbOnMn555/PypUrefnll+ncuTMHDhzgX//6Fz6fj379+tG7d2+2bdsW+FB7/vnnWbNmDf369St3v36/n8cee4w333yTuLg4nn76ad59913sdjsul4tXXnmFX3/9lTvvvJMbbriBKVOmMHv2bJo1a8a8efM4cOAAAwYMYNGiRcybN4+NGzeSn5/PwoULKSwspEePHqXC6ddffx24fcKTTz7J7bffTkJCAh9//DE//fQTAGeddRbTp09n8uTJ/Pbbb7z00ks8++yzfPLJJ4wYMYKYmBgOHz4cWE5eRORYwVzkJtgL6BgJhcFc+AVg5MiRJ2xTrHv37owYMYIuXboE3ue6detISEgos72RAGk0+BqpxWggNNo+PDycxx9/nL17956wvZG2xYwcG6MB0kh7o98jowHSSHsjYROgSZMm3HzzzXz11Vd07979uG2NBsJgtje6KrmRQGg0PBptP2LECDZv3kz9+vXp3LnzCdsHi8KpSDl27dqF0+kMhLYff/yRUaNG0bFjR3bs2BGYNF9QUBCYF9K2bdvATbmbNWvG7t27adCgATNmzCAiIoIDBw6ccG5NZmYmBw8eDPyHl5eXR+fOnWnSpAktWrQAij5cim8MfvDgQZo1awZA+/btWb16dak+mzVrFqirrLkJPp8v8PzOnTsDZ2t79uwJwPvvv0+rVq0AiI6ODtx6ITo6usQNyuPj48nKylI4FZFSpk6dWmqe12WXXVZuIDPSPph9FzvZUGg0bBrpG4zNZxs9ejQ//fQTqampHD58GKfTyQMPPMCFF15YZnsjAdJo8DVSi9FAeCoB0sj6EUbXmvjzsXG5XKSkpJR5bIwGSCPtjdQBxgOkkfZGwmax/v37n1S7YN6mymj7Rx55pMxVxstbaMlIIDQaHk8lbM6YMYP8/PyTahssCqci5di6dStJSUksWrSIkJAQzj33XKKjo7HZbJx77rk8+eSTnHnmmaSmppKeng5AWloahYWFeDwetm/fTpMmTbj77rv56KOPcDqdTJgw4YRnr+rWrUvDhg1ZuHAhUVFRfPzxx0RERLBv374yP3waNmzI9u3bOf/88/nhhx8C2y0WCz6fL/D18YSGhlJYWIjNZqNp06b8+OOPdO7cmVWrVnHkyJGT6gPg6NGjJz23QURql2DOC6tKc87AWNg00vepzGdr1apV4OTiiRgJkEaDr9FajAZCI+3LGgUs1rVr11NuW+zYY5OVlXXcY2M0QBppb6QOMB4gjbY/2bBZbO3atWzYsKHEv4vevXuX+ndqNBAGs/1FF13Ev/71r8Aq45GRkTidzuO+TyOB0Gh4NNo+NDSU0NDQk24fDAqnIuW48sor2bFjBzfeeCMRERH4/X4eeughoqKimDp1KhMmTAis4jtjxgwOHjyI1+tl1KhRZGVlcddddxEbG8u1117L0KFDCQ8PJz4+noMHDx53v1arlUceeYTbb78dv99PZGQks2fPZt++fWW2nzJlCpMmTSIiIgKHwxE4a9yhQwduv/127rnnnhO+13bt2rFlyxZat27NQw89xOTJk1m0aBFhYWHMmTMnsIrg8Rw9epTo6OiTutWDiNQ+wZwXVpXmnIGxIBvM+WynEqyMBEgjbYMZCI22X7FiBZs3b6Zjx44nbG+k7bH27t3Lzp07yc7OJiYmhri4OFq1alUqWBkNkEbbn2wdxYwGSCPtTzZsAjz++OP4fD4SEhICc3GTk5NZt24dM2bMKNHWaCAMdnsAp9N5wjbFjARCo+HRSPvx48eX+9zcuXNPep+nS7eSEakgX3311UndsqWiLVu2jKuvvprY2FjmzZuHw+Fg9OjRhvr4/vvv+eCDD05r1b1ly5bhdDoNf7CJSO0wZMiQUsHK7/czcODAMufWGWkfzL6Ntj82bBb/Ur1u3bpyg6yRvgcOHMhTTz1V4vLCPXv28MADD5CUlFSq73vvvbfcYFU8ZeVYwQyQRmoxWrfR9oWFhQwbNowZM2YEpqmUx0jbYuUFK6/XWypYQVFoKz6ZERMTc9zQZqS90TqK+z7ZAGmkvdFahg0bxtKlS0ttHzx48GmvkB1MRgOekfbB7Bvgo48+Yt68eUydOrXUc5dcckm5fVU0jZyKVHNxcXHceuutREREEBUVRWJiouE+2rZty6pVq9i/f/8p3+f0u+++Y86cOYZfKyK1QzDneVWlOWdGRzeNzmcbM2YMHo+HsLAw8vPzcTgc5V6OOG/ePIYNG8aoUaNOKlgFc0TRSC1G6zba3mazMXv2bHJycvD5fOV+3422LbZt27ZSwapnz54MHjy4VFsjI4RG2xupo6rV4vP5+Pbbb+nQoUNg2zfffFPm3Q6CHdqMtO/du3e5Aa8sRtoHs2+AXr168fXXX3Po0CGuvvrqk3pNMCicilSQjh07lvkhHWy9e/emd+/ep93PlClTTvm1YWFhlXrJh4hUP0aDlZH2wezbaHujwddI37GxsZxxxhl8++23gZGqiy66qNxF6IwGq2AGyGAGQqPt9+zZw6xZs9i8eTN2ux2fz0fz5s2ZOHFiqfuiGmlbzEiwMhrajLQ3UkdVqyUxMZFZs2Yxbtw4/H4/VquVVq1a8cQTT5RqG+zQZqS90YBnpH0w+y72yCOPnFS7YFI4FRERkaAzGqyMtA9m30bbGw2+Rvt+4IEHSsxf3bhxIxMnTizzUkejwSqYATKYgdBo+0ceeYTx48eXuNdtecfRSNtiRoKV0dBmpL2ROqpaLY0bN2bRokVlPvdnwQ5tRtsbDXhG2gezb6P3aA4WzTkVERGRoLv55pvLDFaJiYll/uJjpH0w+zbavjgo/TlslheUjPRd3ny78rbffPPNZQar8t5nMAOkkVqM1m20vZHjaPSYG7V7925mzZrFli1bSoS2CRMmBG5Tdzrtq2stZQWlYlV5zqnRgGekfTD7Bvjhhx/KvUfzWWedVca7DQ6NnIqIiEjQeTyeUivWtmnTpkLaB7Nvo+2Njm4a6fuCCy5g4sSJdOvWLXBv0c8//5wLLrig3LqPDWzH67u49mCNKBqpxWjdRtsbOY5GjzkYC1ZGRgiNtjca8KpSLQ888EC5Qelk+q7I0GakvZG6jbYPZt9wavdoDgaFUxEREQk6o7/kBzNABLO90eBrpO+pU6eydu1aUlNTcblcOJ1OLrvssnJ/kQxmmK1KgdBoeyPH0egxh9MPVsVONiiV195oOKlKtRgJSsEObUbaGw14RtoHs+9iRu/RHAy6rFdERESCzu/3l/olv127dvTq1avM21QYaR/Mvo22nzJlCh6Pp1RQCgkJKXPeqdFajDDat5Hag/k+g/39rAwvv/wyTZo0OWEoMHoppdH2J1tHVavFKKN9B7u9nDqFUxEREZEKUhWD0skKZoCU8lWloFSVapHaSeFURERERERETHfiOwmLiIiIiIiIBJnCqYiIiIiIiJhO4VRERERERERMp3AqIiIiIiIipvs/163+2ycM51gAAAAASUVORK5CYII=\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "## 4 points distance-based classifier ##\n", - "\n", - "# Add the 2 new data points:\n", - "x_tilde = [0.9999, -0.0011] # Label 1\n", - "x0 = [-0.4583, -0.8889] # Label 1\n", - "x1 = [-0.3728, 0.9279] # Label 0\n", - "x2 = [-0.9886, 0.1503] # Label 0\n", - "x3 = [0.9530, 0.3028] # Label 1\n", - "\n", - "\n", - "def four_point_distance_based_classifier_circuit(x_tilde, x0, x1, x2, x3):\n", - " # Set-up a new connection with qi_backend:\n", - " qi_backend, qi_engine = initialize_qi_backend(\"distance_based_classifier_4_points\")\n", - " \n", - " # Angles data points:\n", - " angle_x_tilde = 2 * acos(x_tilde[0]) * sign(x_tilde[1])\n", - " angle_x0 = 2 * acos(x0[0]) * sign(x0[1]) # Label 1\n", - " angle_x1 = 2 * acos(x1[0]) * sign(x1[1]) # Label 0\n", - " angle_x2 = 2 * acos(x2[0]) * sign(x2[1]) # Label 0\n", - " angle_x3 = 2 * acos(x3[0]) * sign(x3[1]) # Label 1\n", - " \n", - " # Quantum circuit:\n", - " qubits = qi_engine.allocate_qureg(5)\n", - "\n", - " # part_a\n", - " for qubit in qubits[0:3]:\n", - " H | qubit\n", - "\n", - " # part_b\n", - " C(Ry(angle_x_tilde), 1) | (qubits[2], qubits[3])\n", - " X | qubits[3]\n", - "\n", - " # part_c\n", - " for angle in [angle_x0, angle_x1, angle_x2]:\n", - " C(Ry(angle), 3) | (qubits[0], qubits[1], qubits[2], qubits[3]) #C1\n", - " CNOT | (qubits[1], qubits[0]) #C2\n", - " X | qubits[1]\n", - " C(Ry(angle_x3), 3) | (qubits[0], qubits[1], qubits[2], qubits[3]) #C1\n", - "\n", - " # part_d\n", - " Toffoli | (qubits[0], qubits[1], qubits[4]) #D1\n", - " CNOT | (qubits[1], qubits[0]) #D2\n", - " X | qubits[1]\n", - " Toffoli | (qubits[0], qubits[1], qubits[4]) #D1\n", - "\n", - " # part_e\n", - " H | qubits[2]\n", - "\n", - " qi_engine.flush()\n", - " temp_results = qi_backend.get_probabilities(qubits)\n", - " \n", - " # Results:\n", - " temp_results = qi_backend.get_probabilities(qubits)\n", - " return temp_results \n", - "\n", - "\n", - "temp_results = four_point_distance_based_classifier_circuit(x_tilde, x0, x1, x2, x3)\n", - "print(temp_results, '\\n')\n", - "prob = DataPlotter.plot_data_points(x_tilde, [x1, x2], [x0, x3], temp_results) # Function to plot the data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Just as we did in previous notebook, we need to count only those outcomes where the third qubit is equal to a 0. That is, we only consider the 16 outcomes in the set: \n", - "$$\\{00000, 01000, 10000, 11000, 00001, 01001, 10001, 11001, 00010, 01010, 10010, 11010, 00011, 01011, 10011, 11011\\}$$\n", - "\n", - "The label $\\tilde{y}$ is then given by a majority vote:\n", - "\n", - "\\begin{equation}\n", - "\\begin{split}\n", - "\\tilde{y}_{0} = \\# \\{00000, 01000, 10000, 11000, 00010, 01010, 10010, 11010\\}\\\\\n", - "\\tilde{y}_{1} = \\#\\{00001, 01001, 10001, 11001, 00011, 01011, 10011, 11011\\}\n", - "\\end{split}\n", - "\\end{equation}\n", - "\n", - "\n", - "\\begin{equation}\n", - "\\tilde{y} = \\left\\{\n", - " \\begin{array}{lr}\n", - " -1 & : \\tilde{y}_{0} > \\tilde{y}_{1}\\\\\n", - " +1 & : \\tilde{y}_{1} > \\tilde{y}_{0}\n", - " \\end{array}\n", - "\\right. \n", - "\\end{equation}\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The sum of the events with label 0 is: 0.0779354\n", - "The sum of the events with label 1 is: 0.3128664\n", - "The label for y_tilde is: 1 because sum_label0 < sum_label1\n" - ] - } - ], - "source": [ - "def summarize_results_4_points(prob, display=1):\n", - " def get_label_0_1(n, index_0, index_label):\n", - " # n = number of qubits excluding ancillas\n", - " # index_0 = index of bit that should always be zero to continue\n", - " # index_label = index of bit that should be used to classify in label 0 and label 1\n", - " index_0 = [i for i in range(2**n) if (format(i, '#0{0}b'.format(2+n))[2+index_0] == '0')]\n", - " label0index = [i for i in index_0 if (format(i, '#0{0}b'.format(2+n))[2+index_label] == '0')]\n", - " label1index = [i for i in index_0 if (format(i, '#0{0}b'.format(2+n))[2+index_label] == '1')]\n", - " return label0index, label1index\n", - " label0indx, label1indx = get_label_0_1(5, 2, 4)\n", - " sum_label0 = 0\n", - " sum_label1 = 0\n", - " \n", - " for indx in label0indx:\n", - " sum_label0 += prob[indx]\n", - " for indx in label1indx:\n", - " sum_label1 += prob[indx]\n", - "\n", - "\n", - " def y_tilde():\n", - " if sum_label0 > sum_label1:\n", - " return 0, \">\"\n", - " elif sum_label0 < sum_label1:\n", - " return 1, \"<\"\n", - " else:\n", - " return \"undefined\", \"=\"\n", - " y_tilde_res, sign = y_tilde()\n", - " if display:\n", - " print(\"The sum of the events with label 0 is: {}\".format(sum_label0))\n", - " print(\"The sum of the events with label 1 is: {}\".format(sum_label1))\n", - " print(\"The label for y_tilde is: {} because sum_label0 {} sum_label1\".format(y_tilde_res, sign))\n", - " return y_tilde_res\n", - "\n", - "\n", - "summarize_results_4_points(prob);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By adding these two points we see that the classifier now gives the correct label for the test data. To see how well this 4-point distance-based classifier performs we also apply it to randomly selected training and test data. " - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The sum of the events with label 0 is: 0.4110741\n", - "The sum of the events with label 1 is: 0.0409845\n", - "The label for y_tilde is: 0 because sum_label0 > sum_label1\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6cAAAGBCAYAAACadsdyAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAABuBElEQVR4nO3deVxU9f7H8desLDMgAqaVaWWW2r2mZplaWJllmlndTCstb2mrS6llaqmZC2lmpWmL2aJeQ7t2r5X5u9lGom2UmcY1NVMrd0SZERiGmd8fXKYIEI4yHJb38/HwIXPmO9/zmQPKvM/5fr/HEgwGg4iIiIiIiIiYyGp2ASIiIiIiIiIKpyIiIiIiImI6hVMRERERERExncKpiIiIiIiImE7hVEREREREREyncCoiIiIiIiKms5tdgIiIiIiIGc455xzOPvtsrFYrFouFnJwc3G43EydO5K9//WtY9rdu3TocDgf3338/b7zxRqXvQ6QmUzgVERERkTrr9ddfJz4+PvT4lVdeYfLkyaSkpIRtn4cPH+b7778PW/8iNZWG9YqIiIiIAH6/n927d1OvXr3Qtnnz5nH99dfTu3dv7rvvPvbu3QvAf/7zH66//npuuOEG+vTpw1dffQXAgAEDWLVqVej1f34MMGbMGHJzc+nduzcFBQU899xz9OrVixtuuIE777yTffv2VcG7Fal+dOVUREREROqs22+/HYvFQmZmJhEREVx22WVMmzYNgH/961/8+OOPLFu2DLvdTkpKCo8++igvv/wy06dP56mnnqJNmzasWbOGL774ggsuuKBC+5w2bRq9evXi3//+N7t37+b1119n3bp1OJ1OFixYwIYNG7jiiivC+bZFqiWFUxERERGps4qG9f7www8MHjyYtm3bkpCQAMDHH3/M999/z9/+9jcAAoEAOTk5APTs2ZMhQ4bQpUsXOnfuzODBg49r/w0bNqRFixZcf/31JCUlkZSURMeOHSvnzYnUMAqnIiIiIlLntWrVijFjxvDoo49y3nnn0bhxYwKBAIMGDeKWW24BwOfzcfjwYQAefPBBbrzxRtasWcPy5ct56aWXWL58OQDBYDDUb35+/jH3a7VaWbRoEd9//z3r1q1j6tSpdOjQgUcffTRM71Sk+tKcUxERERER4JprrqFNmzZMnToVgIsvvpi33noLj8cDwLPPPsvDDz+M3+/n8ssv5+jRo9x8881MmDCBbdu24ff7iY+PZ+PGjQDs3LmTzZs3l9iP3W6noKCAYDDIf//7X6655hqaNWvG3XffzcCBA0t9jUhdoCunIiIiIiL/89hjj3Httdfy2Wef0adPH/bu3ctNN92ExWLh5JNPJjk5GbvdztixYxk1ahR2ux2LxcLUqVNxOp3ce++9PPLII3z66aeceeaZtG/fvsQ+GjRoQKtWrbj66qtZsmQJV199NX/729+Ijo4mMjJSV02lzrIE/zjuQERERERERMQEGtYrIiIiIiIiplM4FREREREREdNpzqmIiEgtFQgEmDhxIps3b8bpdDJ58mSaNm0aen7y5Ml88803uFwuAObOnUtMTIxZ5YqISB2ncCoiIlJLrV69Gp/PR0pKCuvXryc5OZl58+aFnt+0aRPz588nPj7exCpFREQKVatwmp6ebnYJIiJSy5x//vlml2Ca9PR0LrnkEgDatGkTur0FFF5V3bFjB+PHj+fAgQPceOON3HjjjaX2ISIiUpnK/N0crEa+/vprs0so1w8//GB2CTWejuGJ0zE8cTqGJ64mHMOa8HslnMaOHRv85JNPQo+7dOkSzM/PDwaDwWB2dnbw+eefDx49ejSYnZ0dvP7664MZGRkl+gjnMTT6M2SkfTj7rk616H3WrlrqyvusTrXofVZOeyOO9XtFCyKJiIjUUm63G6/XG3ocCASw2wsHTUVFRXHbbbcRFRWF2+3moosu4r///a9ZpYqIiGi1XhERkdqqXbt2pKamArB+/XrOPvvs0HM///wzN998MwUFBeTn5/PNN99w7rnnmlWqiIhI9ZpzKiIiIpWnW7dupKWl0a9fP4LBIFOnTuXVV1+lSZMmdO3ald69e3PTTTfhcDjo3bs3zZs3N7tkERGpwxRORUREaimr1cqkSZOKbWvWrFno60GDBjFo0KCqLktERKRUGtYrIiIiIiIiplM4FREREREREdMpnIqIiIiIiIjpFE5FRERERETEdAqnIiIG/PLLL9x0000ltqemppKSklKhPt5++21uu+02BgwYQL9+/VizZs0x26ekpJCfn39c9R5LMBjkkUcewev1MmDAALZt21Zm28svv5y8vLwK9Vta2/Xr19OnTx/69evHnDlzADhw4ECJxXpERESk7tJqvSIilSApKalC7bKzs5k7dy7vvfceTqeTvXv30qdPHz755BOs1tLPF7744otcd911lVhtoffff59zzz0Xl8tV6X3/2YQJE5g9ezannXYad911Fz/88AOtWrXC5XLx5ZdfcuGFF4a9BhEREaneFE5FpPZLTYW1a6FTJ6hgiKyIAQMGEB8fz+HDh+nZsyc7duxg6NChDB8+HI/HQ05ODg8++CAXX3xx6DVOp5P8/HyWLFnCZZddRpMmTVi9ejVWq5Xdu3fz2GOPkZeXR0REBE888QRr1qxh//79PPjgg8ydO5fk5GTS09MBuOaaa7j99tv5z3/+w8svv4zdbuekk05i1qxZ7Nu3j4kTJ5KXl8f+/ft54IEHuOKKK4rVv3DhQp5//vli2/bs2VPm68aPH8+vv/5KQkICTz75JH6/n7Fjx7Jjxw4CgQAPPPAAHTp0KHGcPB4PPp+PJk2aAHDxxRezdu1aWrVqxTXXXMPs2bMVTkVERETDekWklktNhTvugBdfLPw7NbVSu7/mmmt47bXXsNlsAOzcuZOsrCxeeOEFnn76aQoKCoq1j4iI4PXXX2fHjh0MGjSIyy67jLfeeguAJ598kgEDBrBw4ULuvPNOnnrqKfr06UODBg2YNWsWH3/8Mb/88gtLly7lH//4B++++y6bN2/m3Xff5c477wwFXo/Hw08//cTf//53Xn31VSZNmsTixYuL1ZGbm8vu3buJj48vtv1Yr7v55ptZtGgRp556KkuXLuWDDz6gfv36LF68mLlz55Y5RNfj8eB2u0OPXS4X2dnZAJx11lmhsC0iIiJ1m66cikjttnYtFBRAQgIcPFj4uBKvnp5xxhnFHjdv3py+ffsyYsQI/H4/AwYMKPb83r17yc3NZfz48QBs376dQYMGcf755/Pjjz/y4osvMn/+fILBIHZ78f+it23bRvv27bFYLDgcDs477zy2bdvGmDFjePHFF1m0aBFnnnkmV1xxBQ0aNGDevHm89dZbWCwW/H5/sb4OHz5M/fr1S7yfsl7ncDho06YNAO3atSMtLY3MzEx++uknNmzYAIDf7yczM7NEn263G6/XG3rs9XqJjY0FwGazYbfbCQQCZQ5rFhERkbpBnwREpHbr1AlstsJgarMVPq5EFoul2OPNmzfj9Xp56aWXSE5O5oknnij2/IEDB3jooYfweDwAnHrqqdSvXx+Hw8GZZ57JqFGjWLhwIY8//jjdu3cP7SMQCNCsWbPQVcb8/Hy+/fZbmjZtSkpKCkOHDmXRokUAfPDBBzz77LP07t2bGTNm0KFDB4LBYLE66tevXywwFinrdfn5+WRkZADw9ddf07x5cxo3bkzPnj1ZuHAhL7/8Mt27dycuLq5En263G4fDwc6dOwkGg6xZs4b27dsDhEK4gqmIiIjoyqmI1G5JSbBgQVjmnJbm9NNP5/nnn+f9998nEAgwbNiwYs+fe+65DBgwgP79+xMZGUlBQQF9+vThzDPPZPTo0aH5nrm5uYwbNw6A9u3bc9ddd/HGG2/w5Zdf0rdvX/Lz8+nevTvnnnsue/fu5e6778blchEdHc2ll16Kw+Fg+vTpvPTSSzRq1IhDhw4Vq8PpdJKYmMjBgwdJSEgIbe/evXupr3M4HCxcuJAdO3ZwyimnMHLkSDIyMli0aBH9+/fH4/Fwyy23lBkyH3/8cUaNGkVBQQEXX3wx5513HlAY5ouuyIqIiEjdpnAqIrVfUlKlhdLGjRuzdOlSoHBBoSI33HBD6OvnnnvumH306dOHPn36lNh+2mmn8corr5TY/uSTT4a+Hj16dInnL7/8ci6//PJi26655hquueaaY9Zxyy238M477zBw4MDQe2nWrFmpr/u///u/EtuKAvCfffTRRyW2tWnTJnTc/mjFihXceuutx6xTRERE6gaNoxIRqaN69uzJpk2bSh3eWxX279+Px+MJDfEVERGRuk1XTkVE6iiLxcKMGTNM23+DBg3KXOFXRERE6h5dORURERERERHTKZyKiIiIiIiI6RRORURERERExHQKpyIiIiIiImI6hVMREQN++eUXbrrpphLbU1NTSUlJKff1o0eP5q233iq27bXXXmPWrFnHVc/+/fuZOHGiodd88cUXPPjgg8e1P5/Px0MPPUQgEGDw4MHk5eWV2bZz584V7restjk5OfTr149t27YBcODAAS2iJCIiUkspnIqIVIKkpCT69u1bbrs+ffrw73//u9i2t99+u9T7nlZEgwYNDIfTE/Haa69x9dVXY7WG/9fH999/z6233squXbtC2xITE3G5XHz55Zdh37+IiIhULd1KRkRqvdRUWLsWOnWCpKTK63fAgAHEx8dz+PBhevbsyY4dOxg6dCjDhw/H4/GQk5PDgw8+yMUXXxx6Tfv27cnMzOTXX3/l1FNPZcOGDSQmJtK4cWMWLlzIu+++i8VioUePHtx222088sgjZGVlkZWVxdy5c3nggQcIBoPk5eXx+OOPExMTw4gRI1i6dCkff/wxc+bMIRgMcu655/L444+zbt06nnnmGSIiIoiLi2Pq1KnF3sOKFSt4/fXXcTqdnH766UyaNIl33nmHf/7znwQCAYYNG0bHjh0BCAaDrFixgrfffrtYHz/++CPJyckUFBRw6NAhJk6cSLt27fD5fDz44IPs3r2bc845h4kTJ+LxeBg3bhyHDh0C4NFHH+Wcc84p9fj6fD6ef/55Hn744WLbr7nmGmbPns2FF154wt9DERERqT4UTkWkVktNhTvugIICmD8fFiyo3IB6zTXX0K1bN5YvXw7Azp07ycrKYv78+Rw8eJCff/65xGtuvPFGVqxYwb333svy5cvp168fW7duZeXKlfzjH/8A4O9//3so1F500UUMHDiQTz75hLi4OKZPn87WrVs5evQoMTExAPj9fp544gmWLVtGQkICL7/8Mrt37+axxx5jyZIlNGzYkNdff5158+Zx6aWXAnDo0CFmz57N22+/jdvtZurUqaSkpBAdHU1sbCzz5s0rVvfPP/+M2+3G4XAU275161ZGjx7NOeecwzvvvMPy5ctp164dubm5jBo1ilNPPZXhw4fz0Ucf8c0333DRRRdxyy238PPPPzNmzBiWLFlS6rE9//zzS91+1llnkZ6eXrFvkIiIiNQYCqciUqutXVsYTBMS4ODBwseVGU7POOOMYo+bN29O3759GTFiBH6/nwEDBpR4Te/evRk4cCB33HEHX375JY8++ij/+c9/+O233xg4cCAAhw8fZseOHcX2kZSUxM8//8x9992H3W7n3nvvDfV56NAhYmNjSUhIAGDw4MFkZmbidrtp2LAhABdccAFPP/10KJzu2rWLs846C7fbHXp+zZo1nHfeeSXeV9E+EhMTS2w/6aSTmDt3LpGRkXi93lB/p5xyCqeeeioAbdu2Zfv27fz44498/vnnvP/++6H3aZTNZsNutxMIBKpkeLGIiIhUDf1WF5FarVMnsNkKg6nNVvi4MlkslmKPN2/ejNfr5aWXXiI5OZknnniixGvi4+Np1qwZc+fOpVu3btjtds4880zOOuss3njjDRYuXMgNN9wQGu5atI8vvviCk046iQULFnDvvffy9NNPh/pMSEjgyJEjZGVlATB58mR27dqFx+Nh3759AHz55Zecfvrpodc0btyYbdu2cfTo0dDzRaG0tNBXtI8/mzJlCsOGDePJJ5/k7LPPJhgMArBnz57Qvr/55huaN2/OmWeeycCBA1m4cCHPPPMM1157bfkH+U+CwSB2u13BVEREpJapsiun+fn5jB07ll9//RWfz8e9995L165dq2r3IlJHJSUVDuUNx5zT0px++uk8//zzvP/++6E5m6W56aabGDx4MKtWrQKgRYsWdOzYkZtvvhmfz0fr1q1DVzyLtGjRghEjRrBkyRL8fj/3339/6Dmr1cqECRO4++67sVqttGrVitatWzN58mSGDh2KxWKhXr16TJs2jS1btgCFIXno0KHcdtttWK1WmjRpwqhRo3jvvfdKrblp06ZkZmbi9/ux23//9XHttdcyfPhwYmNjadSoUWg+aVxcHJMnT2bv3r20bduWLl260Lp1a8aNG8fSpUvxeDwMGTLE8DHevHkzbdq0Mfw6ERERqd6qLJyuWLGCuLg4ZsyYQVZWFtddd53CqYhUiaSkyguljRs3ZunSpQAsXLgwtP2GG24Iff3cc8+V20/Hjh3ZuHFjsW2DBg1i0KBBxbYlJyeHvq5fvz6vvvpqib6K6unSpQtdunQp9lynTp3o9KfLxR06dKBDhw4A9OrVi169ehV7/o/v5c969erFxx9/TLdu3Xj55ZeJiIjg73//O3//+99LtP30009LbKtfvz5z584tsT0tLa3Mff7xOEPh75Nbb721zPYiIiJSM1XZmKju3bszfPhwoHBIls1mq6pdi4hIJbn99ttZtWoVgUDAlP3v378fj8dD+/btTdm/iIiIhE+VXTl1uVwAeDwehg0bxgMPPFBqu4yMjKoq6bjk5uZW+xqrOx3DE6djeOJ0DI/foEGD2Lx5s2nH8Oabb9b3TkREpBaq0tV6d+/ezf33388tt9xSYhhZkZYtW1ZlSYZlZGRU+xqrOx3DE6djeOJ0DE9cTTiGuuWMiIhIzVFl4fTAgQPccccdjB8/PnRDdxERERERERGowjmnL7zwAkeOHGHu3LkMGDCAAQMGkJubW1W7FxERERERkWqsyq6cPvroozz66KNVtTsRERERERGpQap0zqmISFXLzssmZVMKWw5uoXlCc/qe25eYiJjj7i85OZlNmzaxf/9+cnNzOe2006hfv36Fbh8DhffoPHLkCBdccMFx1zBkyBDmzJlT5vMpKSnccMMNOByO496HiIiISFVTOBWRWmvNzjX0WNyDQDCAN9+Ly+FixP+NYOWtK0kg4bj6fOSRRwBYvnw5P/30E6NGjTL0+v/85z8kJiaeUDg9VjAFePHFF7nuuuuOu38RERERMyicikitlJ2XTY/FPcj2ZYe2efO9APRY3IOPen5UafvKz89nwoQJ7Nixg0AgwAMPPECHDh2YNWsWX3zxBX6/nyuvvJLevXvz9ttv43A4OPfcc2ndujUAv/zyC8OHD6dBgwbs3buXpKQkHnzwQX755RfGjh1LQUEBFouFRx99lBYtWtC5c2fS0tIYMGAALVq0YMuWLXg8Hp599lnWrl3L/v37efDBB5k7d26lvUcRERGRcKuyBZFERKpSyqYUAsFAqc8FggFW7VpVaftatmwZ9evXZ/HixcydO5dJkyYB8M477/DUU0/xj3/8g9jYWBo2bMj111/PwIEDQ8G0yK+//kpycjJvvfUWn3/+OZs2bWL69OncdtttLF68mHHjxjF27NgS+27dujWvvfYanTt35r333qNPnz40aNCAWbNmVdr7ExEREakKunIqIrXSloNbQldK/8yb72WHZ0el7evHH38kPT2dDRs2AOD3+8nMzGTGjBnMnDmTAwcOcMkllxyzjxYtWhAXFwcUBs7t27ezbdu20PDfli1bsmfPnhKva9WqFQCNGjXiwIEDlfaeRERERKqawqmI1ErNE5rjcrhKDaguh4um7qaVtq8zzzyTRo0acc8995Cbm8u8efNwu92sWrWKp59+GoAePXrQs2dPLBYLgUDJK7rbtm0jJycHp9PJhg0b+Nvf/kazZs34+uuv6dq1KxkZGSQmJlaonrL2ISIiIlKdaViviNRKfc/ti9VS+n9xVouV7qd1r7R99evXj59++on+/fvTr18/Tj31VJxOJ/Xq1eOmm27itttuo3Pnzpxyyin85S9/YfHixXz++efF+nA4HAwfPpw+ffrQtWtXWrRowcMPP8yiRYu49dZbmThxIlOmTKlQPe3bt+euu+4iGAxW2nsUERERCTddORWRWikmIoaVt64ssVqv1WJl5a0rcXldJ9T/DTfcEPra6XQyffr0Em2GDBnCkCFDim279NJLufTSS0u0TUxM5KWXXiq2rXHjxrz66qsl2qalpQGwcOHC0Labb7459PWTTz5ZsTchIiIiUo0o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TKdwKiIiIiIiIqZTOBURERERERHTKZyKiIiIiIiI6RRORURERERExHQKpyIiIiIiImI6hVMRERERERExncKpiIiIiIiImE7hVEREREREREyncCoiIiIiIiKmUzgVERERERER0ymcioiIiIiIiOkUTkVERERERMR0CqciIiIiIiJiOoVTERERERERMZ3CqYiIiIiIiJhO4VRERERERERMp3AqIiIiIiIiplM4FREREREREdMpnIqIiIiIiIjpFE5FRERERETEdAqnIiIiIiIiYjqFUxERERERETGdvap3+N133/HUU0+xcOHCqt61SM2Rmgpr18Lu3bB5M/TsCUOHlt7G5QKvFzp1gqQkc+oVERERETlBVRpOX375ZVasWEFUVFRV7lakeikveKamErjzTgKHDxPIyiJgtxNMS4NAgODgwQBYvviCo9OnYzl6FA4exBIfj/W117DOm4e1SxesVmvxfSm4ioiIiEg1V6XhtEmTJsyePZuHH364zDYZGRlVWJFxubm51b7G6q4uH8O4RYtoMGcOBcEgAZ+PfKeTgq++Yu+ePWT16kUwGCRmxQpio6Nx5OdjCwaxWCxYAwFy3nmHPZ07A+Bevx5PIIDFbscaEYHf6SRgs5H13nsciogAIPKHH2j03HNYCgqwvfEGBx56iPx27bDZbFitViwWC1Fff03Ut98SiIrCmpNDTtu25LRvb+YhqjJ1+eewsugYioiISGWq0nB61VVX8csvvxyzTcuWLauomuOTkZFR7Wus7urSMQwGg/h8Pnw+H/61a/GnpLA/Jgar34/d4cAOOPPzOWP9eqwTJ2K1WrH6fFj+9S/IzobMTLBaC//07k3j/wXHnzMyOOnddwuH8x4+DA4HuFycfO210KkTwWCQwKefEjh6lEBCAgWHDxO5Zw/+007D7/dTUFCAbf167DNnYvd4cO7ZgzM2FuuKFbBgQZ24ylqXfg7DpSYcw/T0dLNLEBERkQqq8jmnIjVRdl42KZtS2HJwC80TmtP33L7ERMSUaBcIBEJh1OfzkZ+fj8PhwOl0EvHNN7jy8rAfPIg1Lw+Cwd+DZ/fuhQEToEuXwoB4jKG/Oe3b/96mlDmnFosFW+fO2F55BfbtA5sNOneG+HigMDT716/Hn5OD32rFGx1NVmIiNqsVZ1oazgsuwOl0YrPZwn9wRURERERQOBUp15qda+ix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yqRJk4pt+/Pvc/1OFxGR6qLc8TuLFy+mZcuWJCYmhv6IVLq1azlqt2OLjSUiNxfWrjW7IqnBbDYbLpcLj8djdikiIiIiUkHlXlKIi4vjrrvuqopapA4LduyI5623qP/bb2CzQadOZpckNZzL5WLfvn3FViYVERERkeqr3E9s9evXZ/z48bRq1Sp0/7O+ffuGvTCpW462b49jyhSc335bGEw1pFdOkNVqxeVykZ2dTf369c0uR0RERETKUaEFkQAOHDgQ9mKkbgoGg3g8HuIvvxyuusrscqQWKbp6mp+fr1vLiIiIiFRz5c457dWrF6effjpDhgwhNzeX6667rgrKkrrE6/XidDoVHqTSWa1W3G432dnZZpciIiIiIuWo0H1OGzduDECXLl0YN25c2IuSuiMYDOL1enG73WaXIrWUy+Xi8899TJvm53+3exQRERGRaqhCd9tu06YNABdccAGBQCCc9Ugdk5eXh81m01VTCZvPPrPwyCPRvPLKUa6/HmbPNrsiERERESlNueE0NjaWlJQUNm/ezLJly3C5XFVRl9QFqakcnTWL6PXrza5EarG1ayEzM4q8vBwOHw4ycSK6gio12qRJk8jIyDC7DBERkUpXbjhNTk5m69atzJgxg23btjF16tSqqEtqu9RUAnfeie+tt4i8916lBQmbTp0gJ8dBfr6V6GgfTqduoys126WXXsoLL7xAv379+Mc//qH7+YqISK1RZjjdtGkTAPHx8YwbN4758+fzyCOPEB8fX+x5keOydi1H7XYio6Ox+v1KCxI2SUkwcSLY7dE0anQUl0u30ZWaLSkpiWeffZa5c+eSnp7OJZdcwiOPPMLOnTvNLk1EROSElHkrmbS0NBYsWED37t0555xzSEhIIDs7m++++46VK1fSokULzj333KqsVWqTTp3IWbaM2D17wGZTWpCwGjoUWreOIi0tm86dAyQlWdGoSKmptm3bxvLly/n444/p0KEDixcvxu/388ADD7B8+XKzyxMRETluZYbTu+66iz179rBkyRJeeeUVDh06RGJiIhdeeCGjR4/mlFNOqco6pZbxd+pE4IkniNiwoTCYJiWZXZLUcl26WGndOoKIiFwg2uxyRI7bo48+Sp8+fRgyZAhRUVGh7X/7299MrEpEROTElRlOARo1asSDDz5YVbVIHZKXl0dEp07Qo4fZpUgdEhER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f02TJk3MLklERKRSlBlOn3766aqsQ+qQvObNidu0icMREQQLCrDs3m12SVIHuFxgteZit7upVw/uuWc/SUknm12WiGFTpkxh2bJlrF27lmbNmjFy5EizSxIREakUZYbTU089FYAdO3awatUq8vPzAdi3bx+TJk2qmuqkVjrSqxfxH32EMzeXHJeL6PT0wjGXunoqYZKaCjNm+LHZCvB6I5gyBa64IgtQOJWa55577mHBggVmlyEiIlLpyp1zWnRG9ptvvuGXX34hKysr3DVJLZfTvj387W9EORzktGgBTiea/CfhtHgxZGcfJSsriqNHLWzcaHZFIscvNjaW1atXs23bNrZv38727dvNLklERKRSlLtab3R0NHfffTc///wz06ZN45ZbbqmKuqS2u/VWIj/8kMMFBfgdDuya/CdhtHt3kIiIHA4eTKCgADSSXGoqj8fDrl27eP3110PbLBYLb7zxholViYiIVI5yw6nFYmH//v14vV6OHj3K0aNHq6Iuqe2SkrAsWEBUWho57dsToyG9EkannJJHMGgjGLRjs8HJGs0rNdCiRYtYsGABNpuN4cOHk6T/N0VEpJYpN5wOGTKEDz74gN69e3PFFVfoXmpSeZKSiO7YkczMTGLMrkVqrdRUsFpzcDiiiImB6Gi49VazqxIx7t1332XVqlV4PB4efvhhhVMREal1yg2nF1xwAc2aNWPXrl2sXLmSuLi4KihL6gqHw4HVaiU3N5fIyEizy5HaJD+bH1en8N0/txB5uCkOy63cdFNhME1KgowMswsUMcbpdOJ0OomPjw8tUigiIlKblBtOFy9ezOuvv07z5s3ZunUr9913n66eSqWKiYkhOztb4VQqz7418EkPmuQGGHCJnZy8SCZ2f4QX/ruSpKSLza5O5IQFg0GzSxAREal05YbTZcuW8c477xAREUFOTg79+/dXOJVKFRkZicfjIScnh6ioKLPLkZouPxs+6QH+bOxWG7n5iTR07cNqCTL0rz0g/zdwuM2uUsSwrVu3MnLkSILBYOjrIjNnzjSxMhERkcpRbjhNSEjAZrMBhSFCw3olHGJiYjh8+DCRkZFYLBazy5GabEcKBAMAZBfE4LJ5sVoKrzI5HQHYmQLN7jSzQpHj8swzz4S+7tevn3mFiIiIhEm54TQYDHLdddfRtm1bfvjhB/x+f+hsrc7USmWJiIjA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0syuQkRERESkclR5OO3QoQMdOnSo6t2KiIiIiIhINWY1uwARERERERERhVMRERERERExncKpiIiIiIiImE7hVEREREREREyncCoiIiIiIiKmUzgVERERERER0ymcioiIiIiIiOkUTkVERERERMR0CqciIiIiIiJiOoVTERERERERMZ3CqYiIiIiIiJhO4VRERERERERMp3AqIiIiIiIiplM4FREREREREdMpnIqIiIiIiIjpFE5FRERERETEdAqnIiIiIiIiYjqFUxERERERETGdwqmIiIiIiIiYTuFURERERERETKdwKiIiIiIiIqZTOBURERERERHTKZyKiIiIiIiI6exmFyAiIiIiIuWwWEpsallau2Aw7KWIhIuunIqIiIiIiIjpFE5FRERqqUAgwPjx4+nbty8DBgxgx44dJdpkZmZy1VVXkZeXZ0KFIiIiv1M4FRERqaVWr16Nz+cjJSWFkSNHkpycXOz5zz77jDvuuIP9+/ebVKGIiMjvNOdURESklkpPT+eSSy4BoE2bNmzcuLHY81arlVdffZW//e1vx+wnIyMjLPXl5uYa6ttI+3D2bWYtBw4cKPY4Ly+PLVu2FNt26NChKqml1PmOf3Ksfen7aaxtRY436JhXp76rUy1G/l8B4/+3VBaFUxERkVrK4/HgdrtDj202G36/H7u98Nd/586dK9RPy5YV/VhsTEZGhqG+jbQPZ99m1rJnz55ij7ds2ULz5s2LbWvUqFGV1FIRx3qtvp8n3ndpdMyrT9/VqRYj/6+A8f9bjEhPTy/zOQ3rFRERqaXcbjderzf0OBAIhIKpiIhIdaNwKiIiUku1a9eO1NRUANavX8/ZZ59tckUiIiJl0+lTERGRWqpbt26kpaXRr18/gsEgU6dO5dVXX6VJkyZ07drV7PJERESKUTgVERGppaxWK5MmTSq2rVmzZiXaffTRR1VVkoiISJk0rFdERERERERMp3AqIiIiIiIiplM4FREREREREdMpnIqIiIiIiIjpFE5FRERERETEdAqnIiIiIiIiYjqFUxERERERETGdwqmIiIiIiIiYTuFURERERERETKdwKiIiIiIiIqZTOBURERERERHTKZyKiIiIiIiI6RRORURERERExHQKpyIiIiIiImI6hVMRERERERExncKpiIiIiIiImE7hVEREREREREyncCoiIiIiIiKmUzgVERERERER0ymcioiIiIiIiOkUTkVERERERMR0CqciIiIiIiJiOrvZBYiIiIiI1AoWS4lNLUtrFwyGvRSj9uzZU+zxgQMHSmxr1KhRVZYkdZCunIqIiIiIiIjpquzKaXZ2Ng899BAej4f8/HweeeQR2rZtW1W7FxERERERkWqsysLpq6++ykUXXcTAgQP56aefGDlyJG+//XZV7V5ERERERESqsSoLpwMHDsTpdAJQUFBAREREVe1aREREREREqrmwhNNly5bx+uuvF9s2depUWrduzf79+3nooYcYO3Zsqa/NyMgIR0mVJjc3t9rXWN3pGJ44HcMTp2N44nQMRUREpDKFJZz26dOHPn36lNi+efNmRowYwcMPP8yFF15Y6mtbtix1TbNqIyMjo9rXWN3pGJ44HcMTp2N44mrCMUxPTze7BBEREamgKhvWu3XrVoYPH84zzzxDixYtqmq3IiIiIiIiUgNUWTidOXMmPp+PKVOmAOB2u5k3b15V7V5ERERERESqsSoLpwqiIiIiIiIiUhar2QWIiIiIiIiIKJyKiIiIiIiI6RRORURERERExHQKpyIiIiIiImI6hVMRERERERExncKpiIiIiIiImE7hVEREREREREyncCoiIiIiIiKms5tdgIiIiIiI1F579uwpse3AgQPFtjdq1KgqS5JqSuFURERERETqBIvlz1talmgTDFZJKYbUlYCvYb0iIiIiIiJiOoVTERERERERMZ3CqYiIiIiIiJhO4VRERERERERMp3AqIiIiIiIiplM4FREREREREdMpnIqIiIiIiIjpFE5FRERERETEdAqnIiIiIiIiYjqFUxERERERETGdwqmIiIiIiIiYTuFURERERERETGc3uwARERGRP7NYStvassSWYDDspRi2Z8+eYo8PHDhQYlujRo2qsiQRkRpBV05FRERERETEdAqnIiIiIiIiYjoN6xUREamlAoEAEydOZPPmzTidTiZPnkzTpk1Dzy9dupQ333wTu93Ovffey2WXXWZitSIiNVtFpiNUx6kI1YnCqYiISC21evVqfD4fKSkprF+/nuTkZObNmwfA/v37WbhwIf/85z/Jy8vjlltuoXPnzjidTpOrlkpTyiflkrN2qZafljVvV6RusgSD1ed/pPT0dLNLEBGRWub88883uwTTTJs2jdatW9OzZ08ALrnkEj77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WDjttNPYuXMnkZGR1KtXj0aNGpUZNI22h4oHzj+uMt64cWO8Xi9z5swpc1VyALvdXuFbtxhpC4U/K/Hx8RVq++ch2n9UNDWgKiicipQhJiaG3377jbfeeoukpCRatmzJW2+9BcDmzZuZPHkyAHFxcUydOpUffviBF154AavVyv79++nbty+33norX375JXPmzCEYDOL1epk5c2aZc4+KfPnll8yaNQubzcZpp53GpEmTeOedd/j000/Jzc1l586dDB48mBtuuIENGzbw+OOP43K5SEhIICIigvPPP5/9+/fz4IMPcvvtt7Njxw4GDRpEZmYml112GUOHDi22v4ULF/L3v/8dgJ9//plHH300dPPmWbNmMX36dOx2O7/99hs+n48ePXrw8ccfs3v3bubOnUuTJk24+uqrGTRokMKpiJQqnPPCqtOcs3Au/HI889n+GAYBTj755DI/hBsJkEaDr5FajAZCo+0XLFhA//79mTFjBg0bNiztUBxX2yJGjo3Rq9tGAqfR71E4GQmbUBjyRowYweLFi8td+fePgfC0004rNxCeSPvyAufatWtLrDI+YMAAbrrpplL7NhIIjYZHo+3j4+NZsmQJ9957L2YOrFU4FSlDw4YNmTdvHosWLeL5558nMjKSBx98kKuuuorHHnuMqVOnctZZZ7Fs2TLmz59Pp06d2Lt3L//6178IBAL06tWL7t27s2XLltAvtRdeeIFVq1bRq1evMvcbDAZ57LHH+Mc//kFCQgLPPPMMb7/9Nna7HY/HwyuvvMLPP//MPffcww033MCECROYPn06zZs3Z9asWezdu5c+ffowb948Zs2axfr168nLy2Pu3LkUFBRw6aWXlginX375Zej2CU8++SR33XUXSUlJfPjhh/zwww8AnHrqqUyePJnx48fzyy+/8PLLL/Pcc8/x0UcfMXDgQOrVq8ehQ4dCy8mLiPxROBe5CfcCOkZCYTgXfgEYNGhQuW2KdOnShYEDB9K5c+fQ+1yzZg1JSUmltjcSII0GXyO1GA2ERttHRUXx+OOP89tvv5Xb3kjbIkaOjdEAaaS90e+R0QBppL2RsAnQtGlTbrvtNr744gu6dOlyzLZGA2E42xtdldxIIDQaHo22HzhwIBs3buSkk06iU6dO5bYPF4VTkTLs2LEDt9sdCm3ff/89gwcPpkOHDmzbti00aT4/Pz80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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Get random data\n", - "data_label_0, data_label_1, x_tilde, random_label = DataPlotter.grab_random_data(size=4)\n", - "x1, x2 = data_label_0\n", - "x0, x3 = data_label_1\n", - "\n", - "temp_results = four_point_distance_based_classifier_circuit(x_tilde, x0, x1, x2, x3)\n", - "prob = DataPlotter.plot_data_points(x_tilde, data_label_0, data_label_1, temp_results) # Function to plot the data\n", - "summarize_results_4_points(prob);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Conclusion and further work #\n", - "\n", - "In general, a distance-based classifier gets better in predicting classes once more data is included. In this notebook we showed how to extend the previous algorithm to 4 data points. We saw an example where the 2-point distance-based classifier fails to predict the right label, while, when extended to four data points, it managed to classify the test point correctly. This is what we expect classically, however, the key takeaway here is that the quantum algorithm itself (step f) did not change. It is independent of the size of dataset and it is from this that we can expect huge speedups.\n", - "\n", - "Extending the classifier to more data points is now analogus. Extending the classifier to $8$ data points will be a nice challenge for the reader to test their understanding, a solution is given below.\n", - "\n", - "In this notebook we used the projectQ backend to generate the quantum algorithm. Note that for controlled gate operations in generall ancillary qubits are required. ProjectQ code does not minimize the amount of ancillary qubits needed in the algorithm, this could be improved. \n", - "\n", - "The next notebook in this series will look at how to include more than two features for the data. \n", - "\n", - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### References ###\n", - "* Book: [Schuld and Petruccione, Supervised learning with Quantum computers, 2018](https://www.springer.com/us/book/9783319964232) \n", - "* Article: [Schuld, Fingerhuth and Petruccione, Implementing a distance-based classifier with a quantum interference circuit, 2017](https://arxiv.org/abs/1703.10793)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Solution for 8 data points\n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'100000': 0.1068858, '110000': 0.1034596, '011101': 0.0987508, '101101': 0.0926906, '000000': 0.0816552, '001101': 0.0775523, '010000': 0.071826, '111001': 0.0592897, '111111': 0.0585862, '010010': 0.0501968, '001111': 0.0458293, '000010': 0.0424755, '101111': 0.0323009, '011111': 0.0256349, '110110': 0.0193298, '100110': 0.0127306, '100010': 0.0048103, '111011': 0.0035832, '111101': 0.0035406, '110010': 0.0018623, '010110': 0.0017523, '010100': 0.0012246, '001011': 0.0010171, '001001': 0.000601, '100100': 0.0005729, '000110': 0.0005717, '011011': 0.0004874, '110100': 0.0003479, '000100': 0.0002974, '011001': 0.0001265, '101011': 6.1e-06, '101001': 2.1e-06}\n", - "Sum Iris Setosa (red, label 0): 0.46317149999999996\n", - "Sum Iris Versicolor (blue, label 1): 0.06511310000000001\n" - ] - }, - { - "data": { - "image/png": "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DjVi61YjmjUzwmg0mvo8e1YCZ2cpXF0ViI6W4/JlR3TuLEfbtnJs2yZBfHx6AG3bNr0v68dSsOeSiIiIiEqefAmn+/fvx44dO8zaAgIC0KtXL5w/fz7T90ZERORHSXkmNTW10NdY2BXnc1imDNC3b/rX9h6iIAimf0aj0eLrF4WHuyA1VQeZTIr0lyRISJDixx8TUKFCvCnkSiQSVKnijJiYioiKAqRSoHLlx7hzJwVlywLz5jnhjz+c0KRJCsqUSTGrOTfHUhQU589hQeE5JCIioryUL+HUz88Pfn5+OXpvYb/lNyIiotDXWNjxHObejRsPsH17aSQm/tdWqhTQr58rGjWqarZtgwZAtWovjoJWN71Wvz7g719ARRcy/BzmXlE4h2FhYWKXQERERNnE2XqJiqBmzVJw5Ej2njkF+IwoERERERV+DKdERZSvLxAaKnYVRERERER5o8DDacuWLdGyZcuC3i0REREREREVYtKsNyEiIiIiIiLKXwynREREREREJDqGUyIiIiIiIhIdwykRERERERGJjuGUiIiIiIiIRMdwSkRERERERKJjOCUiIiIiIiLRMZwSERERERGR6BhOiYiIiIiISHQMp0RERERERCQ6hlMiIiIiIiISHcMpERERERERiY7hlIiIiIiIiETHcEpERERERESiYzglIiIiIiIi0TGcEhERERERkegYTomIiIiIiEh0DKdEREREREQkOoZTIiIiIiIiEh3DKREREREREYmO4ZSIiIiIiIhEx3BKREREREREomM4JSIiIiIiItHJxS6AiIiIiKhYkEgAAPUzvhcE0UohKoo4ckpERERERESiYzglIiIqpoxGI+bOnYuBAwfC398fkZGRFtvExsbi9ddfR1pamggVEhHlgkRi+le/QQPTyDUVXQynRERExVRQUBC0Wi0CAwMxbdo0LF261Oz13377DaNGjUJUVJRIFRIREf2H4ZSIiKiYCgsLQ/v27QEAjRs3xvXr181el0ql2L59O9zd3UWojoiIyJxEEArPk9phYWFo2rSp2GVkKiIiAvXr1896Q7KJ5zD3eA5zj+cw94rCOSwKv1fy0+zZs9G9e3d06NABANCxY0cEBQVBLjefD7Fz5844ceIEHBwcLPoICwuDs7NzntaVmpoKR0fHXLXnRR9i9M3jKZr7zO629Rs0MHs94saNIn08hX2fL59vwPycF7XjEXOf9vaRG8nJybZ/NwuFyKVLl8QuIUs3btwQu4Qij+cw93gOc4/nMPeKwjksCr9X8lNAQIBw7Ngx0/ft27e3ul2nTp2E1NRUq6/lxzm09dmxpz0v+hCjbzH2WdyOJz/2+eTJE9O/4OBg4cmTJznrO31+3v/+5aLGwnquCtU+Xz7fL53zInc8Iu7T3j5yI7PfK7ytl4iIqJjy8fFBcHAwACA8PBx16tQRuSIiIiLbuM4pERFRMdWtWzeEhoZi0KBBEAQBAQEB2L59O6pWrYouXbqIXR4REZEZhlMiIqJiSiqVYuHChWZttWrVstju9OnTBVUSEREA4OnTpwCA6OhoPH36FOXLlxe5IioMeFsvERERERERiY7hlIiIiIiIiETHcEpERERERESiYzglIiIiIiIi0TGcEhERERERkegYTomIiIiIiEh0DKdEREREREQkOq5zSkRERAVDIgEA1M/4XhBEK4WIiAofjpwSERERERGR6BhOiYiIiAqZp0+f4unTp4iOjsbTp0/FLoeIqEAwnBIREREREZHoGE6JiIiIiIhIdAynREREREREJDrO1ktERERUHL08OzLAGZKJqFDjyCkRERERERGJjuGUiIiIiIiIRMdwSkRERERERKJjOCUiIiIiIiLRMZwSERERERGR6BhOiYiIiIiISHQMp0RERERERCQ6rnNKREREREQksn+XJv5X+grFJW1pYoZTIiIiIiIiMnn69CkAIDo62vR1+fLl832/vK2XiIiIiIiIRFdgI6dJSUn46KOPoFarodPp8PHHH6NJkyYFtXsiIiIiIiIqxAosnG7fvh2tWrXCiBEjcO/ePUybNg0HDx4sqN0TERERERFRIVZg4XTEiBFQKpUAAIPBAAcHh4LaNRERERERERVy+RJO9+/fjx07dpi1BQQEoFGjRoiKisJHH32EWbNmWX1vREREfpSUZ1JTUwt9jYUdz2Hu8RzmHs9h7vEcEhUz/04VWj/j+5I2TSgRiS5fwqmfnx/8/Pws2m/evImpU6dixowZaNGihdX31q9f32p7YREREVHoayzseA5zj+cw93gOc68onMOwsDCxSyAiIqJsKrDbeu/cuYMpU6ZgzZo1qFevXkHtloiIiIiIiIqAAgunK1euhFarxeLFiwEALi4u2LhxY0HtnoiIiKhYyliDEPhvTcKCWI+QiCivFVg4ZRAlIiIiIiIiWwosnBIRERFl27+T8wCcoIeIqKSQil0AEREREREREcMpERERERERiY7hlIiIiIiIiETHcEpERERERESiYzglIiIiIiIi0TGcEhERERERkei4lAwRERERERVaT58+NX0dHR2Np0+fonz58iJWRPmFI6dEREREREQkOoZTIiIiIiIiEh3DKREREREREYmOz5wSEREREdkh4xnIjOcfAfAZSKI8wJFTIiIiIiIiEh3DKREREREREYmO4ZSIiIiIiIhEx2dOiYiIiIiIcoHPIecNjpwSERERERGR6BhOiYiIiIiISHQMp0RERERERCQ6hlMiIiIiIiISHSdEIiIiIiIqRjIm5AH+m6CHk/NQUcCRUyIiIiIiIhIdwykRERERERGJjuGUiIiIiIiIRMdwSkRERERERKLjhEhERETFlNFoxPz583Hz5k0olUosWrQI1apVM72+b98+7N27F3K5HOPHj0enTp1ErJaohJFITF/Wz/hCEEQphaiwYDglIiIqpoKCgqDVahEYGIjw8HAsXboUGzduBABERUVh586d+P7775GWloYhQ4agbdu2UCqVIldNRETZ8d/1jfTLG5ld2ygqMzgznBIRERVTYWFhaN++PQCgcePGuH79uum1q1evokmTJlAqlVAqlahatSr++usvNGrUSKxyi7UXBsmQnT8kCzt7/9DN2L4w/1FMJcC//0PkSHXhJRGEwvNTCQsLE7sEIiIqZpo2bSp2CaKZPXs2unfvjg4dOgAAOnbsiKCgIMjlchw6dAi3bt3CRx99BACYMWMG+vXrhzZt2pj1wd/NRESU12z9bi5UI6cl+Q8IIiKivObi4gKNRmP63mg0Qi6XW31No9HA1dXVog/+biYiooLC2XqJiIiKKR8fHwQHBwMAwsPDUadOHdNrjRo1QlhYGNLS0pCUlIS7d++avU5ERFTQCtVtvURERJR3MmbrvXXrFgRBQEBAAIKDg1G1alV06dIF+/btQ2BgIARBwP/93//h9ddfF7tkIiIqwRhO7ZSUlISPPvoIarUaOp0OH3/8MZo0aSJ2WUXSzz//jJMnT2LlypVil1JkZLUsBGXflStX8L///Q87d+4Uu5QiR6fTYdasWfjnn3+g1Woxfvx4dOnSReyyiIiIqIgrVM+cFgXbt29Hq1atMGLECNy7dw/Tpk3DwYMH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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "## 8 points distance-based classifier ##\n", - "### Note: Far from qubit optimal solution\n", - "\n", - "# Get data:\n", - "data_label_0, data_label_1, x_tilde, random_label = DataPlotter.grab_random_data(size=8)\n", - "\n", - "\n", - "def eight_point_distance_based_classifier_circuit(x_tilde, data_label_0, data_label_1):\n", - " # Set-up a new connection with qi_backend:\n", - " qi_backend, qi_engine = initialize_qi_backend(\"distance_based_classifier_8_points\")\n", - " \n", - " # Angles data points:\n", - " angle_label0 = []\n", - " angle_label1 = []\n", - " for data_point in data_label_0:\n", - " angle_label0.append(2 * acos(data_point[0]) * sign(data_point[1]))\n", - " for data_point in data_label_1:\n", - " angle_label1.append(2 * acos(data_point[0]) * sign(data_point[1]))\n", - " angle_x_tilde = 2 * acos(x_tilde[0]) * sign(x_tilde[1])\n", - " \n", - " # Quantum circuit:\n", - " qubits = qi_engine.allocate_qureg(9) # 6 qubits + 3 ancillary qubits for CCCCRy gates\n", - " \n", - " # part_a\n", - " for qubit in qubits[0:4]:\n", - " H | qubit\n", - "\n", - " # part_b\n", - " C(Ry(angle_x_tilde), 1) | (qubits[3], qubits[4])\n", - " X | qubits[3]\n", - "\n", - " # part_c\n", - " for angle in angle_label1:\n", - " # Build CCCCRy gate from 3 ancillary, Toffoli, and a CRy gate.\n", - " Toffoli | (qubits[0], qubits[1], qubits[6])\n", - " Toffoli | (qubits[2], qubits[6], qubits[7]) \n", - " Toffoli | (qubits[3], qubits[7], qubits[8]) \n", - " C(Ry(angle), 1) | (qubits[8], qubits[4])\n", - " Toffoli | (qubits[3], qubits[7], qubits[8]) \n", - " # Set y_m label conditioned on first three qubits being 1, (don't include 4th qubit)\n", - " CNOT | (qubits[7], qubits[5])\n", - " Toffoli | (qubits[2], qubits[6], qubits[7]) \n", - " Toffoli | (qubits[0], qubits[1], qubits[6])\n", - " \n", - " Toffoli | (qubits[0], qubits[1], qubits[2])\n", - " CNOT | (qubits[0], qubits[1])\n", - " X | qubits[0]\n", - " \n", - " for angle in angle_label0:\n", - " # Build CCCCRy gate from 3 ancillary, Toffoli, and a CRy gate.\n", - " Toffoli | (qubits[0], qubits[1], qubits[6])\n", - " Toffoli | (qubits[2], qubits[6], qubits[7]) \n", - " Toffoli | (qubits[3], qubits[7], qubits[8]) \n", - " C(Ry(angle), 1) | (qubits[8], qubits[4])\n", - " Toffoli | (qubits[3], qubits[7], qubits[8]) \n", - " Toffoli | (qubits[2], qubits[6], qubits[7]) \n", - " Toffoli | (qubits[0], qubits[1], qubits[6])\n", - " \n", - " Toffoli | (qubits[0], qubits[1], qubits[2])\n", - " CNOT | (qubits[0], qubits[1])\n", - " X | qubits[0] \n", - "\n", - " # part_d\n", - " H | qubits[3]\n", - "\n", - " qi_engine.flush()\n", - " temp_results = qi_backend.get_probabilities(qubits)\n", - " \n", - " # Results:\n", - " temp_results = qi_backend.get_probabilities(qubits)\n", - " return temp_results \n", - "\n", - "\n", - "temp_results = eight_point_distance_based_classifier_circuit(x_tilde, data_label_0, data_label_1)\n", - "\n", - "\n", - "def strip_ancillary_qubits(results):\n", - " histogram_results = {}\n", - " for k, v in results.items():\n", - " histogram_results[k[:-3]] = v # Strip ancillary qubits\n", - " return histogram_results\n", - "\n", - "\n", - "histogram_results = strip_ancillary_qubits(temp_results)\n", - "print(histogram_results)\n", - "\n", - "\n", - "def summarize_results_8_points(histogram_results):\n", - " sum_label0 = 0\n", - " sum_label1 = 0\n", - " for key, value in histogram_results.items():\n", - " if key[3] == \"0\":\n", - " if key[-1] == \"0\":\n", - " sum_label0 += value\n", - " else:\n", - " sum_label1 += value\n", - " print(\"Sum Iris Setosa (red, label 0): \", sum_label0)\n", - " print(\"Sum Iris Versicolor (blue, label 1): \", sum_label1)\n", - "\n", - " \n", - "summarize_results_8_points(histogram_results)\n", - "# Function to plot the data\n", - "prob = DataPlotter.plot_data_points(x_tilde, data_label_0, data_label_1, histogram_results)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/examples/classifier_example/classification_example3_4_features.ipynb b/docs/examples/classifier_example/classification_example3_4_features.ipynb deleted file mode 100644 index e87ce0e..0000000 --- a/docs/examples/classifier_example/classification_example3_4_features.ipynb +++ /dev/null @@ -1,792 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#
A Quantum distance-based classifier
#\n", - "##
Robert Wezeman, TNO
##" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Table of Contents\n", - "* [Introduction](#introduction)\n", - "* [Problem](#problem)\n", - "* [Theory](#theory)\n", - "* [Algorithm for the arbitrary state preparation](#algorithm)\n", - "* [Implementation of the distance-based classifier](#implementation)\n", - "* [Conclusion and further work](#conclusion)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "$$ \\newcommand{\\ket}[1]{\\left|{#1}\\right\\rangle} $$" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "## Import external python file\n", - "import nbimporter\n", - "from data_plotter import get_bin, DataPlotter # for easier plotting\n", - "DataPlotter = DataPlotter()\n", - "\n", - "# Import math functions \n", - "import numpy as np\n", - "from math import acos, pi, atan, sqrt\n", - "from numpy import sign\n", - "\n", - "# Import plotting tools\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Introduction #\n", - "\n", - "\n", - "This notebook is the third in the series on the quantum distance-based classifier. In the first notebook, we looked at how to build a distance-based classifier with two data points, each having two features. In the second notebook, we looked at how to increase the amount of data points. In this notebook we will look at how to increase the amount of features. \n", - "\n", - "\n", - "\n", - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Problem #\n", - "We repeat the problem description from the previous notebooks. We define the following binary classification problem: Given the data set \n", - "$$\\mathcal{D} = \\Big\\{ ({\\bf x}_1, y_1), \\ldots ({\\bf x}_M , y_M) \\Big\\},$$\n", - "consisting of $M$ data points $x_i\\in\\mathbb{R}^n$ and corresponding labels $y_i\\in \\{-1, 1\\}$, give a prediction for the label $\\tilde{y}$ corresponding to an unlabeled data point $\\bf\\tilde{x}$. The classifier we shall implement with our quantum circuit is a distance-based classifier and is given by\n", - "\\begin{equation}\\newcommand{\\sgn}{{\\rm sgn}}\\newcommand{\\abs}[1]{\\left\\lvert#1\\right\\rvert}\\label{eq:classifier} \\tilde{y} = \\sgn\\left(\\sum_{m=0}^{M-1} y_m \\left[1-\\frac{1}{4M}\\abs{{\\bf\\tilde{x}}-{\\bf x}_m}^2\\right]\\right). \\hspace{3cm} (1)\\end{equation}\n", - "\n", - "This is a typical $M$-nearest-neighbor model where each data point is given a weight related to the distance measure. To implement this classifier on a quantum computer we use amplitude encoding. Details are found in the previous notebooks.\n", - "\n", - "[Back to Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Theory #\n", - "\n", - "In the previous notebooks we encoded the data, each having two features, with a simple $R_y(\\theta)$ rotation. The angle for this rotation was chosen such that it rotated the state $\\ket{0}$ to the desired state $\\ket{\\bf{x}}$ corresponding to the data. If we want to include more features for our data points we need to generalize this rotation. Instead of a simple rotation, we now need a combination of gates such that it maps $\\ket{0\\ldots 0} \\mapsto \\ket{\\bf x} = \\sum_i a_i \\ket{i}$, where $\\ket{i}$ is the $i^{th}$ entry of the computational basis $\\left\\{\\ket{0\\ldots0},\\ldots,\\ket{1\\ldots1}\\right\\}$. Again we only work with normalised data, meaning $\\sum_i \\lvert a_i \\rvert^2=1$. The general procedure how to initialize a state to an arbitrary superposed state can be found in the article by [Long and Sun](https://arxiv.org/abs/quant-ph/0104030). In this notebook we will consider how to implement their scheme for 2 qubits, that is up to 4 features:\n", - "\\begin{equation}\n", - "\\ket{00} \\mapsto a_{00} \\ket{00} + a_{01} \\ket{01} + a_{10} \\ket{10} + a_{11} \\ket{11}\n", - "\\end{equation}\n", - "\n", - "For the implementation we closely follow the reference and use single bit rotation gates $U(\\theta)$ defined by \n", - "\\begin{equation}\n", - "U(\\theta) = \\begin{pmatrix} \n", - "\\cos(\\theta) & \\sin(\\theta) \\\\\n", - "\\sin(\\theta) & -\\cos(\\theta) \n", - "\\end{pmatrix} = R_y(2\\theta) \\cdot Z\n", - "\\end{equation}\n", - "and controlled versions of it. Because we will only act with these gates on $\\ket{0}$ we can even drop the $Z$ gate.\n", - "\n", - "For two qubits the scheme consists of three steps:\n", - "1. Apply a bit rotation $U(\\alpha_1)$ on the first qubit:\n", - "\\begin{equation}\n", - "U(\\alpha_1)\\ket{0}\\otimes\\ket{0}= \\sqrt{\\abs{a_{00}}^2 + \\abs{a_{01}}^2} \\ket{00} + \\sqrt{\\abs{a_{10}}^2 + \\abs{a_{11}}^2} \\ket{10},\n", - "\\end{equation}\n", - "where $\\alpha_1$ is given by\n", - "\\begin{equation}\n", - "\\alpha_1 = \\arctan\\left(\\sqrt{\\frac{\\abs{a_{10}}^2 + \\abs{a_{11}}^2}{\\abs{a_{00}}^2 + \\abs{a_{01}}^2}}\\right)\n", - "\\end{equation}\n", - "2. Next, apply a controlled-rotation $U(\\alpha_2)$ on the second qubit, with as control the first qubit being 0. Choose $\\alpha_2$ such that:\n", - "\\begin{equation}\n", - "\\cos(\\alpha_2) = \\frac{a_{00}}{\\sqrt{\\abs{a_{00}}^2+\\abs{a_{01}}^2}}, \\hspace{1cm} \\sin(\\alpha_2) = \\frac{a_{01}}{\\sqrt{\\abs{a_{00}}^2+\\abs{a_{01}}^2}}\n", - "\\end{equation}\n", - "3. Lastly, apply a controlled-rotation $U(\\alpha_3)$ on the second qubit, with the first qubit being 1 as control. Choose $\\alpha_3$ such that:\n", - "\\begin{equation}\n", - "\\cos(\\alpha_3) = \\frac{a_{10}}{\\sqrt{\\abs{a_{10}}^2+\\abs{a_{11}}^2}}, \\hspace{1cm} \\sin(\\alpha_3) = \\frac{a_{11}}{\\sqrt{\\abs{a_{10}}^2+\\abs{a_{11}}^2}}\n", - "\\end{equation}\n", - "\n", - "The angles $\\alpha_2$ and $\\alpha_3$ are chosen such that the root terms cancel out, leaving us with the desired result:\n", - "\\begin{equation}\n", - "\\begin{split}\n", - "\\sqrt{\\abs{a_{00}}^2 + \\abs{a_{01}}^2}& \\ket{0}\\otimes U(\\alpha_2)\\ket{0} + \\sqrt{\\abs{a_{10}}^2 + \\abs{a_{11}}^2} \\ket{1}\\otimes U(\\alpha_3)\\ket{0}\\\\\n", - "&=a_{11} \\ket{00} + a_{01} \\ket{01} + a_{10} \\ket{10} + a_{11} \\ket{11}\n", - "\\end{split}\n", - "\\end{equation}\n", - "\n", - "Note: this circuit makes it also possible to encode 3 features by simply setting $a_{11}=0$. \n", - "\n", - "The circuit looks something like this:\n", - "\n", - "\n", - "\n", - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Algorithm for the arbitrary state preparation#\n", - "\n", - "In this notebook we will work with the Qiskit backend for the quantum inspire. Let us first take a closer look at the state preparation part of the circuit used to preparing an arbitrary state. The following code loads the scaled and normalised data of the Iris set containing all 4 features. Let us consider the first point in this set" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[-0.32695550305984783,\n", - " 0.4736868636179572,\n", - " -0.5699845991812187,\n", - " -0.5863773622428564]" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load scaled and normalised data\n", - "iris_setosa_normalised, iris_versicolor_normalised = DataPlotter.load_data(max_features=4)\n", - "first_data_point = [feature[0] for feature in iris_setosa_normalised]\n", - "first_data_point" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We want to build the circuit such that:\n", - "\\begin{equation}\\ket{00} \\mapsto -0.3270 \\ket{00} + 0.4737 \\ket{01} - 0.5700 \\ket{10} - 0.5864 \\ket{11}\\end{equation}" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "a00, a01, a10, a11 = first_data_point\n", - "alpha1 = atan(sqrt((a10**2 + a11**2) / (a00**2 + a01**2)))\n", - "alpha2 = np.arctan2(a01, a00) \n", - "alpha3 = np.arctan2(a11, a10) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always the first step is to set up a connection with the Quantum Inspire:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from quantuminspire.credentials import get_authentication\n", - "\n", - "from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit\n", - "from qiskit.tools.visualization import plot_histogram, circuit_drawer\n", - "from qiskit import execute\n", - "\n", - "from quantuminspire.qiskit import QI\n", - "from quantuminspire.api import QuantumInspireAPI\n", - "\n", - "QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/')\n", - "\n", - "\n", - "authentication = get_authentication()\n", - "# Temporairy alternative so that we can give our project a name:\n", - "QI._api = QuantumInspireAPI(QI_URL, authentication, project_name=\"Distance-based Classifier more features (NEW)\")\n", - "\n", - "# Alternative:\n", - "# authentication = [email, password]\n", - "# QI.set_authentication_details(*authentication)\n", - "\n", - "qi_backend = QI.get_backend('QX single-node simulator')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now construct a function which builds the quantum circuit as discussed above. Note that Qiskit contains the general unitary gates $u3(\\theta, \\phi, \\lambda)$ which are related to our definition of $U(\\alpha)$ by:$$U(\\alpha) = u3(2\\alpha, 0, \\pi).$$\n", - "Unfortunately, these $u3$ gates are not yet implemented on the quantum inspire backend and thus we need to make use of regular $R_y$ rotations and CNOT gates." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def QuantumCircuitWithClassicalRegister(q):\n", - " # Return a QuantumCircuit with ClassicalRegister Attached\n", - " circuit_size = len(q)\n", - " b = ClassicalRegister(circuit_size)\n", - " return QuantumCircuit(q, b)\n", - "\n", - "def cRy(q, angle, ctrlidx, idx):\n", - " if len(q) < 2:\n", - " raise ValueError(\"Error, len quantum register must at least be 2.\")\n", - " circuit = QuantumCircuitWithClassicalRegister(q)\n", - " half_angle = angle / 2\n", - " \n", - " circuit.cx(q[ctrlidx], q[idx])\n", - " circuit.ry(-half_angle, q[idx])\n", - " circuit.cx(q[ctrlidx], q[idx])\n", - " circuit.ry(half_angle, q[idx])\n", - " return circuit\n", - "\n", - "\n", - "def features_encoding(q, alpha, idx1, idx2):\n", - " if len(q) < 2:\n", - " raise ValueError(\"Error, len quantum register must at least be 2.\")\n", - " # Alternative use u3(2 * alpha, 0, pi) and cu3(2 * alpha, 0, pi) gates but not yet implemented on Quantum inspire\n", - " alpha1, alpha2, alpha3 = alpha\n", - " \n", - " circuit = QuantumCircuitWithClassicalRegister(q)\n", - " \n", - " # step 1.\n", - " circuit.ry(2 * alpha1, q[idx1])\n", - "\n", - " # # step 2.\n", - " circuit.x(q[idx1])\n", - " circuit = circuit.compose(cRy(q, 2 * alpha2, idx1, idx2))\n", - " circuit.x(q[idx1])\n", - "\n", - " # step 3.\n", - " circuit = circuit.compose(cRy(q, 2 * alpha3, idx1, idx2))\n", - " return circuit\n", - "\n", - "\n", - "def measurement(q):\n", - " # TO DO: Rewrite as loop over b to avoid measurement of ancillary qubits\n", - " circuit_size = len(q)\n", - " b = ClassicalRegister(circuit_size)\n", - " meas = QuantumCircuit(q, b)\n", - " meas.measure(q, b)\n", - " return meas" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "q = QuantumRegister(2)\n", - "circuit = features_encoding(q, [alpha1, alpha2, alpha3], 0, 1)\n", - "circuit.draw(output='mpl')" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'00': 0.1068999, '10': 0.3248824, '01': 0.2243791, '11': 0.3438387}" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "#Initialize quantum register:\n", - "q = QuantumRegister(2)\n", - "meas = measurement(q)\n", - "\n", - "# Build circuit:\n", - "circuit = features_encoding(q, [alpha1, alpha2, alpha3], 0, 1)\n", - "qc = circuit.compose(meas)\n", - "\n", - "\n", - "# Execute the circuit:\n", - "def execute_circuit(circuit, shots=1):\n", - " qi_job = execute(qc, backend=qi_backend, shots=shots)\n", - "\n", - " # Temporary needed to fix naming the project:\n", - " project = next((project for project in QI._api.get_projects()\n", - " if project['name'] == QI._api.project_name), None)\n", - " if project is not None:\n", - " qi_job._job_id = str(project['id'])\n", - "\n", - " # Results of the job:\n", - " qi_result = qi_job.result()\n", - "\n", - " # Select the results of the circuit and print results in a dict\n", - " probabilities = qi_result.get_probabilities(qc)\n", - " return probabilities\n", - "\n", - "\n", - "def create_histogram_results(probabilities, number_of_bits=2):\n", - " histogram_results = {}\n", - " for k, v in probabilities.items():\n", - " # Reversed order of the bin strings to be consistent\n", - " histogram_results[k[::-1]] = v\n", - " return histogram_results\n", - "\n", - "\n", - "# Display results:\n", - "probabilities = execute_circuit(qc)\n", - "create_histogram_results(probabilities)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can compare these results with what we expected if we measure the state $\\psi = a_{00} \\ket{00} + a_{01} \\ket{01} + a_{10} \\ket{10} + a_{11} \\ket{11}$ and obtain the result $X$\n", - "\n", - "| Outcome $x$ | $Prob(X=x)$ |\n", - "|-------------|----------------------|\n", - "| $\\ket{00}$ | $\\abs{a_{00}}^2$ |\n", - "| $\\ket{01}$ | $\\abs{a_{01}}^2$ |\n", - "| $\\ket{10}$ | $\\abs{a_{10}}^2$ |\n", - "| $\\ket{11}$ | $\\abs{a_{11}}^2$ |" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Desired values: [0.10689990098111816, 0.2243792447642172, 0.3248824433037745, 0.34383841095089007]\n" - ] - }, - { - "data": { - "image/png": "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ERUVxSeffMK6det49dVXadWqVaXeIz4+nhtvvJHhw4czbdo0nnrqKWJiYgDo3bs369atA+CLL74gPz+f2NhYevfu7fhvxowZv2MTRURELqr01ZvPPvss/v7+nDlzhujoaB5++GFmzpxJw4YNefHFFyv1Hr6+viQkJJCQkFBhWXp6uuPn/7zYRUREpKpUOvQ8PDzK3ZYwbtw4xo0b55SiREREnOGq7tPbsWMHy5cv59ChQ3h6etK2bVsee+wxTQ0mIiK1QqXP6b3zzjs8/vjj1KtXj7i4OGJjY7Hb7cTFxV1y0mgREZGaptJ7em+99RazZs0iNja2XPvNN9/Myy+/XGHSaBERkZqm0nt6BQUF3HjjjRXaIyMjHdNviYiI1GSVDr0hQ4bwt7/9zTE3JFycT/O1115j0KBBTilORESkKl3x8GafPn0wmS5OLmu32zl58iRRUVGEhobi5ubGsWPHsFqtmk9SRERqhSuG3l/+8hcXlSEiIuJ8Vwy9Bx98sEJbfn4+R44cobS0lFatWl1ysmgREZGaqNJXb1qtVhISElixYgWlpaXY7XY8PDzo378/s2bNqvCsPBERkZqm0heyJCQksG3bNhYvXswPP/zArl27eP3110lOTuaVV15xZo0iIiJVotJ7emvXruXVV1+le/fujrY+ffrg4+PD+PHjee6555xSoIiISFWp9J6e3W6/5INcGzZs6HiMvYiISE1W6dDr2bMnL730Urln2uXl5bFgwQJ69OjhlOJERESqUqUPbz7//PMMGzaM2267jZYtWwJw9OhRwsLCeP31151WoIiISFWpdOh5e3vz+eefs23bNg4dOoSPjw9t2rTh1ltvddzALiIiUpNVOvQeeOABFi1aRHR0NNHR0c6sSURExCkqfU7P29sbq9XqzFpEREScqtJ7elFRUYwcOZLbbruN5s2b4+3tXW75M888U+XFiYiIVKVKh96BAwfo1KkTp0+f5vTp0+WW6ZyeiIjUBr8Zep9++ikbN26kcePGREdHc++997qiLhERkSp3xXN6b775Js8//zxFRUUUFhYSHx/PggULXFWbiIhIlbrint7KlSuZM2cOsbGxAGzYsIH4+HjGjRunQ5oiIlLrXHFPLzs7m1tuucXx+o477qCwsJBTp045vTAREZGqdsXQKykpwcPj3zuDHh4eunVBRERqrUrfpyciIlLb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briBRo0aAXDy5El++eUXCgsLOXLkCNdddx1RUVG6otaJyo52zJs3j7fffhsPDw9CQkK49957iY6Opk2bNrX64pX/pNCrIikpKUyePJkXX3yRzp07U1paipubm+PbUHZ2Nh9//DFffvklr776Kq1bt67miuu+3xqTkydP8tFHH/HVV1/x97//vcJ5PnGOy33A9u/fn759+9K+fXvt0VWT3Nxchg8fzrBhwygsLOTtt98mOzubzp07079/f2699VaaN29eq2+10lenKmC327npppswm828++672Gw2x/Fvm81GSUkJTZs2ZfDgwTRs2JANGzZUd8l1XmXGJCQkhMGDB9OgQYMKVw+K85R96RgzZgzh4eFMnTqVYcOG8c9//pNHHnmEIUOG8M4775CZmakraV0sKCiIpk2bcuLECYYPH86WLVtYvXo1119/PQsXLiQuLo5Ro0bx008/VXepv5tCrwqU/SOOi4vjX//6F7fccguvvfYaJ06cwM3NzXHOKCgoiMaNG3P8+PHqLNcQNCY13+U+YNu3b8+rr75KXFwcTzzxRK3+gK2Nxo8fT3BwMHDxqucOHTowa9YskpKSWLhwIUCtu3jlP+nwZhUrKSkhMTGRL7/8kuzsbEJDQ7nzzjvp0KEDn376KTt27GDRokXcdNNN1V2qYWhMaq79+/fz448/MnjwYMdtJWW+/fZbFi9ezJQpU+jQoUM1Vmk8ZbMS2e127HY7NputzlzwpdBzArvdTk5ODsnJyezatYvt27dTUFBAREQE99xzD/369avuEg1HY1Jz1eUP2Lrmv8+L10YKPScrO2lvtVqx2Wy1+gRwXaExqR3qwges1DwKPRERMQxdyCIiIoah0BMREcNQ6ImIiGEo9ERExDAUeiIiYhgKPRERMQyFnoiIGMb/AzSK6VmLa0PdAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Desired outcomes for the test point:\n", - "desired_outcomes = [coefficient**2 for coefficient in [a00, a01, a10, a11]]\n", - "print('Desired values: ', desired_outcomes)\n", - "plot_histogram(create_histogram_results(probabilities))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the next section we use this feature encoding schema in the distance-based classifier for 2 data points, each having 4 features. In the algorithm however, feature encoding is done in a controlled fashion, with the index qubit as control. Therefore we will also need the same circuit as above but transformed to a controlled version. This can be done by replacing every gate by a controlled equivalent. " - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def ccRy(q, angle, ctrlidx1, ctrlidx2, idx):\n", - " if len(q) < 3:\n", - " raise ValueError(\"Error, len quantum register must at least be 3.\")\n", - " circuit = QuantumCircuitWithClassicalRegister(q)\n", - " quarter_angle = angle / 4\n", - " \n", - " circuit.ccx(q[ctrlidx1], q[ctrlidx2], q[idx])\n", - " circuit.cx(q[ctrlidx2], q[idx])\n", - " circuit.ry(quarter_angle, q[idx])\n", - " circuit.cx(q[ctrlidx2], q[idx])\n", - " circuit.ry(- quarter_angle, q[idx])\n", - " \n", - " circuit.ccx(q[ctrlidx1], q[ctrlidx2], q[idx])\n", - " circuit.cx(q[ctrlidx2], q[idx])\n", - " circuit.ry(-quarter_angle, q[idx])\n", - " circuit.cx(q[ctrlidx2], q[idx])\n", - " circuit.ry(quarter_angle, q[idx]) \n", - " return circuit\n", - "\n", - "\n", - "def c_features_encoding(q, alpha, ctrlidx, idx1, idx2):\n", - " if len(q) < 3:\n", - " raise ValueError(\"Error, len quantum register must at least be 3.\")\n", - " alpha1, alpha2, alpha3 = alpha\n", - " circuit = QuantumCircuitWithClassicalRegister(q)\n", - " \n", - " # step 1.\n", - " circuit = circuit.compose(cRy(q, 2 * alpha1, ctrlidx, idx1)) # old: ry(2 * alpha1, q[idx1])\n", - "\n", - " # # step 2.\n", - " circuit.cx(q[ctrlidx], q[idx1]) # old: x(q[idx1])\n", - " circuit = circuit.compose(ccRy(q, 2 * alpha2, idx1, ctrlidx, idx2)) # old: cRy gates\n", - " circuit.cx(q[ctrlidx], q[idx1]) # old: x(q[idx1])\n", - "\n", - " # step 3.\n", - " circuit = circuit.compose(ccRy(q, 2 * alpha3, idx1, ctrlidx, idx2)) # old: cRy gates\n", - " return circuit" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Implementation of the distance-based classifier#\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We first consider the case with 2 data points and just 2 features, randomly chosen from the Iris data set. This so that we can later compare the results of the distance-based classifier with 2 features to the implementation with 4 features." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def two_features_classifier(q, x_tilde, x0, x1):\n", - " if len(q) != 4:\n", - " raise ValueError(\"Error, len quantum register must be 4.\")\n", - " circuit = QuantumCircuitWithClassicalRegister(q)\n", - " \n", - " # Angles data points:\n", - " angle_x_tilde = 2 * acos(x_tilde[0]) * sign(x_tilde[1]) # Label ?\n", - " angle_x0 = 2 * acos(x0[0]) * sign(x0[1]) # Label 0\n", - " angle_x1 = 2 * acos(x1[0]) * sign(x1[1]) # Label 1\n", - " \n", - " # part_a:\n", - " for i in range(2):\n", - " circuit.h(q[i])\n", - "\n", - " # part_b:\n", - " circuit = circuit.compose(cRy(q, angle_x_tilde, 1, 2))\n", - " circuit.x(q[1])\n", - "\n", - " # part_c:\n", - " circuit = circuit.compose(ccRy(q, angle_x0, 1, 0, 2))\n", - " circuit.x(q[0])\n", - "\n", - " # part_d:\n", - " circuit = circuit.compose(ccRy(q, angle_x1, 1, 0, 2))\n", - "\n", - " # part_e:\n", - " circuit.cx(q[0], q[3])\n", - "\n", - " # part_f:\n", - " circuit.h(q[1]);\n", - " return circuit" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The above algorithm is the same implementation of the algorithm as we did in the first notebook. The following code runs the algorithm on randomly selected data from the iris set." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6cAAAF8CAYAAAAzes21AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAABlWElEQVR4nO3de5yMdf/H8dcc9jizu+yuQyUqEeoWIuWwKimRlPsWHZS70IkURQ4hiY2kIjqI3EiLW6WSu5S7zSm13RJtQlLKYY92Z/YwO4ffH/sztbEWO7PXHt7PHvuwc13XfK7PXCa77/le1/cy+Xw+HyIiIiIiIiIGMhvdgIiIiIiIiIjCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhrEY3ICIiIiJihIsuuoimTZtiNpsxmUzk5+djt9uZNGkSf/vb34Kyv82bNxMSEsJDDz3Ev/71r4DvQ6QqUzgVERERkRpr0aJFxMbG+h+/8cYbTJkyhaSkpKDt8+jRo3z33XdBqy9SVem0XhERERERwO12c/DgQWJiYvzL5s2bxy233ELv3r158MEHOXz4MAAff/wxt9xyC3369KFv37589dVXAAwYMIC1a9f6n//XxwBjxoyhoKCA3r174/F4eOmll+jVqxd9+vTh3nvv5ciRIxXwakUqH42cioiIiEiNdffdd2MymcjMzCQsLIyrr76aadOmAfDuu+/y448/smLFCqxWK0lJSYwfP57XX3+d6dOn89xzz9GqVSs2bNjAl19+Sbt27U5pn9OmTaNXr1689957HDx4kEWLFrF582ZCQ0NZsGAB27dv59prrw3myxaplBRORURERKTGOnZa7/fff8/gwYNp3bo1cXFxAKxfv57vvvuOv//97wB4vV7y8/MB6NmzJ0OHDqVLly507NiRwYMHn9H+69WrR7NmzbjllltISEggISGBK6+8MjAvTqSKUTgVERERkRqvRYsWjBkzhvHjx3PppZfSoEEDvF4vgwYN4vbbbwfA5XJx9OhRAB599FH+8Y9/sGHDBlatWsVrr73GqlWrAPD5fP66RUVFJ92v2WxmyZIlfPfdd2zevJmpU6fSvn17xo8fH6RXKlJ56ZpTERERERHgxhtvpFWrVkydOhWATp06sXLlShwOBwAvvvgio0aNwu12c80115CXl8dtt93GxIkT2bt3L263m9jYWHbs2AHAL7/8wq5du47bj9VqxePx4PP5+OGHH7jxxhtp3Lgx9913HwMHDjzhc0RqAo2cioiIiIj8vyeffJKbbrqJL774gr59+3L48GFuvfVWTCYTZ511FomJiVitVsaOHctjjz2G1WrFZDIxdepUQkNDeeCBB3jiiSf4/PPPueCCC2jbtu1x+6hTpw4tWrTghhtuYNmyZdxwww38/e9/JzIykvDwcI2aSo1l8v35vAMRERERERERA+i0XhERERERETGcwqmIiIiIiIgYTtecioiIVFNer5dJkyaxa9cuQkNDmTJlCo0aNfKvnzJlCt988w02mw2AuXPnEhUVZVS7IiJSwymcioiIVFPr1q3D5XKRlJTEtm3bSExMZN68ef71O3fuZP78+cTGxhrYpYiISDGd1isiIlJNpaSk0LlzZwBatWrlv70FFI+q7t+/nwkTJtC/f39WrlxpVJsiIiJAJRs5TUlJMboFERGpZi677DKjWzCMw+HAbrf7H1ssFtxuN1arlby8PO68807++c9/4vF4uOuuu7jkkkto1qxZiRr62SwiIoFW2s/mShVOofL/EpGamkrz5s2NbqNK0zEsPx3D8tMxLL+qcAxrerCy2+04nU7/Y6/Xi9Va/KM/IiKCu+66i4iICACuuOIKfvjhh+PCKVTsz+Zgvq+C/Z6tqr3ruFR87WDXr6q1g11fvVd87RM52c9mndYrIiJSTbVp04bk5GQAtm3bRtOmTf3rfv75Z2677TY8Hg9FRUV88803XHzxxUa1KiIiUvlGTkVERCQwunXrxsaNG+nfvz8+n4+pU6eycOFCGjZsSNeuXenduze33norISEh9O7dmyZNmhjdsoiI1GAKpyIiItWU2Wxm8uTJJZY1btzY//2gQYMYNGhQRbclIiJyQjqtV0RERERERAyncCoiIiIiIiKGUzgVERERERERwymcioiIiIiIiOEUTkVETsOBAwe49dZbj1uenJxMUlLSKdV45513uOuuuxgwYAD9+/dnw4YNJ90+KSmJoqKiM+r3ZHw+H0888QROp5MBAwawd+/eUre95pprKCwsPKW6J9p227Zt9O3bl/79+zNnzhwA0tPTj5usR0RERGouzdYrIhIACQkJp7Rdbm4uc+fO5cMPPyQ0NJTDhw/Tt29f/vvf/2I2n/jzwldffZWbb745gN0W++ijj7j44oux2WwBr/1XEydOZPbs2Zx77rkMGTKE77//nhYtWmCz2di6dSuXX3550HsQERGRyk3hVESqv+Rk2LQJOnSAUwyRp2LAgAHExsZy9OhRevbsyf79+xk2bBjDhw/H4XCQn5/Po48+SqdOnfzPCQ0NpaioiGXLlnH11VfTsGFD1q1bh9ls5uDBgzz55JMUFhYSFhbG008/zYYNG0hLS+PRRx9l7ty5JCYmkpKSAsCNN97I3Xffzccff8zrr7+O1Wqlbt26zJo1iyNHjjBp0iQKCwtJS0vjkUce4dprry3R/+LFi3n55ZdLLDt06FCpz5swYQK//fYbcXFxPPvss7jdbsaOHcv+/fvxer088sgjtG/f/rjj5HA4cLlcNGzYEIBOnTqxadMmWrRowY033sjs2bMVTkVERESn9YpINZecDPfcA6++WvxncnJAy9944428+eabWCwWAH755Reys7N55ZVXeP755/F4PCW2DwsLY9GiRezfv59BgwZx9dVXs3LlSgCeffZZBgwYwOLFi7n33nt57rnn6Nu3L3Xq1GHWrFmsX7+eAwcOsHz5ct566y0++OADdu3axQcffMC9997rD7wOh4OffvqJf/7znyxcuJDJkyezdOnSEn0UFBRw8OBBYmNjSyw/2fNuu+02lixZwjnnnMPy5cv55JNPqF27NkuXLmXu3LmlnqLrcDiw2+3+xzabjdzcXAAuvPBCf9gWERGRmk0jpyJSvW3aBB4PxMVBRkbx4wCOnp5//vklHjdp0oR+/foxYsQI3G43AwYMKLH+8OHDFBQUMGHCBAD27dvHoEGDuOyyy/jxxx959dVXmT9/Pj6fD6u15D/Re/fupW3btphMJkJCQrj00kvZu3cvY8aM4dVXX2XJkiVccMEFXHvttdSpU4d58+axcuVKTCYTbre7RK2jR49Su3bt415Pac8LCQmhVatWALRp04aNGzeSmZnJTz/9xPbt2wFwu91kZmYeV9Nut+N0Ov2PnU4n0dHRAFgsFqxWK16vt9TTmkVERKRm0G8CIlK9degAFktxMLVYih8HkMlkKvF4165dOJ1OXnvtNRITE3n66adLrE9PT+fxxx/H4XAAcM4551C7dm1CQkK44IILeOyxx1i8eDFPPfUU3bt39+/D6/XSuHFj/yhjUVER//vf/2jUqBFJSUkMGzaMJUuWAPDJJ5/w4osv0rt3b2bMmEH79u3x+Xwl+qhdu3aJwHhMac8rKioiNTUVgK+//pomTZrQoEEDevbsyeLFi3n99dfp3r07tWrVOq6m3W4nJCSEX375BZ/Px4YNG2jbti2AP4QrmIqIiIhGTkWkektIgAULgnLN6Ymcd955vPzyy3z00Ud4vV4efvjhEusvvvhiBgwYwJ133kl4eDgej4e+fftywQUXMHr0aP/1ngUFBYwbNw6Atm3bMmTIEP71r3+xdetW+vXrR1FREd27d+fiiy/m8OHD3HfffdhsNiIjI7nqqqsICQlh+vTpvPbaa9SvX5+srKwSfYSGhhIfH09GRgZxcXH+5d27dz/h80JCQli8eDH79+/n7LPPZuTIkaSmprJkyRLuvPNOHA4Ht99+e6kh86mnnuKxxx7D4/HQqVMnLr30UqA4zB8bkRUREZGaTeFURKq/hISAhdIGDRqwfPlyoHhCoWP69Onj//6ll146aY2+ffvSt2/f45afe+65vPHGG8ctf/bZZ/3fjx49+rj111xzDddcc02JZTfeeCM33njjSfu4/fbbef/99xk4cKD/tTRu3PiEz/vPf/5z3LJjAfivPvvss+OWtWrVyn/c/mz16tXccccdJ+1TREREagadRyUiUkP17NmTnTt3nvD03oqQlpaGw+Hwn+IrIiIiNZtGTkVEaiiTycSMGTMM23+dOnVKneFXREREah6NnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIyGk4cOAAt95663HLk5OTSUpKKvP5o0ePZuXKlSWWvfnmm8yaNeuM+klLS2PSpEmn9Zwvv/ySRx999Iz253K5ePzxx/F6vQwePJjCwsJSt+3YseMp1y1t2/z8fPr378/evXsBSE9P1yRKIiIi1ZTCqYhIACQkJNCvX78yt+vbty/vvfdeiWXvvPPOCe97eirq1Klz2uG0PN58801uuOEGzObg//j47rvvuOOOO/j111/9y+Lj47HZbGzdujXo+xcREZGKpVvJiEi1l5wMmzZBhw6QkBC4ugMGDCA2NpajR4/Ss2dP9u/fz7Bhwxg+fDgOh4P8/HweffRROnXq5H9O27ZtyczM5LfffuOcc85h+/btxMfH06BBAxYvXswHH3yAyWSiR48e3HXXXTzxxBNkZ2eTnZ3N3LlzeeSRR/D5fBQWFvLUU08RFRXFiBEjWL58OevXr2fOnDn4fD4uvvhinnrqKTZv3swLL7xAWFgYtWrVYurUqSVew+rVq1m0aBGhoaGcd955TJ48mffff59///vfeL1eHn74Ya688koAfD4fq1ev5p133ilR48cffyQxMRGPx0NWVhaTJk2iTZs2uFwuHn30UQ4ePMhFF13EpEmTcDgcjBs3jqysLADGjx/PRRdddMLj63K5ePnllxk1alSJ5TfeeCOzZ8/m8ssvL/ffoYiIiFQeCqciUq0lJ8M994DHA/Pnw4IFgQ2oN954I926dWPVqlUA/PLLL2RnZzN//nwyMjL4+eefj3vOP/7xD1avXs0DDzzAqlWr6N+/P3v27GHNmjW89dZbAPzzn//0h9orrriCgQMH8t///pdatWoxffp09uzZQ15eHlFRUQC43W6efvppVqxYQVxcHK+//joHDx7kySefZNmyZdSrV49FixYxb948rrrqKgCysrKYPXs277zzDna7nalTp5KUlERkZCTR0dHMmzevRN8///wzdrudkJCQEsv37NnD6NGjueiii3j//fdZtWoVbdq0oaCggMcee4xzzjmH4cOH89lnn/HNN99wxRVXcPvtt/Pzzz8zZswYli1bdsJje9lll51w+YUXXkhKSsqp/QWJiIhIlaFwKiLV2qZNxcE0Lg4yMoofBzKcnn/++SUeN2nShH79+jFixAjcbjcDBgw47jm9e/dm4MCB3HPPPWzdupXx48fz8ccf8/vvvzNw4EAAjh49yv79+0vsIyEhgZ9//pkHH3wQq9XKAw884K+ZlZVFdHQ0cXFxAAwePJjMzEzsdjv16tUDoF27djz//PP+cPrrr79y4YUXYrfb/es3bNjApZdeetzrOraP+Pj445bXrVuXuXPnEh4ejtPp9Nc7++yzOeeccwBo3bo1+/bt48cff2TLli189NFH/td5uiwWC1arFa/XWyGnF4uIiEjF0E91EanWOnQAi6U4mFosxY8DyWQylXi8a9cunE4nr732GomJiTz99NPHPSc2NpbGjRszd+5cunXrhtVq5YILLuDCCy/kX//6F4sXL6ZPnz7+012P7ePLL7+kbt26LFiwgAceeIDnn3/eXzMuLo6cnByys7MBmDJlCr/++isOh4MjR44AsHXrVs477zz/cxo0aMDevXvJy8vzrz8WSk8U+o7t46+eeeYZHn74YZ599lmaNm2Kz+cD4NChQ/59f/PNNzRp0oQLLriAgQMHsnjxYl544QVuuummsg/yX/h8PqxWq4KpiIhINVNhI6dFRUWMHTuW3377DZfLxQMPPEDXrl0ravciUkMlJBSfyhuMa05P5LzzzuPll1/mo48+8l+zeSK33norgwcPZu3atQA0a9aMK6+8kttuuw2Xy0XLli39I57HNGvWjBEjRrBs2TLcbjcPPfSQf53ZbGbixIncd999mM1mWrRoQcuWLZkyZQrDhg3DZDIRExPDtGnT2L17N1AckocNG8Zdd92F2WymYcOGPPbYY3z44Ycn7LlRo0ZkZmbidruxWv/48XHTTTcxfPhwoqOjqV+/vv960lq1ajFlyhQOHz5M69at6dKlCy1btmTcuHEsX74ch8PB0KFDT/sY79q1i1atWp3280RERKSS81WQlStX+qZMmeLz+Xy+rKwsX5cuXY7b5uuvv66ods7Y999/b3QLVZ6OYfnpGJafjuGZeeWVV3wff/yxz+cz7hg+++yzvq+++uqUtq0KP1cqu4o+hsF8XwX7PVtVe9dxqfjawa5fVWsHu756r/jaJ3KynysVdk5U9+7dGT58+LFAjMViqahdi4hIgNx9992sXbsWr9dryP7T0tJwOBy0bdvWkP2LiIhI8FTYab02mw0Ah8PBww8/zCOPPFJRuxYRkQAJDw9n5syZhu2/Tp06TJ482bD9i4iISPBU6Gy9Bw8e5KGHHuL222+nV69eJ9wmNTW1Ils6bQUFBZW+x8pOx7D8dAzLT8ew/HQMRUREJJAqLJymp6dzzz33MGHCBP8N3U+kefPmFdXSGUlNTa30PVZ2Ooblp2NYfjqG5VcVjqHuhyoiIlJ1VFg4feWVV8jJyWHu3LnMnTsXgNdff53w8PCKakFEREREqohDhw6VuU16enqZ29WvXz9QLYlIkFVYOB0/fjzjx4+vqN2JiIiIiIhIFVKh15yKiFS03MJcknYmsTtjN03imtDv4n5EhUWdcb3ExER27txJWloaBQUFnHvuudSuXZuXXnrplJ6/a9cucnJyaNeu3Rn3MHToUObMmVPq+qSkJPr06UNISMgZ70NERESkoimciki1teGXDfRY2gOvz4uzyIktxMaI/4xgzR1riCPujGo+8cQTAKxatYqffvqJxx577LSe//HHHxMfH1+ucHqyYArw6quvcvPNN59xfREREREjKJyKSLWUW5hLj6U9yHXl+pc5i5wA9Fjag896fhawfRUVFTFx4kT279+P1+vlkUceoX379syaNYsvv/wSt9vNddddR+/evXnnnXcICQnh4osvpmXLlgAcOHCA4cOHU6dOHQ4fPkxCQgKPPvooBw4cYOzYsXg8HkwmE+PHj6dZs2Z07NiRjRs3MmDAAJo1a8bu3btxOBy8+OKLbNq0ibS0NB599FH/9f0iIiIiVYHZ6AZERIIhaWcSXp/3hOu8Pi9rf10bsH2tWLGC2rVrs3TpUubOneu/D+f777/Pc889x1tvvUV0dDT16tXjlltuYeDAgf5gesxvv/1GYmIiK1euZMuWLezcuZPp06dz1113sXTpUsaNG8fYsWOP23fLli1588036dixIx9++CF9+/alTp06zJo1K2CvT0RERKQiaORURKql3Rm7/SOlf+UscrLfsT9g+/rxxx9JSUlh+/btALjdbjIzM5kxYwYzZ84kPT2dzp07n7RGs2bNqFWrFlAcOPft28fevXv9p/82b978hDNStmjRAiiejTI9PT1gr0lERESkoimciki11CSuCbYQ2wkDqi3ERiN7o4Dt64ILLqB+/frcf//9FBQUMG/ePOx2O2vXruX5558HoEePHvTs2ROTyYTXe/yI7t69e8nPzyc0NJTt27fz97//ncaNG/P111/TtWtXUlNTiY+PP6V+StuHiIiISGWm03pFpFrqd3E/zKYT/xNnNpnpfm73gO2rf//+/PTTT9x5553079+fc845h9DQUGJiYrj11lu566676NixI2effTaXXHIJS5cuZcuWLSVqhISEMHz4cPr27UvXrl1p1qwZo0aNYsmSJdxxxx1MmjSJZ5555pT6adu2LUOGDMHn8wXsNYqIiIgEm0ZORaRaigqLYs0da46brddsMrPmjjXYnLZy1e/Tp4//+9DQUKZPn37cNkOHDmXo0KElll111VVcddVVx20bHx/Pa6+9VmJZgwYNWLhw4XHbbty4EYDFixf7l912223+75999tlTexEiIiIilYjCqYhUW50aduL3kb+TtCOJPZl7uDD2Qvpd0g97qJ3U1FSj2xMRERGRP1E4FZFqzR5q59429xrdxkk1aNCA5cuXG92GiIiIiKF0zamIiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDKZyKSPVWlAt75sP/Rhf/WZRbrnJffvklV155JQMGDPDf13TNmjUnfc7vv//OZ599Vq79nkhqaipz5swpdX1hYSErVqwI+H5FREREgkGz9YpI9XVkA/y3B/i84HGCxQbfjICr1gBxZ1z2iiuuYNasWQA4nU4GDBjA+eefT/PmzU+4/ZYtW/jpp5+45pprznifJ9K8efNS9wmQlpbGihUr6Nu3b0D3KyIiIhIMCqciUj0V5RYHU/efRko9zuI//9sDU4vAjGTabDb69evH2rVradq0KRMmTODQoUMcOXKEa665hocffpjXXnuNgoICWrduTVRUFHPmzMHn8+F0Opk5cybnn3++v96qVatYt24dTqeTrKwsHnroIa6//no2btzICy+8QFhYGLVq1WLq1Kmkpqby9ttvM2vWLK677jratGnDvn37iIuLY/bs2bzyyivs2bOHOXPmcOWVV/Lss89itVqJiIjgxRdfxG63B+QYiIiIiASCTusVkeppf1LxiOmJ+LxEZ68N2K7i4uLIysri4MGDtGrVijfeeIOVK1fy9ttvY7FYGDJkCDfeeCNdu3Zl9+7dzJgxg8WLF3Pdddexdu3xfeTn57Nw4UIWLFhAYmIiRUVFPPnkk8yZM4clS5bQrl075s2bV+I5v/76K8OHDycpKYnMzEy+++477r//fi688EKGDh3KunXruOGGG1iyZAm33XYbOTk5AXv9IiIiIoGgkVMRqZ5yd/8xUvpXHiehhfsDtqvff/+d+vXrU6tWLb777ju2bNmC3W7H5XIdt229evV45plniIyM5PDhw7Rp0+a4bdq1a4fZbCY+Pp7o6GjS09Ox2+3Uq1fPv/7555/nqquu8j+ndu3anHXWWQCcddZZFBYWlqh5//3388orr3D33XdTr149WrZsGbDXLyIiIhIIGjkVkeopqknxNaYnYrHhCmsUkN04HA5WrFhB9+7dWbVqFVFRUcycOZN77rmHgoICfD4fZrMZr7d4FPfJJ59k6tSpJCYmUrduXXw+33E1d+7cCUB6ejoOh4O6devicDg4cuQIAFu3buW8884r8RyTyXRcnT/vd/Xq1dxyyy0sXryYJk2asHz58oC8fhEREZFA0cipiFRPjfoVT350IiYzObW6c/YZlt6yZQsDBgzAbDbj8XgYNmwYF1xwAR6Ph5EjR7Jt2zZCQ0Np1KgRR44coWnTpsybN4+LL76Ym266iTvuuIOIiAji4+P9gfPP0tPTufvuu8nNzWXixIlYLBamTJnCsGHDMJlMxMTEMG3aNHbv3n3SPuPi4igqKmLGjBlcd911jB8/noiICMxmM5MnTz7DVy8iIiISHAqnIlI9hUQVz8r719l6TWa4ag2+jFJGVcvQvn17Nm/efMJ1TZo0YfXq1cctr1evHv/5z38A6NmzZ5n7aNeuHY899liJZR06dKBDhw7H9dK+fXsANm7c6F9+bCZhgPfee8//vUZLRUREpDJTOBWR6qtuJ7jld/glCXL3QNSF0LAfhNghI9Xo7kRERETkTxRORaR6C7FD43uN7uKU9enTx+gWRERERAyhCZFERERERETEcAqnIiIi1ZTX62XChAn069ePAQMGsH//8bdQ8nq9DBo0iGXLlhnQoYiIyB8UTkVERKqpdevW4XK5SEpKYuTIkSQmJh63zQsvvEBOTo4B3YmIiJSkcCoiIlJNpaSk0LlzZwBatWrFjh07Sqxfu3YtJpPJv42IiIiRNCGSiIhINeVwOLDb7f7HFosFt9uN1Wrlxx9/5IMPPuCll17i5ZdfPmmd1NSKm926oKAgaPsLZu1g16+qtctTPz09vcxtCgsLy7znc1ZW1mnvGyrvcanOtYNdX71XfO3TpXAqIiJSTdntdpxOp/+x1+vFai3+0f/uu+9y+PBh7r77bn777TdCQkI455xzSEhIOK5O8+bNK6zn1NTUoO0vmLWDXb+q1i5P/UOHDpW5ze7du2nSpMlJt6lfv/5p7xsq73GpzrWDXV+9V3ztE0lJSSl1ncKpiIhINdWmTRvWr19Pjx492LZtG02bNvWvGzVqlP/72bNnEx8ff8JgKiIiUlEUTkVERKqpbt26sXHjRvr374/P52Pq1KksXLiQhg0b0rVrV6PbExERKUHhVEREpJoym81Mnjy5xLLGjRsft92wYcMqqiUREZFSabZeERERERERMZzCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDKZyKiIiIiIiI4RRORURE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8vNd1K1biMPhZONGL3/7WyihocVfVqsVs9mYz4NONKKbnAz33OMD3Cxe7OL55120auUC4KuvQiksDCU62k5ampVvvoHrrit+3p+vz60OI8QiIiIiUjlUaDidMmUKGzZsoHnz5hW5W5EzYg+1c2+be0ssO9m9OTt2tPDGGxEcOBCBxQIdOniIiHDhcrk4evQobrcbs9mM1Wo97stisZx2f8nJMGYM7N4NTZrAtGkn7i05Ge6910txCHUza5abtm3dbNjgJibGQ1SUhYyMUL75JoyuXaOwWq107AhvvAFpafz/aylZU/coFREREZFAq9Bw2qZNG6699lqSknQrDql+jh9RtAARRERE+LfxeDy43W7cbjdFRUXk5+fjdrvx+XyYzeaTfgGYTMW3tPnySwujRhWQm+vDZIJt23z07+/lzTe9XH65F6/3j68NG7xER/uIirKSkRFCSoqVhIRIrrzSyoIFFjIzTVgs0LEjWK2lvZaKPpoiIiIiUtMEJZyuWLGCRYsWlVg2depUevTowZdffnnS56ampgajpYApKCio9D1WdtX5GNapA717F39/Oi/R5/Ph9XpL/PnX7/+87bZtdrxeBxER5v9fZiI/38R//pNF3bpHMZlMmM1mTCYTDRpEkpbWgMOHiycuatDgd/bty6d+fZg4MYL//S+C1q3zqVMnv0TPZ/paqorq/D6sKDqGIiIiEkhBCad9+/alb9++Z/Tcyn7Kb2pqaqXvsbLTMSy/Xbt+ZuHCeHJy/lgWEwO9e9eiVauS215yCZx33p9HQc/zr2veHAYMqIiOKx+9D8uvKhzDlJQUo1sQERGRU6RbyYhUQW3b5vP++6d2zSnoGlERERERqfwUTkWqqIQE2LjR6C5ERERERAKjwsNp+/btad++fUXvVkRERERERCoxY266KCIiIiIiIvInCqciIiIiIiJiOIVTERERERERMZzCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGcwqmIiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDKZyKiIiIiIiI4RRORURERERExHAKpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGE7hVERERERERAyncCoiIiIiIiKGUzgVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiLVlNfrZcKECfTr148BAwawf//+47bJzMzk+uuvp7Cw0IAORURE/qBwKiIiUk2tW7cOl8tFUlISI0eOJDExscT6L774gnvuuYe0tDSDOhQREfmDwqmIiEg1lZKSQufOnQFo1aoVO3bsKLHebDazcOFCatWqZUB3IiIiJVmNbkBERESCw+FwYLfb/Y8tFgtutxurtfjHf8eOHU+pTmpqalD6O5GCgoKg7S+YtYNdv6rWLk/99PT0MrcpLCxk9+7dJ90mKyvrtPcNlfe4VN3azQPWw5m+tmC+F6Hqvh+D/V4/HQqnIiIi1ZTdbsfpdPofe71efzA9Hc2bB+6XyrKkpqYGbX/BrB3s+lW1dnnqHzp0qMxtdu/eTZMmTU66Tf369U9731B5j0t1rn2qznT/wXwvQtV9P1b032lKSkqp63Rar4iISDXVpk0bkpOTAdi2bRtNmzY1uCMREZHSaeRURESkmurWrRsbN26kf//++Hw+pk6dysKFC2nYsCFdu3Y1uj0REZESFE5FRESqKbPZzOTJk0ssa9y48XHbffbZZxXVkoiISKl0Wq+IiIiIiIgYTuFUREREREREDKdwKiIiIiIiIoZTOBURERERERHDKZyKiIiIiIiI4RRORURERERExHAKpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGE7hVERERERERAyncCoiIiIiIiKGUzgVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiIiIiJiOIVTERERERERMZzCqYiIiIiIiBhO4VREREREREQMp3AqIiIiIiIihlM4FREREREREcMpnIqIiIiIiIjhFE5FRERERETEcAqnIiIiIiIiYjiFUxERERERETGctaJ2lJuby+OPP47D4aCoqIgnnniC1q1bV9TuRUREREREpBKrsHC6cOFCrrjiCgYOHMhPP/3EyJEjeeeddypq9yIiIiIiIlKJVVg4HThwIKGhoQB4PB7CwsIqatciIiIiIiJSyQUlnK5YsYJFixaVWDZ16lRatmxJWloajz/+OGPHjj3hc1NTU4PRUsAUFBRU+h4rOx3D8tMxLD8dw/LTMRQREZFACko47du3L3379j1u+a5duxgxYgSjRo3i8ssvP+FzmzdvHoyWAiY1NbXS91jZ6RiWn45h+ekYll9VOIYpKSlGtyAiIiKnqMJO692zZw/Dhw/nhRdeoFmzZhW1WxEREREREakCKiyczpw5E5fLxTPPPAOA3W5n3rx5FbV7ERERERERqcQqLJwqiIqIiIiIiEhpzEY3ICIiIiIiIqJwKiIiIiIiIoZTOBURERERERHDKZyKiIiIiIiI4RRORURERERExHAKpyIiIiIiImK4CruVjIiIiNRwJlOZmzQ/lTo+X7lbERGRykcjpyIiIiIiImI4hVMRERERERExnMKpiIiIiIiIGE7hVERERERERAyncCoiIiIiIiKG02y9IiIiIiJVxClMes2pzHutSa9rhkOHDpW5TXp6+iltV79+/UC0dFIaORURERERERHDKZyKiIiIiIiI4RRORURERERExHC65lRERESkmqpq15uJSM2mkVMRERERERExnMKpiIiIiIiIGE7hVERERERERAyncCoiIiIiIiKGUzgVERERERERwymcioiIiIiIiOEUTkVERERERMRwCqciIiIiIiJiOIVTERGRasrr9TJhwgT69evHgAED2L9/f4n1y5cvp0+fPtx6662sX7/eoC5FRESKWY1uQERERIJj3bp1uFwukpKS2LZtG4mJicybNw+AtLQ0Fi9ezL///W8KCwu5/fbb6dixI6GhoQZ3LSIiNZVGTkVERKqplJQUOnfuDECrVq3YsWOHf9327dtp3bo1oaGhREVF0bBhQ3744QejWq3cTKZT+mreokXZ24mISKlMPp/PZ3QTx6SkpBjdgoiIVDOXXXaZ0S0YZty4cVx33XV06dIFgKuuuop169ZhtVp57733+PHHH3n88ccBGDVqFDfffDMdOnQoUUM/m0VEJNBK+9lcqU7rrcm/QIiIiASa3W7H6XT6H3u9XqxW6wnXOZ1OoqKijquhn80iIlJRdFqviIhINdWmTRuSk5MB2LZtG02bNvWva9myJSkpKRQWFpKbm8vevXtLrBcREaloleq0XhEREQkcr9fLpEmT+PHHH/H5fEydOpXk5GQaNmxI165dWb58OUlJSfh8Pu677z6uv/56o1sWEZEaTOH0NOXm5vL444/jcDgoKiriiSeeoHXr1ka3VSV98sknrF27lpkzZxrdSpVx7BfNXbt2ERoaypQpU2jUqJHRbVVJ3377Lc899xyLFy82upUqp6io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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Get 2 random data points with 2 features.\n", - "data_label0, data_label1, x_tilde, random_label = DataPlotter.grab_random_data(size=2, features=2)\n", - "\n", - "#Initialize quantum register:\n", - "q = QuantumRegister(4)\n", - "meas = measurement(q)\n", - "\n", - "# Initialize circuit:\n", - "circuit = two_features_classifier(q, x_tilde, data_label0[0], data_label1[0])\n", - "qc = circuit.compose(meas)\n", - "\n", - "# Execute the circuit:\n", - "probabilities = execute_circuit(qc)\n", - "\n", - "# Display the results:\n", - "histogram_results = create_histogram_results(probabilities, number_of_bits=4)\n", - "DataPlotter.plot_data_points(x_tilde, data_label0, data_label1, histogram_results); # Function to plot the data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Below the implementation for four features is given. Note that the structure of the algorithm is similar to the case with two features. We use one ancillary qubit for the controlled encoding of the features." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def four_features_classifier(q, x_tilde, x0, x1):\n", - " if len(q) != 6:\n", - " raise ValueError(\"Error, len quantum register must be 5 + 1 ancillary qubit.\")\n", - " circuit = QuantumCircuitWithClassicalRegister(q)\n", - "\n", - " def get_alpha(data_point):\n", - " a00, a01, a10, a11 = data_point\n", - " alpha1 = atan(sqrt((a10**2 + a11**2) / (a00**2 + a01**2)))\n", - " alpha2 = np.arctan2(a01, a00) \n", - " alpha3 = np.arctan2(a11, a10) \n", - " return [alpha1, alpha2, alpha3]\n", - "\n", - " # part_a:\n", - " for i in range(2):\n", - " circuit.h(q[i])\n", - "\n", - " # part_b:\n", - " alpha = get_alpha(x_tilde)\n", - " circuit = circuit.compose(c_features_encoding(q, alpha, 1, 2, 3))\n", - " circuit.x(q[1])\n", - "\n", - " # part_c:\n", - " # Use ancillary qubit + c_features_encoding for cc_features_encoding\n", - " circuit.ccx(q[0], q[1], q[5])\n", - " alpha = get_alpha(x0)\n", - " circuit = circuit.compose(c_features_encoding(q, alpha, 5, 2, 3))\n", - " circuit.ccx(q[0], q[1], q[5])\n", - " circuit.x(q[0])\n", - " \n", - " # part_d:\n", - " # Use ancillary qubit + c_features_encoding for cc_features_encoding\n", - " circuit.ccx(q[0], q[1], q[5])\n", - " alpha = get_alpha(x1)\n", - " circuit = circuit.compose(c_features_encoding(q, alpha, 5, 2, 3))\n", - " circuit.ccx(q[0], q[1], q[5])\n", - "\n", - " # part_e:\n", - " circuit.cx(q[0], q[4])\n", - "\n", - " # part_f:\n", - " circuit.h(q[1])\n", - "\n", - " return circuit" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following code runs the algorithm for 2+1 random data points with 4 features each." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sum Iris Setosa (red, label 0): 0.31083859999999996\n", - "Sum Iris Versicolor (blue, label 1): 0.1604722\n", - "Random label is: 0\n", - "Prediction by true classifier: 0\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Get 2 random data points with 4 features.\n", - "data_label0, data_label1, x_tilde, random_label = DataPlotter.grab_random_data(size=2, features=4)\n", - "\n", - "#Initialize quantum register:\n", - "q = QuantumRegister(6)\n", - "meas = measurement(q)\n", - "\n", - "# Initialize circuit:\n", - "circuit = four_features_classifier(q, x_tilde, data_label0[0], data_label1[0])\n", - "qc = circuit.compose(meas)\n", - "\n", - "# Execute the circuit:\n", - "probabilities = execute_circuit(qc)\n", - "\n", - "# Display the results:\n", - "histogram_results = create_histogram_results(probabilities, number_of_bits=6)\n", - "\n", - "\n", - "def strip_ancillary_qubit(histogram_results):\n", - " new_histogram_results = {}\n", - " for k, v in histogram_results.items():\n", - " new_histogram_results[k[:-1]] = v # Strip ancillary qubit\n", - " return new_histogram_results\n", - "\n", - "\n", - "histogram_results = strip_ancillary_qubit(histogram_results)\n", - "\n", - "\n", - "def summarize_results_4_features(histogram_results):\n", - " sum_label0 = 0\n", - " sum_label1 = 0\n", - " for key, value in histogram_results.items():\n", - " if key[1] == \"0\":\n", - " if key[-1] == \"0\":\n", - " sum_label0 += value\n", - " else:\n", - " sum_label1 += value\n", - " print(\"Sum Iris Setosa (red, label 0): \", sum_label0)\n", - " print(\"Sum Iris Versicolor (blue, label 1): \", sum_label1)\n", - "\n", - "\n", - "summarize_results_4_features(histogram_results)\n", - "print(\"Random label is: \", random_label)\n", - "print(\"Prediction by true classifier: \", DataPlotter.true_classifier(data_label0, data_label1, x_tilde))\n", - " \n", - "# Plot results:\n", - "DataPlotter.plot_data_points_multiple_features(\n", - " data_label0, data_label1, x_tilde, random_label, histogram_results) # Plot features + bar plot results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the case of an infinite amount of shots the quantum inspire gives as a result the true probability distribution which coincides with the classical solution of the distance-based classifier. The following table shows the quality of the distance-based classifier depending on the amount of data points and included features. The table contains the percentage of correct predictions for random selected data from the iris set, the results are over a sample of 10.000 runs and can be reproduced using the quality_classifier method of DataPlotter class.\n", - "\n", - "| % correct prediction | 2 features | 3 features | 4 features |\n", - "|----------------------|------------|------------|------------|\n", - "| 2 data points | 0.9426 | 0.9870 | 0.9940 |\n", - "| 4 data points | 0.9735 | 0.9933 | 0.9986 |\n", - "| 8 data points | 0.9803 | 0.9975 | 0.9998 |\n", - "\n", - "\n", - "These results show why one is not only interested in extending the amount of data points but also in including more features for data." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Conclusion and further work #\n", - "\n", - "In this notebook we demonstrated how to extended the distance-based classifier to be able to handle data containing up to four features. We saw that the quality of the distance-based classifier improves both by including more data points but also by including data which containing more features.\n", - "\n", - "So far in this notebook series we have only looked at binary classification, the results belong either to the class with a label 0 or to the class with the label 1. For some problems one is interessted in identifying between more than two classes, for example number recognition. A possible next extention for the current classifier is to extend it to being able to classify between more than two labels. This can be done by encoding the label in multiple qubits instead of one qubit.\n", - "\n", - "We have only tested the distance-based classifier on rescaled data from the iris data set, this data set is well classified by the the distance-based classifier. For other data sets this might not necessary be the case. Suppose a different data set which after scaling has different the classes lie in concentric circles, at first glance we do not expect the distance-based classifier to yield good predictions. These problems can possibly be solved by an alternative data pre-processing or by a totally different type of classifier. The task of selecting the right methods for data preprocessing and the corresponding classifier is not a task for the quantum computer but for the data analyst. It will be interessting to see different classifiers implemented on quantum computers in the near future.\n", - "\n", - "[Back to Table of Contents](#contents)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### References ###\n", - "* Book: [Schuld and Petruccione, Supervised learning with Quantum computers, 2018](https://www.springer.com/us/book/9783319964232) \n", - "* Article: [Schuld, Fingerhuth and Petruccione, Implementing a distance-based classifier with a quantum interference circuit, 2017](https://arxiv.org/abs/1703.10793)\n", - "* Article: [Long & Sun: Efficient scheme for initializing a quantum register with an arbitrary superposed state, 2001](https://arxiv.org/abs/quant-ph/0104030)\n", - "* Post: [Build an arbitrary (n)-controlled quantum gate](https://physics.stackexchange.com/questions/142470/realisation-of-arbitrary-controlled-quantum-gate)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/examples/classifier_example/data_plotter.py b/docs/examples/classifier_example/data_plotter.py deleted file mode 100644 index 79ecb3d..0000000 --- a/docs/examples/classifier_example/data_plotter.py +++ /dev/null @@ -1,408 +0,0 @@ -from sklearn.datasets import load_iris -from sklearn import preprocessing -import matplotlib.pyplot as plt -import numpy as np -from random import sample - -plt.style.use('seaborn-whitegrid') - - -def get_bin(x, n): - return format(int(x), 'b').zfill(n) - - -class DataPlotter: - - @staticmethod - def plot_original_data(data1, data2): - # Plot original data: - plt.rcParams['figure.figsize'] = [8, 6] - plt.scatter(data1[0], data1[1], alpha=0.8, s=10, c='red') # Scatter plot data class 1 - plt.scatter(data2[0], data2[1], alpha=0.8, s=10, c='blue') # Scatter plot data class 2 - plt.xlabel("Sepal length (cm)") # x-label - plt.ylabel("Sepal width (cm)") # y-label - plt.xlim(-1, 8) # x-range - plt.ylim(-1, 5) # y-range - plt.legend(["Iris Setosa", "Iris Versicolor"]) - fig = plt - return fig - - @staticmethod - def plot_standardised_data(data1, data2): - plt.rcParams['figure.figsize'] = [8, 6] # Plot size - unit_circle = plt.Circle((0, 0), 1, color='grey', alpha=0.2, fill=False) # Circle - - plt.scatter(data1[0], data1[1], alpha=0.8, s=10, c='red') # Scatter plot data class 1 - plt.scatter(data2[0], data2[1], alpha=0.8, s=10, c='blue') # Scatter plot data class 2 - plt.xlabel("Sepal length (cm)") # x-label - plt.ylabel("Sepal width (cm)") # y-label - plt.xlim(-2.5, 2.5) # x-range - plt.ylim(-2.5, 2.5) # y-range - fig = plt.gcf() # unit circle plotting - ax = fig.gca() - ax.add_artist(unit_circle) - plt.legend(["Iris Setosa", "Iris Versicolor"]) - plt.show() - - @staticmethod - def plot_normalised_data(data1, data2): - # Scatter plot normalised data - plt.rcParams['figure.figsize'] = [8, 6] # Plot size - unit_circle = plt.Circle((0, 0), 1, color='grey', alpha=0.2, fill=False) # Circle - - plt.scatter(data1[0], data1[1], alpha=0.8, s=10, c='red') # Scatter plot data class 1 - plt.scatter(data2[0], data2[1], alpha=0.8, s=10, c='blue') # Scatter plot data class 2 - plt.xlabel("Sepal length (cm)") # x-label - plt.ylabel("Sepal width (cm)") # y-label - plt.xlim(-2.5, 2.5) # x-range - plt.ylim(-2.5, 2.5) # y-range - - fig = plt.gcf() # unit circle plotting - ax = fig.gca() - ax.add_artist(unit_circle) - plt.legend(["Iris Setosa", "Iris Versicolor"]) - # plt.show() - - @staticmethod - def load_data(max_features=2): - iris = load_iris() - features = iris.data.T - if max_features > 4: - print("Error, maximum is 4 features in Iris data set") - # Default: only the first two features of the data set - data = [el[0:100] for el in features][0:max_features] - half_len_data = len(data[0]) // 2 - # Rescale the data - features_scaled = [preprocessing.scale(el) for el in data] - iris_setosa_scaled = [el[0:half_len_data] for el in features_scaled] - iris_versicolor_scaled = [el[half_len_data:] for el in features_scaled] - - # Normalise the data - def normalise_data(*args): - """Normalise data to unit length - input: *args, arrays of same length - output: normalised *args - """ - for idx in range(len(args[0])): - norm = 0 - for arg in args: - norm += arg[idx]**2 - norm **= (1 / 2) - for arg in args: - arg[idx] /= norm - return args - - iris_setosa_normalised = normalise_data(*iris_setosa_scaled) - iris_versicolor_normalised = normalise_data(*iris_versicolor_scaled) - return iris_setosa_normalised, iris_versicolor_normalised - - def plot_data_points(self, test_data, data_label0, data_label1, results): - # Scatter plot full data set,test point and data points - # Bar plot results (Project Q! ordering) - - plt.rcParams['figure.figsize'] = [16, 6] # Plot size - # load data: - iris_setosa_normalised, iris_versicolor_normalised = self.load_data() - - # Scatter plot data points: - plt.subplot(1, 2, 1) # Scatter plot - self.plot_normalised_data(iris_setosa_normalised, iris_versicolor_normalised) - # Scatter plot data class ? - plt.scatter(test_data[0], test_data[1], s=50, c='green') - - for data_point in data_label0: - # Scatter plot data class 0 - plt.scatter(data_point[0], data_point[1], s=50, c='orange') - for data_point in data_label1: - # Scatter plot data class 1 - plt.scatter(data_point[0], data_point[1], s=50, c='orange') - plt.legend(["Iris Setosa (label 0)", - "Iris Versicolor (label 1)", "Test point", "Data points"]) - - # Bar plot results: - plt.subplot(1, 2, 2) # Bar plot - size = len(list(results.keys())[0]) - res = [get_bin(el, size) for el in range(2 ** size)] - prob = [0] * 2**size - for key, value in results.items(): - prob[int(key, 2)] = value - - # Set color=light grey when 2nd qubit = 1 - # Set color=blue when 2nd qubit = 0, and last qubit = 1 - # Set color=red when 2nd qubit = 0, and last qubit = 0 - color_list = ['red', 'blue', 'red', 'blue', (0.1, 0.1, 0.1, 0.1), - (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1)] - plt.bar(res, prob, color=color_list) - plt.ylabel('Probability') - plt.title('Results') - plt.ylim(0, .5) - plt.xticks(rotation='vertical') - return prob - - def grab_random_data(self, size=4, features=2): - """Grabs random points from Iris set of which: - size/2 points of label 0 - size/2 points of label 1 - 1 point of label random""" - - if size % 2 != 0: - return "Size must be an even number" - - # load data: - iris_setosa_normalised, iris_versicolor_normalised = self.load_data(max_features=features) - - random_label = 0 - data_label0 = [] # iris_setosa_normalised # Label 0 - data_label1 = [] # iris_versicolor_normalised # Label 1 - # Not strictly necessary but for educational purposed we don't want coinciding data points - coinciding_data = True - while coinciding_data: - coinciding_data = False - # Find index values - random_label = sample([1, 0], 1)[0] - len_label0 = int(size / 2 + 1 - random_label) - len_label1 = int(size / 2 + random_label) - - index_label0 = sample(range(50), len_label0) - index_label1 = sample(range(50), len_label1) - - # Find data points - data_label0 = [] # iris_setosa_normalised # Label 0 - data_label1 = [] # iris_versicolor_normalised # Label 1 - - for data_point in index_label0: - data_label0.append([feature[data_point] for feature in iris_setosa_normalised]) - for data_point in index_label1: - data_label1.append([feature[data_point] for feature in iris_versicolor_normalised]) - - for i in range(len(data_label0)): - for j in range(i + 1, len(data_label0)): - if data_label0[i] == data_label0[j]: - print("Coinciding data point found, restart") - coinciding_data = True - - for i in range(len(data_label1)): - for j in range(i + 1, len(data_label1)): - if data_label1[i] == data_label1[j]: - print("Coinciding data point found, restart") - coinciding_data = True - - if random_label: - test_data = data_label1.pop() - else: - test_data = data_label0.pop() - - return data_label0, data_label1, test_data, random_label - - def plot_data_points_multiple_features(self, data_label0, data_label1, test_data, random_label, results): - # Scatter plot full data set, test point and data points for all combinations of features - # Bar plot results (Project Q! ordering) - - # For now only 2 data points, 4 features - - # Load data: - iris_setosa_normalised, iris_versicolor_normalised = self.load_data(max_features=4) - - # Find index of data points: - def find_idx(needle, hay): - for idx in range(len(hay[0])): - if hay[0][idx] == needle[0] and hay[1][idx] == needle[1] and hay[2][idx] == needle[2]\ - and hay[3][idx] == needle[3]: - return idx - return "Data not found" - - idx_data_label0 = find_idx(data_label0[0], iris_setosa_normalised) - idx_data_label1 = find_idx(data_label1[0], iris_versicolor_normalised) - if random_label == 0: - hay_test_data = iris_setosa_normalised - else: - hay_test_data = iris_versicolor_normalised - idx_test_data = find_idx(test_data, hay_test_data) - - plt.rcParams['figure.figsize'] = [16, 6] # Plot size - - plt.subplot2grid((2, 6), (0, 0)) - # load data: - iris_setosa_normalised, iris_versicolor_normalised = self.load_data_selected_features(0, 1) - # Scatter plot data points: - self.plot_normalised_data(iris_setosa_normalised, iris_versicolor_normalised) - - # Scatter plot data - if random_label == 0: - test_data = iris_setosa_normalised - else: - test_data = iris_versicolor_normalised - plt.scatter(test_data[0][idx_test_data], test_data[1][idx_test_data], s=50, c='green') - plt.scatter(iris_setosa_normalised[0][idx_data_label0], - iris_setosa_normalised[1][idx_data_label0], s=50, c='orange') - plt.scatter(iris_versicolor_normalised[0][idx_data_label1], - iris_versicolor_normalised[1][idx_data_label1], s=50, c='orange') - plt.xlabel("Sepal length (cm)") # x-label - plt.ylabel("Sepal width (cm)") # y-label - - plt.subplot2grid((2, 6), (0, 1)) - # load data: - iris_setosa_normalised, iris_versicolor_normalised = self.load_data_selected_features(0, 2) - # Scatter plot data points: - self.plot_normalised_data(iris_setosa_normalised, iris_versicolor_normalised) - # Scatter plot data - if random_label == 0: - test_data = iris_setosa_normalised - else: - test_data = iris_versicolor_normalised - plt.scatter(test_data[0][idx_test_data], test_data[1][idx_test_data], s=50, c='green') - plt.scatter(iris_setosa_normalised[0][idx_data_label0], - iris_setosa_normalised[1][idx_data_label0], s=50, c='orange') - plt.scatter(iris_versicolor_normalised[0][idx_data_label1], - iris_versicolor_normalised[1][idx_data_label1], s=50, c='orange') - plt.xlabel("Sepal length (cm)") # x-label - plt.ylabel("Petal length (cm)") # y-label - - plt.subplot2grid((2, 6), (0, 2)) - # load data: - iris_setosa_normalised, iris_versicolor_normalised = self.load_data_selected_features(0, 3) - # Scatter plot data points: - self.plot_normalised_data(iris_setosa_normalised, iris_versicolor_normalised) - # Scatter plot data - if random_label == 0: - test_data = iris_setosa_normalised - else: - test_data = iris_versicolor_normalised - plt.scatter(test_data[0][idx_test_data], test_data[1][idx_test_data], s=50, c='green') - plt.scatter(iris_setosa_normalised[0][idx_data_label0], - iris_setosa_normalised[1][idx_data_label0], s=50, c='orange') - plt.scatter(iris_versicolor_normalised[0][idx_data_label1], - iris_versicolor_normalised[1][idx_data_label1], s=50, c='orange') - plt.xlabel("Sepal length (cm)") # x-label - plt.ylabel("Petal width (cm)") # y-label - - plt.subplot2grid((2, 6), (1, 0)) - # load data: - iris_setosa_normalised, iris_versicolor_normalised = self.load_data_selected_features(1, 2) - # Scatter plot data points: - self.plot_normalised_data(iris_setosa_normalised, iris_versicolor_normalised) - # Scatter plot data - if random_label == 0: - test_data = iris_setosa_normalised - else: - test_data = iris_versicolor_normalised - plt.scatter(test_data[0][idx_test_data], test_data[1][idx_test_data], s=50, c='green') - plt.scatter(iris_setosa_normalised[0][idx_data_label0], - iris_setosa_normalised[1][idx_data_label0], s=50, c='orange') - plt.scatter(iris_versicolor_normalised[0][idx_data_label1], - iris_versicolor_normalised[1][idx_data_label1], s=50, c='orange') - plt.xlabel("Sepal width (cm)") # x-label - plt.ylabel("Petal length (cm)") # y-label - - plt.subplot2grid((2, 6), (1, 1)) - # load data: - iris_setosa_normalised, iris_versicolor_normalised = self.load_data_selected_features(1, 3) - # Scatter plot data points: - self.plot_normalised_data(iris_setosa_normalised, iris_versicolor_normalised) - # Scatter plot data - if random_label == 0: - test_data = iris_setosa_normalised - else: - test_data = iris_versicolor_normalised - plt.scatter(test_data[0][idx_test_data], test_data[1][idx_test_data], s=50, c='green') - plt.scatter(iris_setosa_normalised[0][idx_data_label0], - iris_setosa_normalised[1][idx_data_label0], s=50, c='orange') - plt.scatter(iris_versicolor_normalised[0][idx_data_label1], - iris_versicolor_normalised[1][idx_data_label1], s=50, c='orange') - plt.xlabel("Sepal width (cm)") # x-label - plt.ylabel("Petal width (cm)") # y-label - - plt.subplot2grid((2, 6), (1, 2)) - # load data: - iris_setosa_normalised, iris_versicolor_normalised = self.load_data_selected_features(2, 3) - # Scatter plot data points: - self.plot_normalised_data(iris_setosa_normalised, iris_versicolor_normalised) - # Scatter plot data - if random_label == 0: - test_data = iris_setosa_normalised - else: - test_data = iris_versicolor_normalised - plt.scatter(test_data[0][idx_test_data], test_data[1][idx_test_data], s=50, c='green') - plt.scatter(iris_setosa_normalised[0][idx_data_label0], - iris_setosa_normalised[1][idx_data_label0], s=50, c='orange') - plt.scatter(iris_versicolor_normalised[0][idx_data_label1], - iris_versicolor_normalised[1][idx_data_label1], s=50, c='orange') - plt.xlabel("Petal length (cm)") # x-label - plt.ylabel("Petal width (cm)") # y-label - - # Bar plot results: - plt.subplot2grid((2, 6), (0, 3), colspan=2, rowspan=3) - size = len(list(results.keys())[0]) - res = [get_bin(el, size) for el in range(2 ** size)] - prob = [0] * 2**size - for key, value in results.items(): - prob[int(key, 2)] = value - - # Set color=light grey when 2nd qubit = 1 - # Set color=blue when 2nd qubit = 0, and last qubit = 1 - # Set color=red when 2nd qubit = 0, and last qubit = 0 - color_list = ['red', 'blue', 'red', 'blue', 'red', 'blue', 'red', 'blue', - (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1), - (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1), (0.1, 0.1, 0.1, 0.1)] - plt.bar(res, prob, color=color_list) - plt.ylabel('Probability') - plt.title('Results') - plt.ylim(0, .5) - plt.xticks(rotation='vertical') - return plt.show() - - @staticmethod - def load_data_selected_features(feature1, feature2): - iris = load_iris() - features = iris.data.T - data = [el[0:100] for el in features][0:4] - data = [data[feature1], data[feature2]] - half_len_data = len(data[0]) // 2 - # Rescale the data - features_scaled = [preprocessing.scale(el) for el in data] - iris_setosa_scaled = [el[0:half_len_data] for el in features_scaled] - iris_versicolor_scaled = [el[half_len_data:] for el in features_scaled] - - # Normalise the data - def normalise_data(*args): - """Normalise data to unit length - input: *args, arrays of same length - output: normalised *args - """ - for idx in range(len(args[0])): - norm = 0 - for arg in args: - norm += arg[idx]**2 - norm **= (1 / 2) - for arg in args: - arg[idx] /= norm - return args - - iris_setosa_normalised = normalise_data(*iris_setosa_scaled) - iris_versicolor_normalised = normalise_data(*iris_versicolor_scaled) - return iris_setosa_normalised, iris_versicolor_normalised - - @staticmethod - def true_classifier(data_label0, data_label1, test_data): - label0 = 0 - label1 = 0 - for element in data_label0: - label0 += np.linalg.norm(np.array(element) - np.array(test_data)) - for element in data_label1: - label1 += np.linalg.norm(np.array(element) - np.array(test_data)) - if label0 > label1: - return 1 - return 0 - - def quality_classifier(self, input_size, input_features, sample_size): - correct = 0 - wrong = 0 - for idx in range(sample_size): - data_label0, data_label1, test_data, random_label = self.grab_random_data(size=input_size, - features=input_features) - prediction = self.true_classifier(data_label0, data_label1, test_data) - if prediction == random_label: - correct += 1 - else: - wrong += 1 - return correct/sample_size, wrong/sample_size diff --git a/docs/examples/classifier_example/images/4pointsclassifier.png b/docs/examples/classifier_example/images/4pointsclassifier.png deleted file mode 100644 index 2375037..0000000 Binary files a/docs/examples/classifier_example/images/4pointsclassifier.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/experimentaltest.png b/docs/examples/classifier_example/images/experimentaltest.png deleted file mode 100644 index cda646c..0000000 Binary files a/docs/examples/classifier_example/images/experimentaltest.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/full_circuit.png b/docs/examples/classifier_example/images/full_circuit.png deleted file mode 100644 index c03e37a..0000000 Binary files a/docs/examples/classifier_example/images/full_circuit.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/fullalgorithm.png b/docs/examples/classifier_example/images/fullalgorithm.png deleted file mode 100644 index 280191e..0000000 Binary files a/docs/examples/classifier_example/images/fullalgorithm.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/images4features/rotations.png b/docs/examples/classifier_example/images/images4features/rotations.png deleted file mode 100644 index 16dcfa3..0000000 Binary files a/docs/examples/classifier_example/images/images4features/rotations.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/images4points/datapoints.png b/docs/examples/classifier_example/images/images4points/datapoints.png deleted file mode 100644 index 1e1f3d5..0000000 Binary files a/docs/examples/classifier_example/images/images4points/datapoints.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/images4points/parta.png b/docs/examples/classifier_example/images/images4points/parta.png deleted file mode 100644 index fe4d495..0000000 Binary files a/docs/examples/classifier_example/images/images4points/parta.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/images4points/partb.png b/docs/examples/classifier_example/images/images4points/partb.png deleted file mode 100644 index fe44738..0000000 Binary files a/docs/examples/classifier_example/images/images4points/partb.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/images4points/partc.png b/docs/examples/classifier_example/images/images4points/partc.png deleted file mode 100644 index 72eeab4..0000000 Binary files a/docs/examples/classifier_example/images/images4points/partc.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/images4points/partd.png b/docs/examples/classifier_example/images/images4points/partd.png deleted file mode 100644 index 868337a..0000000 Binary files a/docs/examples/classifier_example/images/images4points/partd.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/images4points/parte.png b/docs/examples/classifier_example/images/images4points/parte.png deleted file mode 100644 index eb9d289..0000000 Binary files a/docs/examples/classifier_example/images/images4points/parte.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/partb.png b/docs/examples/classifier_example/images/partb.png deleted file mode 100644 index f2c5be6..0000000 Binary files a/docs/examples/classifier_example/images/partb.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/partc.png b/docs/examples/classifier_example/images/partc.png deleted file mode 100644 index 36af32e..0000000 Binary files a/docs/examples/classifier_example/images/partc.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/partf.png b/docs/examples/classifier_example/images/partf.png deleted file mode 100644 index 0aed369..0000000 Binary files a/docs/examples/classifier_example/images/partf.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/plot.png b/docs/examples/classifier_example/images/plot.png deleted file mode 100644 index 4109c88..0000000 Binary files a/docs/examples/classifier_example/images/plot.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/randompoint.png b/docs/examples/classifier_example/images/randompoint.png deleted file mode 100644 index cab9733..0000000 Binary files a/docs/examples/classifier_example/images/randompoint.png and /dev/null differ diff --git a/docs/examples/classifier_example/images/stateprep.png b/docs/examples/classifier_example/images/stateprep.png deleted file mode 100644 index 65352f0..0000000 Binary files a/docs/examples/classifier_example/images/stateprep.png and /dev/null differ diff --git a/docs/examples/example_projectq_entangle.py b/docs/examples/example_projectq_entangle.py index a681522..f38bfd3 100644 --- a/docs/examples/example_projectq_entangle.py +++ b/docs/examples/example_projectq_entangle.py @@ -2,7 +2,6 @@ This example is copied from https://github.com/ProjectQ-Framework/ProjectQ and is covered under the Apache 2.0 license. """ - import os from projectq import MainEngine @@ -10,34 +9,32 @@ from projectq.ops import CNOT, H, Measure, All from projectq.setups import restrictedgateset -from quantuminspire.credentials import get_authentication from quantuminspire.api import QuantumInspireAPI +from quantuminspire.credentials import get_authentication from quantuminspire.projectq.backend_qx import QIBackend QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/') -if __name__ == '__main__': - - project_name = 'ProjectQ-entangle' - authentication = get_authentication() - qi_api = QuantumInspireAPI(QI_URL, authentication, project_name=project_name) - qi_backend = QIBackend(quantum_inspire_api=qi_api) +project_name = 'ProjectQ-entangle' +authentication = get_authentication() +qi_api = QuantumInspireAPI(QI_URL, authentication, project_name=project_name) +qi_backend = QIBackend(quantum_inspire_api=qi_api) - compiler_engines = restrictedgateset.get_engine_list(one_qubit_gates=qi_backend.one_qubit_gates, - two_qubit_gates=qi_backend.two_qubit_gates) - compiler_engines.extend([ResourceCounter()]) - engine = MainEngine(backend=qi_backend, engine_list=compiler_engines) +compiler_engines = restrictedgateset.get_engine_list(one_qubit_gates=qi_backend.one_qubit_gates, + two_qubit_gates=qi_backend.two_qubit_gates) +compiler_engines.extend([ResourceCounter()]) +engine = MainEngine(backend=qi_backend, engine_list=compiler_engines) - qubits = engine.allocate_qureg(2) - q1 = qubits[0] - q2 = qubits[1] +qubits = engine.allocate_qureg(2) +q1 = qubits[0] +q2 = qubits[1] - H | q1 - CNOT | (q1, q2) - All(Measure) | qubits +H | q1 +CNOT | (q1, q2) +All(Measure) | qubits - engine.flush() +engine.flush() - print('\nMeasured: {0}'.format([int(q) for q in qubits])) - print('Probabilities {0}'.format(qi_backend.get_probabilities(qubits))) +print('\nMeasured: {0}'.format([int(q) for q in qubits])) +print('Probabilities {0}'.format(qi_backend.get_probabilities(qubits))) diff --git a/docs/examples/example_projectq_grover.py b/docs/examples/example_projectq_grover.py index 3523cd8..dd2e3d8 100644 --- a/docs/examples/example_projectq_grover.py +++ b/docs/examples/example_projectq_grover.py @@ -11,8 +11,8 @@ from projectq.ops import CNOT, CZ, All, H, Measure, X, Z from projectq.setups import restrictedgateset -from quantuminspire.credentials import get_authentication from quantuminspire.api import QuantumInspireAPI +from quantuminspire.credentials import get_authentication from quantuminspire.projectq.backend_qx import QIBackend QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/') @@ -87,28 +87,26 @@ def alternating_bits_oracle(eng, qubits, output): Uncompute(eng) -if __name__ == '__main__': - - # Remote Quantum-Inspire backend # - authentication = get_authentication() - qi = QuantumInspireAPI(QI_URL, authentication) - qi_backend = QIBackend(quantum_inspire_api=qi) +# Remote Quantum-Inspire backend +authentication = get_authentication() +qi = QuantumInspireAPI(QI_URL, authentication) +qi_backend = QIBackend(quantum_inspire_api=qi) - compiler_engines = restrictedgateset.get_engine_list(one_qubit_gates=qi_backend.one_qubit_gates, - two_qubit_gates=qi_backend.two_qubit_gates, - other_gates=qi_backend.three_qubit_gates) - compiler_engines.extend([ResourceCounter()]) - qi_engine = MainEngine(backend=qi_backend, engine_list=compiler_engines) +compiler_engines = restrictedgateset.get_engine_list(one_qubit_gates=qi_backend.one_qubit_gates, + two_qubit_gates=qi_backend.two_qubit_gates, + other_gates=qi_backend.three_qubit_gates) +compiler_engines.extend([ResourceCounter()]) +qi_engine = MainEngine(backend=qi_backend, engine_list=compiler_engines) - # Run remote Grover search to find a n-bit solution - result_qi = run_grover(qi_engine, 3, alternating_bits_oracle) - print("\nResult from the remote Quantum-Inspire backend: {}".format(result_qi)) +# Run remote Grover search to find a n-bit solution +result_qi = run_grover(qi_engine, 3, alternating_bits_oracle) +print("\nResult from the remote Quantum-Inspire backend: {}".format(result_qi)) - # Local ProjectQ simulator backend # - compiler_engines = restrictedgateset.get_engine_list(one_qubit_gates="any", two_qubit_gates=(CNOT, CZ)) - compiler_engines.append(ResourceCounter()) - local_engine = MainEngine(Simulator(), compiler_engines) +# Local ProjectQ simulator backend +compiler_engines = restrictedgateset.get_engine_list(one_qubit_gates="any", two_qubit_gates=(CNOT, CZ)) +compiler_engines.append(ResourceCounter()) +local_engine = MainEngine(Simulator(), compiler_engines) - # Run local Grover search to find a n-bit solution - result_local = run_grover(local_engine, 3, alternating_bits_oracle) - print("Result from the local ProjectQ simulator backend: {}\n".format(result_local)) +# Run local Grover search to find a n-bit solution +result_local = run_grover(local_engine, 3, alternating_bits_oracle) +print("Result from the local ProjectQ simulator backend: {}\n".format(result_local)) diff --git a/docs/examples/example_qiskit_conditional.py b/docs/examples/example_qiskit_conditional.py index 23be5ca..9037231 100644 --- a/docs/examples/example_qiskit_conditional.py +++ b/docs/examples/example_qiskit_conditional.py @@ -1,4 +1,4 @@ -"""Example usage of the Quantum Inspire backend with the QisKit SDK. +"""Example usage of the Quantum Inspire backend with the Qiskit SDK. A simple example that demonstrates how to use the SDK to create a circuit to demonstrate conditional gate execution. @@ -6,52 +6,49 @@ For documentation on how to use Qiskit we refer to [https://qiskit.org/](https://qiskit.org/). -Specific to Quantum Inspire is the creation of the QI instance, which is used to set the authentication of the user and -provides a Quantum Inspire backend that is used to execute the circuit. +Specific to Quantum Inspire is the creation of the QI instance, which is used to set the authentication +of the user and provides a Quantum Inspire backend that is used to execute the circuit. Copyright 2018-19 QuTech Delft. Licensed under the Apache License, Version 2.0. """ import os -from quantuminspire.credentials import get_authentication -from qiskit import BasicAer +from qiskit import BasicAer, execute from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit -from qiskit import execute +from quantuminspire.credentials import get_authentication from quantuminspire.qiskit import QI QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/') -if __name__ == '__main__': - - authentication = get_authentication() - QI.set_authentication(authentication, QI_URL) - qi_backend = QI.get_backend('QX single-node simulator') - - q = QuantumRegister(3, "q") - c0 = ClassicalRegister(1, "c0") - c1 = ClassicalRegister(1, "c1") - c2 = ClassicalRegister(1, "c2") - qc = QuantumCircuit(q, c0, c1, c2, name="conditional") - - qc.h(q[0]) - qc.h(q[1]).c_if(c0, 0) # h-gate on q[1] is executed - qc.h(q[2]).c_if(c1, 1) # h-gate on q[2] is not executed - - qc.measure(q[0], c0) - qc.measure(q[1], c1) - qc.measure(q[2], c2) - - qi_job = execute(qc, backend=qi_backend, shots=1024) - qi_result = qi_job.result() - histogram = qi_result.get_counts(qc) - print("\nResult from the remote Quantum Inspire backend:\n") - print('State\tCounts') - [print('{0}\t{1}'.format(state, counts)) for state, counts in histogram.items()] - - print("\nResult from the local Qiskit simulator backend:\n") - backend = BasicAer.get_backend("qasm_simulator") - job = execute(qc, backend=backend, shots=1024) - result = job.result() - print(result.get_counts(qc)) +authentication = get_authentication() +QI.set_authentication(authentication, QI_URL) +qi_backend = QI.get_backend('QX single-node simulator') + +q = QuantumRegister(3, "q") +c0 = ClassicalRegister(1, "c0") +c1 = ClassicalRegister(1, "c1") +c2 = ClassicalRegister(1, "c2") +qc = QuantumCircuit(q, c0, c1, c2, name="conditional") + +qc.h(q[0]) +qc.h(q[1]).c_if(c0, 0) # h-gate on q[1] is executed +qc.h(q[2]).c_if(c1, 1) # h-gate on q[2] is not executed + +qc.measure(q[0], c0) +qc.measure(q[1], c1) +qc.measure(q[2], c2) + +qi_job = execute(qc, backend=qi_backend, shots=1024) +qi_result = qi_job.result() +histogram = qi_result.get_counts(qc) +print("\nResult from the remote Quantum Inspire backend:\n") +print('State\tCounts') +[print('{0}\t{1}'.format(state, counts)) for state, counts in histogram.items()] + +print("\nResult from the local Qiskit simulator backend:\n") +backend = BasicAer.get_backend("qasm_simulator") +job = execute(qc, backend=backend, shots=1024) +result = job.result() +print(result.get_counts(qc)) diff --git a/docs/examples/example_qiskit_entangle.py b/docs/examples/example_qiskit_entangle.py index 2af3526..3a1f196 100644 --- a/docs/examples/example_qiskit_entangle.py +++ b/docs/examples/example_qiskit_entangle.py @@ -1,50 +1,47 @@ -"""Example usage of the Quantum Inspire backend with the QisKit SDK. +"""Example usage of the Quantum Inspire backend with the Qiskit SDK. A simple example that demonstrates how to use the SDK to create a circuit to create a Bell state, and simulate the circuit on Quantum Inspire. -For documentation on how to use QisKit we refer to +For documentation on how to use Qiskit we refer to [https://qiskit.org/](https://qiskit.org/). -Specific to Quantum Inspire is the creation of the QI instance, which is used to set the authentication of the user and -provides a Quantum Inspire backend that is used to execute the circuit. - +Specific to Quantum Inspire is the creation of the QI instance, which is used to set the authentication +of the user and provides a Quantum Inspire backend that is used to execute the circuit. Copyright 2018-19 QuTech Delft. Licensed under the Apache License, Version 2.0. """ import os -from quantuminspire.credentials import get_authentication -from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit import execute +from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit +from quantuminspire.credentials import get_authentication from quantuminspire.qiskit import QI QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/') -if __name__ == '__main__': - - project_name = 'Qiskit-entangle' - authentication = get_authentication() - QI.set_authentication(authentication, QI_URL, project_name=project_name) - qi_backend = QI.get_backend('QX single-node simulator') - - q = QuantumRegister(2) - b = ClassicalRegister(2) - circuit = QuantumCircuit(q, b) - - circuit.h(q[0]) - circuit.cx(q[0], q[1]) - circuit.measure(q, b) - - qi_job = execute(circuit, backend=qi_backend, shots=256) - qi_result = qi_job.result() - histogram = qi_result.get_counts(circuit) - print('\nState\tCounts') - [print('{0}\t\t{1}'.format(state, counts)) for state, counts in histogram.items()] - # Print the full state probabilities histogram - probabilities_histogram = qi_result.get_probabilities(circuit) - print('\nState\tProbabilities') - [print('{0}\t\t{1}'.format(state, val)) for state, val in probabilities_histogram.items()] +project_name = 'Qiskit-entangle' +authentication = get_authentication() +QI.set_authentication(authentication, QI_URL, project_name=project_name) +qi_backend = QI.get_backend('QX single-node simulator') + +q = QuantumRegister(2) +b = ClassicalRegister(2) +circuit = QuantumCircuit(q, b) + +circuit.h(q[0]) +circuit.cx(q[0], q[1]) +circuit.measure(q, b) + +qi_job = execute(circuit, backend=qi_backend, shots=256) +qi_result = qi_job.result() +histogram = qi_result.get_counts(circuit) +print('\nState\tCounts') +[print('{0}\t\t{1}'.format(state, counts)) for state, counts in histogram.items()] +# Print the full state probabilities histogram +probabilities_histogram = qi_result.get_probabilities(circuit) +print('\nState\tProbabilities') +[print('{0}\t\t{1}'.format(state, val)) for state, val in probabilities_histogram.items()] diff --git a/docs/examples/grover_algorithm_qi.ipynb b/docs/examples/grover_algorithm_qi.ipynb index e029b24..31b9faa 100644 --- a/docs/examples/grover_algorithm_qi.ipynb +++ b/docs/examples/grover_algorithm_qi.ipynb @@ -11,16 +11,16 @@ "For more information about how to use the IBM Q Experience (QX), consult the [tutorials](https://quantumexperience.ng.bluemix.net/qstage/#/tutorial?sectionId=c59b3710b928891a1420190148a72cce&pageIndex=0), or check out the [community](https://quantumexperience.ng.bluemix.net/qstage/#/community).\n", "\n", "*Contributors*\n", - "Pieter Eendebak, Giacomo Nannicini and Rudy Raymond (based on [this article](https://arxiv.org/abs/1708.03684)) " + "Pieter Eendebak, Giacomo Nannicini and Rudy Raymond (based on [this article](https://arxiv.org/abs/1708.03684))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Introduction\n", + "## Introduction\n", "\n", - "Grover search is one of the most popular algorithms used for searching a solution among many possible candidates using Quantum Computers. If there are $N$ possible solutions among which there is exactly one solution (that can be verified by some function evaluation), then Grover search can be used to find the solution with $O(\\sqrt{N})$ function evaluations. This is in contrast to classical computers that require $\\Omega(N)$ function evaluations: the Grover search is a quantum algorithm that provably can be used search the correct solutions quadratically faster than its classical counterparts. \n", + "Grover search is one of the most popular algorithms used for searching a solution among many possible candidates using Quantum Computers. If there are $N$ possible solutions among which there is exactly one solution (that can be verified by some function evaluation), then Grover search can be used to find the solution with $O(\\sqrt{N})$ function evaluations. This is in contrast to classical computers that require $\\Omega(N)$ function evaluations: the Grover search is a quantum algorithm that provably can be used search the correct solutions quadratically faster than its classical counterparts.\n", "\n", "Here, we are going to illustrate the use of Grover search to find a particular value in a binary number.\n", "The key elements of Grovers algorithm are:\n", @@ -54,9 +54,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# The oracle function\n", + "## The oracle function\n", "\n", - "We implement an oracle function (black box) that acts as -1 on a single basis state, and +1 on all other status. " + "We implement an oracle function (black box) that acts as -1 on a single basis state, and +1 on all other status." ] }, { @@ -122,7 +122,7 @@ " for kk in range(number_of_qubits):\n", " if state % 2 == 1:\n", " pre_circuit.x(q[kk])\n", - " state = state // 2 \n", + " state = state // 2\n", "\n", " input_state = r'\\left\\lvert{0:0{1}b}\\right\\rangle'.format(base_state, number_of_qubits)\n", " circuit_total = pre_circuit.compose(q_circuit)\n", @@ -271,14 +271,14 @@ "qc = QuantumCircuit(q)\n", "\n", "if n==1:\n", - " def black_box(qc, q): \n", + " def black_box(qc, q):\n", " qc.z(q)\n", "elif n==2:\n", " def black_box(qc, q):\n", " for i in range(n):\n", " qc.s(q[i])\n", " qc.h(q[1])\n", - " qc.cx(q[0], q[1]) \n", + " qc.cx(q[0], q[1])\n", " qc.h(q[1])\n", " for i in range(n):\n", " qc.s(q[i])\n", @@ -299,12 +299,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Inversion about the average\n", + "## Inversion about the average\n", "\n", "Another important procedure in Grover search is to have an operation that perfom the *inversion-about-the-average* step, namely, it performs the following transformation:\n", "\n", "$$\n", - "\\sum_{j=0}^{2^{n}-1} \\alpha_j |j\\rangle \\rightarrow \\sum_{j=0}^{2^{n}-1}\\left(2 \\left( \\sum_{k=0}^{k=2^{n}-1} \\frac{\\alpha_k}{2^n} \\right) - \\alpha_j \\right) |j\\rangle \n", + "\\sum_{j=0}^{2^{n}-1} \\alpha_j |j\\rangle \\rightarrow \\sum_{j=0}^{2^{n}-1}\\left(2 \\left( \\sum_{k=0}^{k=2^{n}-1} \\frac{\\alpha_k}{2^n} \\right) - \\alpha_j \\right) |j\\rangle\n", "$$\n", "\n", "The above transformation can be used to amplify the probability amplitude $\\alpha_s$ when s is the solution and $\\alpha_s$ is negative (and small), while $\\alpha_j$ for $j \\neq s$ is positive. Roughly speaking, the value of $\\alpha_s$ increases by twice the average of the amplitudes, while others are reduced. The inversion-about-the-average can be realized with the sequence of unitary matrices as below:\n", @@ -313,7 +313,7 @@ "H^{\\otimes n} \\left(2|0\\rangle \\langle 0 | - I \\right) H^{\\otimes n}\n", "$$\n", "\n", - "The first and last $H$ are just Hadamard gates applied to each qubit. The operation in the middle requires us to design a sub-circuit that flips the probability amplitude of the component of the quantum state corresponding to the all-zero binary string. The sub-circuit can be realized by the following function, which is a multi-qubit controlled-Z which flips the probability amplitude of the component of the quantum state corresponding to the all-one binary string. Applying X gates to all qubits before and after the function realizes the sub-circuit. " + "The first and last $H$ are just Hadamard gates applied to each qubit. The operation in the middle requires us to design a sub-circuit that flips the probability amplitude of the component of the quantum state corresponding to the all-zero binary string. The sub-circuit can be realized by the following function, which is a multi-qubit controlled-Z which flips the probability amplitude of the component of the quantum state corresponding to the all-one binary string. Applying X gates to all qubits before and after the function realizes the sub-circuit." ] }, { @@ -523,7 +523,7 @@ "source": [ "print('inversion average circuit:')\n", "qc = QuantumCircuit(q)\n", - "inversion_about_average(qc, q, n) \n", + "inversion_about_average(qc, q, n)\n", "run_circuit(qc, q, n)" ] }, @@ -531,7 +531,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Grover Search: putting all together\n", + "## Grover Search: putting all together\n", "\n", "The complete steps of Grover search is as follow.\n", "\n", @@ -540,7 +540,7 @@ "2. Repeat for $T$ times:\n", " * Apply the blackbox function\n", " * Apply the inversion-about-the-average function\n", - " \n", + "\n", "3. Measure to obtain the solution" ] }, @@ -660,7 +660,7 @@ } ], "source": [ - "\"\"\"Grover search implemented in QISKit.\n", + "\"\"\"Grover search implemented in Qiskit.\n", "\n", "This module contains the code necessary to run Grover search on 3\n", "qubits, both with a simulator and with a real quantum computing\n", @@ -747,7 +747,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Run the cirquit on the Quantum Inspire simulator" + "## Run the cirquit on the Quantum Inspire simulator" ] }, { @@ -802,7 +802,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We create a QisKit backend for the Quantum Inspire interface and execute the circuit generated above." + "We create a Qiskit backend for the Quantum Inspire interface and execute the circuit generated above." ] }, { @@ -846,7 +846,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Visualization can be done with the normal Python plotting routines, or with the QisKit SDK." + "Visualization can be done with the normal Python plotting routines, or with the Qiskit SDK." ] }, { @@ -944,4 +944,4 @@ }, "nbformat": 4, "nbformat_minor": 1 -} +} \ No newline at end of file diff --git a/docs/examples/qi-performance-test.ipynb b/docs/examples/qi-performance-test.ipynb index 273607e..17f315e 100644 --- a/docs/examples/qi-performance-test.ipynb +++ b/docs/examples/qi-performance-test.ipynb @@ -6,7 +6,7 @@ "source": [ "# Quantum Inspire performance test\n", "\n", - "We compare performance of the simulator with the circuit from \n", + "We compare performance of the simulator with the circuit from\n", "\n", "\"Overview and Comparison of Gate Level Quantum Software Platforms\", https://arxiv.org/abs/1807.02500" ] @@ -15,7 +15,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Define the circuit" + "## Define the circuit" ] }, { @@ -107,7 +107,7 @@ " for qidx in range(nqubits):\n", " qc.rx(np.pi/2, q[qidx])\n", " qc.barrier()\n", - " \n", + "\n", " for qidx in range(nqubits):\n", " if qidx!=0:\n", " qc.cx(q[qidx], q[0])\n", @@ -123,7 +123,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Run the cirquit on the Quantum Inspire simulator" + "## Run the cirquit on the Quantum Inspire simulator" ] }, { @@ -148,7 +148,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We create a QisKit backend for the Quantum Inspire interface and execute the circuit generated above." + "We create a Qiskit backend for the Quantum Inspire interface and execute the circuit generated above." ] }, { @@ -157,7 +157,7 @@ "metadata": {}, "outputs": [], "source": [ - "qi_backend = QI.get_backend('QX single-node simulator') \n", + "qi_backend = QI.get_backend('QX single-node simulator')\n", "job = execute(qc, qi_backend)" ] }, @@ -192,7 +192,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Visualization can be done with the normal Python plotting routines, or with the QisKit SDK." + "Visualization can be done with the normal Python plotting routines, or with the Qiskit SDK." ] }, { @@ -222,7 +222,7 @@ "source": [ "To compare we will run the circuit with 20 qubits and depth 20. This takes:\n", "\n", - "* QisKit: 3.7 seconds\n", + "* Qiskit: 3.7 seconds\n", "* ProjectQ: 2.0 seconds" ] }, @@ -304,4 +304,4 @@ }, "nbformat": 4, "nbformat_minor": 1 -} +} \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index f125235..2e7d498 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,11 +7,12 @@ Welcome to Quantum Inspire's SDK documentation! =============================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 3 :caption: Contents: introduction modules + examplenotebooks examples license diff --git a/docs/introduction.rst b/docs/introduction.rst index 23e1ffa..9843fa9 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -1,9 +1,6 @@ Introduction ============ -*Note: this SDK is made available as a public beta, please report any -issues or bugs in the github issue tracker.* - The Quantum Inspire platform allows to execute quantum algorithms using the cQASM language. @@ -23,6 +20,9 @@ https://www.quantum-inspire.com/. Detailed information on cQASM can be found in the Quantum Inspire `knowledge base `__. +Examples of more complex algorithms that make use of Quantum Inspire SDK can be found in +`Quantum Inspire Examples `__. + Installation ------------ @@ -84,6 +84,8 @@ To build the 'readthedocs' documentation do: cd docs make html +The documentation is then build in 'docs/_build/html'. + Running ------- @@ -107,7 +109,7 @@ or when you want to choose which example notebook to run from the browser do: jupyter notebook --notebook-dir="docs/notebooks" -and select an ipython notebook (file with extension ``ipynb``) to run from one of the directories. +and select an IPython notebook (file with extension ``ipynb``) to run. To perform Grover's with the ProjectQ backend from a Python script: @@ -167,6 +169,7 @@ API wrapper directly: Configure a project name for Quantum Inspire -------------------------------------------- + As a default, SDK stores the jobs in a Quantum Inspire project with the name "qi-sdk-project-" concatenated with a unique identifier for each run. Providing a project name yourself makes it easier to find the project in the Quantum Inspire web-interface and makes it possible to gather related jobs to the same project. @@ -248,8 +251,8 @@ you do: .. code:: python - from quantuminspire.credentials import get_token_authentication - auth = get_token_authentication() + from quantuminspire.credentials import get_authentication + auth = get_authentication() This ``auth`` can then be used to initialize the Quantum Inspire API object. @@ -267,7 +270,6 @@ Run all unit tests and collect the code coverage using: Known issues ------------ -- Some test-cases call protected methods - Known issues and common questions regarding the Quantum Inspire platform can be found in the `FAQ `__. @@ -278,16 +280,5 @@ Bug reports Please submit bug-reports `on the github issue tracker `__. -.. note:: - - If you are getting import errors related to ``tests.quantuminspire`` - when running the above commands after a ``pip install -e .``, as a - workaround you should remove the package :file:`tests` installed by older - versions of ``marshmallow-polyfield`` (a Qiskit dependency): - - .. code-block:: bash - - rm -Rf env/lib/python3.7/site-packages/tests - .. |Binder| image:: https://mybinder.org/badge_logo.svg - :target: https://mybinder.org/v2/gh/QuTech-Delft/quantuminspire/master?filepath=docs + :target: https://mybinder.org/v2/gh/QuTech-Delft/quantuminspire/master?filepath=%2Fdocs%2Fexamples diff --git a/docs/sat_example/Grover's Algorithm.pdf b/docs/sat_example/Grover's Algorithm.pdf deleted file mode 100644 index 9e14d08..0000000 Binary files a/docs/sat_example/Grover's Algorithm.pdf and /dev/null differ diff --git a/docs/sat_example/README.md b/docs/sat_example/README.md deleted file mode 100644 index 5f59f38..0000000 --- a/docs/sat_example/README.md +++ /dev/null @@ -1,10 +0,0 @@ -This is an advanced user example adapted from the work 'Grover’s Algorithm: implementation in cQASM and -performance analysis' by bachelor students David Bakker, Hitesh Dialani, Rembrandt Klazinga and Maurits van der Tol. The -work was received as part of the Quantum Science and Quantum Information minor, TU Delft. - -The work can be found in PDF format in the current folder (docs/sat_example/Grover's Algorithm.pdf) - -The original code as written by the students can be found at https://github.com/FluffyToaster/QuantumGrover. The code -is slightly altered, and large pieces are removed that were not relevant for the example. - -See https://www.quantum-inspire.com/kbase/code-example-sat/ for an introduction to the example. \ No newline at end of file diff --git a/docs/sat_example/main.py b/docs/sat_example/main.py deleted file mode 100644 index ac9ad57..0000000 --- a/docs/sat_example/main.py +++ /dev/null @@ -1,44 +0,0 @@ -from src.run import generate_sat_qasm, execute_sat_qasm -from quantuminspire.api import QuantumInspireAPI -from quantuminspire.credentials import get_authentication -import os - -QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/') - - -# Authenticate, create project and define backend -name = 'SAT_Problem' -authentication = get_authentication() -qi = QuantumInspireAPI(QI_URL, authentication, project_name=name) -backend = qi.get_backend_type_by_name( - 'QX single-node simulator') # if the project already exists in My QI, the existing backend will be used and this line will not overwrite the backend information. -shot_count = 512 - -# define SAT problem -boolean_expr = "(a and not(b) and c and d) or (not(a) and b and not(c) and d)" - -# choose variables; see src.run.generate_sat_qasm() for details. -# recommended settings for a simulator -cnot_mode = "normal" -sat_mode = "reuse qubits" -apply_optimization_to_qasm = False -connected_qubit = None - -# # recommended settings for Starmon-5. As of now, any sat problem involving more than one variable will contain too many gates, -# # and decoherence will occur. If you try this, be sure to use a boolean_expr and sat_mode that initiates a total of 5 qubits. -# cnot_mode = "no toffoli" -# sat_mode = "reuse qubits" -# apply_optimization_to_qasm = False -# connected_qubit = '2' - - -# generate the qasm code that will solve the SAT problem -qasm, _, qubit_count, data_qubits = generate_sat_qasm(expr_string=boolean_expr, cnot_mode=cnot_mode, sat_mode=sat_mode, - apply_optimization=apply_optimization_to_qasm, - connected_qubit=connected_qubit) - -print(qasm) - -# execute the qasm code and show the results -execute_sat_qasm(qi=qi, qasm=qasm, shot_count=shot_count, backend=backend, qubit_count=qubit_count, - data_qubits=data_qubits, plot=True) diff --git a/docs/sat_example/src/optimizer.py b/docs/sat_example/src/optimizer.py deleted file mode 100644 index 7b17f9a..0000000 --- a/docs/sat_example/src/optimizer.py +++ /dev/null @@ -1,467 +0,0 @@ -from src.sat_utilities import alternative_and - -optimizations = { - "HXH": "Z", - "HH": "", - "XX": "", - "ZX": "Y" -} -largest_opt = 3 - -forbidden = [ - ".grover_loop", - "Toffoli", - "CNOT", - "CR" -] - - -def apply_optimizations(qasm, qubit_count, data_qubits): - """ - Apply 3 types of optimization to the given QASM code: - Combine groups of gates, such as H-X-H, to faster equivalent gates, Z in this case. - Shift gates to be executed in parallel. - Clean QASM code itself (q[1,2,3] becomes q[1:3]) - - Args: - qasm: Valid QASM code to optimize - qubit_count: The total number of qubits - data_qubits: The number of qubits that are not ancillaries - - Returns: A equivalent piece of QASM with optimizations applied - """ - - # run "speed" mode until QASM does not change - prev_qasm = "" - while prev_qasm != qasm: - prev_qasm = qasm[:] - qasm = optimize(qasm, qubit_count, data_qubits, mode="speed") - - # run "style" mode until QASM does not change - prev_qasm = "" - while prev_qasm != qasm: - prev_qasm = qasm[:] - qasm = optimize(qasm, qubit_count, data_qubits, mode="style") - - prev_qasm = "" - while prev_qasm != qasm: - prev_qasm = qasm[:] - qasm = optimize_toffoli(qasm) - - # tidy up "ugly" optimized code - qasm = clean_code(qasm) - - return qasm - - -def remove_gate_from_line(local_qasm_line, gate_symbol, qubit_index): - """ - Removes the application of a specific gate on a specific qubit. - - Args: - local_qasm_line: The line from which this call should be removed. - gate_symbol: The symbol representing the gate - qubit_index: The index of the target qubit - - Returns: The same line of QASM, with the gate removed. - """ - - # if gate applied to single qubit, remove gate call entirely - single_application = "{} q[{}]".format(gate_symbol, qubit_index) - if single_application in local_qasm_line: - # if there is a parallel bar right - local_qasm_line = local_qasm_line.replace(single_application + " | ", "") - # else: if there is a parallel bar left - local_qasm_line = local_qasm_line.replace(" | " + single_application, "") - # else: if it is the only gate in parallelized brackets - local_qasm_line = local_qasm_line.replace("{" + single_application + "}", "") - # else: if it is not parellelized at all - local_qasm_line = local_qasm_line.replace(single_application, "") - - # else remove just the number - else: - local_qasm_line = local_qasm_line.replace(",{},".format(qubit_index), ",") - local_qasm_line = local_qasm_line.replace("[{},".format(qubit_index), "[") - local_qasm_line = local_qasm_line.replace(",{}]".format(qubit_index), "]") - - return local_qasm_line - - -def add_gate_to_line(local_qasm_line, gate_symbol, qubit_index): - """ - Add in parallel the application of a gate on a qubit. - Args: - local_qasm_line: The existing line of QASM to add the gate in. - gate_symbol: The symbol representing the gate. - qubit_index: The index of the target qubit. - - Returns: The same line of QASM with the gate added. - """ - - # if another operation is already called on this qubit, we have to put the new gate on a new line - if "[" + str(qubit_index) + "]" in local_qasm_line \ - or "[" + str(qubit_index) + "," in local_qasm_line \ - or "," + str(qubit_index) + "," in local_qasm_line \ - or "," + str(qubit_index) + "]" in local_qasm_line: - local_qasm_line += "\n{} q[{}]\n".format(gate_symbol, qubit_index) - - # if the line is not empty, we need to consider what's already present - elif local_qasm_line != "": - # a bracket indicates this line is parallelized with the { gate | gate | gate } syntax - if "{" in local_qasm_line: - # remove } from the line and add it back at the end - local_qasm_line = local_qasm_line.rstrip("}| \n") + \ - " | " + \ - "{} q[{}]".format(gate_symbol, qubit_index) + \ - "}\n" - - # no bracket means we have to add the parallelization syntax ourselves - else: - local_qasm_line = "{" + local_qasm_line.rstrip("\n") + \ - " | " + \ - "{} q[{}]".format(gate_symbol, qubit_index) + "}\n" - - # else, if the line IS empty, we can just put this gate in directly - else: - local_qasm_line = "{} q[{}]\n".format(gate_symbol, qubit_index) - return local_qasm_line - - -def remove_toffoli_from_line(local_qasm_line, qubit_1, qubit_2, target_qubit): - """ - Remove a specific Toffoli gate from a line of qasm. - - Args: - local_qasm_line: The line of qasm - qubit_1: The first control qubit of the Toffoli gate - qubit_2: The second control qubit - target_qubit: The target qubit - - Returns: The same line of qasm without the Toffoli gate call - - """ - single_application = "Toffoli q[{}],q[{}],q[{}]".format(qubit_1, qubit_2, target_qubit) - - # if there is a parallel bar right - local_qasm_line = local_qasm_line.replace(single_application + " | ", "") - # else: if there is a parallel bar left - local_qasm_line = local_qasm_line.replace(" | " + single_application, "") - # else: if it is the only gate in parallelized brackets - local_qasm_line = local_qasm_line.replace("{" + single_application + "}", "") - # else: if it is not parellelized at all - local_qasm_line = local_qasm_line.replace(single_application, "") - return local_qasm_line - - -def add_toffoli_to_line(local_qasm_line, qubit_1, qubit_2, target_qubit): - """ - Add a single Toffoli gate application to the given line of qasm. - - Args: - local_qasm_line: The line of qasm - qubit_1: The first control qubit - qubit_2: The second control qubit - target_qubit: The target qubit - - Returns: The same line of qasm with the Toffoli gate added in parallel - """ - - single_application = "Toffoli q[{}],q[{}],q[{}]".format(qubit_1, qubit_2, target_qubit) - - # if the line is not empty, we need to consider what's already present - if local_qasm_line != "": - # a bracket indicates this line is parallelized with the { gate_1 | gate_2 | gate_3 } syntax - if "{" in local_qasm_line: - # remove } from the line and add it back at the end - local_qasm_line = local_qasm_line.rstrip("}| \n") + \ - " | " + \ - single_application + \ - "}\n" - - # no bracket means we have to add the parallelization syntax ourselves - else: - local_qasm_line = "{" + local_qasm_line.rstrip("\n") + \ - " | " + \ - single_application + "}\n" - - # else, if the line IS empty, we can just put this gate in directly - else: - local_qasm_line = single_application + "\n" - return local_qasm_line - - -def optimize(qasm, qubit_count, data_qubits, mode="speed"): - """ - Apply a single pass of performance-oriented optimizations to the given QASM. - - Args: - qasm: A valid QASM program to optimize. - qubit_count: The total number of qubits - data_qubits: The number of qubits that are not ancillaries - mode: Setting that determines the type of optimization: - "speed" -> combine gates into equivalent smaller gates - "style" -> parallelize gates for speedup and aesthetics - - Returns: Functionally the same QASM code, with one run of optimizations applied. - """ - - qasm_lines = qasm.split("\n") - gates_applied = [] - for i in range(qubit_count): - gates_applied.append([]) - - for qasm_line_index in range(len(qasm_lines)): - line = qasm_lines[qasm_line_index] - if len(line) == 0: - continue - - gate = line.split()[0].lstrip("{") - if gate not in ["H", "X", "Y", "Z"]: - gate = "_" - - if ":" in line: - raise ValueError("Optimizer does not work well with q[0:3] notation!\n" - "Line: {}".format(line)) - - if "[" in line: - for element in line.split(" | "): - gate = element.split()[0].lstrip("{") - if gate not in ["H", "X", "Y", "Z"]: - gate = "_" - - alt_affected_qubits = [] - for possibly_affected in range(qubit_count): - hits = [ - ",{},".format(possibly_affected), - "[{},".format(possibly_affected), - ",{}]".format(possibly_affected), - "[{}]".format(possibly_affected) - ] - if any(h in element for h in hits): - alt_affected_qubits.append(possibly_affected) - - for a in alt_affected_qubits: - gates_applied[a].append((qasm_line_index, gate)) - else: - for a in range(data_qubits): - gates_applied[a].append((qasm_line_index, gate)) - - for qubit_index in range(len(gates_applied)): - gates = gates_applied[qubit_index] - skip_counter = 0 - for gate_index in range(len(gates)): - if skip_counter > 0: - skip_counter -= 1 - continue - - if mode == "speed": - for offset in range(0, largest_opt - 1): - next_gates = "".join(map(lambda _: _[1], gates[gate_index:gate_index + largest_opt - offset])) - if next_gates in optimizations: - replacement = optimizations[next_gates] - - line_indices = list(map(lambda _: _[0], gates[gate_index:gate_index + largest_opt - offset])) - # first, remove all gates that are to be replaced - for idx, line_number in enumerate(line_indices): - qasm_lines[line_number] = remove_gate_from_line(qasm_lines[line_number], - next_gates[idx], - qubit_index) - - # add replacement gate to first line index - # unless there is no replacement gate, of course - if replacement != "": - qasm_lines[line_indices[0]] = add_gate_to_line(qasm_lines[line_indices[0]], - replacement, - qubit_index) - - # ensure we skip a few gates - skip_counter += len(next_gates) - - elif mode == "style": - # check if we can shift left to align with other gates - current_line, current_gate = gates[gate_index] - prev_line = current_line - 1 - - if any(f in qasm_lines[current_line] or f in qasm_lines[prev_line] for f in forbidden): - continue - # if this or the previous line has a break statement, no shifting possible - if current_gate == "_" or (gates[gate_index - 1][1] == "_" and gates[gate_index - 1][0] == prev_line): - continue - - if qasm_lines[prev_line] == "": - continue - - # having passed these checks, we can try to actually shift - if current_gate in ["H", "X", "Y", "Z"] and str(qubit_index) not in qasm_lines[prev_line]: - # remove from current line - qasm_lines[current_line] = remove_gate_from_line(qasm_lines[current_line], current_gate, - qubit_index) - # add to left - qasm_lines[prev_line] = add_gate_to_line(qasm_lines[prev_line], current_gate, qubit_index) - - # remove blank lines - qasm_lines = list(filter(lambda x: x not in ["", "{}", " "], qasm_lines)) - return "\n".join(qasm_lines).replace("\n\n", "\n") - - -def optimize_toffoli(qasm): - """ - Specific style optimizer capable of left-shifting and annihilating Toffoli gates. - - Args: - qasm: QASM to optimize - - Returns: Equivalent QASM with Toffoli gates optimized. - """ - - qasm_lines = qasm.split("\n") - for current_line_index in range(1, len(qasm_lines)): - cur_line = qasm_lines[current_line_index] - prev_line = qasm_lines[current_line_index - 1] - if "Toffoli" in cur_line and "Toffoli" in prev_line and ".grover_loop" not in prev_line: - # find all Toffoli triplets in both lines - prev_line_gates = prev_line.strip("{} |").split(" | ") - prev_line_toffolis = list(filter(lambda x: "Toffoli" in x, prev_line_gates)) - - cur_line_gates = cur_line.strip("{} |").split(" | ") - cur_line_toffolis = list(filter(lambda x: "Toffoli" in x, cur_line_gates)) - - any_updated = False - for c_t in cur_line_toffolis: - if any_updated: - break - - c_qubit_1, c_qubit_2, c_target_qubit = tuple(map(int, c_t.strip("Toffoli q[]").split("],q["))) - shiftable = True - - for p_t in prev_line_toffolis: - p_qubit_1, p_qubit_2, p_target_qubit = tuple(map(int, p_t.strip("Toffoli q[]").split("],q["))) - - if {c_qubit_1, c_qubit_2} == {p_qubit_1, p_qubit_2} and c_target_qubit == p_target_qubit: - # remove toffolis from both lines - cur_line = remove_toffoli_from_line(cur_line, c_qubit_1, c_qubit_2, c_target_qubit) - prev_line = remove_toffoli_from_line(prev_line, p_qubit_1, p_qubit_2, p_target_qubit) - shiftable = False - any_updated = True - break - elif len({c_qubit_1, c_qubit_2, c_target_qubit}.intersection( - {p_qubit_1, p_qubit_2, p_target_qubit})) != 0: - shiftable = False - break - - # check if anything has blocked us from shifting left - if shiftable: - # otherwise we can go! - cur_line = remove_toffoli_from_line(cur_line, c_qubit_1, c_qubit_2, c_target_qubit) - prev_line = add_toffoli_to_line(prev_line, c_qubit_1, c_qubit_2, c_target_qubit) - - any_updated = True - - if any_updated: - qasm_lines[current_line_index] = cur_line - qasm_lines[current_line_index - 1] = prev_line - - # remove blank lines - qasm_lines = list(filter(lambda x: x not in ["", "{}", " "], qasm_lines)) - - return "\n".join(qasm_lines).replace("\n\n", "\n") - - -def replace_toffoli_with_alt(qasm): - """ - Replace all Toffoli gates (including parallelized ones) by their alternative representation. - See src.grover.search_utilies.alternative_toffoli for more details. - - Args: - qasm: The full qasm program that contains Toffoli gates to replace - - Returns: The same qasm with Toffoli gates replaced. - """ - qasm_lines = qasm.split("\n") - for current_line_index in range(len(qasm_lines)): - cur_line = qasm_lines[current_line_index] - if "Toffoli" in cur_line: - # find all Toffoli triplets in this line - - cur_line_gates = cur_line.strip("{} |").split(" | ") - cur_line_toffolis = list(filter(lambda x: "Toffoli" in x, cur_line_gates)) - - multiple = len(cur_line_toffolis) > 1 - - line_strings = [] - for i in range(7): - if multiple: - line_strings.append("{") - else: - line_strings.append("") - - for current_t in cur_line_toffolis: - qubit_1, qubit_2, target_qubit = tuple(map(int, current_t.strip("Toffoli q[]").split("],q["))) - alt_qasm_lines = alternative_and(qubit_1, qubit_2, target_qubit).split("\n") - for j in range(7): - line_strings[j] += alt_qasm_lines[j] - if multiple: - line_strings[j] += " | " - - if multiple: - for i in range(7): - line_strings[i] = line_strings[i][:-3] + "}" - - cur_line = "\n".join(line_strings) - - qasm_lines[current_line_index] = cur_line - - # remove blank lines - qasm_lines = list(filter(lambda x: x not in ["", "{}", " "], qasm_lines)) - return "\n".join(qasm_lines).replace("\n\n", "\n") - - -def clean_code(qasm): - """ - Clean given QASM by rewriting each line to a more readable format. - For example, "{ X q[0] | H q[3,4,5] | X q[1] | X q[2] }" - Would become "{ X q[0:2] | H q[3:5]" - - Args: - qasm: Valid QASM code to clean - - Returns: The same QASM code with improved formatting. - """ - - qasm_lines = qasm.split("\n") - for idx in range(len(qasm_lines)): - line = qasm_lines[idx] - gate_dict = {} - new_line = "" - if "Toffoli" not in line and "CR" not in line and "CNOT" not in line and ("{" in line or "," in line): - line = line.strip("{}") - elements = line.split("|") - for e in elements: - gate, target = e.split() - indices = list(map(int, target.strip("q[]").split(","))) - if gate not in gate_dict: - gate_dict[gate] = indices - else: - gate_dict[gate] += indices - - parallel = len(gate_dict.keys()) > 1 - if parallel: - new_line += "{ " - for gate, indices in gate_dict.items(): - if max(indices) - min(indices) + 1 == len(indices) > 1: - new_line += "{} q[{}:{}]".format(gate, min(indices), max(indices)) - else: - new_line += "{} q[{}]".format(gate, ",".join(map(str, indices))) - new_line += " | " - - new_line = new_line[:-3] - if parallel: - new_line += " }" - else: - new_line = line - - qasm_lines[idx] = new_line - - return "\n".join(qasm_lines) - - diff --git a/docs/sat_example/src/run.py b/docs/sat_example/src/run.py deleted file mode 100644 index 7cb6ba3..0000000 --- a/docs/sat_example/src/run.py +++ /dev/null @@ -1,136 +0,0 @@ -from src.sat_utilities import * -from src.optimizer import * -import math - - -def generate_sat_qasm(expr_string, cnot_mode, sat_mode, apply_optimization=True, connected_qubit=None): - """ - Generate the QASM needed to evaluate the SAT problem for a given boolean expression. - - Args: - expr_string: A boolean expression as a string - cnot_mode: The mode for CNOTs. 'normal' and 'no toffoli' are verified to be working. 'crot' and 'fancy cnot' are experimental. - - normal: use toffoli gates and ancillary qubits for max speed - - no toffoli: same as normal, but replace toffoli gates for 2-gate equivalent circuits. uses ancillary qubits. This mode must be used if using a backend that doesn't support toffoli gates, like starmon-5. - - crot: no ancillary qubits or toffoli gates, but scales with 3^n gates for n bits - - fancy cnot: no ancillary qubits or toffoli gates, scales 2^n - sat_mode: The mode for the SAT solving circuit: - - reuse gates: use minimal amount of gates - - reuse qubits: use minimal amount of ancillary qubits - apply_optimization: Whether to apply the optimization algorithm to our generated QASM, saving ~20-50% lines of code - connected_qubit: This functionallity is meant for backends in which not all qubits are connected to each other. - For example, in Starmon-5, only the third qubit is connected to all other qubits. In order to make the qasm work on - this backend, connected_qubit='2' should be given as argument. Qubits are then swapped during the algorithm such that every gate is - between the connected_qubit and another one. This function can only be used in combination with - cnot_mode='no toffoli', to ensure no three qubit gates are present. - - None: do not swap any gates - - '2': swap qubits to ensure all gates involve qubit 2. - - Returns: A tuple of the following values: - - qasm: The QASM representing the requested Grover search - - line count: The total number of parallel lines that is executed (including grover loops) - - qubit count: The total number of qubits required - - data qubits: The number of data qubits - """ - - algebra = boolean.BooleanAlgebra() - expr = algebra.parse(expr_string) - - control_names = sorted(list(expr.symbols), reverse=True) - - # note that the number of data qubits also includes an extra bit which must be 1 for the algorithm to succeed - data_qubits = len(control_names) + 1 - - expr = split_expression_evenly(expr.simplify()) - - if sat_mode == "reuse gates": - oracle_qasm, _, last_qubit_index = generate_sat_oracle_reuse_gates(expr, control_names, is_toplevel=True, - mode=cnot_mode) - elif sat_mode == "reuse qubits": - oracle_qasm, _, last_qubit_index = generate_sat_oracle_reuse_qubits(expr, control_names, [], is_toplevel=True, - mode=cnot_mode) - else: - raise ValueError("Invalid SAT mode: {} instead of 'reuse gates' or 'reuse qubits'".format(sat_mode)) - - qubit_count = last_qubit_index + 1 - - # some modes may require many ancillary qubits for the diffusion operator! - if cnot_mode in ["normal", "no toffoli"]: - qubit_count = max(qubit_count, data_qubits * 2 - 3) - - qasm = "version 1.0\n" \ - "qubits {}\n".format(qubit_count) - - # initialisation - qasm += fill("H", data_qubits) - - # looping grover - iterations = int(math.pi * math.sqrt(2 ** data_qubits - 1) / 4) - qasm += ".grover_loop({})\n".format(iterations) - - qasm += oracle_qasm + "\n" - - # diffusion - qasm += fill("H", data_qubits) - qasm += fill("X", data_qubits) - qasm += cnot_pillar(cnot_mode, data_qubits) - qasm += fill("X", data_qubits) - qasm += fill("H", data_qubits) - - if apply_optimization: - qasm = apply_optimizations(qasm, qubit_count, data_qubits) - - if connected_qubit is not None: - qasm = swap_qubits(qasm=qasm, cnot_mode=cnot_mode, apply_optimization=apply_optimization, - connected_qubit=connected_qubit) - - # remove blank lines - qasm_lines = qasm.split("\n") - qasm_lines = list(filter(lambda x: x not in ["", "{}", " "], qasm_lines)) - qasm = "\n".join(qasm_lines).replace("\n\n", "\n") - - return qasm + "\n.do_measurement\nmeasure_all", iterations * qasm.count("\n"), qubit_count, data_qubits - - -def execute_sat_qasm(qi, qasm, shot_count, backend, qubit_count, data_qubits, plot): - """ - Execute the given QASM code and parse the results as though we are evaluating a SAT problem. - - Args: - qi: An instance of the Quantum Inspire API - qasm: The qasm program - shot_count: The number of shots to execute on the circuit - backend: An instance a QI API backend - qubit_count: The total number of qubits used in the qasm program - data_qubits: The number of qubits used by Grover's Algorithm (aka non-ancillary) - plot: Whether to plot the results of this run - - Returns: A tuple of the following values: - - histogram_list: a list of pairs, specifying a name and probability, as returned from QI - - likely_solutions: a list of bit strings, all of which seem to solve the given formula, according to grover - - runtime: The execution time on the QI backend - """ - - line_count = qasm.count("\n") - print("Executing QASM code ({} instructions, {} qubits, {} shots)".format(line_count, qubit_count, shot_count)) - result = qi.execute_qasm(qasm, backend_type=backend, number_of_shots=shot_count) - runtime = result["execution_time_in_seconds"] - print("Ran on simulator in {} seconds".format(str(runtime)[:5])) - - if qubit_count > 15: - print("No plot because of large qubit count") - histogram_list = interpret_results(result, qubit_count, data_qubits, False) - else: - histogram_list = interpret_results(result, qubit_count, data_qubits, plot) - - likely_solutions = [] - print("Interpreting SAT results:") - highest_prob = max(map(lambda _: _[1], histogram_list)) - for h in histogram_list: - # remove all ancillaries and the first data bit - name, prob = h[0][-data_qubits + 1:], h[1] - if prob > highest_prob / 2: - print("{} satisfies the SAT problem".format(name)) - likely_solutions.append(name) - - return histogram_list, likely_solutions, runtime diff --git a/docs/sat_example/src/sat_utilities.py b/docs/sat_example/src/sat_utilities.py deleted file mode 100644 index 6da8162..0000000 --- a/docs/sat_example/src/sat_utilities.py +++ /dev/null @@ -1,663 +0,0 @@ -import boolean -from boolean.boolean import AND, OR, NOT, Symbol -import random - -import matplotlib.pyplot as plt -import math - - -def apply(gate, qubit): - """ - Simply apply a gate to a single qubit - - Args: - gate: The gate to apply - qubit: The target qubit - - Returns: Valid QASM that represents this application - - """ - return "{} q[{}]\n".format(gate, qubit) - - -def fill(character, data_qubits): - """ - Apply a specific gate to all data qubits - Args: - character: The QASM gate to apply - data_qubits: The number of data qubits - - Returns: Valid QASM to append to the program - """ - # create a list of the qubit indices that need the gate applied - indices = ",".join(map(str, range(data_qubits))) - - return "{} q[{}]\n".format(character, indices) - - -def normal_n_size_cnot(n, mode): - """ - Generate a CNOT with n control bits. - It is assumed the control bits have indices [0:n-1], - and the target bit is at index [n]. - - Args: - n: The number of control bits - mode: The method by which we will make CNOT gates - - Returns: Valid QASM to append to the program - """ - - if n == 1: - local_qasm = "CNOT q[0],q[1]\n" - elif n == 2: - if mode == "no toffoli": - local_qasm = alternative_and(0, 1, 2) - else: - local_qasm = "Toffoli q[0],q[1],q[2]\n" - else: - # for n > 2, there is no direct instruction in QASM, so we must generate an equivalent circuit - # the core idea of a large CNOT is that we must AND-gate together all the control bits - # we do this with Toffoli gates, and store the result of each Toffoli on ancillary qubits - - # we keep a list of all the bits that should be AND-ed together - bits_to_and = list(range(n)) - ancillary_count = 0 - - # make a list of all the Toffoli gates to eventually write to the program - gate_list = [] - - # we will continue looping until all bits are and-ed together - while len(bits_to_and) > 0: - # take the first two - a, b = bits_to_and[:2] - # if these are the only two elements to AND, we're almost done! - # just combine these 2 in a Toffoli... - # ...which targets the global "target bit" of this n-CNOT - if len(bits_to_and) == 2: - target = n - bits_to_and = [] - - # the default case is to write the result to an ancillary bit - else: - target = n + 1 + ancillary_count - ancillary_count += 1 - # remove the used qubits from the list of bits to AND - bits_to_and = bits_to_and[2:] + [target] - - if mode == "no toffoli": - gate_list.append(alternative_and(a, b, target)) - else: - gate_list.append("Toffoli q[{}],q[{}],q[{}]".format(a, b, target)) - # gate_list.append("Toffoli q[{}],q[{}],q[{}]".format(a, b, target)) - - # Apply the complete list of gates in reverse after the target is flipped - # This undoes all operations on the ancillary qubits (so they remain 0) - gate_list = gate_list + gate_list[-2::-1] - local_qasm = "\n".join(gate_list) + "\n" - - return local_qasm - - -def n_size_crot(n, start_n, target, angle): - """ - Generate a controlled rotation with n control bits without using Toffoli gates. - It is assumed the control bits have indices [start_n : start_n + n-1]. - - Args: - n: The number of control bits - start_n: The first index that is a control bit - target: The target bit index - angle: The angle in radians by which to shift the phase - An angle of pi gives an H-Z-H aka X gate - An angle of pi/2 gives an H-S-H gate - An angle of pi/4 gives an H-T-H gate - Etc. - - Returns: Valid QASM to append to the program - """ - local_qasm = "" - - if n == 1: - # Simply a CROT with the given angle - local_qasm += apply("H", target) - local_qasm += "CR q[{}],q[{}],{}\n".format(start_n, target, angle) - local_qasm += apply("H", target) - else: - # V gate using the lowest control bit - local_qasm += n_size_crot(1, start_n + n - 1, target, angle / 2) - - # n-1 CNOT on highest bits (new_angle = angle) - local_qasm += n_size_crot(n - 1, 0, start_n + n - 1, math.pi) - - # V dagger gate on lowest two bits - local_qasm += n_size_crot(1, start_n + n - 1, target, -angle / 2) - - # n-1 CNOT on highest bits (new_angle = angle) - local_qasm += n_size_crot(n - 1, 0, start_n + n - 1, math.pi) - - # controlled V gate using highest as controls and lowest as target (new_angle = angle / 2) - local_qasm += n_size_crot(n - 1, 0, target, angle / 2) - - return local_qasm - - -def cnot_pillar(mode, data_qubits): - """ - Generate a common structure that applies a Hadamard, CNOT, and Hadamard again to the lowest data bit - - Returns: Valid QASM to append to the program - """ - local_qasm = "H q[{}]\n".format(data_qubits - 1) - if mode in ["normal", "no toffoli"]: - local_qasm += normal_n_size_cnot(data_qubits - 1, mode) - elif mode == "crot": - local_qasm += n_size_crot(data_qubits - 1, 0, data_qubits - 1, math.pi) - elif mode == "fancy cnot": - local_qasm += fancy_cnot(data_qubits - 1) - local_qasm += "H q[{}]\n".format(data_qubits - 1) - return local_qasm - - -def search_oracle(search_term, data_qubits): - """ - Generate a common structure that is used in the oracle circuit. - It flips all bits that correspond to a 0 in the search target. - Args: - search_term: The search term for which to generate an oracle - data_qubits: The number of data qubits - - Returns: Valid QASM to append to the program - """ - if "0" not in search_term: - return "\n" - - local_qasm = "X q[" - for i in range(data_qubits): - if search_term[i] == "0": - local_qasm += str(i) + "," - - # remove last comma and add closing bracket - local_qasm = local_qasm[:-1] + "]\n" - return local_qasm - - -def int_to_bits(int_str, qubit_count): - """ - Convert a number (possibly in string form) to a readable bit format. - For example, the result '11', which means both qubits were measured as 1, is returned by the API as "3". - This converts that output to the readable version. - - Args: - int_str: A string or integer in base 10 that represents the measurement outcome. - - Returns: A string of 0's and 1's - """ - # convert to an integer, then generate the binary string - # remove the "0b" prefix from the binary string - # then pad (using zfill) with 0's - return str(bin(int(int_str)))[2:].zfill(qubit_count) - - -def interpret_results(result_dict, qubit_count, data_qubits, plot=True): - """ - Parse the result dictionary given by the API into a readable format, and plot it. - - Args: - result_dict: The dictionary given by qi.execute_qasm - plot: Whether to plot the results in a bar chart - - Returns: Parsed result - """ - - num_of_measurements = 2 ** qubit_count - - # we still store the histogram bars in here, and later sort them in ascending order - ordered_bars = [None for _ in range(num_of_measurements)] - - for i in range(num_of_measurements): - # find result in dictionary and add to list of bars - # zero-valued bars don't show up in the dictionary, hence the try-except - try: - bar = result_dict["histogram"][str(i)] - except KeyError: - bar = 0 - - # generate corresponding binary name (so "11" instead of "3") - name = int_to_bits(i, qubit_count) - - ordered_bars[i] = (name, bar) - - if plot: - for b in ordered_bars: - # check if the given bar has 0's for all the ancillary qubits - # if it does not, we assume it is irrelevant for the histogram, so we don't plot it - if int(b[0], 2) < 2 ** data_qubits: - plt.bar(b[0][-data_qubits:], b[1]) - # if a result is returned where some ancillary qubits were not zero, we have a problem - elif b[1] != 0: - raise ValueError("\tNonzero result from 'impossible' measurement:\n" - "\tColumn {} has fraction {}. This means not all control bits were 0!".format(b[0], - b[1])) - - # set styling for the x-axis markers - plt.xticks(fontsize=6, rotation=45, ha="right") - plt.title("Measurements, discarding ancilla qubits") - plt.show() - - return ordered_bars - - -def gray_code(n): - """ - Generate a Gray code sequence of bit string with length n. - - Args: - n: The size for each element in the Gray code - - Returns: An array of strings forming a Gray code - """ - - if n == 1: - return ["0", "1"] - else: - g_previous = gray_code(n - 1) - mirrored_paste = g_previous + g_previous[::-1] - g_current = mirrored_paste[:] - for i in range(2 ** n): - if i < 2 ** (n - 1): - g_current[i] = g_current[i] + "0" - else: - g_current[i] = g_current[i] + "1" - return g_current - - -def fancy_cnot(n): - """ - Generate a circuit equivalent to an n-bit CNOT. - This avoids using Toffoli gates or ancillary qubits. - Args: - n: Number of control bits - - Returns: Valid QASM that represents a CNOT - - """ - gray_code_list = gray_code(n)[1:] - local_qasm = apply("H", n) - - for i in range(len(gray_code_list)): - if i == 0: - local_qasm += "CR q[0],q[{}],{}\n".format(n, math.pi / (2 ** (n - 1))) - else: - prev_gray = gray_code_list[i - 1] - cur_gray = gray_code_list[i] - - flip_idx = -1 - for j in range(len(cur_gray)): - if cur_gray[j] != prev_gray[j]: - flip_idx = j - break - - last_1_bit_cur = len(cur_gray) - 1 - cur_gray[::-1].index("1") - last_1_bit_prev = len(prev_gray) - 1 - prev_gray[::-1].index("1") - - bit_a = flip_idx - - if flip_idx == last_1_bit_cur: - bit_b = last_1_bit_prev - else: - bit_b = last_1_bit_cur - - control_bit = min(bit_a, bit_b) - target_bit = max(bit_a, bit_b) - - local_qasm += "CNOT q[{}],q[{}]\n".format(control_bit, target_bit) - - parity = cur_gray.count("1") % 2 - if parity == 0: - angle = -math.pi / (2 ** (n - 1)) - else: - angle = math.pi / (2 ** (n - 1)) - - local_qasm += "CR q[{}],q[{}],{}\n".format(target_bit, n, angle) - - local_qasm += apply("H", n) - return local_qasm - - -def alternative_and(control_1, control_2, target): - """ - Generate a circuit from 1 and 2 qubit gates that performs an operation equivalent to a Toffoli gate. - - Args: - control_1: First control bit index - control_2: Second control bit index - target: Target bit index - - Returns: Valid QASM that performs a CCNOT - """ - - if control_1 == control_2: - return "CNOT q[{}],q[{}]\n".format(control_1, target) - - local_qasm = "" - local_qasm += apply("H", target) - local_qasm += f"CNOT q[{control_2}],q[{target}]\n" - local_qasm += apply("Tdag", target) - local_qasm += f"CNOT q[{control_1}],q[{target}]\n" - local_qasm += apply("T", target) - local_qasm += f"CNOT q[{control_2}],q[{target}]\n" - local_qasm += apply("Tdag", target) - local_qasm += f"CNOT q[{control_1}],q[{target}]\n" - local_qasm += apply("T", control_2) - local_qasm += apply("T", target) - local_qasm += apply("H", target) - local_qasm += f"CNOT q[{control_1}],q[{control_2}]\n" - local_qasm += apply("T", control_1) - local_qasm += apply("Tdag", control_2) - local_qasm += f"CNOT q[{control_1}],q[{control_2}]\n" - - return local_qasm - - -def generate_sat_oracle_reuse_gates(expr: boolean.Expression, control_names, is_toplevel=False, mode='normal'): - """ - Generate the circuit needed for an oracle solving the SAT problem, given a boolean expression. - This uses a new ancillary qubit for every boolean gate, UNLESS that sub-expression has already been calculated before. - - Args: - expr: The boolean expression (instance of boolean.Expression, not a string) - control_names: The names of the control variables - is_toplevel: Whether this is the main call, or a recursive call - - Returns: A tuple of the following values: - - qasm: The QASM for this expression - - target_qubit: The qubit line on which the output of this expression is placed - """ - - global highest_qubit_used - global expressions_calculated - - if is_toplevel: - highest_qubit_used = len(control_names) - expressions_calculated = {} - local_qasm = "" - - # go through possible types - if type(expr) == AND or type(expr) == OR: - # left side - left_qasm, left_qubit, _ = generate_sat_oracle_reuse_gates(expr.args[0], control_names, mode=mode) - expressions_calculated[expr.args[0]] = left_qubit - right_qasm, right_qubit, _ = generate_sat_oracle_reuse_gates(expr.args[1], control_names, mode=mode) - expressions_calculated[expr.args[1]] = right_qubit - local_qasm += left_qasm - local_qasm += right_qasm - elif type(expr) == NOT: - inner_qasm, inner_qubit, _ = generate_sat_oracle_reuse_gates(expr.args[0], control_names, mode=mode) - local_qasm += inner_qasm - local_qasm += "X q[{}]\n".format(inner_qubit) - return local_qasm, inner_qubit, highest_qubit_used - elif type(expr) == Symbol: - # nothing to do here - return local_qasm, control_names.index(expr), highest_qubit_used - else: - raise ValueError("Unknown boolean expr type: {}".format(type(expr))) - - if expr in expressions_calculated: - already_calculated_index = expressions_calculated[expr] - # we don't need to add any qasm, just say where this expression can be found - return "", already_calculated_index, highest_qubit_used - - if is_toplevel: - target_qubit = len(control_names) - local_qasm += "H q[{}]\n".format(len(control_names)) - left_half_qasm = local_qasm[:] - else: - # we need another ancillary bit - highest_qubit_used += 1 - target_qubit = highest_qubit_used - - if type(expr) == AND: - if mode == 'normal': - local_qasm += generate_and(left_qubit, right_qubit, target_qubit) - else: - local_qasm += alternative_and(left_qubit, right_qubit, target_qubit) - elif type(expr) == OR: - if mode == 'normal': - local_qasm += generate_or(left_qubit, right_qubit, target_qubit) - else: - local_qasm += alternative_or(left_qubit, right_qubit, target_qubit) - - # undo NOT applications - if len(expr.args) == 2 and not is_toplevel: - if type(expr.args[0]) == NOT: - local_qasm += "X q[{}]\n".format(left_qubit) - if type(expr.args[1]) == NOT: - local_qasm += "X q[{}]\n".format(right_qubit) - - # indicate to other calls of this function that this expression has been generated already - expressions_calculated[expr] = target_qubit - - if is_toplevel: - local_qasm += "\n".join(left_half_qasm.split("\n")[::-1]) - return local_qasm, target_qubit, highest_qubit_used - - return local_qasm, target_qubit, highest_qubit_used - - -def generate_sat_oracle_reuse_qubits(expr, control_names, avoid, last_qubit=-1, is_toplevel=False, mode='normal'): - """ - Generate a SAT oracle that saves on ancillary qubits by resetting them, so that they can be reused. - - Args: - expr: The boolean expression to generate an oracle for - avoid: The ancillary lines that we can't use because they already contain data (default is an empty list) - control_names: The names of the variables in the expression (such as "a", "b" and "c") - last_qubit: The highest qubit index we have ever used. This is needed to calculate the total number of ancillaries - is_toplevel: Whether this function call is the "original". In that case, its output is a specific qubit line - - Returns: A tuple of the following values: - - target_line: The output line of the qasm representing the input expression - All other lines are guaranteed to be reset to 0 - - qasm: The QASM code - - last_qubit: The highest ancillary qubit index encountered in this expression - """ - - first_ancillary_bit = len(control_names) + 1 - - if len(expr.args) > 2: - raise ValueError("Fancy SAT Oracle expects only 1 and 2-argument expressions, but got {}".format(expr.args)) - - if type(expr) == Symbol: - return "", control_names.index(expr), last_qubit - elif type(expr) == NOT: - qubit_index = control_names.index(expr.args[0]) - return "X q[{}]".format(qubit_index), qubit_index, last_qubit - elif type(expr) == AND: - if mode == 'normal': - generate_func = generate_and - else: - generate_func = alternative_and - elif type(expr) == OR: - if mode == 'normal': - generate_func = generate_or - else: - generate_func = alternative_or - else: - raise ValueError("Unknown type in Boolean expression: {}".format(type(expr))) - - left_expr = expr.args[0] - right_expr = expr.args[1] - - left_qasm, left_target_qubit, left_last_qubit = generate_sat_oracle_reuse_qubits(left_expr, control_names, - avoid[:], - last_qubit, mode=mode) - avoid.append(left_target_qubit) - right_qasm, right_target_qubit, right_last_qubit = generate_sat_oracle_reuse_qubits(right_expr, control_names, - avoid[:], - last_qubit, mode=mode) - avoid.append(right_target_qubit) - - target_qubit = -1 - # if toplevel, we know what to target: the specific line that is set to the |1> state - if is_toplevel: - target_qubit = first_ancillary_bit - 1 - my_qasm = "H q[{}]\n".format(target_qubit) + \ - generate_func(left_target_qubit, right_target_qubit, target_qubit) + \ - "H q[{}]\n".format(target_qubit) - else: - # find the lowest line we can use - # if necessary, we can target an entirely new line (if all the others are used) - for i in range(first_ancillary_bit, first_ancillary_bit + max(avoid) + 1): - if i not in avoid: - target_qubit = i - break - my_qasm = generate_func(left_target_qubit, right_target_qubit, target_qubit) - - last_qubit = max(last_qubit, max(avoid), left_last_qubit, right_last_qubit, target_qubit) - - local_qasm = "\n".join([ - left_qasm, - right_qasm, - my_qasm, - *right_qasm.split("\n")[::-1], - *left_qasm.split("\n")[::-1] - ]) - - return local_qasm, target_qubit, last_qubit - - -def generate_and(qubit_1, qubit_2, target_qubit): - """ - Generate an AND in qasm code (just a Toffoli). - """ - if qubit_1 == qubit_2: - return "CNOT q[{}],q[{}]\n".format(qubit_1, target_qubit) - - return "Toffoli q[{}],q[{}],q[{}]\n".format(qubit_1, qubit_2, target_qubit) - - -def generate_or(qubit_1, qubit_2, target_qubit): - """ - Generate an OR in qasm code (Toffoli with X gates). - """ - if qubit_1 == qubit_2: - return "CNOT q[{}],q[{}]\n".format(qubit_1, target_qubit) - - local_qasm = "X q[{},{}]\n".format(qubit_1, qubit_2) - local_qasm += generate_and(qubit_1, qubit_2, target_qubit) - local_qasm += "X q[{},{},{}]\n".format(qubit_1, qubit_2, target_qubit) - return local_qasm - - -def alternative_or(qubit_1, qubit_2, target_qubit): - if qubit_1 == qubit_2: - return "CNOT q[{}],q[{}]\n".format(qubit_1, target_qubit) - - local_qasm = "X q[{},{}]\n".format(qubit_1, qubit_2) - local_qasm += alternative_and(qubit_1, qubit_2, target_qubit) - local_qasm += "X q[{},{},{}]\n".format(qubit_1, qubit_2, target_qubit) - return local_qasm - - -def split_expression_evenly(expr): - """ - Split a Boolean expression as evenly as possible into a binary tree. - - Args: - expr: The Boolean expression to split - - Returns: The same expression, where all gates are applied to exactly 2 elements - """ - - expr_type = type(expr) - if len(expr.args) > 2: - halfway = int(len(expr.args) / 2) - right_expanded = split_expression_evenly(expr_type(*expr.args[halfway:])) - - if len(expr.args) > 3: - left_expanded = split_expression_evenly(expr_type(*expr.args[:halfway])) - else: - left_expanded = split_expression_evenly(expr.args[0]) - - return expr_type(left_expanded, right_expanded) - elif len(expr.args) == 2: - return expr_type(split_expression_evenly(expr.args[0]), - split_expression_evenly(expr.args[1])) - else: - return expr - - -def generate_ksat_expression(n, m, k): - """ - Generate an arbitrary k-SAT expression according to the given parameters. - - Args: - n: The number of groups - m: The number of variables in a group - k: The number of variables - - Returns: A Boolean expression - """ - if m > k: - raise ValueError("m > k not possible for kSAT") - - alphabet = [] - for i in range(k): - alphabet.append(chr(97 + i)) - - expression = "" - - for i in range(n): - literals = random.sample(alphabet, m) - expression += " and ({}".format(literals[0]) - for l in literals[1:]: - if random.random() < 0.5: - expression += " or not({})".format(l) - else: - expression += " or {}".format(l) - expression += ")" - - return expression.lstrip("and ") - - -def swap_qubits(qasm, cnot_mode, apply_optimization, connected_qubit='2'): - """ - Implement swap gates to ensure all gates are between the connected_qubit and some other qubit. This is necesarry in - for example starmon-5 QPU backend, as only qubit 2 is connected to all other qubits. - For example, "CNOT q[1],q[4]" - Would become "SWAP q[1],q[2] - CNOT q[2],q[4] - SWAP q[1],q[2]" if connected_qubit='2' - - Args: - qasm: Valid QASM code - connected_qubit: The qubit that is connected to all other qubits. For starmon-5 this is the third qubit, so connected_qubit='2'. - - Returns: functionally equal QASM code that only has gates between connected qubits. - - This function should not be used on optimized code, or on code that contains three qubit gates. In practice, this - means you should only apply the function when using the mode cnot_mode='no toffoli' and apply_optimization=False - """ - if connected_qubit is None: - return qasm - - if cnot_mode != 'no toffoli' or apply_optimization: - raise ValueError( - "Invalid mode: can only use swap_qubits() in combination with cnot_mode='no toffoli' and apply_optimization=False") - - new_lines = [] - for line in qasm.split('\n'): - if ' ' in line: - args = line.split(' ')[1].split(',') - if len(args) > 1: - if args[0][0] == 'q' and args[1][0] == 'q': - q0 = args[0][2] - q1 = args[1][2] - if q0 != connected_qubit and q1 != connected_qubit: - to_swap = min([q0, q1]) - swap_arg = f'SWAP q[{to_swap}],q[{connected_qubit}]' - new_lines += [swap_arg, line.replace(to_swap, connected_qubit), swap_arg] - continue - new_lines += [line] - return '\n'.join(new_lines) diff --git a/setup.py b/setup.py index 381a37e..1fc6c88 100644 --- a/setup.py +++ b/setup.py @@ -47,39 +47,6 @@ def get_long_description(): author='QuantumInspire', python_requires='>=3.7', package_dir={'': 'src'}, - data_files=[('share/doc/quantuminspire/examples', - ['docs/examples/example_qiskit_entangle.py', - 'docs/examples/example_projectq_entangle.py', - 'docs/examples/example_projectq_grover.py', - 'docs/examples/example_projectq.ipynb', - 'docs/examples/grover_algorithm_qi.ipynb', - 'docs/examples/qi-performance-test.ipynb', - 'docs/examples/grover-qi.png', - 'docs/examples/classifier_example/data_plotter.py', - 'docs/examples/classifier_example/classification_example1_2_data_points.ipynb', - 'docs/examples/classifier_example/classification_example2_4_data_points.ipynb', - 'docs/examples/classifier_example/classification_example3_4_features.ipynb']), - ('share/doc/quantuminspire/examples/images', - ['docs/examples/classifier_example/images/4pointsclassifier.png', - 'docs/examples/classifier_example/images/experimentaltest.png', - 'docs/examples/classifier_example/images/full_circuit.png', - 'docs/examples/classifier_example/images/fullalgorithm.png', - 'docs/examples/classifier_example/images/partb.png', - 'docs/examples/classifier_example/images/partc.png', - 'docs/examples/classifier_example/images/partf.png', - 'docs/examples/classifier_example/images/plot.png', - 'docs/examples/classifier_example/images/randompoint.png', - 'docs/examples/classifier_example/images/stateprep.png']), - ('share/doc/quantuminspire/examples/images/images4features', - ['docs/examples/classifier_example/images/images4features/rotations.png']), - ('share/doc/quantuminspire/examples/images/images4points', - ['docs/examples/classifier_example/images/images4points/datapoints.png', - 'docs/examples/classifier_example/images/images4points/parta.png', - 'docs/examples/classifier_example/images/images4points/partb.png', - 'docs/examples/classifier_example/images/images4points/partc.png', - 'docs/examples/classifier_example/images/images4points/partd.png', - 'docs/examples/classifier_example/images/images4points/parte.png']) - ], classifiers=[ 'Development Status :: 4 - Beta', 'Programming Language :: Python :: 3', @@ -90,7 +57,7 @@ def get_long_description(): license='Apache 2.0', packages=['quantuminspire', 'quantuminspire.qiskit', 'quantuminspire.projectq'], install_requires=['coverage>=4.5.1', 'matplotlib>=2.1', 'pylatexenc', 'coreapi>=2.3.3', 'numpy>=1.17', 'jupyter', - 'nbimporter', 'sklearn', 'boolean.py'], + 'nbimporter', 'sklearn'], extras_require={ 'qiskit': ["qiskit>=0.20.0"], 'projectq': ["projectq>=0.4"], diff --git a/src/quantuminspire/projectq/backend_qx.py b/src/quantuminspire/projectq/backend_qx.py index a367d95..12a40c9 100644 --- a/src/quantuminspire/projectq/backend_qx.py +++ b/src/quantuminspire/projectq/backend_qx.py @@ -206,7 +206,7 @@ def _allocate_qubit(self, index_to_add: int) -> None: qubits are needed in total for the algorithm. A source of reusing qubits is when qubits are used as ancilla bits. Ancilla bits are used to downgrade complicated quantum gates into simple gates by placing controls on ancilla bits or when doing quantum error - correction. In projectQ, a qubit can be re-used when it is de-allocated after usage. ProjectQ sends an + correction. In ProjectQ, a qubit can be re-used when it is de-allocated after usage. ProjectQ sends an Allocate-gate for a qubit that is going to be used and a Deallocate gate for qubits that are not used anymore. :attr:`_allocation_map` is the store in which the administration diff --git a/src/tests/quantuminspire/test_credentials.py b/src/tests/quantuminspire/test_credentials.py index 438250b..bce34f8 100644 --- a/src/tests/quantuminspire/test_credentials.py +++ b/src/tests/quantuminspire/test_credentials.py @@ -169,7 +169,6 @@ def test_get_authentication_basic(self): auth_expected = BasicAuthentication(email, secret_password) self.assertEqual(auth, auth_expected) - # @skipUnless(sys.platform.startswith("win"), "getpass mocking fails on Linux") def test_get_authentication_basic_stdin(self): email = os.environ.get('QI_EMAIL', None) if email is not None: