{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Quantum tomography\n",
"\n",
"This example demonstrates how Lightworks can be used to be perform quantum state and process tomography on actual hardware."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import lightworks as lw\n",
"from lightworks import qubit, remote, tomography\n",
"\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"\n",
"try:\n",
" remote.token.load(\"main_token\")\n",
"except remote.TokenError:\n",
" print(\n",
" \"Token could not be automatically loaded, this will need to be \"\n",
" \"manually configured.\"\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## State tomography\n",
"\n",
"State tomography enables the density matrix of an output quantum state to be calculated.\n",
"\n",
"To perform State tomography, an experiment function needs to be defined which takes a list of circuits to run and returns a list of Results. It can optionally feature additional arguments which are used for generalisation of the functions. The experiment function should contain all logic required for the creation of tasks and execution on a backend, below this includes defining the input_state and post-selection, as well as compiling all tasks into a batch for execution. A batch is used as it enables all tasks to be submitted at the same time, meaning they are all placed in the queue, and as it simplifies management of job status and downloading of results. The batch job is also saved to file to ensure this can be recovered in the event the program is interrupted and a load data option is included to automatically load existing jobs. Once all jobs are completed, the results from these are returned in a list, which should match the order that the circuits were provided to the function."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def run_state_tomo(\n",
" experiments: list,\n",
" n_qubits: int,\n",
" job_name: str,\n",
" load_data: bool = False,\n",
") -> list:\n",
" \"\"\"\n",
" Experiment function which is required for performing state tomography on a\n",
" system. It takes a list of circuits and generates a corresponding list of\n",
" results for each of them.\n",
" \"\"\"\n",
" if not load_data:\n",
" # Post-select on 1 photon across each pair of qubit modes\n",
" post_select = lw.PostSelection()\n",
" for i in range(n_qubits):\n",
" post_select.add((2 * i, 2 * i + 1), 1)\n",
" # Start in all 0 state\n",
" input_state = lw.convert.qubit_to_dual_rail(\"0\" * n_qubits)\n",
" # Create a batch of jobs to submit\n",
" batch = lw.Batch(\n",
" lw.Sampler,\n",
" task_args=[experiments.all_circuits, [input_state], [100000]],\n",
" task_kwargs={\"post_selection\": [post_select]},\n",
" )\n",
" # Generate QPU backend and run batch\n",
" backend = remote.QPU(\"Artemis\")\n",
" jobs = backend.run(batch, job_name=job_name)\n",
" # Save in case these need to be recovered later\n",
" jobs.save_to_file(job_name, allow_overwrite=True)\n",
" else:\n",
" jobs = remote.load_job_from_file(job_name)\n",
" # Once jobs are complete then return results\n",
" jobs.wait_until_complete()\n",
" return list(jobs.get_all_results().values())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To start, we'll look at the $\\ket{+} = \\frac{1}{\\sqrt{2}}(\\ket{0}+\\ket{1})$ state, which can be generated by applying the hadamard gate to $\\ket{0}$. This circuit can be created using the built-in gate."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"h_gate = qubit.H()\n",
"\n",
"h_gate.display()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The tomography can then be performed with the StateTomography object from Lightworks. With this, the experiments are generated which can then be use with the run function earlier to automatically submit these and retrieve results.\n",
"\n",
".. note::\n",
" The load data option is omitted here. If you need to recover jobs which have already been submitted then this can be achieved by adding True to experiment_args, so the new value would be ``[n_qubits, \"plus_state_tomography\", True]``."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"n_qubits = 1\n",
"\n",
"tomo = tomography.StateTomography(\n",
" n_qubits,\n",
" h_gate,\n",
")\n",
"experiments = tomo.get_experiments()\n",
"\n",
"results = run_state_tomo(experiments, n_qubits, \"plus_state_tomography\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"These results are then passed to the process method, in this case we'll use the projection option to ensure the calculated density matrix is physical. "
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"rho = tomo.process(results, project_to_physical=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This can then be plotted against the expected density matrix. This is defined as $\\rho = \\ket{\\psi}\\bra{\\psi}$, so for the expected state $\\ket{+}$ we find:\n",
"\n",
"\\begin{equation}\\rho = \\ket{+}\\bra{+} = \\begin{bmatrix} 0.5 & 0.5\\\\ 0.5 & 0.5 \\end{bmatrix}\\end{equation}\n",
"\n",
"This can be calculated from Lightworks by providing the vector representation of the state to the density_from_state function. For $\\ket{+}$ the vector representation is $\\frac{1}{\\sqrt{2}}\\cdot\\begin{bmatrix} 1\\\\ 1 \\end{bmatrix}$ "
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"rho_exp = tomography.density_from_state([2**-0.5, 2**-0.5])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The plots can then be created."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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grhSXuIKldSo7d/KpFGy8TQhGeRenXvuuTFt2rSBHkbljRo1Kl74whd27r/iFa+IU089Nf793/+9CJhDhgzp1ett3749vvrVr8aKFStaHlNbW1s8+9nPjv/4j/8oPtikK/MA/U1cKSdxBfKjTJTDtnjx4uKX/9q1a7sdf/nLXx5Dhw6NH/zgB8X+ddddVwSF1atXx5vf/OYYO3Zs3Pe+942nP/3psWXLloNe97vf/W48+clPLpKoESNGxFlnnVWUrxzojjvuiJe+9KUxbty4GDZsWHG1+ZWvfGXs27evKEl6znOeUzzunHPO6SxFSmPp8N///d/x+Mc/vhjLscceG0996lPjJz/5yUHnufrqq4uSnOHDhxdfr7rqqsN639LrnHnmmXHXXXcVAbirT33qUzF16tSinPSEE06I5z3veQe9Rylg//Wvf41Zs2b1+Prf+MY34rTTTiv+DtJ7mO5D+eMf/3jQ4574xCfG7bffHjfddNNhfT8AfUVcaU0ax4IFC+Lzn/98TJo0qYghM2fOjB/96EfFn3/oQx8qZvzS+dKs5oFlruIK5MfMIPfqzjvvLMoaDww497vf/Yr/fstb3hJf+cpXisCZAk4KfNdee2185CMficsvvzwmT57c7bmpJDI9P91XkJKgdN9cCjwpaKTA1RFw0pXjlBB1fCj4+Mc/Hk94whPif//3f2P69OnF437zm98U/52CUfqQkGbaUhD/whe+EH/605/i7//+7+O1r31tvP/97y8+KKQglnR8/eQnPxlz586N2bNnxzvf+c7iOR/84AfjcY97XHz/+9/vLP9J908861nPKoJrKvX83e9+F/PmzStKPw9HCsTpvTjuuOO6vT9vfetb47nPfW687GUvK8pI0+xh+l7SmDoe++1vf7v4O3jQgx500Oum9+9JT3pSPOYxj4lXvepVccsttxTf16233tr54alDeo+T9IHojDPOOKzvB+BQiCv9F1fS9/LlL385Xv3qVxf76bWf9rSnFU1dUulmigl/+MMf4l3vele85CUvKd6XDuIKlWXR+ZbVGo1GRYbKkZaufqbA1JN0pfTuu+/u3P/xj39c/PK/4IILivsN0hXOBz7wgbFu3bo46qj91xxSsEhXUcePHx8/+9nPiuCepCuYKfF53/veVwTY9CP58Ic/PE4++eTi6mpHgPnzn/8cf/d3f1dc1UxB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",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Find plot range\n",
"vmin = min(\n",
" a.min()\n",
" for a in [np.real(rho), np.imag(rho), np.real(rho_exp), np.imag(rho_exp)]\n",
")\n",
"vmax = max(\n",
" a.max()\n",
" for a in [np.real(rho), np.imag(rho), np.real(rho_exp), np.imag(rho_exp)]\n",
")\n",
"\n",
"# Create figure\n",
"fig, ax = plt.subplots(2, 2, figsize=(12, 10))\n",
"ax[0, 0].imshow(np.real(rho), vmin=vmin, vmax=vmax)\n",
"ax[0, 0].set_title(\"Re(\\u03c1)\")\n",
"ax[0, 1].imshow(np.imag(rho), vmin=vmin, vmax=vmax)\n",
"ax[0, 1].set_title(\"Re(\\u03c1)\")\n",
"ax[1, 0].imshow(np.real(rho_exp), vmin=vmin, vmax=vmax)\n",
"ax[1, 0].set_title(\"Expected Re(\\u03c1)\")\n",
"im = ax[1, 1].imshow(np.imag(rho_exp), vmin=vmin, vmax=vmax)\n",
"ax[1, 1].set_title(\"Expected Im(\\u03c1)\")\n",
"fig.colorbar(im, ax=ax.ravel().tolist())\n",
"\n",
"# Set ticks as integer values and create state labels\n",
"ticks = range(rho.shape[0])\n",
"n_qubits = int(np.log2(len(ticks)))\n",
"basis = [\"0\", \"1\"]\n",
"labels = list(basis)\n",
"for _ in range(n_qubits - 1):\n",
" labels = [q1 + q2 for q1 in labels for q2 in basis]\n",
"for i in range(2):\n",
" for j in range(2):\n",
" ax[i, j].set_xticks(ticks, labels=labels)\n",
" ax[i, j].set_yticks(ticks, labels=labels)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It is also possible to evaluate the fidelity of state generation using state_fidelity."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fidelity = 97.9 %\n"
]
}
],
"source": [
"f = tomography.state_fidelity(rho, rho_exp)\n",
"print(f\"Fidelity = {round(f * 100, 2)} %\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Process tomography\n",
"\n",
"Process tomography can be used to gain an understanding of the actual operation implemented by a quantum processor.\n",
"\n",
"One form of process tomography is the calculation of average gate fidelity. To achieve this, the following equation can be used (Nielsen_2002):\n",
"\n",
"$\\begin{equation}\\overline{F}(\\mathcal{E}, U) = \\frac{\\sum_{jk}\\alpha_{jk}tr(UU_j^{\\dagger}U^{\\dagger}\\mathcal{E}(\\rho_k))+d^2}{d^2(d+1)}\\end{equation},$\n",
"\n",
"In the same way as with state tomography, an experiment function should be defined - this time accepting a list of circuits and a list of input states."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"def run_process_tomo(\n",
" experiments: list,\n",
" n_qubits: int,\n",
" job_name: str,\n",
" load_data: bool = False,\n",
") -> list:\n",
" \"\"\"\n",
" Experiment function which is required for performing process tomography on a\n",
" system. It takes a list of circuits and generates a corresponding list of\n",
" results for each of them.\n",
" \"\"\"\n",
" if not load_data:\n",
" # Post-select on 1 photon across each pair of qubit modes\n",
" post_select = lw.PostSelection()\n",
" for i in range(n_qubits):\n",
" post_select.add((2 * i, 2 * i + 1), 1)\n",
" # Create a batch of jobs to submit\n",
" batch = lw.Batch(\n",
" lw.Sampler,\n",
" task_args=[\n",
" experiments.all_circuits, experiments.all_inputs, [25000]\n",
" ],\n",
" task_kwargs={\"post_selection\": [post_select]},\n",
" )\n",
" # Generate QPU backend and run batch\n",
" backend = remote.QPU(\"Artemis\")\n",
" jobs = backend.run(batch, job_name=job_name)\n",
" # Save in case these need to be recovered later\n",
" jobs.save_to_file(job_name, allow_overwrite=True)\n",
" else:\n",
" jobs = remote.load_job_from_file(job_name)\n",
" # Once jobs are complete then return results\n",
" jobs.wait_until_complete()\n",
" return list(jobs.get_all_results().values())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Again, we'll look at the hadamard gate which is re-defined below."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"h_gate = qubit.H()\n",
"\n",
"h_gate.display()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As part of the gate fidelity calculation, the unitary for the expected process needs to be defined. For the Hadamard this is:\n",
"\n",
"\\begin{equation} \\text{U}_\\text{H} = \\frac{1}{\\sqrt{2}}\\begin{bmatrix} 1 & 1 \\\\ 1 & -1 \\end{bmatrix},\\end{equation}"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"target_process = 1 / 2**0.5 * np.array([[1, 1], [1, -1]])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The tomography can then run to compute the fidelity from the target system."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"n_qubits = 1\n",
"\n",
"tomo = tomography.GateFidelity(n_qubits, h_gate)\n",
"experiments = tomo.get_experiments()\n",
"\n",
"results = run_process_tomo(experiments, n_qubits, \"H_process_tomography\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The results are again processed, using a projection to ensure the result is physical."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Average gate fidelity = 97.8949410082671 %\n"
]
}
],
"source": [
"avg_fidelity = tomo.process(results, target_process, project_to_physical=True)\n",
"print(f\"Average gate fidelity = {avg_fidelity * 100} %\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Next steps\n",
"\n",
"It should now be possible for you to perform benchmarking of a range of quantum gates. The code above is intended to be mostly generalised, meaning it should be a simple process to switch to different target states/processes. "
]
}
],
"metadata": {
"kernelspec": {
"display_name": "venv",
"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.11.6"
}
},
"nbformat": 4,
"nbformat_minor": 2
}