{ "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": [ "[](https://colab.research.google.com/github/Infleqtion/client-superstaq/blob/main/docs/source/optimizations/eeroq/eeroq_css.ipynb) [](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": [
""
]
},
{
"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
}