{ "cells": [ { "cell_type": "markdown", "id": "05bddada-7a84-4269-8da9-f5672d459d35", "metadata": {}, "source": [ "# Compiling Circuits for EeroQ via Cirq" ] }, { "cell_type": "markdown", "id": "8ce4b770", "metadata": {}, "source": [ "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Infleqtion/client-superstaq/blob/main/docs/source/optimizations/eeroq/eeroq_css.ipynb) [![Launch Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/Infleqtion/client-superstaq/HEAD?labpath=docs/source/optimizations/eeroq/eeroq_css.ipynb)" ] }, { "cell_type": "markdown", "id": "694fee6f", "metadata": {}, "source": [ "Below is a brief tutorial on Superstaq compilation for EeroQ Quantum Hardware whose quantum computer uses electrons bound to superfluid helium. For more information on EeroQ, visit their website [here](https://eeroq.com/)." ] }, { "cell_type": "markdown", "id": "d06c9dcf-5ea8-4f3f-92b9-14444be41504", "metadata": {}, "source": [ "## Imports and API Token\n", "\n", "This example tutorial notebook uses `cirq-superstaq`, our Superstaq client for Cirq; you can try it out by running `pip install cirq-superstaq`:" ] }, { "cell_type": "code", "execution_count": 1, "id": "bb3fbbd3-986b-4457-8b87-e3cb42363a28", "metadata": {}, "outputs": [], "source": [ "# Required imports\n", "try:\n", " import cirq\n", " import cirq_superstaq as css\n", "except ImportError:\n", " print(\"Installing cirq-superstaq...\")\n", " %pip install --quiet 'cirq-superstaq[examples]'\n", " print(\"Installed cirq-superstaq.\")\n", " print(\"You may need to restart the kernel to import newly installed packages.\")\n", " import cirq\n", " import cirq_superstaq as css\n", "\n", "# Optional imports\n", "import numpy as np\n", "import os # Used if setting a token as an environment variable" ] }, { "cell_type": "markdown", "id": "dc3c6580-50e3-4edc-807b-e33dedbf8ca2", "metadata": {}, "source": [ "To interface Superstaq via Cirq, we must first instantiate a service provider in ```cirq-superstaq``` with ```Service()```. We then supply a Superstaq API key (which you can get from https://superstaq.infleqtion.com) by either providing the API key as an argument of Service, i.e., ```css.Service(api_key=\"token\")```, or by setting it as an environment variable. (see more details [here](https://superstaq.readthedocs.io/en/latest/get_started/basics/basics_css.html#Set-up-access-to-Superstaq%E2%80%99s-API))." ] }, { "cell_type": "code", "execution_count": 2, "id": "99b54393-f9f0-48c8-be31-033fd62d4624", "metadata": {}, "outputs": [], "source": [ "# Instantiate a cirq superstaq service\n", "service = css.Service()" ] }, { "cell_type": "markdown", "id": "275bcdda-0eef-41c0-b7ea-017accab90eb", "metadata": {}, "source": [ "## EeroQ Gates" ] }, { "cell_type": "markdown", "id": "b5f12edc", "metadata": {}, "source": [ "One of the native gates that EeroQ devices operate is the Dipole-Dipole (DD) gate, which couples two electrons. This gate can be applied to electrons within a qubit to perform rotation gates, or across qubits to perform entangling gates. The DD gate is available as a custom gate in ``cirq-superstaq``." ] }, { "cell_type": "code", "execution_count": 3, "id": "702504fe-5a52-425b-a52f-29973c7f94b3", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
0: ───DD───\n",
       "      │\n",
       "1: ───DD───
" ], "text/plain": [ "0: ───DD───\n", " │\n", "1: ───DD───" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dd_gate = css.DDPowGate(exponent=1)\n", "print(cirq.Circuit(dd_gate.on(cirq.q(0), cirq.q(1))))" ] }, { "cell_type": "code", "execution_count": 4, "id": "307cdd6c-d407-4b5d-bc2b-b3d52f1ca6ee", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0.-1.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", " [ 0.+0.j, 0.+0.j, -1.+0.j, 0.+0.j],\n", " [ 0.+0.j, -1.+0.j, 0.+0.j, 0.+0.j],\n", " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j]])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Unitary definition of the DD gate\n", "cirq.unitary(dd_gate)" ] }, { "cell_type": "markdown", "id": "26cd7f88-ab49-48bd-beae-ef6883d61cd0", "metadata": {}, "source": [ "Below is the EeroQ protocol for a CZ Gate." ] }, { "cell_type": "markdown", "id": "0bc3fd9e-8e18-45df-803f-733ca7cc5854", "metadata": {}, "source": [ "![title](images/DD_gate.png)" ] }, { "cell_type": "markdown", "id": "ec16efb2", "metadata": {}, "source": [ "## Single Circuit Compilation\n", "\n", "With that gateset, we can compile to the EeroQ Wonderlake device by calling the `compile()` method and setting the `target` argument to `\"eeroq_wonderlake_qpu\"`." ] }, { "cell_type": "code", "execution_count": 5, "id": "b89ef795-04c6-4193-9b23-a5fa4178640b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0+: ───────│──────────────────────────────\n", " │\n", "0-: ───X───│───DD────────S──────DD────────\n", " │ │ │\n", "1+: ───────│───┼─────────Z──────┼─────────\n", " │ │ │\n", "1-: ───X───│───DD^0.25───S^-1───DD^0.25───\n" ] } ], "source": [ "# Define the `cirq` circuit to compile\n", "circuit = cirq.Circuit(cirq.CZ(cirq.q(0), cirq.q(1)))\n", "\n", "# Compile to native gateset\n", "compiler_output = service.compile(circuit, target=\"eeroq_wonderlake_qpu\")\n", "\n", "# Call `.circuit` on the compiler output to get the corresponding output circuit\n", "compiled_circuit = compiler_output.circuit\n", "\n", "# Visualize the compiled circuit\n", "print(compiled_circuit)" ] }, { "cell_type": "markdown", "id": "4f40a7aa", "metadata": {}, "source": [ "To verify that the compiled circuit achieves the same unitary action, we can manually compute the corresponding unitary matrix and compare it to the unitary of the original `circuit`: " ] }, { "cell_type": "code", "execution_count": 6, "id": "8f899e26-f79b-487c-9376-44bfe464db37", "metadata": {}, "outputs": [], "source": [ "def compute_unitary(circuit: cirq.Circuit):\n", " \"\"\"Helper function to compute the n x n unitary of a 2n electron EeroQ circuit\"\"\"\n", " unitary = cirq.unitary(circuit[1:]).reshape((4,) * cirq.num_qubits(circuit))\n", " mat = unitary[tuple(slice(1, 3) for _ in range(cirq.num_qubits(circuit)))]\n", " dim = round(np.sqrt(mat.size))\n", " mat = mat.reshape(dim, dim)\n", " return mat" ] }, { "cell_type": "code", "execution_count": 7, "id": "c34c7c2d-076f-471c-acea-77371f8131f4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 1.-0.00000000e+00j, -0.+0.00000000e+00j, -0.+0.00000000e+00j,\n", " -0.+0.00000000e+00j],\n", " [-0.+0.00000000e+00j, 1.+7.85046229e-17j, -0.+0.00000000e+00j,\n", " -0.+0.00000000e+00j],\n", " [-0.+0.00000000e+00j, -0.+0.00000000e+00j, 1.+7.85046229e-17j,\n", " -0.+0.00000000e+00j],\n", " [-0.+0.00000000e+00j, -0.+0.00000000e+00j, -0.+0.00000000e+00j,\n", " -1.-0.00000000e+00j]])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mat = compute_unitary(compiled_circuit)\n", "mat / mat[0][0]" ] }, { "cell_type": "code", "execution_count": 8, "id": "eeb8dd38-daf6-483e-bc5c-ceb303859f50", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cirq.allclose_up_to_global_phase(cirq.unitary(circuit), mat)" ] }, { "cell_type": "markdown", "id": "2fac1577", "metadata": {}, "source": [ "As we can see, the initial circuit and compiled circuit have equivalent unitaries, despite the difference in circuit width." ] }, { "cell_type": "markdown", "id": "7cb64f54-69d8-417f-9b1d-38a5900e5b7f", "metadata": {}, "source": [ "## Multiple Circuit Compilation" ] }, { "cell_type": "markdown", "id": "c753d799", "metadata": {}, "source": [ "We can repeat the above experiment with larger circuits to see how they compile. Instead of compiling a single circuit at a time, we can also compile a list of circuits in one-go. To illustrate this, let us create two circuits: a Bell circuit, and another randomly generated circuit given a `gate_domain`: " ] }, { "cell_type": "code", "execution_count": 9, "id": "eb5ed3ae-3ec6-4c10-b48c-db25019a882f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0: ───H───@───\n", " │\n", "1: ───────X───\n" ] } ], "source": [ "qubits = cirq.LineQubit.range(2)\n", "bell_circuit = cirq.Circuit(cirq.H(qubits[0]), cirq.CNOT(qubits[0], qubits[1]))\n", "print(bell_circuit)" ] }, { "cell_type": "code", "execution_count": 10, "id": "f66099d2-05b0-4361-bbdf-7ded20127d0f", "metadata": {}, "outputs": [], "source": [ "# The gateset to choose from\n", "gate_domain = {\n", " cirq.X: 1,\n", " cirq.Y: 1,\n", " cirq.Z: 1,\n", " cirq.S: 1,\n", " cirq.T: 1,\n", " cirq.H: 1,\n", " cirq.rx(1.23): 1,\n", " cirq.ry(2.34): 1,\n", " cirq.CZ: 2,\n", " cirq.CX: 2,\n", " cirq.CX**0.5: 2,\n", " cirq.SWAP: 2,\n", " cirq.ISWAP: 2,\n", " css.ZZSwapGate(1.23): 2,\n", " css.Barrier(3): 3,\n", "}" ] }, { "cell_type": "code", "execution_count": 11, "id": "4ba055bd-13ed-43cd-96d4-f3b51edbe6ff", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0: ───X───────────H───Ry(0.745π)────────────────────────iSwap───\n", " │ │\n", "1: ───┼───iSwap────────────────────Rx(0.392π)───Y───────┼───────\n", " │ │ │\n", "2: ───┼───iSwap────────────────────H────────────────×───┼───────\n", " │ │ │\n", "3: ───@─────────────────────────────────────────────×───iSwap───\n" ] } ], "source": [ "n, depth, op_density = (4, 8, 0.8)\n", "qubits = cirq.LineQubit.range(n)\n", "rand_circuit = cirq.testing.random_circuit(qubits, depth, op_density, gate_domain=gate_domain)\n", "print(rand_circuit)" ] }, { "cell_type": "code", "execution_count": 12, "id": "2ffdfa4d-562e-4463-80f5-23db7926abf2", "metadata": {}, "outputs": [], "source": [ "# Barriers inserted for visualization\n", "rand_circuit.insert(depth // 2, css.barrier(*qubits))\n", "\n", "# Pass in a list of circuits to `compile`\n", "compiled_outputs = service.compile([bell_circuit, rand_circuit], \"eeroq_wonderlake_qpu\")\n", "\n", "# To get the list of compiled circuits from the compiled outputs list, call `circuits` instead of just `circuit` which is called for a single circuit input\n", "compiled_circuits = compiled_outputs.circuits" ] }, { "cell_type": "markdown", "id": "51081c2b", "metadata": {}, "source": [ "Here's the compiled Bell circuit," ] }, { "cell_type": "code", "execution_count": 13, "id": "0a427564", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0+: ───────│───S^-1───│───DD────────S──────│─────────────Z────────────────│───DD────────Z───\n", " │ │ │ │ │ │\n", "0-: ───X───│──────────│───DD^-0.5──────────│───DD────────S──────DD────────│───DD────────────\n", " │ │ │ │ │ │\n", "1+: ───────│───S──────│───DD────────S^-1───│───┼─────────S^-1───┼─────────│───DD────────S───\n", " │ │ │ │ │ │ │ │\n", "1-: ───X───│──────────│───DD^-0.5──────────│───DD^0.25───S^-1───DD^0.25───│───DD^-0.5───────\n" ] } ], "source": [ "print(compiled_circuits[0])" ] }, { "cell_type": "markdown", "id": "d05993ff", "metadata": {}, "source": [ "And the compiled random circuit, fully expressed in the native gateset of EeroQ:" ] }, { "cell_type": "code", "execution_count": 14, "id": "94d10838", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " ┌──────────────┐ ┌──────────────┐\n", "0+: ───────│───S────────│───DD────────S^-1──────│──────────────────────S─────────────────────────│───DD──────────S^-1──────│─────────────Z──────────────────│──────────────────────│───Z^0.75─────│───DD──────────Z^-0.75────│────────────────────────────────────│─────────────────────────│──────────────────────────────────│─────────────────────────│──────────────────────────────────│──────────────────────────│─────────────T^-1─────────────────│───DD────────T────────│─────────────Z^0.75──────────────│───DD────────T^-1─────\n", " │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", "0-: ───X───│────────────│───DD^-0.5─────────────│────DD────────────────S───────DD────────────────│───DD^-0.745─────────────│────────────────────────────────│──────────────────────│──────────────│───DD^-0.5────────────────│────────────────────────────────────│─────────────────────────│──────────────────────────────────│─────────────────────────│──────────────────────────────────│──────────────────────────│───DD────────S──────────DD────────│───DD^-0.5────────────│───DD────────S─────────DD────────│───DD^-0.5────────────\n", " │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", "1+: ───────│───Z^0.75───│───DD────────Z^-0.75───│────┼─────────────────T^-1────┼─────────────────│───DD──────────T─────────│─────────────Z^0.75─────────────│───DD────────T^-1─────│───Z──────────│───DD──────────Z──────────│─────────────Z──────────────────────│─────────────────────────│──────────────────────────────────│─────────────────────────│──────────────────────────────────│──────────────────────────│───┼────────────────────┼─────────│──────────────────────│───┼───────────────────┼─────────│──────────────────────\n", " │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", "1-: ───X───│────────────│───DD^-0.5─────────────│────┼──────DD─────────S───────┼──────DD─────────│───DD^-0.5───────────────│───DD────────S────────DD────────│───DD^-0.5────────────│──────────────│───DD^-0.608──────────────│────────────────────────────────────│─────────────────────────│──────────────────────────────────│─────────────────────────│──────────────────────────────────│──────────────────────────│───┼────────────────────┼─────────│──────────────────────│───┼───────────────────┼─────────│──────────────────────\n", " │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", "2+: ───────│───T^-1─────│───DD────────T─────────│────┼──────┼──────────T^-1────┼──────┼──────────│───DD──────────Z^-0.75───│───┼─────────Z^0.75───┼─────────│───DD────────Z^0.75───│───Z^0.276────│───DD──────────Z^-0.276───│─────────────Z^(-15/16)─────────────│───DD────────Z^(15/16)───│─────────────Z^(1/16)─────────────│───DD────────Z^(-1/16)───│─────────────Z^0.229──────────────│───DD──────────Z^-0.229───│───┼─────────Z^-0.309───┼─────────│──────────────────────│───┼───────────────────┼─────────│──────────────────────\n", " │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", "2-: ───X───│────────────│───DD^-0.5─────────────│────┼──────DD^0.25────S^-1────┼──────DD^0.25────│───DD^-0.5───────────────│───DD^0.25───S^-1─────DD^0.25───│───DD^-0.5────────────│──────────────│───DD^-0.762──────────────│───DD────────S────────────DD────────│───DD^-0.5───────────────│───DD────────S──────────DD────────│───DD^-0.5───────────────│───DD────────S──────────DD────────│───DD^-0.321──────────────│───┼────────────────────┼─────────│──────────────────────│───┼───────────────────┼─────────│──────────────────────\n", " │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", "3+: ───────│────────────│───────────────────────│────┼─────────────────Z───────┼─────────────────│─────────────────────────│────────────────────────────────│──────────────────────│───Z^-0.724───│───DD──────────Z^0.724────│───┼─────────Z^(-15/16)───┼─────────│───DD────────Z^(-1/16)───│───┼─────────Z^(1/16)───┼─────────│───DD────────Z^(15/16)───│───┼─────────Z^-0.507───┼─────────│───DD──────────Z^-0.493───│───┼─────────Z^0.65─────┼─────────│───DD────────Z^0.35───│───┼─────────Z^-0.35───┼─────────│───DD────────Z^0.75───\n", " │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", "3-: ───X───│────────────│───────────────────────│────DD^0.25───────────S^-1────DD^0.25───────────│─────────────────────────│────────────────────────────────│──────────────────────│──────────────│───DD^-0.762──────────────│───DD^0.25───S^-1─────────DD^0.25───│───DD^-0.5───────────────│───DD^0.25───S^-1───────DD^0.25───│───DD^-0.5───────────────│───DD^0.25───S^-1───────DD^0.25───│───DD^-0.599──────────────│───DD^0.25───S^-1───────DD^0.25───│───DD^-0.5────────────│───DD^0.25───S^-1──────DD^0.25───│───DD^-0.5────────────\n", " └──────────────┘ └──────────────┘\n" ] } ], "source": [ "print(compiled_circuits[1])" ] }, { "cell_type": "markdown", "id": "6e53ee2f", "metadata": {}, "source": [ "As earlier, we can also double check the equivalence of these compiled circuits by comparing their respective unitaries:" ] }, { "cell_type": "code", "execution_count": 15, "id": "0f18dd0a-80f3-42ad-b686-44290aba2812", "metadata": {}, "outputs": [], "source": [ "for uncompiled_circuit, compiled_circuit in zip([bell_circuit, rand_circuit], compiled_circuits):\n", " mat = compute_unitary(compiled_circuit)\n", " cirq.testing.assert_allclose_up_to_global_phase(\n", " cirq.unitary(uncompiled_circuit), mat, atol=1e-8\n", " )" ] }, { "cell_type": "markdown", "id": "cd09e5d5-60f2-4361-b879-f2479df82e70", "metadata": {}, "source": [ "## Using the Superstaq Simulator\n", "\n", "Lastly, we will go over how to simulate a circuit to the EeroQ Wonderlake QPU. This feature is available to free trial users, and can be done by passing the `\"dry-run\"` method parameter when calling `create_job()` to instruct Superstaq to ideally sample the circuit. Let us generate a random circuit again to demonstrate: " ] }, { "cell_type": "code", "execution_count": 16, "id": "81f64cd2-7735-4a95-aada-1efa1a0da946", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0: ───ZZSwap(0.392π)───X───iSwap───M───\n", " │ │ │ │\n", "1: ───ZZSwap(0.392π)───@───iSwap───M───\n" ] } ], "source": [ "# Example random circuit\n", "n, depth, op_density = (2, 3, 0.8)\n", "qubits = cirq.LineQubit.range(n)\n", "circuit = cirq.testing.random_circuit(qubits, depth, op_density, gate_domain=gate_domain)\n", "circuit += cirq.measure(*qubits)\n", "print(circuit)" ] }, { "cell_type": "code", "execution_count": null, "id": "fd3b9157", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'00': 1000}\n" ] } ], "source": [ "# Specify EeroQ target to service\n", "job = service.create_job(\n", " circuit, repetitions=1000, target=\"eeroq_wonderlake_qpu\", method=\"dry-run\"\n", ") # Specify \"dry-run\" as the method to run an ideal Superstaq execution\n", "\n", "# Get the counts from the measurement\n", "print(job.counts(0))" ] }, { "cell_type": "markdown", "id": "68967736", "metadata": {}, "source": [ "We can additionally perform a noisy simulation of the circuit by setting the `method` argument to `\"noise-sim\"` and specifying an error rate." ] }, { "cell_type": "code", "execution_count": 18, "id": "9f4e257f-8d61-4578-a038-3713a5678fbf", "metadata": {}, "outputs": [], "source": [ "noisy_job = service.create_job(\n", " circuit, target=\"eeroq_wonderlake_qpu\", repetitions=1000, method=\"noise-sim\", error_rate=0.01\n", ")" ] }, { "cell_type": "code", "execution_count": 19, "id": "cf99f946-020d-4108-8aed-a0c3fdb65559", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'00': 914, '20': 8, '02': 18, '22': 3, '01': 17, '21': 15}" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Get the counts from the measurement\n", "noisy_job.counts(0)" ] }, { "cell_type": "markdown", "id": "e8ae78af-f701-40b3-ad82-9f494356152b", "metadata": {}, "source": [ "With the effect of noise, we no longer just measure $\\ket{00}$ like in the dry-run simulation. Note that a measurement value of 2 refers to an out-of-codespace error." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.9.16" } }, "nbformat": 4, "nbformat_minor": 5 }