From 9cde75f35760c6dcf0b9add055ce09f53af36f2f Mon Sep 17 00:00:00 2001 From: Chenyi Zhang Date: Fri, 19 Dec 2025 00:01:54 -0800 Subject: [PATCH 1/2] a draft for approximating multi-qubit toffoli gate --- .../basic_gates/multi_qubit_toffoli.ipynb | 3622 +++++++++++++++++ 1 file changed, 3622 insertions(+) create mode 100644 qualtran/bloqs/basic_gates/multi_qubit_toffoli.ipynb diff --git a/qualtran/bloqs/basic_gates/multi_qubit_toffoli.ipynb b/qualtran/bloqs/basic_gates/multi_qubit_toffoli.ipynb new file mode 100644 index 0000000000..8b748d3b7c --- /dev/null +++ b/qualtran/bloqs/basic_gates/multi_qubit_toffoli.ipynb @@ -0,0 +1,3622 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8e64607b-13fb-42b1-aa8f-f7103c8dc8d9", + "metadata": {}, + "source": [ + "In this note, we give 3 implementations of the $n$-qubit Toffoli gate, which contains $n-1$ controll qubits, grouped into a register named $x$, and a target qubit $t$." + ] + }, + { + "cell_type": "markdown", + "id": "10d0452e-f279-4844-bf33-bc099727787c", + "metadata": {}, + "source": [ + "We first implement the $n$-qubit Toffoli gate naively with depth $n$" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7b638d0e-f828-4a78-904b-08b91f5345e2", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "x_G53\n", + "x\n", + "\n", + "\n", + "\n", + "Split\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "x_G53:e->Split:w\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "t\n", + "t\n", + "\n", + "\n", + "\n", + "Toffoli_G17\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "t:e->Toffoli_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "a_G108\n", + "a\n", + "\n", + "\n", + "\n", + "Split_G0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "a_G108:e->Split_G0:w\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "Toffoli\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G2\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G2:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G5\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G8\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G11\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G11:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G14\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G2:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G11:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli:e->Toffoli_G2:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G35\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli:e->Toffoli_G35:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli:e->Toffoli_G35:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G2:e->Toffoli_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G32\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G2:e->Toffoli_G32:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G2:e->Toffoli_G32:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G5:e->Toffoli_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G29\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G5:e->Toffoli_G29:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G5:e->Toffoli_G29:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G8:e->Toffoli_G11:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G26\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G8:e->Toffoli_G26:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G8:e->Toffoli_G26:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G11:e->Toffoli_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G23\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G11:e->Toffoli_G23:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G11:e->Toffoli_G23:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G14:e->Toffoli_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G20\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G14:e->Toffoli_G20:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G14:e->Toffoli_G20:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G17:e->Toffoli_G20:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Toffoli_G17:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "t_G89\n", + "t\n", + "\n", + "\n", + "\n", + "Toffoli_G17:e->t_G89:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G20:e->Toffoli_G23:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G20:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G39\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Toffoli_G20:e->Join_G39:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G23:e->Toffoli_G26:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G23:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G23:e->Join_G39:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G26:e->Toffoli_G29:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G26:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G26:e->Join_G39:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G29:e->Toffoli_G32:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G29:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G29:e->Join_G39:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G32:e->Toffoli_G35:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G32:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G32:e->Join_G39:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G35:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G35:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G35:e->Join_G39:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x\n", + "x\n", + "\n", + "\n", + "\n", + "Join:e->x:w\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "a\n", + "a\n", + "\n", + "\n", + "\n", + "Join_G39:e->a:w\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from qualtran import BloqBuilder, QBit, QAny\n", + "from qualtran.bloqs.basic_gates import Toffoli\n", + "from qualtran.drawing import show_bloq\n", + "\n", + "\n", + "def n_qubit_toffoli(n: int):\n", + " assert n >= 3\n", + " k = n - 1 # number of controls\n", + "\n", + " bb = BloqBuilder()\n", + "\n", + " # Group all controls into one register x of length k\n", + " x = bb.add_register('x', QAny(k))\n", + " t = bb.add_register('t', QBit())\n", + "\n", + " # Split x into individual control bits\n", + " x_bits = list(bb.split(x))\n", + "\n", + " # Base case: ordinary Toffoli for n=3 (2 controls)\n", + " if k == 2:\n", + " (x_bits[0], x_bits[1]), t = bb.add(\n", + " Toffoli(), ctrl=[x_bits[0], x_bits[1]], target=t\n", + " )\n", + " x = bb.join(x_bits)\n", + " return bb.finalize(x=x, t=t)\n", + "\n", + " # For k >= 3, we need (k-2) = (n-3) clean ancillas, grouped into one register a.\n", + " a = bb.add_register('a', QAny(k - 2))\n", + " a_bits = list(bb.split(a))\n", + "\n", + " # Compute prefix ANDs into ancillas:\n", + " (x_bits[0], x_bits[1]), a_bits[0] = bb.add(\n", + " Toffoli(), ctrl=[x_bits[0], x_bits[1]], target=a_bits[0]\n", + " )\n", + " for i in range(1, k - 2):\n", + " (a_bits[i - 1], x_bits[i + 1]), a_bits[i] = bb.add(\n", + " Toffoli(), ctrl=[a_bits[i - 1], x_bits[i + 1]], target=a_bits[i]\n", + " )\n", + "\n", + " # Flip target using last ancilla and last control\n", + " (a_bits[k - 3], x_bits[k - 1]), t = bb.add(\n", + " Toffoli(), ctrl=[a_bits[k - 3], x_bits[k - 1]], target=t\n", + " )\n", + "\n", + " # Uncompute ancillas in reverse\n", + " for i in range(k - 3, 0, -1):\n", + " (a_bits[i - 1], x_bits[i + 1]), a_bits[i] = bb.add(\n", + " Toffoli(), ctrl=[a_bits[i - 1], x_bits[i + 1]], target=a_bits[i]\n", + " )\n", + " (x_bits[0], x_bits[1]), a_bits[0] = bb.add(\n", + " Toffoli(), ctrl=[x_bits[0], x_bits[1]], target=a_bits[0]\n", + " )\n", + "\n", + " # Re-pack grouped registers\n", + " x = bb.join(x_bits)\n", + " a = bb.join(a_bits)\n", + "\n", + " return bb.finalize(x=x, t=t, a=a)\n", + "\n", + "\n", + "# Example\n", + "prog = n_qubit_toffoli(9)\n", + "show_bloq(prog)\n" + ] + }, + { + "cell_type": "markdown", + "id": "c36e9695-a17b-472f-90c7-8f01c0d5ea55", + "metadata": {}, + "source": [ + "Next, we give an implementation with depth $\\log n$, and define it to be a bloq. Without loss of generality, we assume $n$ is a power of $2$. In the end, we provide an example of $n=9$" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "68917f9b-0b8f-45a3-ad22-79c90aebe375", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "x_G7\n", + "x\n", + "\n", + "\n", + "\n", + "NQToffoliLogDepth\n", + "\n", + "NQToffoliLogDepth\n", + "\n", + "x\n", + "\n", + "a\n", + "\n", + "t\n", + "\n", + "\n", + "\n", + "x_G7:e->NQToffoliLogDepth:w\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "a_G1\n", + "a\n", + "\n", + "\n", + "\n", + "a_G1:e->NQToffoliLogDepth:w\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "t_G0\n", + "t\n", + "\n", + "\n", + "\n", + "t_G0:e->NQToffoliLogDepth:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_G6\n", + "x\n", + "\n", + "\n", + "\n", + "NQToffoliLogDepth:e->x_G6:w\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "a_G4\n", + "a\n", + "\n", + "\n", + "\n", + "NQToffoliLogDepth:e->a_G4:w\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "t_G5\n", + "t\n", + "\n", + "\n", + "\n", + "NQToffoliLogDepth:e->t_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "x_G59\n", + "x\n", + "\n", + "\n", + "\n", + "Split\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "x_G59:e->Split:w\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "a_G119\n", + "a\n", + "\n", + "\n", + "\n", + "Split_G0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "a_G119:e->Split_G0:w\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "t\n", + "t\n", + "\n", + "\n", + "\n", + "CNOT\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "t:e->CNOT:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G2\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G2:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G2:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G5\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G8\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G2:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G11\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G11:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G14\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G17\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G0:e->Toffoli_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli:e->Toffoli_G11:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G31\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli:e->Toffoli_G31:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli:e->Toffoli_G31:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G2:e->Toffoli_G11:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G34\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G2:e->Toffoli_G34:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G2:e->Toffoli_G34:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G5:e->Toffoli_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G37\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G5:e->Toffoli_G37:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G5:e->Toffoli_G37:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G8:e->Toffoli_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G40\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G8:e->Toffoli_G40:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G8:e->Toffoli_G40:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G11:e->Toffoli_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G25\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G11:e->Toffoli_G25:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G11:e->Toffoli_G25:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G14:e->Toffoli_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G28\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G14:e->Toffoli_G28:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G14:e->Toffoli_G28:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G17:e->CNOT:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G22\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G17:e->Toffoli_G22:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G17:e->Toffoli_G22:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT:e->Toffoli_G22:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "t_G98\n", + "t\n", + "\n", + "\n", + "\n", + "CNOT:e->t_G98:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G22:e->Toffoli_G25:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G22:e->Toffoli_G28:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G44\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Toffoli_G22:e->Join_G44:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G25:e->Toffoli_G31:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G25:e->Toffoli_G34:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G25:e->Join_G44:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G28:e->Toffoli_G37:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G28:e->Toffoli_G40:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G28:e->Join_G44:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Toffoli_G31:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G31:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G31:e->Join_G44:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G34:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G34:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G34:e->Join_G44:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G37:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G37:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G37:e->Join_G44:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G40:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G40:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G40:e->Join_G44:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x\n", + "x\n", + "\n", + "\n", + "\n", + "Join:e->x:w\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "a\n", + "a\n", + "\n", + "\n", + "\n", + "Join_G44:e->a:w\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from dataclasses import dataclass\n", + "from qualtran import Bloq, BloqBuilder, Signature, Register, QBit, QAny\n", + "from qualtran.bloqs.basic_gates import Toffoli, CNOT\n", + "from qualtran.drawing import show_bloq\n", + "\n", + "\n", + "@dataclass(frozen=True)\n", + "class NQToffoliLogDepth(Bloq):\n", + " \"\"\"Log-depth (n-1)-controlled X using a balanced AND tree of Toffolis.\n", + "\n", + " Inputs/outputs:\n", + " - x : QAny(m) where m = n-1 (must be power of 2)\n", + " - a : QAny(m-1) ancilla bus (assumed clean)\n", + " - t : QBit target\n", + "\n", + " Implements: t ^= AND(x[0..m-1]) and uncomputes ancillas.\n", + " \"\"\"\n", + "\n", + " n: int\n", + "\n", + " @property\n", + " def m(self) -> int:\n", + " return self.n - 1\n", + "\n", + " @property\n", + " def signature(self) -> Signature:\n", + " m = self.m\n", + " if self.n < 3:\n", + " raise ValueError(\"Require n >= 3.\")\n", + " if (m & (m - 1)) != 0:\n", + " raise ValueError(\"Require m=n-1 to be a power of 2.\")\n", + " return Signature(\n", + " [\n", + " Register(\"x\", QAny(m)),\n", + " Register(\"a\", QAny(m - 1)),\n", + " Register(\"t\", QBit()),\n", + " ]\n", + " )\n", + "\n", + " def build_composite_bloq(self, bb: BloqBuilder, **soqs):\n", + " m = self.m\n", + "\n", + " # Incoming packed registers\n", + " x = soqs[\"x\"]\n", + " a = soqs[\"a\"]\n", + " t = soqs[\"t\"]\n", + "\n", + " # Split packed registers into per-qubit wires for internal logic\n", + " ctrls = list(bb.split(x))\n", + " a_bits = list(bb.split(a))\n", + " a_off = 0\n", + "\n", + " # Base case\n", + " if m == 2:\n", + " (ctrls[0], ctrls[1]), t = bb.add(\n", + " Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=t\n", + " )\n", + " x = bb.join(ctrls)\n", + " a = bb.join(a_bits)\n", + " return {\"x\": x, \"a\": a, \"t\": t}\n", + "\n", + " levels = [ctrls]\n", + " level_starts = [] # start index in a_bits for each level\n", + "\n", + " level = 0\n", + " while len(levels[level]) > 1:\n", + " prev = levels[level]\n", + " m_level = len(prev) // 2\n", + "\n", + " start = a_off\n", + " level_starts.append(start)\n", + " curr = a_bits[start : start + m_level]\n", + " a_off += m_level\n", + "\n", + " for j in range(m_level):\n", + " u = prev[2 * j]\n", + " v = prev[2 * j + 1]\n", + " a_j = curr[j]\n", + "\n", + " (u, v), a_j = bb.add(Toffoli(), ctrl=[u, v], target=a_j)\n", + "\n", + " prev[2 * j] = u\n", + " prev[2 * j + 1] = v\n", + " curr[j] = a_j\n", + " a_bits[start + j] = a_j # keep global pool updated\n", + "\n", + " levels.append(curr)\n", + " level += 1\n", + "\n", + " # Apply AND to target: replace old Toffoli(root, |1>) by CNOT(root)\n", + " root = levels[-1][0]\n", + " root, t = bb.add(CNOT(), ctrl=root, target=t)\n", + " levels[-1][0] = root\n", + "\n", + " # Uncompute the ancilla\n", + " for level in reversed(range(len(levels) - 1)):\n", + " prev = levels[level]\n", + " curr = levels[level + 1]\n", + " start = level_starts[level]\n", + " for j in range(len(curr)):\n", + " u = prev[2 * j]\n", + " v = prev[2 * j + 1]\n", + " a_j = curr[j]\n", + "\n", + " (u, v), a_j = bb.add(Toffoli(), ctrl=[u, v], target=a_j)\n", + "\n", + " prev[2 * j] = u\n", + " prev[2 * j + 1] = v\n", + " curr[j] = a_j\n", + " a_bits[start + j] = a_j # keep global pool updated\n", + "\n", + " # Re-pack to output registers\n", + " x = bb.join(levels[0])\n", + " a = bb.join(a_bits)\n", + " return {\"x\": x, \"a\": a, \"t\": t}\n", + "\n", + "\n", + "# Example\n", + "Toff9 = NQToffoliLogDepth(n=9)\n", + "show_bloq(Toff9)\n", + "show_bloq(Toff9.decompose_bloq())\n" + ] + }, + { + "cell_type": "markdown", + "id": "2e55e147-23a6-4fed-ac4e-d0993f418b70", + "metadata": {}, + "source": [ + "The recent work [GKZ25] gives an $\\epsilon$-approximate implementation of the $n$-qubit Toffoli gate using exponentially fewer $T$ gates. The construction is based on the following classical randomized algorithm that computes the OR function: Samples $k=O(\\log n)$ subsets of $[n-1]$, computes their parity values, and then applies a $k$-bit OR function together with additional Clifford operations. Implementing the above procedure in superpositions with proper negations yields an approximate implementation of the $n$-qubit Toffoli gate.\n", + "\n", + "Here, we assume the sampling procedure is done, and the $k$ subsets are given as $k$ classical $(n-1)$-bit strings $s_1,\\ldots,s_k$, where $[s_i]_j=1$ if and only if $j$ belows to the $i$-th subset. We first prepare the following block that computes $k$ $(n-1)$-bit strings $x_1,\\ldots,x_k$, where $[x_i]_j=x_j\\cdot [s_i]_j$ for any $i,j$." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "30094710-de73-49d1-bf42-e74d94f88818", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "x_G8\n", + "x\n", + "\n", + "\n", + "\n", + "SamplePreparationBloq\n", + "\n", + "SamplePreparationBloq\n", + "\n", + "x\n", + "\n", + "x_0\n", + "\n", + "x_1\n", + "\n", + "x_2\n", + "\n", + "\n", + "\n", + "x_G8:e->SamplePreparationBloq:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_0_G5\n", + "x_0\n", + "\n", + "\n", + "\n", + "x_0_G5:e->SamplePreparationBloq:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_1_G4\n", + "x_1\n", + "\n", + "\n", + "\n", + "x_1_G4:e->SamplePreparationBloq:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_2_G2\n", + "x_2\n", + "\n", + "\n", + "\n", + "x_2_G2:e->SamplePreparationBloq:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_G9\n", + "x\n", + "\n", + "\n", + "\n", + "SamplePreparationBloq:e->x_G9:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_0_G1\n", + "x_0\n", + "\n", + "\n", + "\n", + "SamplePreparationBloq:e->x_0_G1:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_1_G11\n", + "x_1\n", + "\n", + "\n", + "\n", + "SamplePreparationBloq:e->x_1_G11:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_2_G10\n", + "x_2\n", + "\n", + "\n", + "\n", + "SamplePreparationBloq:e->x_2_G10:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "x_G69\n", + "x\n", + "\n", + "\n", + "\n", + "Split\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "x_G69:e->Split:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_0_G76\n", + "x_0\n", + "\n", + "\n", + "\n", + "Split_G0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "x_0_G76:e->Split_G0:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_1\n", + "x_1\n", + "\n", + "\n", + "\n", + "Split_G6\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "x_1:e->Split_G6:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_2\n", + "x_2\n", + "\n", + "\n", + "\n", + "Split_G16\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "x_2:e->Split_G16:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "CNOT\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->CNOT:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G2\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->CNOT_G2:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G8\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->CNOT_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G24\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->CNOT_G24:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->CNOT:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->CNOT_G2:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Split_G0:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G0:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G18\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "CNOT:e->CNOT_G18:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G2:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G11\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "CNOT_G2:e->CNOT_G11:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_0\n", + "x_0\n", + "\n", + "\n", + "\n", + "Join:e->x_0:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "Split_G6:e->CNOT_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G6:e->CNOT_G11:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G14\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Split_G6:e->Join_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G6:e->Join_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G8:e->Join_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G21\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "CNOT_G8:e->CNOT_G21:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G11:e->Join_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G29\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT_G11:e->Join_G29:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_1_G88\n", + "x_1\n", + "\n", + "\n", + "\n", + "Join_G14:e->x_1_G88:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "Split_G16:e->CNOT_G18:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G16:e->CNOT_G21:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G16:e->CNOT_G24:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G27\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Split_G16:e->Join_G27:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G18:e->Join_G27:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G18:e->Join_G29:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G21:e->Join_G27:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G21:e->Join_G29:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G24:e->Join_G27:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G24:e->Join_G29:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_2_G85\n", + "x_2\n", + "\n", + "\n", + "\n", + "Join_G27:e->x_2_G85:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x\n", + "x\n", + "\n", + "\n", + "\n", + "Join_G29:e->x:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from dataclasses import dataclass\n", + "from typing import Tuple, Dict, Any as AnyType\n", + "\n", + "from qualtran import Bloq, BloqBuilder, Signature, Register, QAny\n", + "from qualtran.bloqs.basic_gates import CNOT\n", + "from qualtran.drawing import show_bloq\n", + "\n", + "\n", + "@dataclass(frozen=True)\n", + "class SamplePreparationBloq(Bloq):\n", + " n: int\n", + " k: int\n", + " # randomly sampled classical subsets: shape (k, n-1), entries are 0/1\n", + " sample_strings: Tuple[Tuple[int, ...], ...] # length k, each length n-1\n", + "\n", + " @property\n", + " def signature(self) -> Signature:\n", + " regs = [\n", + " # controls: length n-1 (same as ApproxToffoli.x)\n", + " Register(\"x\", QAny(self.n - 1)),\n", + " ]\n", + " # k output registers, each also length n-1 (same as ApproxToffoli.x_i)\n", + " regs += [Register(f\"x_{i}\", QAny(self.n - 1)) for i in range(self.k)]\n", + " return Signature(regs)\n", + "\n", + " def build_composite_bloq(self, bb: BloqBuilder, **soqs) -> Dict[str, AnyType]:\n", + " if len(self.sample_strings) != self.k:\n", + " raise ValueError(\"sample_strings must have length k.\")\n", + " for row in self.sample_strings:\n", + " if len(row) != self.n - 1:\n", + " raise ValueError(\"Each mask in sample_strings must have length n-1.\")\n", + "\n", + " # Input controls\n", + " x = soqs[\"x\"]\n", + " x_bits = list(bb.split(x))\n", + "\n", + " # Prepare each sampled subset into x_0, ..., x_{k-1}\n", + " out_regs = []\n", + " for i in range(self.k):\n", + " xi = soqs[f\"x_{i}\"]\n", + " xi_bits = list(bb.split(xi))\n", + "\n", + " for j in range(self.n - 1):\n", + " if self.sample_strings[i][j]:\n", + " # copy x_j into xi_j if sample_strings[i][j] == 1\n", + " x_bits[j], xi_bits[j] = bb.add(CNOT(), ctrl=x_bits[j], target=xi_bits[j])\n", + "\n", + " out_regs.append(bb.join(xi_bits))\n", + "\n", + " x = bb.join(x_bits)\n", + "\n", + " return {\n", + " \"x\": x,\n", + " **{f\"x_{i}\": out_regs[i] for i in range(self.k)},\n", + " }\n", + "\n", + "\n", + "# Example for this sample preparation subroutine with n=5, k=3\n", + "sample_strings = (\n", + " (1, 0, 1, 0),\n", + " (0, 1, 1, 0),\n", + " (1, 1, 0, 1),\n", + ")\n", + "\n", + "B = SamplePreparationBloq(n=5, k=3, sample_strings=sample_strings)\n", + "show_bloq(B)\n", + "show_bloq(B.decompose_bloq())\n" + ] + }, + { + "cell_type": "markdown", + "id": "f5ab1f58-9332-4521-af42-4167dfabce0b", + "metadata": {}, + "source": [ + "Using this sample preparation step as a subroutine, we obtain the circuit that approximates the $n$-qubit Toffoli gate." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "63c7d14d-5212-42e1-af97-f486c46514ea", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "x_G11\n", + "x\n", + "\n", + "\n", + "\n", + "ApproxToffoli\n", + "\n", + "ApproxToffoli\n", + "\n", + "x\n", + "\n", + "t\n", + "\n", + "x_0\n", + "\n", + "x_1\n", + "\n", + "OR_values\n", + "\n", + "ancilla\n", + "\n", + "\n", + "\n", + "x_G11:e->ApproxToffoli:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "t_G0\n", + "t\n", + "\n", + "\n", + "\n", + "t_G0:e->ApproxToffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_0_G5\n", + "x_0\n", + "\n", + "\n", + "\n", + "x_0_G5:e->ApproxToffoli:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "x_1_G16\n", + "x_1\n", + "\n", + "\n", + "\n", + "x_1_G16:e->ApproxToffoli:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "OR_values_G6\n", + "OR_values\n", + "\n", + "\n", + "\n", + "OR_values_G6:e->ApproxToffoli:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "ancilla_G4\n", + "ancilla\n", + "\n", + "\n", + "\n", + "ancilla_G4:e->ApproxToffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_G13\n", + "x\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->x_G13:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "t_G10\n", + "t\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->t_G10:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_0_G12\n", + "x_0\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->x_0_G12:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "x_1_G17\n", + "x_1\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->x_1_G17:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "OR_values_G1\n", + "OR_values\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->OR_values_G1:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "ancilla_G9\n", + "ancilla\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->ancilla_G9:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "x_G148\n", + "x\n", + "\n", + "\n", + "\n", + "Split_G0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "x_G148:e->Split_G0:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "t_G85\n", + "t\n", + "\n", + "\n", + "\n", + "NQToffoliLogDepth\n", + "\n", + "NQToffoliLogDepth\n", + "\n", + "x\n", + "\n", + "a\n", + "\n", + "t\n", + "\n", + "\n", + "\n", + "t_G85:e->NQToffoliLogDepth:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_0_G118\n", + "x_0\n", + "\n", + "\n", + "\n", + "SamplePreparationBloq\n", + "\n", + "SamplePreparationBloq\n", + "\n", + "x\n", + "\n", + "x_0\n", + "\n", + "x_1\n", + "\n", + "\n", + "\n", + "x_0_G118:e->SamplePreparationBloq:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "x_1_G151\n", + "x_1\n", + "\n", + "\n", + "\n", + "x_1_G151:e->SamplePreparationBloq:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "OR_values\n", + "OR_values\n", + "\n", + "\n", + "\n", + "Split\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "OR_values:e->Split:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "ancilla_G143\n", + "ancilla\n", + "\n", + "\n", + "\n", + "ancilla_G143:e->NQToffoliLogDepth:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->CNOT:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G14\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->CNOT_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Split_G0:e->XGate:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G2\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Split_G0:e->XGate_G2:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "XGate:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G2:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join:e->SamplePreparationBloq:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G5\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "SamplePreparationBloq:e->Split_G5:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G12\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "SamplePreparationBloq:e->Split_G12:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G57\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "SamplePreparationBloq:e->Split_G57:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G5:e->CNOT:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G7\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G5:e->CNOT_G7:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT:e->CNOT_G7:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G10\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT:e->Join_G10:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G7:e->Join_G10:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G22\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "CNOT_G7:e->XGate_G22:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G45\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Join_G10:e->Split_G45:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G12:e->CNOT_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G17\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G12:e->CNOT_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G14:e->CNOT_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G20\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT_G14:e->Join_G20:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G17:e->Join_G20:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G24\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "CNOT_G17:e->XGate_G24:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G35\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Join_G20:e->Split_G35:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Join_G26\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "XGate_G22:e->Join_G26:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G24:e->Join_G26:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G26:e->NQToffoliLogDepth:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G29\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "NQToffoliLogDepth:e->Split_G29:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "t_G131\n", + "t\n", + "\n", + "\n", + "\n", + "NQToffoliLogDepth:e->t_G131:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ancilla\n", + "ancilla\n", + "\n", + "\n", + "\n", + "NQToffoliLogDepth:e->ancilla:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G31\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Split_G29:e->XGate_G31:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G33\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Split_G29:e->XGate_G33:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G47\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "XGate_G31:e->CNOT_G47:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G37\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "XGate_G33:e->CNOT_G37:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G35:e->CNOT_G37:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G40\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G35:e->CNOT_G40:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G37:e->CNOT_G40:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G43\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT_G37:e->Join_G43:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G40:e->Join_G43:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G55\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT_G40:e->Join_G55:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_1_G86\n", + "x_1\n", + "\n", + "\n", + "\n", + "Join_G43:e->x_1_G86:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G45:e->CNOT_G47:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G50\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G45:e->CNOT_G50:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G47:e->CNOT_G50:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G53\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT_G47:e->Join_G53:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G50:e->Join_G53:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G50:e->Join_G55:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_0_G106\n", + "x_0\n", + "\n", + "\n", + "\n", + "Join_G53:e->x_0_G106:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "OR_values_G113\n", + "OR_values\n", + "\n", + "\n", + "\n", + "Join_G55:e->OR_values_G113:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "XGate_G59\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Split_G57:e->XGate_G59:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G61\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Split_G57:e->XGate_G61:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G63\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "XGate_G59:e->Join_G63:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G61:e->Join_G63:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_G77\n", + "x\n", + "\n", + "\n", + "\n", + "Join_G63:e->x_G77:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from dataclasses import dataclass\n", + "from typing import Tuple, Dict, Any as AnyType\n", + "\n", + "from qualtran import Bloq, BloqBuilder, Signature, Register, QAny, QBit\n", + "from qualtran.bloqs.basic_gates import XGate, CNOT\n", + "from qualtran.drawing import show_bloq\n", + "\n", + "\n", + "@dataclass(frozen=True)\n", + "class ApproxToffoli(Bloq):\n", + " n: int\n", + " k: int\n", + " sample_strings: Tuple[Tuple[int, ...], ...]\n", + "\n", + " @property\n", + " def signature(self) -> Signature:\n", + " regs = [\n", + " Register(\"x\", QAny(self.n - 1)),\n", + " Register(\"t\", QBit()),\n", + " ]\n", + " regs += [Register(f\"x_{i}\", QAny(self.n - 1)) for i in range(self.k)]\n", + " regs += [Register(\"OR_values\", QAny(self.k))]\n", + " regs += [Register(\"ancilla\", QAny(self.k - 1))] # << renamed here\n", + " return Signature(regs)\n", + "\n", + " def build_composite_bloq(self, bb: BloqBuilder, **soqs) -> Dict[str, AnyType]:\n", + " n, k = self.n, self.k\n", + "\n", + " x = soqs[\"x\"]\n", + " t = soqs[\"t\"]\n", + "\n", + " OR_pack = soqs[\"OR_values\"]\n", + " OR_regs = list(bb.split(OR_pack)) # length k, each is QBit\n", + "\n", + " # 1) Flip x\n", + " x_bits = list(bb.split(x))\n", + " for j in range(n - 1):\n", + " x_bits[j] = bb.add(XGate(), q=x_bits[j])\n", + " x = bb.join(x_bits)\n", + "\n", + " # 2) Sample prep\n", + " sp = SamplePreparationBloq(n=n, k=k, sample_strings=self.sample_strings)\n", + " sp_kwargs = {\"x\": x}\n", + " for i in range(k):\n", + " sp_kwargs[f\"x_{i}\"] = soqs[f\"x_{i}\"]\n", + " sp_out = bb.add(sp, **sp_kwargs)\n", + " x = sp_out[0]\n", + " x_regs = list(sp_out[1:])\n", + "\n", + " # 3) Parity compute\n", + " for i in range(k):\n", + " xi = x_regs[i]\n", + " oi = OR_regs[i]\n", + "\n", + " xi_bits = list(bb.split(xi))\n", + " for j in range(n - 1):\n", + " xi_bits[j], oi = bb.add(CNOT(), ctrl=xi_bits[j], target=oi)\n", + "\n", + " x_regs[i] = bb.join(xi_bits)\n", + " OR_regs[i] = oi\n", + "\n", + " # 4) k-control Toffoli\n", + " and_k = NQToffoliLogDepth(n=k + 1)\n", + " ancilla = soqs[\"ancilla\"] # << renamed here\n", + "\n", + " for i in range(k):\n", + " OR_regs[i] = bb.add(XGate(), q=OR_regs[i])\n", + "\n", + " OR_pack = bb.join(OR_regs)\n", + " OR_pack, ancilla, t = bb.add(and_k, x=OR_pack, a=ancilla, t=t) # << renamed here\n", + "\n", + " OR_regs = list(bb.split(OR_pack))\n", + " for i in range(k):\n", + " OR_regs[i] = bb.add(XGate(), q=OR_regs[i])\n", + "\n", + " # 5) Uncompute parity\n", + " for i in reversed(range(k)):\n", + " xi = x_regs[i]\n", + " oi = OR_regs[i]\n", + "\n", + " xi_bits = list(bb.split(xi))\n", + " for j in range(n - 1):\n", + " xi_bits[j], oi = bb.add(CNOT(), ctrl=xi_bits[j], target=oi)\n", + "\n", + " x_regs[i] = bb.join(xi_bits)\n", + " OR_regs[i] = oi\n", + "\n", + " OR_pack = bb.join(OR_regs)\n", + "\n", + " # 6) Unflip x\n", + " x_bits = list(bb.split(x))\n", + " for j in range(n - 1):\n", + " x_bits[j] = bb.add(XGate(), q=x_bits[j])\n", + " x = bb.join(x_bits)\n", + "\n", + " return {\n", + " \"x\": x,\n", + " \"t\": t,\n", + " \"OR_values\": OR_pack,\n", + " \"ancilla\": ancilla, # << renamed here\n", + " **{f\"x_{i}\": x_regs[i] for i in range(k)},\n", + " }\n", + "\n", + "\n", + "# Example: n = 3, k = 2\n", + "n = 3\n", + "k = 2\n", + "sample_strings = (\n", + " (1, 0),\n", + " (0, 1),\n", + ")\n", + "\n", + "B = ApproxToffoli(n=n, k=k, sample_strings=sample_strings)\n", + "show_bloq(B)\n", + "show_bloq(B.decompose_bloq())\n" + ] + }, + { + "cell_type": "markdown", + "id": "7aab3b41-d463-43b1-a935-35fae37c3331", + "metadata": {}, + "source": [ + "Finally, we test the correctness of the algorithm on classical inputs/outputs. In the example below, we set $n=17$ and $k=4$. We randomly sample $x$ and sample_strings every time. The expected error probability is $2^{-k}=1/16$." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7d44573e-9902-4b15-8edc-f26947445a97", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Single randomized test case\n", + " x (binary) = 1000100111111100\n", + " sample_strings = ((1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0), (0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1), (0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0), (0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0))\n", + " Approx t = 1\n", + " Ideal t = 0\n", + " MATCH = False\n" + ] + } + ], + "source": [ + "import random\n", + "\n", + "# n = 17 (16 controls), k = 4\n", + "n = 17\n", + "k = 4\n", + "m = n - 1\n", + "ALL_ONES = (1 << m) - 1\n", + "\n", + "def random_sample_string(width: int) -> tuple[int, ...]:\n", + " return tuple(random.randint(0, 1) for _ in range(width))\n", + "\n", + "def ideal_toffoli_t(x_int: int, t_in: int) -> int:\n", + " # Ideal 16-controlled Toffoli\n", + " return t_in ^ int(x_int == ALL_ONES)\n", + "\n", + "# ----------------------------\n", + "# Single randomized example\n", + "# ----------------------------\n", + "\n", + "# Randomly sample x\n", + "x_int = random.randint(0, ALL_ONES)\n", + "t_in = 0\n", + "\n", + "# Fresh random sample strings\n", + "sample_strings = tuple(random_sample_string(m) for _ in range(k))\n", + "\n", + "B = ApproxToffoli(n=n, k=k, sample_strings=sample_strings)\n", + "\n", + "out = B.call_classically(\n", + " x=x_int,\n", + " t=t_in,\n", + " x_0=0,\n", + " x_1=0,\n", + " x_2=0,\n", + " x_3=0,\n", + " OR_values=0,\n", + " ancilla=0,\n", + ")\n", + "\n", + "t_out = out[1]\n", + "t_expected = ideal_toffoli_t(x_int, t_in)\n", + "\n", + "print(\"Single randomized test case\")\n", + "print(f\" x (binary) = {x_int:016b}\")\n", + "print(f\" sample_strings = {sample_strings}\")\n", + "print(f\" Approx t = {t_out}\")\n", + "print(f\" Ideal t = {t_expected}\")\n", + "print(f\" MATCH = {t_out == t_expected}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "adc97d02-a180-4230-b311-3cf7ff4c12e3", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (qualtran311)", + "language": "python", + "name": "qualtran311" + }, + "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.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 45142b39a0ecab09be0c8dbe423ddb4b09b6723b Mon Sep 17 00:00:00 2001 From: Chenyi Zhang Date: Wed, 15 Apr 2026 22:07:59 -0700 Subject: [PATCH 2/2] notation_changes --- .../basic_gates/multi_qubit_toffoli.ipynb | 3622 ----------------- qualtran/bloqs/mcmt/multi_qubit_toffoli.ipynb | 2975 ++++++++++++++ 2 files changed, 2975 insertions(+), 3622 deletions(-) delete mode 100644 qualtran/bloqs/basic_gates/multi_qubit_toffoli.ipynb create mode 100644 qualtran/bloqs/mcmt/multi_qubit_toffoli.ipynb diff --git a/qualtran/bloqs/basic_gates/multi_qubit_toffoli.ipynb b/qualtran/bloqs/basic_gates/multi_qubit_toffoli.ipynb deleted file mode 100644 index 8b748d3b7c..0000000000 --- a/qualtran/bloqs/basic_gates/multi_qubit_toffoli.ipynb +++ /dev/null @@ -1,3622 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "8e64607b-13fb-42b1-aa8f-f7103c8dc8d9", - "metadata": {}, - "source": [ - "In this note, we give 3 implementations of the $n$-qubit Toffoli gate, which contains $n-1$ controll qubits, grouped into a register named $x$, and a target qubit $t$." - ] - }, - { - "cell_type": "markdown", - "id": "10d0452e-f279-4844-bf33-bc099727787c", - "metadata": {}, - "source": [ - "We first implement the $n$-qubit Toffoli gate naively with depth $n$" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "7b638d0e-f828-4a78-904b-08b91f5345e2", - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "my_graph\n", - "\n", - "\n", - "\n", - "x_G53\n", - "x\n", - "\n", - "\n", - "\n", - "Split\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "x_G53:e->Split:w\n", - "\n", - "\n", - "8\n", - "\n", - "\n", - "\n", - "t\n", - "t\n", - "\n", - "\n", - "\n", - "Toffoli_G17\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "t:e->Toffoli_G17:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "a_G108\n", - "a\n", - "\n", - "\n", - "\n", - "Split_G0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "a_G108:e->Split_G0:w\n", - "\n", - "\n", - "6\n", - "\n", - "\n", - "\n", - "Toffoli\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G2\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G2:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G5\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G5:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G8\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G8:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G11\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G11:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G14\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G17:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G2:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G5:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G8:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G11:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli:e->Toffoli_G2:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G35\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli:e->Toffoli_G35:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli:e->Toffoli_G35:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G2:e->Toffoli_G5:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G32\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G2:e->Toffoli_G32:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G2:e->Toffoli_G32:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G5:e->Toffoli_G8:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G29\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G5:e->Toffoli_G29:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G5:e->Toffoli_G29:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G8:e->Toffoli_G11:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G26\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G8:e->Toffoli_G26:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G8:e->Toffoli_G26:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G11:e->Toffoli_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G23\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G11:e->Toffoli_G23:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G11:e->Toffoli_G23:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G14:e->Toffoli_G17:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G20\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G14:e->Toffoli_G20:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G14:e->Toffoli_G20:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G17:e->Toffoli_G20:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Toffoli_G17:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "t_G89\n", - "t\n", - "\n", - "\n", - "\n", - "Toffoli_G17:e->t_G89:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G20:e->Toffoli_G23:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G20:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G39\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Toffoli_G20:e->Join_G39:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G23:e->Toffoli_G26:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G23:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G23:e->Join_G39:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G26:e->Toffoli_G29:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G26:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G26:e->Join_G39:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G29:e->Toffoli_G32:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G29:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G29:e->Join_G39:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G32:e->Toffoli_G35:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G32:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G32:e->Join_G39:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G35:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G35:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G35:e->Join_G39:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x\n", - "x\n", - "\n", - "\n", - "\n", - "Join:e->x:w\n", - "\n", - "\n", - "8\n", - "\n", - "\n", - "\n", - "a\n", - "a\n", - "\n", - "\n", - "\n", - "Join_G39:e->a:w\n", - "\n", - "\n", - "6\n", - "\n", - "\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from qualtran import BloqBuilder, QBit, QAny\n", - "from qualtran.bloqs.basic_gates import Toffoli\n", - "from qualtran.drawing import show_bloq\n", - "\n", - "\n", - "def n_qubit_toffoli(n: int):\n", - " assert n >= 3\n", - " k = n - 1 # number of controls\n", - "\n", - " bb = BloqBuilder()\n", - "\n", - " # Group all controls into one register x of length k\n", - " x = bb.add_register('x', QAny(k))\n", - " t = bb.add_register('t', QBit())\n", - "\n", - " # Split x into individual control bits\n", - " x_bits = list(bb.split(x))\n", - "\n", - " # Base case: ordinary Toffoli for n=3 (2 controls)\n", - " if k == 2:\n", - " (x_bits[0], x_bits[1]), t = bb.add(\n", - " Toffoli(), ctrl=[x_bits[0], x_bits[1]], target=t\n", - " )\n", - " x = bb.join(x_bits)\n", - " return bb.finalize(x=x, t=t)\n", - "\n", - " # For k >= 3, we need (k-2) = (n-3) clean ancillas, grouped into one register a.\n", - " a = bb.add_register('a', QAny(k - 2))\n", - " a_bits = list(bb.split(a))\n", - "\n", - " # Compute prefix ANDs into ancillas:\n", - " (x_bits[0], x_bits[1]), a_bits[0] = bb.add(\n", - " Toffoli(), ctrl=[x_bits[0], x_bits[1]], target=a_bits[0]\n", - " )\n", - " for i in range(1, k - 2):\n", - " (a_bits[i - 1], x_bits[i + 1]), a_bits[i] = bb.add(\n", - " Toffoli(), ctrl=[a_bits[i - 1], x_bits[i + 1]], target=a_bits[i]\n", - " )\n", - "\n", - " # Flip target using last ancilla and last control\n", - " (a_bits[k - 3], x_bits[k - 1]), t = bb.add(\n", - " Toffoli(), ctrl=[a_bits[k - 3], x_bits[k - 1]], target=t\n", - " )\n", - "\n", - " # Uncompute ancillas in reverse\n", - " for i in range(k - 3, 0, -1):\n", - " (a_bits[i - 1], x_bits[i + 1]), a_bits[i] = bb.add(\n", - " Toffoli(), ctrl=[a_bits[i - 1], x_bits[i + 1]], target=a_bits[i]\n", - " )\n", - " (x_bits[0], x_bits[1]), a_bits[0] = bb.add(\n", - " Toffoli(), ctrl=[x_bits[0], x_bits[1]], target=a_bits[0]\n", - " )\n", - "\n", - " # Re-pack grouped registers\n", - " x = bb.join(x_bits)\n", - " a = bb.join(a_bits)\n", - "\n", - " return bb.finalize(x=x, t=t, a=a)\n", - "\n", - "\n", - "# Example\n", - "prog = n_qubit_toffoli(9)\n", - "show_bloq(prog)\n" - ] - }, - { - "cell_type": "markdown", - "id": "c36e9695-a17b-472f-90c7-8f01c0d5ea55", - "metadata": {}, - "source": [ - "Next, we give an implementation with depth $\\log n$, and define it to be a bloq. Without loss of generality, we assume $n$ is a power of $2$. In the end, we provide an example of $n=9$" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "68917f9b-0b8f-45a3-ad22-79c90aebe375", - "metadata": {}, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "my_graph\n", - "\n", - "\n", - "\n", - "x_G7\n", - "x\n", - "\n", - "\n", - "\n", - "NQToffoliLogDepth\n", - "\n", - "NQToffoliLogDepth\n", - "\n", - "x\n", - "\n", - "a\n", - "\n", - "t\n", - "\n", - "\n", - "\n", - "x_G7:e->NQToffoliLogDepth:w\n", - "\n", - "\n", - "8\n", - "\n", - "\n", - "\n", - "a_G1\n", - "a\n", - "\n", - "\n", - "\n", - "a_G1:e->NQToffoliLogDepth:w\n", - "\n", - "\n", - "7\n", - "\n", - "\n", - "\n", - "t_G0\n", - "t\n", - "\n", - "\n", - "\n", - "t_G0:e->NQToffoliLogDepth:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_G6\n", - "x\n", - "\n", - "\n", - "\n", - "NQToffoliLogDepth:e->x_G6:w\n", - "\n", - "\n", - "8\n", - "\n", - "\n", - "\n", - "a_G4\n", - "a\n", - "\n", - "\n", - "\n", - "NQToffoliLogDepth:e->a_G4:w\n", - "\n", - "\n", - "7\n", - "\n", - "\n", - "\n", - "t_G5\n", - "t\n", - "\n", - "\n", - "\n", - "NQToffoliLogDepth:e->t_G5:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "my_graph\n", - "\n", - "\n", - "\n", - "x_G59\n", - "x\n", - "\n", - "\n", - "\n", - "Split\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "x_G59:e->Split:w\n", - "\n", - "\n", - "8\n", - "\n", - "\n", - "\n", - "a_G119\n", - "a\n", - "\n", - "\n", - "\n", - "Split_G0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "a_G119:e->Split_G0:w\n", - "\n", - "\n", - "7\n", - "\n", - "\n", - "\n", - "t\n", - "t\n", - "\n", - "\n", - "\n", - "CNOT\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "t:e->CNOT:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G2\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G2:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G2:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G5\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G5:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G5:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G8\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G8:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split:e->Toffoli_G8:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G2:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G5:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G8:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G11\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G11:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G14\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G17\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split_G0:e->Toffoli_G17:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli:e->Toffoli_G11:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G31\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli:e->Toffoli_G31:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli:e->Toffoli_G31:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G2:e->Toffoli_G11:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G34\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G2:e->Toffoli_G34:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G2:e->Toffoli_G34:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G5:e->Toffoli_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G37\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G5:e->Toffoli_G37:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G5:e->Toffoli_G37:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G8:e->Toffoli_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G40\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G8:e->Toffoli_G40:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G8:e->Toffoli_G40:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G11:e->Toffoli_G17:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G25\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G11:e->Toffoli_G25:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G11:e->Toffoli_G25:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G14:e->Toffoli_G17:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G28\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G14:e->Toffoli_G28:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G14:e->Toffoli_G28:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G17:e->CNOT:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G22\n", - "\n", - "Toffoli\n", - "\n", - "ctrl[0]\n", - "\n", - "ctrl[1]\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Toffoli_G17:e->Toffoli_G22:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G17:e->Toffoli_G22:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT:e->Toffoli_G22:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "t_G98\n", - "t\n", - "\n", - "\n", - "\n", - "CNOT:e->t_G98:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G22:e->Toffoli_G25:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G22:e->Toffoli_G28:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G44\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Toffoli_G22:e->Join_G44:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G25:e->Toffoli_G31:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G25:e->Toffoli_G34:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G25:e->Join_G44:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G28:e->Toffoli_G37:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G28:e->Toffoli_G40:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G28:e->Join_G44:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Toffoli_G31:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G31:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G31:e->Join_G44:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G34:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G34:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G34:e->Join_G44:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G37:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G37:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G37:e->Join_G44:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G40:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G40:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Toffoli_G40:e->Join_G44:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x\n", - "x\n", - "\n", - "\n", - "\n", - "Join:e->x:w\n", - "\n", - "\n", - "8\n", - "\n", - "\n", - "\n", - "a\n", - "a\n", - "\n", - "\n", - "\n", - "Join_G44:e->a:w\n", - "\n", - "\n", - "7\n", - "\n", - "\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from dataclasses import dataclass\n", - "from qualtran import Bloq, BloqBuilder, Signature, Register, QBit, QAny\n", - "from qualtran.bloqs.basic_gates import Toffoli, CNOT\n", - "from qualtran.drawing import show_bloq\n", - "\n", - "\n", - "@dataclass(frozen=True)\n", - "class NQToffoliLogDepth(Bloq):\n", - " \"\"\"Log-depth (n-1)-controlled X using a balanced AND tree of Toffolis.\n", - "\n", - " Inputs/outputs:\n", - " - x : QAny(m) where m = n-1 (must be power of 2)\n", - " - a : QAny(m-1) ancilla bus (assumed clean)\n", - " - t : QBit target\n", - "\n", - " Implements: t ^= AND(x[0..m-1]) and uncomputes ancillas.\n", - " \"\"\"\n", - "\n", - " n: int\n", - "\n", - " @property\n", - " def m(self) -> int:\n", - " return self.n - 1\n", - "\n", - " @property\n", - " def signature(self) -> Signature:\n", - " m = self.m\n", - " if self.n < 3:\n", - " raise ValueError(\"Require n >= 3.\")\n", - " if (m & (m - 1)) != 0:\n", - " raise ValueError(\"Require m=n-1 to be a power of 2.\")\n", - " return Signature(\n", - " [\n", - " Register(\"x\", QAny(m)),\n", - " Register(\"a\", QAny(m - 1)),\n", - " Register(\"t\", QBit()),\n", - " ]\n", - " )\n", - "\n", - " def build_composite_bloq(self, bb: BloqBuilder, **soqs):\n", - " m = self.m\n", - "\n", - " # Incoming packed registers\n", - " x = soqs[\"x\"]\n", - " a = soqs[\"a\"]\n", - " t = soqs[\"t\"]\n", - "\n", - " # Split packed registers into per-qubit wires for internal logic\n", - " ctrls = list(bb.split(x))\n", - " a_bits = list(bb.split(a))\n", - " a_off = 0\n", - "\n", - " # Base case\n", - " if m == 2:\n", - " (ctrls[0], ctrls[1]), t = bb.add(\n", - " Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=t\n", - " )\n", - " x = bb.join(ctrls)\n", - " a = bb.join(a_bits)\n", - " return {\"x\": x, \"a\": a, \"t\": t}\n", - "\n", - " levels = [ctrls]\n", - " level_starts = [] # start index in a_bits for each level\n", - "\n", - " level = 0\n", - " while len(levels[level]) > 1:\n", - " prev = levels[level]\n", - " m_level = len(prev) // 2\n", - "\n", - " start = a_off\n", - " level_starts.append(start)\n", - " curr = a_bits[start : start + m_level]\n", - " a_off += m_level\n", - "\n", - " for j in range(m_level):\n", - " u = prev[2 * j]\n", - " v = prev[2 * j + 1]\n", - " a_j = curr[j]\n", - "\n", - " (u, v), a_j = bb.add(Toffoli(), ctrl=[u, v], target=a_j)\n", - "\n", - " prev[2 * j] = u\n", - " prev[2 * j + 1] = v\n", - " curr[j] = a_j\n", - " a_bits[start + j] = a_j # keep global pool updated\n", - "\n", - " levels.append(curr)\n", - " level += 1\n", - "\n", - " # Apply AND to target: replace old Toffoli(root, |1>) by CNOT(root)\n", - " root = levels[-1][0]\n", - " root, t = bb.add(CNOT(), ctrl=root, target=t)\n", - " levels[-1][0] = root\n", - "\n", - " # Uncompute the ancilla\n", - " for level in reversed(range(len(levels) - 1)):\n", - " prev = levels[level]\n", - " curr = levels[level + 1]\n", - " start = level_starts[level]\n", - " for j in range(len(curr)):\n", - " u = prev[2 * j]\n", - " v = prev[2 * j + 1]\n", - " a_j = curr[j]\n", - "\n", - " (u, v), a_j = bb.add(Toffoli(), ctrl=[u, v], target=a_j)\n", - "\n", - " prev[2 * j] = u\n", - " prev[2 * j + 1] = v\n", - " curr[j] = a_j\n", - " a_bits[start + j] = a_j # keep global pool updated\n", - "\n", - " # Re-pack to output registers\n", - " x = bb.join(levels[0])\n", - " a = bb.join(a_bits)\n", - " return {\"x\": x, \"a\": a, \"t\": t}\n", - "\n", - "\n", - "# Example\n", - "Toff9 = NQToffoliLogDepth(n=9)\n", - "show_bloq(Toff9)\n", - "show_bloq(Toff9.decompose_bloq())\n" - ] - }, - { - "cell_type": "markdown", - "id": "2e55e147-23a6-4fed-ac4e-d0993f418b70", - "metadata": {}, - "source": [ - "The recent work [GKZ25] gives an $\\epsilon$-approximate implementation of the $n$-qubit Toffoli gate using exponentially fewer $T$ gates. The construction is based on the following classical randomized algorithm that computes the OR function: Samples $k=O(\\log n)$ subsets of $[n-1]$, computes their parity values, and then applies a $k$-bit OR function together with additional Clifford operations. Implementing the above procedure in superpositions with proper negations yields an approximate implementation of the $n$-qubit Toffoli gate.\n", - "\n", - "Here, we assume the sampling procedure is done, and the $k$ subsets are given as $k$ classical $(n-1)$-bit strings $s_1,\\ldots,s_k$, where $[s_i]_j=1$ if and only if $j$ belows to the $i$-th subset. We first prepare the following block that computes $k$ $(n-1)$-bit strings $x_1,\\ldots,x_k$, where $[x_i]_j=x_j\\cdot [s_i]_j$ for any $i,j$." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "30094710-de73-49d1-bf42-e74d94f88818", - "metadata": {}, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "my_graph\n", - "\n", - "\n", - "\n", - "x_G8\n", - "x\n", - "\n", - "\n", - "\n", - "SamplePreparationBloq\n", - "\n", - "SamplePreparationBloq\n", - "\n", - "x\n", - "\n", - "x_0\n", - "\n", - "x_1\n", - "\n", - "x_2\n", - "\n", - "\n", - "\n", - "x_G8:e->SamplePreparationBloq:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x_0_G5\n", - "x_0\n", - "\n", - "\n", - "\n", - "x_0_G5:e->SamplePreparationBloq:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x_1_G4\n", - "x_1\n", - "\n", - "\n", - "\n", - "x_1_G4:e->SamplePreparationBloq:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x_2_G2\n", - "x_2\n", - "\n", - "\n", - "\n", - "x_2_G2:e->SamplePreparationBloq:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x_G9\n", - "x\n", - "\n", - "\n", - "\n", - "SamplePreparationBloq:e->x_G9:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x_0_G1\n", - "x_0\n", - "\n", - "\n", - "\n", - "SamplePreparationBloq:e->x_0_G1:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x_1_G11\n", - "x_1\n", - "\n", - "\n", - "\n", - "SamplePreparationBloq:e->x_1_G11:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x_2_G10\n", - "x_2\n", - "\n", - "\n", - "\n", - "SamplePreparationBloq:e->x_2_G10:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "my_graph\n", - "\n", - "\n", - "\n", - "x_G69\n", - "x\n", - "\n", - "\n", - "\n", - "Split\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "x_G69:e->Split:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x_0_G76\n", - "x_0\n", - "\n", - "\n", - "\n", - "Split_G0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "x_0_G76:e->Split_G0:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x_1\n", - "x_1\n", - "\n", - "\n", - "\n", - "Split_G6\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "x_1:e->Split_G6:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x_2\n", - "x_2\n", - "\n", - "\n", - "\n", - "Split_G16\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "x_2:e->Split_G16:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "CNOT\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->CNOT:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G2\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->CNOT_G2:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G8\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->CNOT_G8:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G24\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->CNOT_G24:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->CNOT:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->CNOT_G2:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Split_G0:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G0:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G18\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "CNOT:e->CNOT_G18:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G2:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G11\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "CNOT_G2:e->CNOT_G11:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_0\n", - "x_0\n", - "\n", - "\n", - "\n", - "Join:e->x_0:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "Split_G6:e->CNOT_G8:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G6:e->CNOT_G11:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G14\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Split_G6:e->Join_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G6:e->Join_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G8:e->Join_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G21\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "CNOT_G8:e->CNOT_G21:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G11:e->Join_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G29\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "CNOT_G11:e->Join_G29:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_1_G88\n", - "x_1\n", - "\n", - "\n", - "\n", - "Join_G14:e->x_1_G88:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "Split_G16:e->CNOT_G18:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G16:e->CNOT_G21:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G16:e->CNOT_G24:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G27\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Split_G16:e->Join_G27:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G18:e->Join_G27:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G18:e->Join_G29:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G21:e->Join_G27:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G21:e->Join_G29:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G24:e->Join_G27:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G24:e->Join_G29:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_2_G85\n", - "x_2\n", - "\n", - "\n", - "\n", - "Join_G27:e->x_2_G85:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "x\n", - "x\n", - "\n", - "\n", - "\n", - "Join_G29:e->x:w\n", - "\n", - "\n", - "4\n", - "\n", - "\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from dataclasses import dataclass\n", - "from typing import Tuple, Dict, Any as AnyType\n", - "\n", - "from qualtran import Bloq, BloqBuilder, Signature, Register, QAny\n", - "from qualtran.bloqs.basic_gates import CNOT\n", - "from qualtran.drawing import show_bloq\n", - "\n", - "\n", - "@dataclass(frozen=True)\n", - "class SamplePreparationBloq(Bloq):\n", - " n: int\n", - " k: int\n", - " # randomly sampled classical subsets: shape (k, n-1), entries are 0/1\n", - " sample_strings: Tuple[Tuple[int, ...], ...] # length k, each length n-1\n", - "\n", - " @property\n", - " def signature(self) -> Signature:\n", - " regs = [\n", - " # controls: length n-1 (same as ApproxToffoli.x)\n", - " Register(\"x\", QAny(self.n - 1)),\n", - " ]\n", - " # k output registers, each also length n-1 (same as ApproxToffoli.x_i)\n", - " regs += [Register(f\"x_{i}\", QAny(self.n - 1)) for i in range(self.k)]\n", - " return Signature(regs)\n", - "\n", - " def build_composite_bloq(self, bb: BloqBuilder, **soqs) -> Dict[str, AnyType]:\n", - " if len(self.sample_strings) != self.k:\n", - " raise ValueError(\"sample_strings must have length k.\")\n", - " for row in self.sample_strings:\n", - " if len(row) != self.n - 1:\n", - " raise ValueError(\"Each mask in sample_strings must have length n-1.\")\n", - "\n", - " # Input controls\n", - " x = soqs[\"x\"]\n", - " x_bits = list(bb.split(x))\n", - "\n", - " # Prepare each sampled subset into x_0, ..., x_{k-1}\n", - " out_regs = []\n", - " for i in range(self.k):\n", - " xi = soqs[f\"x_{i}\"]\n", - " xi_bits = list(bb.split(xi))\n", - "\n", - " for j in range(self.n - 1):\n", - " if self.sample_strings[i][j]:\n", - " # copy x_j into xi_j if sample_strings[i][j] == 1\n", - " x_bits[j], xi_bits[j] = bb.add(CNOT(), ctrl=x_bits[j], target=xi_bits[j])\n", - "\n", - " out_regs.append(bb.join(xi_bits))\n", - "\n", - " x = bb.join(x_bits)\n", - "\n", - " return {\n", - " \"x\": x,\n", - " **{f\"x_{i}\": out_regs[i] for i in range(self.k)},\n", - " }\n", - "\n", - "\n", - "# Example for this sample preparation subroutine with n=5, k=3\n", - "sample_strings = (\n", - " (1, 0, 1, 0),\n", - " (0, 1, 1, 0),\n", - " (1, 1, 0, 1),\n", - ")\n", - "\n", - "B = SamplePreparationBloq(n=5, k=3, sample_strings=sample_strings)\n", - "show_bloq(B)\n", - "show_bloq(B.decompose_bloq())\n" - ] - }, - { - "cell_type": "markdown", - "id": "f5ab1f58-9332-4521-af42-4167dfabce0b", - "metadata": {}, - "source": [ - "Using this sample preparation step as a subroutine, we obtain the circuit that approximates the $n$-qubit Toffoli gate." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "63c7d14d-5212-42e1-af97-f486c46514ea", - "metadata": {}, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "my_graph\n", - "\n", - "\n", - "\n", - "x_G11\n", - "x\n", - "\n", - "\n", - "\n", - "ApproxToffoli\n", - "\n", - "ApproxToffoli\n", - "\n", - "x\n", - "\n", - "t\n", - "\n", - "x_0\n", - "\n", - "x_1\n", - "\n", - "OR_values\n", - "\n", - "ancilla\n", - "\n", - "\n", - "\n", - "x_G11:e->ApproxToffoli:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "t_G0\n", - "t\n", - "\n", - "\n", - "\n", - "t_G0:e->ApproxToffoli:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_0_G5\n", - "x_0\n", - "\n", - "\n", - "\n", - "x_0_G5:e->ApproxToffoli:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "x_1_G16\n", - "x_1\n", - "\n", - "\n", - "\n", - "x_1_G16:e->ApproxToffoli:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "OR_values_G6\n", - "OR_values\n", - "\n", - "\n", - "\n", - "OR_values_G6:e->ApproxToffoli:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "ancilla_G4\n", - "ancilla\n", - "\n", - "\n", - "\n", - "ancilla_G4:e->ApproxToffoli:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_G13\n", - "x\n", - "\n", - "\n", - "\n", - "ApproxToffoli:e->x_G13:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "t_G10\n", - "t\n", - "\n", - "\n", - "\n", - "ApproxToffoli:e->t_G10:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_0_G12\n", - "x_0\n", - "\n", - "\n", - "\n", - "ApproxToffoli:e->x_0_G12:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "x_1_G17\n", - "x_1\n", - "\n", - "\n", - "\n", - "ApproxToffoli:e->x_1_G17:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "OR_values_G1\n", - "OR_values\n", - "\n", - "\n", - "\n", - "ApproxToffoli:e->OR_values_G1:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "ancilla_G9\n", - "ancilla\n", - "\n", - "\n", - "\n", - "ApproxToffoli:e->ancilla_G9:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "my_graph\n", - "\n", - "\n", - "\n", - "x_G148\n", - "x\n", - "\n", - "\n", - "\n", - "Split_G0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "x_G148:e->Split_G0:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "t_G85\n", - "t\n", - "\n", - "\n", - "\n", - "NQToffoliLogDepth\n", - "\n", - "NQToffoliLogDepth\n", - "\n", - "x\n", - "\n", - "a\n", - "\n", - "t\n", - "\n", - "\n", - "\n", - "t_G85:e->NQToffoliLogDepth:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_0_G118\n", - "x_0\n", - "\n", - "\n", - "\n", - "SamplePreparationBloq\n", - "\n", - "SamplePreparationBloq\n", - "\n", - "x\n", - "\n", - "x_0\n", - "\n", - "x_1\n", - "\n", - "\n", - "\n", - "x_0_G118:e->SamplePreparationBloq:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "x_1_G151\n", - "x_1\n", - "\n", - "\n", - "\n", - "x_1_G151:e->SamplePreparationBloq:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "OR_values\n", - "OR_values\n", - "\n", - "\n", - "\n", - "Split\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "OR_values:e->Split:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "ancilla_G143\n", - "ancilla\n", - "\n", - "\n", - "\n", - "ancilla_G143:e->NQToffoliLogDepth:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->CNOT:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G14\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split:e->CNOT_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "XGate\n", - "\n", - "X\n", - "\n", - "q\n", - "\n", - "\n", - "\n", - "Split_G0:e->XGate:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "XGate_G2\n", - "\n", - "X\n", - "\n", - "q\n", - "\n", - "\n", - "\n", - "Split_G0:e->XGate_G2:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "XGate:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "XGate_G2:e->Join:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join:e->SamplePreparationBloq:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "Split_G5\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "SamplePreparationBloq:e->Split_G5:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "Split_G12\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "SamplePreparationBloq:e->Split_G12:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "Split_G57\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "SamplePreparationBloq:e->Split_G57:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "Split_G5:e->CNOT:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G7\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split_G5:e->CNOT_G7:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT:e->CNOT_G7:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G10\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "CNOT:e->Join_G10:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G7:e->Join_G10:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "XGate_G22\n", - "\n", - "X\n", - "\n", - "q\n", - "\n", - "\n", - "\n", - "CNOT_G7:e->XGate_G22:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G45\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Join_G10:e->Split_G45:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "Split_G12:e->CNOT_G14:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G17\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split_G12:e->CNOT_G17:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G14:e->CNOT_G17:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G20\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "CNOT_G14:e->Join_G20:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G17:e->Join_G20:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "XGate_G24\n", - "\n", - "X\n", - "\n", - "q\n", - "\n", - "\n", - "\n", - "CNOT_G17:e->XGate_G24:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G35\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Join_G20:e->Split_G35:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "Join_G26\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "XGate_G22:e->Join_G26:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "XGate_G24:e->Join_G26:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G26:e->NQToffoliLogDepth:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "Split_G29\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "NQToffoliLogDepth:e->Split_G29:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "t_G131\n", - "t\n", - "\n", - "\n", - "\n", - "NQToffoliLogDepth:e->t_G131:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "ancilla\n", - "ancilla\n", - "\n", - "\n", - "\n", - "NQToffoliLogDepth:e->ancilla:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "XGate_G31\n", - "\n", - "X\n", - "\n", - "q\n", - "\n", - "\n", - "\n", - "Split_G29:e->XGate_G31:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "XGate_G33\n", - "\n", - "X\n", - "\n", - "q\n", - "\n", - "\n", - "\n", - "Split_G29:e->XGate_G33:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G47\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "XGate_G31:e->CNOT_G47:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G37\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "XGate_G33:e->CNOT_G37:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Split_G35:e->CNOT_G37:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G40\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split_G35:e->CNOT_G40:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G37:e->CNOT_G40:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G43\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "CNOT_G37:e->Join_G43:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G40:e->Join_G43:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G55\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "CNOT_G40:e->Join_G55:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_1_G86\n", - "x_1\n", - "\n", - "\n", - "\n", - "Join_G43:e->x_1_G86:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "Split_G45:e->CNOT_G47:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G50\n", - "\n", - "CNOT\n", - "\n", - "ctrl\n", - "\n", - "target\n", - "\n", - "\n", - "\n", - "Split_G45:e->CNOT_G50:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G47:e->CNOT_G50:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G53\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "CNOT_G47:e->Join_G53:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G50:e->Join_G53:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "CNOT_G50:e->Join_G55:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_0_G106\n", - "x_0\n", - "\n", - "\n", - "\n", - "Join_G53:e->x_0_G106:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "OR_values_G113\n", - "OR_values\n", - "\n", - "\n", - "\n", - "Join_G55:e->OR_values_G113:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "XGate_G59\n", - "\n", - "X\n", - "\n", - "q\n", - "\n", - "\n", - "\n", - "Split_G57:e->XGate_G59:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "XGate_G61\n", - "\n", - "X\n", - "\n", - "q\n", - "\n", - "\n", - "\n", - "Split_G57:e->XGate_G61:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "Join_G63\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "XGate_G59:e->Join_G63:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "XGate_G61:e->Join_G63:w\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "x_G77\n", - "x\n", - "\n", - "\n", - "\n", - "Join_G63:e->x_G77:w\n", - "\n", - "\n", - "2\n", - "\n", - "\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from dataclasses import dataclass\n", - "from typing import Tuple, Dict, Any as AnyType\n", - "\n", - "from qualtran import Bloq, BloqBuilder, Signature, Register, QAny, QBit\n", - "from qualtran.bloqs.basic_gates import XGate, CNOT\n", - "from qualtran.drawing import show_bloq\n", - "\n", - "\n", - "@dataclass(frozen=True)\n", - "class ApproxToffoli(Bloq):\n", - " n: int\n", - " k: int\n", - " sample_strings: Tuple[Tuple[int, ...], ...]\n", - "\n", - " @property\n", - " def signature(self) -> Signature:\n", - " regs = [\n", - " Register(\"x\", QAny(self.n - 1)),\n", - " Register(\"t\", QBit()),\n", - " ]\n", - " regs += [Register(f\"x_{i}\", QAny(self.n - 1)) for i in range(self.k)]\n", - " regs += [Register(\"OR_values\", QAny(self.k))]\n", - " regs += [Register(\"ancilla\", QAny(self.k - 1))] # << renamed here\n", - " return Signature(regs)\n", - "\n", - " def build_composite_bloq(self, bb: BloqBuilder, **soqs) -> Dict[str, AnyType]:\n", - " n, k = self.n, self.k\n", - "\n", - " x = soqs[\"x\"]\n", - " t = soqs[\"t\"]\n", - "\n", - " OR_pack = soqs[\"OR_values\"]\n", - " OR_regs = list(bb.split(OR_pack)) # length k, each is QBit\n", - "\n", - " # 1) Flip x\n", - " x_bits = list(bb.split(x))\n", - " for j in range(n - 1):\n", - " x_bits[j] = bb.add(XGate(), q=x_bits[j])\n", - " x = bb.join(x_bits)\n", - "\n", - " # 2) Sample prep\n", - " sp = SamplePreparationBloq(n=n, k=k, sample_strings=self.sample_strings)\n", - " sp_kwargs = {\"x\": x}\n", - " for i in range(k):\n", - " sp_kwargs[f\"x_{i}\"] = soqs[f\"x_{i}\"]\n", - " sp_out = bb.add(sp, **sp_kwargs)\n", - " x = sp_out[0]\n", - " x_regs = list(sp_out[1:])\n", - "\n", - " # 3) Parity compute\n", - " for i in range(k):\n", - " xi = x_regs[i]\n", - " oi = OR_regs[i]\n", - "\n", - " xi_bits = list(bb.split(xi))\n", - " for j in range(n - 1):\n", - " xi_bits[j], oi = bb.add(CNOT(), ctrl=xi_bits[j], target=oi)\n", - "\n", - " x_regs[i] = bb.join(xi_bits)\n", - " OR_regs[i] = oi\n", - "\n", - " # 4) k-control Toffoli\n", - " and_k = NQToffoliLogDepth(n=k + 1)\n", - " ancilla = soqs[\"ancilla\"] # << renamed here\n", - "\n", - " for i in range(k):\n", - " OR_regs[i] = bb.add(XGate(), q=OR_regs[i])\n", - "\n", - " OR_pack = bb.join(OR_regs)\n", - " OR_pack, ancilla, t = bb.add(and_k, x=OR_pack, a=ancilla, t=t) # << renamed here\n", - "\n", - " OR_regs = list(bb.split(OR_pack))\n", - " for i in range(k):\n", - " OR_regs[i] = bb.add(XGate(), q=OR_regs[i])\n", - "\n", - " # 5) Uncompute parity\n", - " for i in reversed(range(k)):\n", - " xi = x_regs[i]\n", - " oi = OR_regs[i]\n", - "\n", - " xi_bits = list(bb.split(xi))\n", - " for j in range(n - 1):\n", - " xi_bits[j], oi = bb.add(CNOT(), ctrl=xi_bits[j], target=oi)\n", - "\n", - " x_regs[i] = bb.join(xi_bits)\n", - " OR_regs[i] = oi\n", - "\n", - " OR_pack = bb.join(OR_regs)\n", - "\n", - " # 6) Unflip x\n", - " x_bits = list(bb.split(x))\n", - " for j in range(n - 1):\n", - " x_bits[j] = bb.add(XGate(), q=x_bits[j])\n", - " x = bb.join(x_bits)\n", - "\n", - " return {\n", - " \"x\": x,\n", - " \"t\": t,\n", - " \"OR_values\": OR_pack,\n", - " \"ancilla\": ancilla, # << renamed here\n", - " **{f\"x_{i}\": x_regs[i] for i in range(k)},\n", - " }\n", - "\n", - "\n", - "# Example: n = 3, k = 2\n", - "n = 3\n", - "k = 2\n", - "sample_strings = (\n", - " (1, 0),\n", - " (0, 1),\n", - ")\n", - "\n", - "B = ApproxToffoli(n=n, k=k, sample_strings=sample_strings)\n", - "show_bloq(B)\n", - "show_bloq(B.decompose_bloq())\n" - ] - }, - { - "cell_type": "markdown", - "id": "7aab3b41-d463-43b1-a935-35fae37c3331", - "metadata": {}, - "source": [ - "Finally, we test the correctness of the algorithm on classical inputs/outputs. In the example below, we set $n=17$ and $k=4$. We randomly sample $x$ and sample_strings every time. The expected error probability is $2^{-k}=1/16$." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "7d44573e-9902-4b15-8edc-f26947445a97", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Single randomized test case\n", - " x (binary) = 1000100111111100\n", - " sample_strings = ((1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0), (0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1), (0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0), (0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0))\n", - " Approx t = 1\n", - " Ideal t = 0\n", - " MATCH = False\n" - ] - } - ], - "source": [ - "import random\n", - "\n", - "# n = 17 (16 controls), k = 4\n", - "n = 17\n", - "k = 4\n", - "m = n - 1\n", - "ALL_ONES = (1 << m) - 1\n", - "\n", - "def random_sample_string(width: int) -> tuple[int, ...]:\n", - " return tuple(random.randint(0, 1) for _ in range(width))\n", - "\n", - "def ideal_toffoli_t(x_int: int, t_in: int) -> int:\n", - " # Ideal 16-controlled Toffoli\n", - " return t_in ^ int(x_int == ALL_ONES)\n", - "\n", - "# ----------------------------\n", - "# Single randomized example\n", - "# ----------------------------\n", - "\n", - "# Randomly sample x\n", - "x_int = random.randint(0, ALL_ONES)\n", - "t_in = 0\n", - "\n", - "# Fresh random sample strings\n", - "sample_strings = tuple(random_sample_string(m) for _ in range(k))\n", - "\n", - "B = ApproxToffoli(n=n, k=k, sample_strings=sample_strings)\n", - "\n", - "out = B.call_classically(\n", - " x=x_int,\n", - " t=t_in,\n", - " x_0=0,\n", - " x_1=0,\n", - " x_2=0,\n", - " x_3=0,\n", - " OR_values=0,\n", - " ancilla=0,\n", - ")\n", - "\n", - "t_out = out[1]\n", - "t_expected = ideal_toffoli_t(x_int, t_in)\n", - "\n", - "print(\"Single randomized test case\")\n", - "print(f\" x (binary) = {x_int:016b}\")\n", - "print(f\" sample_strings = {sample_strings}\")\n", - "print(f\" Approx t = {t_out}\")\n", - "print(f\" Ideal t = {t_expected}\")\n", - "print(f\" MATCH = {t_out == t_expected}\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "adc97d02-a180-4230-b311-3cf7ff4c12e3", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python (qualtran311)", - "language": "python", - "name": "qualtran311" - }, - "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.13" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/qualtran/bloqs/mcmt/multi_qubit_toffoli.ipynb b/qualtran/bloqs/mcmt/multi_qubit_toffoli.ipynb new file mode 100644 index 0000000000..f98613eebd --- /dev/null +++ b/qualtran/bloqs/mcmt/multi_qubit_toffoli.ipynb @@ -0,0 +1,2975 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8e64607b-13fb-42b1-aa8f-f7103c8dc8d9", + "metadata": {}, + "source": [ + "In this note, we give an approxiamte implementation of the $n$-bit AND containing $n-1$ controlls and $1$ target" + ] + }, + { + "cell_type": "markdown", + "id": "10d0452e-f279-4844-bf33-bc099727787c", + "metadata": {}, + "source": [ + "We begin with the construction for $n$-qubit Toffoli ($n$-bit AND) with depth $n$, which is given in the bloq MultiAnd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7b638d0e-f828-4a78-904b-08b91f5345e2", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "ctrl_G17\n", + "ctrl[0]\n", + "\n", + "\n", + "\n", + "MultiAnd\n", + "\n", + "MultiAnd(n=5)\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "ctrl[2]\n", + "\n", + "ctrl[3]\n", + "\n", + "ctrl[4]\n", + "\n", + "\n", + "junk[0]\n", + "\n", + "\n", + "junk[1]\n", + "\n", + "\n", + "junk[2]\n", + "\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "ctrl_G17:e->MultiAnd:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G13\n", + "ctrl[1]\n", + "\n", + "\n", + "\n", + "ctrl_G13:e->MultiAnd:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G19\n", + "ctrl[2]\n", + "\n", + "\n", + "\n", + "ctrl_G19:e->MultiAnd:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G1\n", + "ctrl[3]\n", + "\n", + "\n", + "\n", + "ctrl_G1:e->MultiAnd:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G5\n", + "ctrl[4]\n", + "\n", + "\n", + "\n", + "ctrl_G5:e->MultiAnd:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G8\n", + "ctrl[0]\n", + "\n", + "\n", + "\n", + "MultiAnd:e->ctrl_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G7\n", + "ctrl[1]\n", + "\n", + "\n", + "\n", + "MultiAnd:e->ctrl_G7:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G12\n", + "ctrl[2]\n", + "\n", + "\n", + "\n", + "MultiAnd:e->ctrl_G12:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G21\n", + "ctrl[3]\n", + "\n", + "\n", + "\n", + "MultiAnd:e->ctrl_G21:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G22\n", + "ctrl[4]\n", + "\n", + "\n", + "\n", + "MultiAnd:e->ctrl_G22:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G14\n", + "junk[0]\n", + "\n", + "\n", + "\n", + "MultiAnd:e->junk_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G11\n", + "junk[1]\n", + "\n", + "\n", + "\n", + "MultiAnd:e->junk_G11:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G15\n", + "junk[2]\n", + "\n", + "\n", + "\n", + "MultiAnd:e->junk_G15:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "target_G18\n", + "target\n", + "\n", + "\n", + "\n", + "MultiAnd:e->target_G18:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from qualtran.drawing import show_bloq\n", + "from qualtran.bloqs.mcmt import MultiAnd\n", + "\n", + "bloq = MultiAnd(cvs=(1, 1, 1, 1, 1))\n", + "show_bloq(bloq)\n" + ] + }, + { + "cell_type": "markdown", + "id": "c36e9695-a17b-472f-90c7-8f01c0d5ea55", + "metadata": {}, + "source": [ + "Next, we give an implementation of the $n$-qubit Toffoli gate with depth $\\log n$, and define it to be a bloq with the same IO." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "68917f9b-0b8f-45a3-ad22-79c90aebe375", + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass\n", + "from typing import Dict, Optional, Tuple\n", + "\n", + "import numpy as np\n", + "\n", + "from qualtran import Bloq, BloqBuilder, Signature, Register, QBit, Side\n", + "from qualtran.bloqs.basic_gates import Toffoli, CNOT, XGate\n", + "from qualtran.drawing import (\n", + " Circle,\n", + " Text,\n", + " WireSymbol,\n", + " directional_text_box,\n", + " show_bloq,\n", + ")\n", + "\n", + "\n", + "@dataclass(frozen=True)\n", + "class MultiAndLogDepth(Bloq):\n", + " r\"\"\"A log-depth many-bit AND with the same public I/O as `MultiAnd`.\n", + "\n", + " This bloq computes the conjunction of a control register into a right-sided\n", + " output qubit using a balanced tree of Toffoli gates. The public register\n", + " interface matches `MultiAnd`: the input control register is preserved, the\n", + " output is written to a right-sided `target` qubit, and the intermediate tree\n", + " values are exposed as a right-sided `junk` register of length `n_ctrls - 2`.\n", + "\n", + " The control pattern is specified by `cvs`. An entry `cvs[i] = 1` denotes a\n", + " positive control on `ctrl[i]`, while `cvs[i] = 0` denotes a negative control.\n", + " Negative controls are implemented by conjugating the corresponding wire with\n", + " `X` before and after the tree computation.\n", + "\n", + " This implementation differs from the upstream `MultiAnd` in the semantics of\n", + " the `junk` register: here, `junk` stores balanced-tree intermediate values\n", + " rather than the ladder-style prefix-AND values used by the reference\n", + " implementation.\n", + "\n", + " Args:\n", + " cvs: A tuple of control values. Each entry must be `0` or `1`. The number\n", + " of controls is `len(cvs)`, and must be at least `3`.\n", + "\n", + " Registers:\n", + " ctrl: An `n`-bit control register.\n", + " junk [right]: An `n - 2` qubit junk register storing intermediate tree values.\n", + " target [right]: The output bit. \n", + " \"\"\"\n", + "\n", + " cvs: Tuple[int, ...]\n", + "\n", + " def __post_init__(self):\n", + " if len(self.cvs) < 3:\n", + " raise ValueError(\"MultiAndLogDepth must have at least 3 control values.\")\n", + " if any(cv not in (0, 1) for cv in self.cvs):\n", + " raise ValueError(\"Each control value in `cvs` must be 0 or 1.\")\n", + "\n", + " @property\n", + " def n_ctrls(self) -> int:\n", + " return len(self.cvs)\n", + "\n", + " @property\n", + " def concrete_cvs(self) -> Tuple[int, ...]:\n", + " return self.cvs\n", + "\n", + " @property\n", + " def signature(self) -> Signature:\n", + " return Signature(\n", + " [\n", + " Register(\"ctrl\", QBit(), shape=(self.n_ctrls,)),\n", + " Register(\"junk\", QBit(), shape=(self.n_ctrls - 2,), side=Side.RIGHT),\n", + " Register(\"target\", QBit(), side=Side.RIGHT),\n", + " ]\n", + " )\n", + "\n", + " def _build_tree(\n", + " self,\n", + " bb: BloqBuilder,\n", + " ctrls,\n", + " junk,\n", + " out,\n", + " ):\n", + " m = len(ctrls)\n", + "\n", + " if m == 1:\n", + " ctrls[0], out = bb.add(CNOT(), ctrl=ctrls[0], target=out)\n", + " return ctrls, junk, out\n", + "\n", + " if m == 2:\n", + " (ctrls[0], ctrls[1]), out = bb.add(\n", + " Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=out\n", + " )\n", + " return ctrls, junk, out\n", + "\n", + " a = (m + 1) // 2\n", + " b = m - a\n", + "\n", + " left_ctrls = list(ctrls[:a])\n", + " right_ctrls = list(ctrls[a:])\n", + "\n", + " left_need = a - 1 if a > 1 else 0\n", + " right_need = b - 1 if b > 1 else 0\n", + "\n", + " left_pool = list(junk[:left_need])\n", + " right_pool = list(junk[left_need:left_need + right_need])\n", + "\n", + " if a == 1:\n", + " left_out = left_ctrls[0]\n", + " else:\n", + " left_out = left_pool[0]\n", + " left_ctrls, left_internal, left_out = self._build_tree(\n", + " bb, left_ctrls, left_pool[1:], left_out\n", + " )\n", + " left_pool = [left_out] + left_internal\n", + "\n", + " if b == 1:\n", + " right_out = right_ctrls[0]\n", + " else:\n", + " right_out = right_pool[0]\n", + " right_ctrls, right_internal, right_out = self._build_tree(\n", + " bb, right_ctrls, right_pool[1:], right_out\n", + " )\n", + " right_pool = [right_out] + right_internal\n", + "\n", + " (left_out, right_out), out = bb.add(\n", + " Toffoli(), ctrl=[left_out, right_out], target=out\n", + " )\n", + "\n", + " if a == 1:\n", + " left_ctrls[0] = left_out\n", + " else:\n", + " left_pool[0] = left_out\n", + "\n", + " if b == 1:\n", + " right_ctrls[0] = right_out\n", + " else:\n", + " right_pool[0] = right_out\n", + "\n", + " return left_ctrls + right_ctrls, left_pool + right_pool, out\n", + "\n", + " def build_composite_bloq(self, bb: BloqBuilder, **soqs):\n", + " ctrls = list(np.ravel(soqs[\"ctrl\"]))\n", + "\n", + " junk_reg = bb.allocate(self.n_ctrls - 2)\n", + " junk = list(bb.split(junk_reg))\n", + "\n", + " target_reg = bb.allocate(1)\n", + " target = bb.split(target_reg)[0]\n", + "\n", + " for i, cv in enumerate(self.concrete_cvs):\n", + " if cv == 0:\n", + " ctrls[i] = bb.add(XGate(), q=ctrls[i])\n", + "\n", + " ctrls, junk, target = self._build_tree(bb, ctrls, junk, target)\n", + "\n", + " for i, cv in enumerate(self.concrete_cvs):\n", + " if cv == 0:\n", + " ctrls[i] = bb.add(XGate(), q=ctrls[i])\n", + "\n", + " return {\n", + " \"ctrl\": np.asarray(ctrls, dtype=object),\n", + " \"junk\": np.asarray(junk, dtype=object),\n", + " \"target\": target,\n", + " }\n", + "\n", + " def _classical_tree(self, vals):\n", + " m = len(vals)\n", + "\n", + " if m == 1:\n", + " return [], np.uint8(vals[0])\n", + "\n", + " if m == 2:\n", + " return [], np.uint8(vals[0] & vals[1])\n", + "\n", + " a = (m + 1) // 2\n", + " b = m - a\n", + "\n", + " left_junk, left_out = self._classical_tree(vals[:a])\n", + " right_junk, right_out = self._classical_tree(vals[a:])\n", + "\n", + " out = np.uint8(left_out & right_out)\n", + " junk = [left_out] + left_junk + [right_out] + right_junk\n", + " return junk, out\n", + "\n", + " def on_classical_vals(self, ctrl: np.ndarray) -> Dict[str, np.ndarray]:\n", + " ctrl = np.asarray(ctrl, dtype=np.uint8)\n", + " effective_ctrl = np.equal(ctrl, np.asarray(self.concrete_cvs)).astype(np.uint8)\n", + " junk, target = self._classical_tree(list(effective_ctrl))\n", + " return {\n", + " \"ctrl\": ctrl,\n", + " \"junk\": np.asarray(junk, dtype=np.uint8),\n", + " \"target\": np.uint8(target),\n", + " }\n", + "\n", + " def __pow__(self, power: int):\n", + " if power == 1:\n", + " return self\n", + " if power == -1:\n", + " return self.adjoint()\n", + " return NotImplemented\n", + "\n", + " def wire_symbol(\n", + " self, reg: Optional[Register], idx: Tuple[int, ...] = tuple()\n", + " ) -> WireSymbol:\n", + " if reg is None:\n", + " return Text(\"\")\n", + " if reg.name == \"ctrl\":\n", + " return Circle(filled=self.concrete_cvs[idx[0]] == 1)\n", + " if reg.name == \"target\":\n", + " return directional_text_box(\"∧\", side=reg.side)\n", + " if len(idx) > 0:\n", + " pretty_text = f'{reg.name}[{\", \".join(str(i) for i in idx)}]'\n", + " else:\n", + " pretty_text = reg.name\n", + " return directional_text_box(text=pretty_text, side=reg.side)\n", + "\n", + " def __str__(self):\n", + " return f\"MultiAndLogDepth(n={self.n_ctrls})\"\n", + "\n", + " def build_call_graph(self, ssa=None):\n", + " cost = {Toffoli(): self.n_ctrls - 1}\n", + " n_neg = sum(int(cv == 0) for cv in self.concrete_cvs)\n", + " if n_neg:\n", + " cost[XGate()] = 2 * n_neg\n", + " return cost" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "59d77954-2b87-423e-90fa-26331ec46ea2", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "ctrl_G11\n", + "ctrl[0]\n", + "\n", + "\n", + "\n", + "MultiAndLogDepth\n", + "\n", + "MultiAndLogDepth(n=4)\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "ctrl[2]\n", + "\n", + "ctrl[3]\n", + "\n", + "\n", + "junk[0]\n", + "\n", + "\n", + "junk[1]\n", + "\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "ctrl_G11:e->MultiAndLogDepth:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G8\n", + "ctrl[1]\n", + "\n", + "\n", + "\n", + "ctrl_G8:e->MultiAndLogDepth:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G14\n", + "ctrl[2]\n", + "\n", + "\n", + "\n", + "ctrl_G14:e->MultiAndLogDepth:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G2\n", + "ctrl[3]\n", + "\n", + "\n", + "\n", + "ctrl_G2:e->MultiAndLogDepth:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G5\n", + "ctrl[0]\n", + "\n", + "\n", + "\n", + "MultiAndLogDepth:e->ctrl_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G3\n", + "ctrl[1]\n", + "\n", + "\n", + "\n", + "MultiAndLogDepth:e->ctrl_G3:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G7\n", + "ctrl[2]\n", + "\n", + "\n", + "\n", + "MultiAndLogDepth:e->ctrl_G7:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G15\n", + "ctrl[3]\n", + "\n", + "\n", + "\n", + "MultiAndLogDepth:e->ctrl_G15:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G0\n", + "junk[0]\n", + "\n", + "\n", + "\n", + "MultiAndLogDepth:e->junk_G0:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G17\n", + "junk[1]\n", + "\n", + "\n", + "\n", + "MultiAndLogDepth:e->junk_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "target_G13\n", + "target\n", + "\n", + "\n", + "\n", + "MultiAndLogDepth:e->target_G13:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "ctrl_G26\n", + "ctrl[0]\n", + "\n", + "\n", + "\n", + "Toffoli\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "ctrl_G26:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G25\n", + "ctrl[1]\n", + "\n", + "\n", + "\n", + "ctrl_G25:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G30\n", + "ctrl[2]\n", + "\n", + "\n", + "\n", + "Toffoli_G5\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "ctrl_G30:e->Toffoli_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G14\n", + "ctrl[3]\n", + "\n", + "\n", + "\n", + "ctrl_G14:e->Toffoli_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Allocate\n", + "\n", + "Allocate\n", + "\n", + "\n", + "reg\n", + "\n", + "\n", + "\n", + "Split\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Allocate:e->Split:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split:e->Toffoli_G5:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Allocate_G1\n", + "\n", + "Allocate\n", + "\n", + "\n", + "reg\n", + "\n", + "\n", + "\n", + "Split_G3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Allocate_G1:e->Split_G3:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G8\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G3:e->Toffoli_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli:e->Toffoli_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G23\n", + "ctrl[0]\n", + "\n", + "\n", + "\n", + "Toffoli:e->ctrl_G23:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G18\n", + "ctrl[1]\n", + "\n", + "\n", + "\n", + "Toffoli:e->ctrl_G18:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Toffoli_G5:e->Toffoli_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G24\n", + "ctrl[2]\n", + "\n", + "\n", + "\n", + "Toffoli_G5:e->ctrl_G24:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G32\n", + "ctrl[3]\n", + "\n", + "\n", + "\n", + "Toffoli_G5:e->ctrl_G32:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk\n", + "junk[0]\n", + "\n", + "\n", + "\n", + "Toffoli_G8:e->junk:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G34\n", + "junk[1]\n", + "\n", + "\n", + "\n", + "Toffoli_G8:e->junk_G34:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "target_G28\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli_G8:e->target_G28:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "bloq = MultiAndLogDepth(cvs=(1, 1, 1, 1))\n", + "show_bloq(bloq)\n", + "show_bloq(bloq.decompose_bloq())" + ] + }, + { + "cell_type": "markdown", + "id": "162d97fb-419f-48d8-803e-da0a6b875e5f", + "metadata": {}, + "source": [ + "The recent work [GKZ25] gives an $\\epsilon$-approximate implementation of the $n$-qubit Toffoli gate using exponentially fewer $T$ gates. The construction is based on the following classical randomized algorithm that computes the OR function: Samples $k=O(\\log n)$ subsets of $[n-1]$, computes their parity values, and then applies a $k$-bit OR function together with additional Clifford operations. Implementing the above procedure in superpositions with proper negations yields an approximate implementation of the $n$-bit AND gate.\n", + "\n", + "Here, we assume the sampling procedure is done, and the $k$ subsets are given as $k$ classical $(n-1)$-bit strings $s_1,\\ldots,s_k$, where $[s_i]_j=1$ if and only if $j$ belows to the $i$-th subset. We first prepare the following intermediate bloq that computes $k$ $(n-1)$-bit strings $x_1,\\ldots,x_k$, where $[x_i]_j=x_j\\cdot [s_i]_j$ for any $i,j$. The parity of each string $x_i$ equals the parity function of the $i$-th subset, which will be computed later." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "a6d66b78-7f14-4fc7-b231-1c0fe754bca2", + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass\n", + "from typing import Tuple, Dict, Any as AnyType\n", + "\n", + "from qualtran import Bloq, BloqBuilder, Signature, Register, QAny, Side\n", + "from qualtran.bloqs.basic_gates import CNOT, ZeroState\n", + "from qualtran.drawing import show_bloq\n", + "\n", + "\n", + "@dataclass(frozen=True)\n", + "class ParityMask(Bloq):\n", + " \"\"\"Prepare masked copies of an input register for sampled subset-parity computation.\n", + "\n", + " This bloq takes an $(n - 1)$-qubit input register `x` and prepares `k` output\n", + " registers `x_0, ..., x_{k-1}` according to a collection of classical sample\n", + " strings `s[0], ..., s[k-1]`. For each `i` and `j`,\n", + " the output bit $[x_i]_j = x_j \\cdot s[i]_j$.\n", + "\n", + " Equivalently, `x_i` is obtained by copying into the `i`-th output register\n", + " exactly those positions of `x` selected by the `i`-th mask, leaving all other\n", + " positions unchanged. The parity of each output string `x_i` therefore equals\n", + " the parity of the subset of input bits selected by `s[i]`.\n", + "\n", + " This bloq is intended as a sample-preparation subroutine in an approximate\n", + " multi-controlled AND / Toffoli construction based on classically sampled\n", + " subsets. The sampling itself is assumed to have already been performed, and\n", + " the sampled subsets are provided explicitly through `sample_strings`.\n", + "\n", + " Args:\n", + " n: The total number of qubits in the target approximate Toffoli construction.\n", + " This bloq acts on registers of length `n - 1`.\n", + " k: The number of sampled subsets, equivalently the number of output registers.\n", + " sample_strings: A tuple of `k` classical bit strings `s_1, ..., s_k`, each of length `n - 1`.\n", + " The entry `s[i]_j` equals `1` iff the `j`-th input bit is\n", + " included in the `i`-th sampled subset.\n", + "\n", + " Registers:\n", + " x: An `(n - 1)`-qubit input register.\n", + " x_0 [right]: An `(n - 1)`-qubit output register containing the masked copy\n", + " for the first sampled subset.\n", + " x_1 [right]: An `(n - 1)`-qubit output register containing the masked copy\n", + " for the second sampled subset.\n", + " ...\n", + " x_{k-1} [right]: An `(n - 1)`-qubit output register containing the masked\n", + " copy for the last sampled subset.\n", + " \"\"\"\n", + "\n", + " n: int\n", + " k: int\n", + " sample_strings: Tuple[Tuple[int, ...], ...]\n", + "\n", + " @property\n", + " def signature(self) -> Signature:\n", + " regs = [Register(\"x\", QAny(self.n - 1))]\n", + " regs += [\n", + " Register(f\"x_{i}\", QAny(self.n - 1), side=Side.RIGHT)\n", + " for i in range(self.k)\n", + " ]\n", + " return Signature(regs)\n", + "\n", + " def build_composite_bloq(self, bb: BloqBuilder, **soqs) -> Dict[str, AnyType]:\n", + " if len(self.sample_strings) != self.k:\n", + " raise ValueError(\"sample_strings must have length k.\")\n", + " for row in self.sample_strings:\n", + " if len(row) != self.n - 1:\n", + " raise ValueError(\"Each mask in sample_strings must have length n-1.\")\n", + "\n", + " x = soqs[\"x\"]\n", + " x_bits = list(bb.split(x))\n", + "\n", + " out_regs = []\n", + " for i in range(self.k):\n", + " # Explicitly prepare |0...0> for the right-output register x_i.\n", + " xi_bits = [bb.add(ZeroState()) for _ in range(self.n - 1)]\n", + "\n", + " for j in range(self.n - 1):\n", + " if self.sample_strings[i][j]:\n", + " x_bits[j], xi_bits[j] = bb.add(\n", + " CNOT(), ctrl=x_bits[j], target=xi_bits[j]\n", + " )\n", + "\n", + " out_regs.append(bb.join(xi_bits))\n", + "\n", + " x = bb.join(x_bits)\n", + "\n", + " return {\n", + " \"x\": x,\n", + " **{f\"x_{i}\": out_regs[i] for i in range(self.k)},\n", + " }\n", + "\n", + " def build_call_graph(self, ssa=None):\n", + " cnot_count = sum(sum(int(bit) for bit in row) for row in self.sample_strings)\n", + " return {\n", + " ZeroState(): self.k * (self.n - 1),\n", + " CNOT(): cnot_count,\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "bb44882d-3e2b-418f-80f0-38046fe9965f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "x_G1\n", + "x\n", + "\n", + "\n", + "\n", + "ParityMask\n", + "\n", + "ParityMask\n", + "\n", + "x\n", + "\n", + "\n", + "x_0\n", + "\n", + "\n", + "x_1\n", + "\n", + "\n", + "x_2\n", + "\n", + "\n", + "\n", + "x_G1:e->ParityMask:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_G3\n", + "x\n", + "\n", + "\n", + "\n", + "ParityMask:e->x_G3:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_0_G2\n", + "x_0\n", + "\n", + "\n", + "\n", + "ParityMask:e->x_0_G2:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_1_G4\n", + "x_1\n", + "\n", + "\n", + "\n", + "ParityMask:e->x_1_G4:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_2_G7\n", + "x_2\n", + "\n", + "\n", + "\n", + "ParityMask:e->x_2_G7:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "x\n", + "x\n", + "\n", + "\n", + "\n", + "Split\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "x:e->Split:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "CNOT\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->CNOT:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G6\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->CNOT_G6:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G18\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->CNOT_G18:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G40\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split:e->CNOT_G40:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "ZeroState:e->CNOT:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G0\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Join\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ZeroState_G0:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G2\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "ZeroState_G2:e->CNOT_G6:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G4\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "ZeroState_G4:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G34\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "CNOT:e->CNOT_G34:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G6:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G21\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "CNOT_G6:e->CNOT_G21:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_0\n", + "x_0\n", + "\n", + "\n", + "\n", + "Join:e->x_0:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "ZeroState_G10\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Join_G24\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ZeroState_G10:e->Join_G24:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G12\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "ZeroState_G12:e->CNOT_G18:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G14\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "ZeroState_G14:e->CNOT_G21:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G16\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "ZeroState_G16:e->Join_G24:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G18:e->Join_G24:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G37\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "CNOT_G18:e->CNOT_G37:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G21:e->Join_G24:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G45\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT_G21:e->Join_G45:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_1\n", + "x_1\n", + "\n", + "\n", + "\n", + "Join_G24:e->x_1:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "ZeroState_G26\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "ZeroState_G26:e->CNOT_G34:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G28\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "ZeroState_G28:e->CNOT_G37:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G30\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Join_G43\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ZeroState_G30:e->Join_G43:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G32\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "ZeroState_G32:e->CNOT_G40:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G34:e->Join_G43:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G34:e->Join_G45:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G37:e->Join_G43:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G37:e->Join_G45:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G40:e->Join_G43:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G40:e->Join_G45:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "x_2\n", + "x_2\n", + "\n", + "\n", + "\n", + "Join_G43:e->x_2:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "x_G77\n", + "x\n", + "\n", + "\n", + "\n", + "Join_G45:e->x_G77:w\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Example for this ParityMask subroutine with n=5, k=3\n", + "sample_strings = (\n", + " (1, 0, 1, 0),\n", + " (0, 1, 1, 0),\n", + " (1, 1, 0, 1),\n", + ")\n", + "\n", + "B = ParityMask(n=5, k=3, sample_strings=sample_strings)\n", + "show_bloq(B)\n", + "show_bloq(B.decompose_bloq())" + ] + }, + { + "cell_type": "markdown", + "id": "f5ab1f58-9332-4521-af42-4167dfabce0b", + "metadata": {}, + "source": [ + "Using the bloq ParityMask as a subroutine, we obtain the circuit that approximates the $n$-qubit Toffoli gate." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "4e9e8c28-824b-4779-b433-0768370c462e", + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass\n", + "from typing import Tuple, Dict, Any as AnyType\n", + "\n", + "import numpy as np\n", + "\n", + "from qualtran import Bloq, BloqBuilder, Signature, Register, QBit, QAny, Side\n", + "from qualtran.bloqs.basic_gates import XGate, CNOT, Toffoli, ZeroState\n", + "from qualtran.drawing import show_bloq\n", + "\n", + "\n", + "@dataclass(frozen=True)\n", + "class ApproxToffoli(Bloq):\n", + "\n", + " \"\"\"Approximate multi-controlled Toffoli via sampled subset parities.\n", + "\n", + " This bloq implements an approximate `n`-qubit Toffoli / AND construction based on\n", + " classically sampled subsets. Given an `(n - 1)`-qubit control register `ctrl`, it\n", + " first prepares `k` masked copies determined by the sampled subset indicators in\n", + " `sample_strings`, computes the parity of each masked copy, negates those parity\n", + " bits, and then computes their conjunction into the output qubit `target`.\n", + "\n", + " The resulting output approximates the `(n - 1)`-bit AND of the control register.\n", + " Equivalently, it approximates an `n`-qubit Toffoli with `(n - 1)` controls and one\n", + " target, except that this bloq exposes a fresh right-sided output qubit `target`\n", + " rather than toggling an input target in place.\n", + "\n", + " All intermediate data are exposed through the right-sided `junk` register. In the\n", + " current implementation, `junk` is packed in the order\n", + " `x_0, ..., x_{k-1}, OR_values, ancilla`, where `x_i` are the masked copies,\n", + " `OR_values` are the computed parity bits, and `ancilla` are the auxiliary qubits\n", + " used by the multi-control AND subroutine.\n", + "\n", + " Args:\n", + " n: The total number of qubits in the target Toffoli interpretation. The bloq\n", + " acts on an `(n - 1)`-qubit control register.\n", + " k: The number of sampled subsets used in the approximation.\n", + " sample_strings: A tuple of `k` classical bit strings, each of length `n - 1`.\n", + " The entry `sample_strings[i][j]` equals `1` iff the `j`-th control bit is\n", + " included in the `i`-th sampled subset.\n", + "\n", + " Registers:\n", + " ctrl: An `(n - 1)`-qubit control register.\n", + " junk [right]: A right-sided junk register containing all intermediate masked\n", + " copies, parity values, and ancilla qubits.\n", + " target [right]: The output bit of the approximate multi-controlled AND.\n", + " \"\"\"\n", + " \n", + " n: int\n", + " k: int\n", + " sample_strings: Tuple[Tuple[int, ...], ...]\n", + "\n", + " @property\n", + " def n_junk(self) -> int:\n", + " return self.k * (self.n - 1) + self.k + max(self.k - 2, 0)\n", + "\n", + " @property\n", + " def signature(self) -> Signature:\n", + " return Signature(\n", + " [\n", + " Register(\"ctrl\", QBit(), shape=(self.n - 1,)),\n", + " Register(\"junk\", QBit(), shape=(self.n_junk,), side=Side.RIGHT),\n", + " Register(\"target\", QBit(), side=Side.RIGHT),\n", + " ]\n", + " )\n", + "\n", + " def build_composite_bloq(self, bb: BloqBuilder, **soqs) -> Dict[str, AnyType]:\n", + " n, k = self.n, self.k\n", + "\n", + " ctrl = list(np.ravel(soqs[\"ctrl\"]))\n", + " x = bb.join(ctrl)\n", + "\n", + " # 1) Flip x\n", + " x_bits = list(bb.split(x))\n", + " for j in range(n - 1):\n", + " x_bits[j] = bb.add(XGate(), q=x_bits[j])\n", + " x = bb.join(x_bits)\n", + "\n", + " # 2) Parity Mask: produces right-output registers x_0, ..., x_{k-1}\n", + " sp = ParityMask(n=n, k=k, sample_strings=self.sample_strings)\n", + " sp_out = bb.add(sp, x=x)\n", + " x = sp_out[0]\n", + " x_regs = list(sp_out[1:])\n", + "\n", + " # 3) Explicitly prepare OR_values in |0...0>\n", + " OR_regs = [bb.add(ZeroState()) for _ in range(k)]\n", + "\n", + " # 4) Parity compute\n", + " for i in range(k):\n", + " xi = x_regs[i]\n", + " oi = OR_regs[i]\n", + "\n", + " xi_bits = list(bb.split(xi))\n", + " for j in range(n - 1):\n", + " xi_bits[j], oi = bb.add(CNOT(), ctrl=xi_bits[j], target=oi)\n", + "\n", + " x_regs[i] = bb.join(xi_bits)\n", + " OR_regs[i] = oi\n", + "\n", + " # 5) Compute AND of negated parity bits into target\n", + " for i in range(k):\n", + " OR_regs[i] = bb.add(XGate(), q=OR_regs[i])\n", + "\n", + " ancilla_bits = []\n", + " if k == 1:\n", + " target = bb.add(ZeroState())\n", + " OR_regs[0], target = bb.add(CNOT(), ctrl=OR_regs[0], target=target)\n", + " elif k == 2:\n", + " target = bb.add(ZeroState())\n", + " (OR_regs[0], OR_regs[1]), target = bb.add(\n", + " Toffoli(), ctrl=[OR_regs[0], OR_regs[1]], target=target\n", + " )\n", + " else:\n", + " and_k = MultiAndLogDepth(cvs=(1,) * k)\n", + " OR_ctrl = np.asarray(OR_regs, dtype=object)\n", + " OR_ctrl, ancilla, target = bb.add(and_k, ctrl=OR_ctrl)\n", + " OR_regs = list(np.ravel(OR_ctrl))\n", + " ancilla_bits = list(np.ravel(ancilla))\n", + "\n", + " for i in range(k):\n", + " OR_regs[i] = bb.add(XGate(), q=OR_regs[i])\n", + "\n", + " # 6) Uncompute parity so OR_values return to |0...0>\n", + " for i in reversed(range(k)):\n", + " xi = x_regs[i]\n", + " oi = OR_regs[i]\n", + "\n", + " xi_bits = list(bb.split(xi))\n", + " for j in range(n - 1):\n", + " xi_bits[j], oi = bb.add(CNOT(), ctrl=xi_bits[j], target=oi)\n", + "\n", + " x_regs[i] = bb.join(xi_bits)\n", + " OR_regs[i] = oi\n", + "\n", + " # 7) Unflip x\n", + " x_bits = list(bb.split(x))\n", + " for j in range(n - 1):\n", + " x_bits[j] = bb.add(XGate(), q=x_bits[j])\n", + " x = bb.join(x_bits)\n", + "\n", + " ctrl = np.asarray(bb.split(x), dtype=object)\n", + "\n", + " # Pack junk as: x_0, ..., x_{k-1}, OR_values, ancilla\n", + " junk_bits = []\n", + " for i in range(k):\n", + " junk_bits.extend(list(bb.split(x_regs[i])))\n", + " junk_bits.extend(OR_regs)\n", + " junk_bits.extend(ancilla_bits)\n", + "\n", + " return {\n", + " \"ctrl\": ctrl,\n", + " \"junk\": np.asarray(junk_bits, dtype=object),\n", + " \"target\": target,\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "47ccbac3-07b7-48ed-97db-c9c11c4ed3df", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "ctrl_G16\n", + "ctrl[0]\n", + "\n", + "\n", + "\n", + "ApproxToffoli\n", + "\n", + "ApproxToffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "\n", + "junk[0]\n", + "\n", + "\n", + "junk[1]\n", + "\n", + "\n", + "junk[2]\n", + "\n", + "\n", + "junk[3]\n", + "\n", + "\n", + "junk[4]\n", + "\n", + "\n", + "junk[5]\n", + "\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "ctrl_G16:e->ApproxToffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G13\n", + "ctrl[1]\n", + "\n", + "\n", + "\n", + "ctrl_G13:e->ApproxToffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G10\n", + "ctrl[0]\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->ctrl_G10:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G6\n", + "ctrl[1]\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->ctrl_G6:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G9\n", + "junk[0]\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->junk_G9:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G8\n", + "junk[1]\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->junk_G8:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G11\n", + "junk[2]\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->junk_G11:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G0\n", + "junk[3]\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->junk_G0:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G2\n", + "junk[4]\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->junk_G2:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G1\n", + "junk[5]\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->junk_G1:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "target_G14\n", + "target\n", + "\n", + "\n", + "\n", + "ApproxToffoli:e->target_G14:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "my_graph\n", + "\n", + "\n", + "\n", + "ctrl_G111\n", + "ctrl[0]\n", + "\n", + "\n", + "\n", + "Join\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ctrl_G111:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G84\n", + "ctrl[1]\n", + "\n", + "\n", + "\n", + "ctrl_G84:e->Join:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Join:e->Split:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "XGate\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Split:e->XGate:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G1\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Split:e->XGate_G1:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "XGate:e->Join_G3:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G1:e->Join_G3:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ParityMask\n", + "\n", + "ParityMask\n", + "\n", + "x\n", + "\n", + "\n", + "x_0\n", + "\n", + "\n", + "x_1\n", + "\n", + "\n", + "\n", + "Join_G3:e->ParityMask:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G8\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ParityMask:e->Split_G8:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G15\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ParityMask:e->Split_G15:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G57\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ParityMask:e->Split_G57:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "ZeroState\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "CNOT\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "ZeroState:e->CNOT:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G6\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "CNOT_G17\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "ZeroState_G6:e->CNOT_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G8:e->CNOT:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G10\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G8:e->CNOT_G10:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT:e->CNOT_G10:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G13\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT:e->Join_G13:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G10:e->Join_G13:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G25\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "CNOT_G10:e->XGate_G25:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G47\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Join_G13:e->Split_G47:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G15:e->CNOT_G17:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G20\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G15:e->CNOT_G20:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G17:e->CNOT_G20:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G23\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT_G17:e->Join_G23:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G20:e->Join_G23:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G27\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "CNOT_G20:e->XGate_G27:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G37\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Join_G23:e->Split_G37:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Toffoli\n", + "\n", + "Toffoli\n", + "\n", + "ctrl[0]\n", + "\n", + "ctrl[1]\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "XGate_G25:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G27:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ZeroState_G29\n", + "\n", + "|0>\n", + "\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "ZeroState_G29:e->Toffoli:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G33\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Toffoli:e->XGate_G33:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G35\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Toffoli:e->XGate_G35:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "target_G156\n", + "target\n", + "\n", + "\n", + "\n", + "Toffoli:e->target_G156:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G49\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "XGate_G33:e->CNOT_G49:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G39\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "XGate_G35:e->CNOT_G39:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G37:e->CNOT_G39:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G42\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G37:e->CNOT_G42:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G39:e->CNOT_G42:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G45\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT_G39:e->Join_G45:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G42:e->Join_G45:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk\n", + "junk[5]\n", + "\n", + "\n", + "\n", + "CNOT_G42:e->junk:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G69\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Join_G45:e->Split_G69:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "Split_G47:e->CNOT_G49:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G52\n", + "\n", + "CNOT\n", + "\n", + "ctrl\n", + "\n", + "target\n", + "\n", + "\n", + "\n", + "Split_G47:e->CNOT_G52:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G49:e->CNOT_G52:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G55\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "CNOT_G49:e->Join_G55:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "CNOT_G52:e->Join_G55:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G145\n", + "junk[4]\n", + "\n", + "\n", + "\n", + "CNOT_G52:e->junk_G145:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G67\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Join_G55:e->Split_G67:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "XGate_G59\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Split_G57:e->XGate_G59:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G61\n", + "\n", + "X\n", + "\n", + "q\n", + "\n", + "\n", + "\n", + "Split_G57:e->XGate_G61:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Join_G63\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "XGate_G59:e->Join_G63:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "XGate_G61:e->Join_G63:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "Split_G65\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Join_G63:e->Split_G65:w\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "ctrl_G107\n", + "ctrl[0]\n", + "\n", + "\n", + "\n", + "Split_G65:e->ctrl_G107:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "ctrl_G125\n", + "ctrl[1]\n", + "\n", + "\n", + "\n", + "Split_G65:e->ctrl_G125:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G102\n", + "junk[0]\n", + "\n", + "\n", + "\n", + "Split_G67:e->junk_G102:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G81\n", + "junk[1]\n", + "\n", + "\n", + "\n", + "Split_G67:e->junk_G81:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G131\n", + "junk[2]\n", + "\n", + "\n", + "\n", + "Split_G69:e->junk_G131:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "junk_G92\n", + "junk[3]\n", + "\n", + "\n", + "\n", + "Split_G69:e->junk_G92:w\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Example: n = 3, k = 2\n", + "n = 3\n", + "k = 2\n", + "sample_strings = (\n", + " (1, 0),\n", + " (0, 1),\n", + ")\n", + "\n", + "B = ApproxToffoli(n=n, k=k, sample_strings=sample_strings)\n", + "show_bloq(B)\n", + "show_bloq(B.decompose_bloq())" + ] + }, + { + "cell_type": "markdown", + "id": "7aab3b41-d463-43b1-a935-35fae37c3331", + "metadata": {}, + "source": [ + "Finally, we test the correctness of the algorithm on classical inputs/outputs. In the example below, we set $n=17$ and $k=4$. We randomly sample $x$ and sample_strings every time. The expected error probability is $2^{-k}=1/16$." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "7d44573e-9902-4b15-8edc-f26947445a97", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Single randomized test case\n", + " x (binary) = 1100111100000011\n", + " sample_strings = ((0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0), (0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1), (1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1), (1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0))\n", + " Approx t = 0\n", + " Ideal t = 0\n", + " MATCH = True\n" + ] + } + ], + "source": [ + "import random\n", + "\n", + "# n = 17 (16 controls), k = 4\n", + "n = 17\n", + "k = 4\n", + "m = n - 1\n", + "ALL_ONES = (1 << m) - 1\n", + "\n", + "def random_sample_string(width: int) -> tuple[int, ...]:\n", + " return tuple(random.randint(0, 1) for _ in range(width))\n", + "\n", + "def int_to_bits(x: int, width: int) -> tuple[int, ...]:\n", + " return tuple((x >> j) & 1 for j in range(width))\n", + "\n", + "def ideal_toffoli_t(x_int: int, t_in: int) -> int:\n", + " # New ApproxToffoli outputs a fresh target bit\n", + " return int(x_int == ALL_ONES)\n", + "\n", + "# ----------------------------\n", + "# Single randomized example\n", + "# ----------------------------\n", + "\n", + "# Randomly sample x\n", + "x_int = random.randint(0, ALL_ONES)\n", + "t_in = 0\n", + "\n", + "# Fresh random sample strings\n", + "sample_strings = tuple(random_sample_string(m) for _ in range(k))\n", + "\n", + "B = ApproxToffoli(n=n, k=k, sample_strings=sample_strings)\n", + "\n", + "out = B.call_classically(\n", + " ctrl=int_to_bits(x_int, m),\n", + ")\n", + "\n", + "t_out = out[2]\n", + "t_expected = ideal_toffoli_t(x_int, t_in)\n", + "\n", + "print(\"Single randomized test case\")\n", + "print(f\" x (binary) = {x_int:016b}\")\n", + "print(f\" sample_strings = {sample_strings}\")\n", + "print(f\" Approx t = {t_out}\")\n", + "print(f\" Ideal t = {t_expected}\")\n", + "print(f\" MATCH = {t_out == t_expected}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e0e04ff-557c-4478-8d03-0ffcb2588773", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (qualtran311)", + "language": "python", + "name": "qualtran311" + }, + "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.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}