mirror of https://github.com/intel/intel-qs.git
476 lines
15 KiB
Plaintext
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
|
|
}
|