intel-qs/docs/source/get_started_examples.ipynb

476 lines
15 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Getting started with Intel Quantum Simulator: Examples\n",
"Tutorial on the basic use of Intel QS through its Python interface: Two examples are provided.\n",
"\n",
"**NOTE:**\n",
"Currently, the Python implementation only allows for single-core execution and does not take advantages of the MPI protocol.\n",
"However the user can familiarize with the same functionalities available in the distributed implementation (only C++ at the moment) and the transition should be relatively straighforward since all methods maintain name and effect."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"----\n",
"## Import Intel QS library\n",
"Let's start by importing the Python library with the class and methods defined in the C++ implementation."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"hideCode": false,
"hidePrompt": false,
"scrolled": true,
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"# Import the Python library with the C++ class and methods of Intel Quantum Simulator.\n",
"# If the library is not contained in the same folder of this notebook, its path has to be added.\n",
"import sys\n",
"sys.path.insert(0, '../lib_python')\n",
"import intelqs as simulator\n",
"\n",
"# Import NumPy library with Intel specialization.\n",
"import numpy as np\n",
"from numpy import random_intel\n",
"\n",
"# Import graphical library for plots.\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example 1\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Create the state of a quantum register, having $N>3$ qubits.\n",
"\n",
"The state is initialized as a computational basis state (using the keyword \"base\") corresponding to the index 0.\n",
"\n",
"The index corresponds to a $N$-bit integer in decimal representation.\n",
"With $N$ qubits there are $2^N$ indices, from 0 to $2^{N-1}$."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# Allocate memory for the quantum register's state and initialize it to |000...0>.\n",
"num_qubits = 4;\n",
"if num_qubits<3:\n",
" num_qubits = 4;\n",
"psi1 = simulator.QubitRegister(num_qubits, \"base\", 0, 0);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let us apply a X Pauli gate on qubit 0, effectively flipping it from |0> to |1>, followed by the Hadamard gate on all other qubits."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# Let us apply a X Pauli gate on qubit 0, effectively flipping it from |0> to |1>.\n",
"psi1.ApplyPauliX(0);\n",
"\n",
"# Let us apply an Hadamard gate on all other qubits.\n",
"for q in range(1,num_qubits):\n",
" psi1.ApplyHadamard(q);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In addition to one-qubit gates, universal quantum computation can be achieved via 2-qubit entangling gates.\n",
"For example, we now apply a CNOT between qubit 2 (here the control qubit) and qubit 1 (target qubit)."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# Two qubit gates are applied in a similar way. For example, a C-NOT between qubit 2 (control qubit) and qubit 1 (target qubit):\n",
"control = 2;\n",
"target = 1;\n",
"psi1.ApplyCPauliX( control , target );"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To extract information from the quantum register, one can obtain the probability of measuring a certain qubit in the computational basis and obtaining the outcome \"1\" (meaning that the state is in |1>). In this example we measure qubit 1.\n",
"Once the probability is known, one can draw a random number to simulate the stochastic outcome of the measurement and collapse the wavefunction accordingly."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"hidePrompt": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Probability that qubit 1, if measured, is in state |1> = 0.0\n",
"\n",
"Simulated outcome is 0. Collapse the function accordingly.\n"
]
}
],
"source": [
"# Compute the probability of qubit 1 being in state |1>.\n",
"measured_qubit = 1;\n",
"prob = psi1.GetProbability( measured_qubit );\n",
"\n",
"print(\"Probability that qubit {}, if measured, is in state |1> = {}\\n\".format(measured_qubit, prob));\n",
"\n",
"# Draw random number in [0,1)\n",
"r = np.random.rand()\n",
"if r < prob:\n",
" # Collapse the wavefunction according to qubit 1 being in |1>.\n",
" print(\"Simulated outcome is 1. Collapse the function accordingly.\")\n",
" psi1.CollapseQubit(measured_qubit,True);\n",
"else:\n",
" # Collapse the wavefunction according to qubit 1 being in |0>\n",
" print(\"Simulated outcome is 0. Collapse the function accordingly.\")\n",
" psi1.CollapseQubit(measured_qubit,False);\n",
"\n",
"# In both cases one needs to re-normalize the wavefunction:\n",
"psi1.Normalize();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example 2\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Create the state of a quantum register, having $N>3$ qubits.\n",
"\n",
"The state is initialized as a random state (using the keyword \"rand\"):\n",
"\n",
"This requires a random number generator (RNG), that we initialize just before the second register. Notice that '777' plays the role of the seed to initialize the RNG."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"num_qubits = 4;\n",
"if num_qubits<3:\n",
" num_qubits = 4;\n",
"psi2 = simulator.QubitRegister(num_qubits, \"rand\", 777, 0);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let us apply one- and two-qubit gates as in the previous exmaple."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"# Let us apply a X Pauli gate on qubit 0, effectively flipping it from |0> to |1>.\n",
"psi2.ApplyPauliX(0);\n",
"\n",
"# Let us apply an Hadamard gate on all other qubits.\n",
"for q in range(1,num_qubits):\n",
" psi2.ApplyHadamard(q);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"One can define an arbitrary single-qubit gate and apply it to the chosen qubit.\n",
"\n",
"In addition one can apply a custom one-qubit gate conditionally on the state of a control qubit."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<<the output has been redirected to the terminal>>\n"
]
}
],
"source": [
"# Define an arbitrary single qubit gate and apply it to the chosen qubit.\n",
"# The quantum gate G is given by a 2x2 unitary matrix, here using a bi-dimensional NumPy array.\n",
"G = np.zeros((2,2),dtype=np.complex_);\n",
"G[0,0] = 0.592056606032915 + 0.459533060553574j;\n",
"G[0,1] = -0.314948020757856 - 0.582328159830658j;\n",
"G[1,0] = 0.658235557641767 + 0.070882241549507j;\n",
"G[1,1] = 0.649564427121402 + 0.373855203932477j;\n",
"\n",
"qubit = 0;\n",
"psi2.Apply1QubitGate(qubit,G);\n",
"\n",
"# It is also possible to apply the arbitrary gate specified by G controlled on the state of another qubit.\n",
"# G is applied conditioned on the control qubit being in |1>.\n",
"control = 1;\n",
"target = 2;\n",
"psi2.ApplyControlled1QubitGate( control, target, G);\n",
"\n",
"# Notice that this output is directed to the terminal and not re-directed to the iPython notebook.\n",
"psi2.Print(\"After all gates.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To extract information from the quantum register, one can obtain the expectation value of Pauli strings.\n",
"\n",
"For example, consider the Pauli string given by: $$X_0 \\otimes id_1 \\otimes Z_2 \\otimes Y_3$$\n",
"\n",
"Such observable is defined by:\n",
"- the position of the non-trivial Pauli matrices, in this case {0,2,3}\n",
"- the corresponding Pauli matrices (X=1, Y=2, Z=3).\n",
"\n",
"To facilitate the verification of the expectation value, we reinitialize the quantum state to |+-01>.\n",
"\n",
"We also consider the Pauli staing $$X_0 \\otimes id_1 \\otimes Z_2 \\otimes Z_3$$."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Probability that qubit 0, if measured, is in state |1> = 0.0\n",
"\n",
"Probability that qubit 1, if measured, is in state |1> = 1.0\n",
"\n",
"Probability that qubit 2, if measured, is in state |1> = 0.0\n",
"\n",
"Probability that qubit 3, if measured, is in state |1> = 1.0\n",
"\n",
"Expectation value <+-01|X_0.id_1.Z_2.Y_3|+-01> = 0.0\n",
"\n",
"Expectation value <+-01|X_0.id_1.Z_2.Z_3|+-01> = -0.9999999999999989\n",
"\n",
"Expectation value <+-01|X_0|+-01> = 1.0\n",
"\n",
"Expectation value <+-01|X_0.Z_3|+-01> = -1.0000000000000004\n",
"\n"
]
}
],
"source": [
"# Prepare the state |+-01>\n",
"index = 2+8;\n",
"psi2.Initialize(\"base\",index);\n",
"# Notice that GetProbability() does not change the state.\n",
"for qubit in range(0,num_qubits):\n",
" prob = psi2.GetProbability( qubit );\n",
" print(\"Probability that qubit {}, if measured, is in state |1> = {}\\n\".format(qubit, prob));\n",
"\n",
"psi2.ApplyHadamard(0);\n",
"psi2.ApplyHadamard(1);\n",
"\n",
"# The Pauli string given by: X_0 . id_1 . Z_2 . Y_3\n",
"# Such observable is defined by the position of the non-trivial Pauli matrices:\n",
"qubits_to_be_measured = [0,2,3]\n",
"\n",
"# And by the corresponding Pauli matrices (X=1, Y=2, Z=3)\n",
"observables = [1,3,2]\n",
"\n",
"# The expectation value <psi2|X_0.id_1.Z_2.Y_3|psi2> is obtained via:\n",
"average = psi2.ExpectationValue(qubits_to_be_measured, observables, 1.);\n",
"print(\"Expectation value <+-01|X_0.id_1.Z_2.Y_3|+-01> = {}\\n\".format(average));\n",
"\n",
"# The expectation value <psi2|X_0.id_1.Z_2.Y_3|psi2> is obtained via:\n",
"qubits_to_be_measured = [0,2,3]\n",
"observables = [1,3,3]\n",
"average = psi2.ExpectationValue(qubits_to_be_measured, observables, 1.);\n",
"print(\"Expectation value <+-01|X_0.id_1.Z_2.Z_3|+-01> = {}\\n\".format(average));\n",
"\n",
"# Trivial expectation:\n",
"average = psi2.ExpectationValue([0],[1], 1.);\n",
"print(\"Expectation value <+-01|X_0|+-01> = {}\\n\".format(average));\n",
"\n",
"# The expectation value <psi2|X_0.id_1.id_2.Z_3|psi2> is obtained via:\n",
"average = psi2.ExpectationValue([0,3],[1,3], 1.);\n",
"print(\"Expectation value <+-01|X_0.Z_3|+-01> = {}\\n\".format(average));"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Probability that qubit 0, if measured, is in state |1> = 0.0\n",
"\n",
"Probability that qubit 1, if measured, is in state |1> = 1.0\n",
"\n",
"Probability that qubit 2, if measured, is in state |1> = 0.0\n",
"\n",
"Probability that qubit 3, if measured, is in state |1> = 1.0\n",
"\n",
"Expectation value <+-01|X_0.id_1.Z_2.Y_3|+-01> = 0.0\n",
"\n",
"Expectation value <+-01|X_0.id_1.Z_2.Z_3|+-01> = -0.9999999999999989\n",
"\n"
]
}
],
"source": [
"# Prepare the state |+-01>\n",
"index = 2+8;\n",
"psi2.Initialize(\"base\",index);\n",
"# Notice that GetProbability() does not change the state.\n",
"for qubit in range(0,num_qubits):\n",
" prob = psi2.GetProbability( qubit );\n",
" print(\"Probability that qubit {}, if measured, is in state |1> = {}\\n\".format(qubit, prob));\n",
"\n",
"psi2.ApplyHadamard(0);\n",
"psi2.ApplyHadamard(1);\n",
"\n",
"# The Pauli string given by: X_0 . id_1 . Z_2 . Y_3\n",
"# Such observable is defined by the position of the non-trivial Pauli matrices:\n",
"qubits_to_be_measured = [0,2,3]\n",
"\n",
"# And by the corresponding Pauli matrices (X=1, Y=2, Z=3)\n",
"observables = [1,3,2]\n",
"\n",
"# The expectation value <psi2|X_0.id_1.Z_2.Y_3|psi2> is obtained via:\n",
"average = psi2.ExpectationValue(qubits_to_be_measured, observables, 1.);\n",
"print(\"Expectation value <+-01|X_0.id_1.Z_2.Y_3|+-01> = {}\\n\".format(average));\n",
"\n",
"# The expectation value <psi2|X_0.id_1.Z_2.Y_3|psi2> is obtained via:\n",
"qubits_to_be_measured = [0,2,3]\n",
"observables = [1,3,3]\n",
"average = psi2.ExpectationValue(qubits_to_be_measured, observables, 1.);\n",
"print(\"Expectation value <+-01|X_0.id_1.Z_2.Z_3|+-01> = {}\\n\".format(average));"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Expectation value <+-01|X_0|+-01> = 1.0\n",
"\n",
"Expectation value <+-01|X_0.Z_3|+-01> = -1.0000000000000002\n",
"\n",
"Expectation value <+-01|X_0.Z_2|+-01> = 0.999999999999998\n",
"\n",
"Expectation value <+-01|X_1.Z_2|+-01> = -1.0000000000000002\n",
"\n"
]
}
],
"source": [
"# Extra expectation values.\n",
"\n",
"# Prepare the state |+-01>\n",
"index = 2+8;\n",
"psi2.Initialize(\"base\",index);\n",
"psi2.ApplyHadamard(0);\n",
"psi2.ApplyHadamard(1);\n",
"\n",
"# The expectation value of X_0:\n",
"average = psi2.ExpectationValue([0],[1], 1.);\n",
"print(\"Expectation value <+-01|X_0|+-01> = {}\\n\".format(average));\n",
"\n",
"# The expectation value of X_0.Z_3:\n",
"average = psi2.ExpectationValue([0,3],[1,3], 1.);\n",
"print(\"Expectation value <+-01|X_0.Z_3|+-01> = {}\\n\".format(average));\n",
"\n",
"# The expectation value of X_0.Z_2:\n",
"average = psi2.ExpectationValue([0,2],[1,3], 1.);\n",
"print(\"Expectation value <+-01|X_0.Z_2|+-01> = {}\\n\".format(average));\n",
"\n",
"# The expectation value of X_1.Z_2:\n",
"average = psi2.ExpectationValue([1,2],[1,3], 1.);\n",
"print(\"Expectation value <+-01|X_1.Z_2|+-01> = {}\\n\".format(average));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"----\n",
"### END\n",
"----"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}