Skip to content

Instantly share code, notes, and snippets.

@karalekas
Last active January 31, 2019 00:25
Show Gist options
  • Save karalekas/123d2bef774881bfed6ebb13f8181a17 to your computer and use it in GitHub Desktop.
Save karalekas/123d2bef774881bfed6ebb13f8181a17 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Active Qubit Reset on QCS"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this notebook, we will walk through how to use **Active Qubit Reset** to drastically decrease the amount of time it takes to run a job on the QPU. Although we use a toy example in this notebook, the principles here can be extended to rapidly iterate through real-world applications such as optimizing a variational quantum algorithm (as we will do in the **Max-Cut QAOA** notebook).\n",
"\n",
"**NOTE**: This notebook depends on `pyquil >= 2.3.0`, which comes preinstalled on all new QMIs."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import itertools\n",
"import time\n",
"from typing import List\n",
"\n",
"from pyquil import get_qc, Program\n",
"from pyquil.gates import CNOT, H, MEASURE, RESET"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Get Started\n",
"\n",
"Before running on the QPU, users must book a block of time on a lattice using the QCS command-line interface. To determine the lattices that are available for booking, you can run `qcs lattices` from within the QMI. For our [Aspen QPUs](https://www.rigetti.com/qpu), one of the currently available 8-qubit lattices is `Aspen-1-8Q-B`. Once your QPU time has started (which we call being \"QPU-engaged\"), you must then set up the `QuantumComputer` object associated with the booked lattice, which we do in the following cell.\n",
"\n",
"**NOTE**: When running this notebook, you will need to edit the `lattice` and `qubits` entries in the following cell to match whatever QPU lattice you end up booking. And remember that this code will only work from within the QMI!"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"All qubits on Aspen-1-8Q-B: [1, 10, 11, 13, 14, 15, 16, 17]\n",
"\n",
"Selected qubits: [15, 16, 17]\n"
]
}
],
"source": [
"lattice = 'Aspen-1-8Q-B' # edit as necessary\n",
"qpu = get_qc(lattice)\n",
"qubits = qpu.device.qubits()[-3:] # edit as necessary\n",
"print(f'All qubits on {lattice}: {qpu.device.qubits()}')\n",
"print(f'\\nSelected qubits: {qubits}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Build GHZ Program\n",
"\n",
"We begin by putting the first qubit in the superposition state |+⟩ by using the [Hadamard](https://en.wikipedia.org/wiki/Quantum_logic_gate) gate. Then, we produce our [Greenberger–Horne–Zeilinger](https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) (GHZ) state (which looks like |000⟩ + |111⟩ for 3 qubits), by entangling all the qubits successively using [Controlled-NOT](https://en.wikipedia.org/wiki/Controlled_NOT_gate) (`CNOT`) gates. As in the **Parametric Compilation** notebook, we also declare our readout memory \"ro\", and measure each qubit into a readout register."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def ghz_program(qubits: List[int]) -> Program:\n",
" program = Program()\n",
" program.inst(H(qubits[0]))\n",
" for i in range(len(qubits) - 1):\n",
" program.inst(CNOT(qubits[i], qubits[i + 1]))\n",
" ro = program.declare('ro', 'BIT', len(qubits))\n",
" program.inst([MEASURE(qubit, ro[idx]) for idx, qubit in enumerate(qubits)])\n",
" return program"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Enable Active Reset\n",
"\n",
"We create two GHZ state programs, one with an initial `RESET` command, and one without. The `RESET` directive enables active qubit reset for all the measured qubits in the program. We then set the number of shots to take for each Quil program, and compile each into instrument binaries."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"program = Program()\n",
"program.inst(ghz_program(qubits))\n",
"program.wrap_in_numshots_loop(10_000)\n",
"binary = qpu.compile(program)\n",
"\n",
"program_reset = Program(RESET())\n",
"program_reset.inst(ghz_program(qubits))\n",
"program_reset.wrap_in_numshots_loop(10_000)\n",
"binary_reset = qpu.compile(program_reset)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Compare Execution Time\n",
"\n",
"We run each binary on the QPU, comparing the total run time, to see a drastic speed increase with active qubit reset. For our Aspen-1 system, we are able to achieve an order of magnitude reduction in reset time between passive and active reset (~100μs vs. ~10μs). However, there are additional components beyond reset time that make up the total execution time, which is why we only see a ~5x overall improvement."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Execution time without active reset: 1.100 s\n",
"\n",
"Execution time with active reset: 0.208 s\n"
]
}
],
"source": [
"start = time.time()\n",
"results = qpu.run(binary)\n",
"total = time.time() - start\n",
"print(f'Execution time without active reset: {total:.3f} s')\n",
"\n",
"start_reset = time.time()\n",
"results_reset = qpu.run(binary_reset)\n",
"total_reset = time.time() - start_reset\n",
"print(f'\\nExecution time with active reset: {total_reset:.3f} s')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Compare Execution Quality\n",
"\n",
"We compare the bitstring counts to see that there is no material performance hit for using active qubit reset. As we see in both count dictionaries, the `000` and `111` bitstrings are the most prevalent, as expected. However, we can see that the bitstring counts aren't perfect, which we can attribute to gate infidelity and decoherence."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Measurement results without active reset:\n",
"(0, 0, 0) 4782\n",
"(0, 0, 1) 146\n",
"(0, 1, 0) 173\n",
"(0, 1, 1) 416\n",
"(1, 0, 0) 1090\n",
"(1, 0, 1) 266\n",
"(1, 1, 0) 519\n",
"(1, 1, 1) 2608\n",
"\n",
"Measurement results with active reset:\n",
"(0, 0, 0) 4697\n",
"(0, 0, 1) 169\n",
"(0, 1, 0) 190\n",
"(0, 1, 1) 467\n",
"(1, 0, 0) 1070\n",
"(1, 0, 1) 274\n",
"(1, 1, 0) 512\n",
"(1, 1, 1) 2621\n"
]
}
],
"source": [
"counts = {bit_tuple: 0 for bit_tuple in itertools.product((0, 1), repeat=3)}\n",
"for shot_result in results:\n",
" bit_tuple = tuple(shot_result)\n",
" counts[bit_tuple] += 1 \n",
"print(f'Measurement results without active reset:')\n",
"for bit_tuple, count in counts.items():\n",
" print(bit_tuple, count)\n",
"\n",
"counts_reset = {bit_tuple: 0 for bit_tuple in itertools.product((0, 1), repeat=3)}\n",
"for shot_result in results_reset:\n",
" bit_tuple = tuple(shot_result)\n",
" counts_reset[bit_tuple] += 1\n",
"print(f'\\nMeasurement results with active reset:')\n",
"for bit_tuple, count in counts_reset.items():\n",
" print(bit_tuple, count)"
]
}
],
"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.6.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment