diff --git a/qualtran/bloqs/multiplexers/unary_iteration.ipynb b/qualtran/bloqs/multiplexers/unary_iteration.ipynb index b0a18619c..274fb6a04 100644 --- a/qualtran/bloqs/multiplexers/unary_iteration.ipynb +++ b/qualtran/bloqs/multiplexers/unary_iteration.ipynb @@ -1,577 +1,1051 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e2fa907b", - "metadata": {}, - "outputs": [], - "source": [ - "# Copyright 2023 Google LLC\n", - "#\n", - "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License." - ] - }, - { - "cell_type": "markdown", - "id": "49b5e1e6", - "metadata": {}, - "source": [ - "# Unary Iteration" - ] - }, - { - "cell_type": "markdown", - "id": "fcdb39f2", - "metadata": {}, - "source": [ - "Given an array of potential operations, for example:\n", - "\n", - " ops = [X(i) for i in range(5)]\n", - " \n", - "we would like to select an operation to apply:\n", - "\n", - " n = 4 --> apply ops[4]\n", - " \n", - "If $n$ is a quantum integer, we need to apply the transformation\n", - "\n", - "$$\n", - " |n \\rangle |\\psi\\rangle \\rightarrow |n\\rangle \\, \\mathrm{ops}_n \\cdot |\\psi\\rangle\n", - "$$\n", - "\n", - "The simplest conceptual way to do this is to use a \"total control\" quantum circuit where you introduce a multi-controlled operation for each of the `len(ops)` possible `n` values." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0148f529", - "metadata": {}, - "outputs": [], - "source": [ - "import cirq\n", - "from cirq.contrib.svg import SVGCircuit\n", - "import numpy as np\n", - "from typing import *" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "32e90969", - "metadata": {}, - "outputs": [], - "source": [ - "import operator\n", - "import cirq._compat\n", - "import itertools" - ] - }, - { - "cell_type": "markdown", - "id": "a6d947da", - "metadata": {}, - "source": [ - "## Total Control\n", - "\n", - "Here, we'll use Sympy's boolean logic to show how total control works. We perform an `And( ... )` for each possible bit pattern. We use an `Xnor` on each selection bit to toggle whether it's a positive or negative control (filled or open circle in quantum circuit diagrams).\n", - "\n", - "In this example, we indeed consider $X_n$ as our potential operations and toggle bits in the `target` register according to the total control." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8e61bf03", - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "import sympy as S\n", - "import sympy.logic.boolalg as slb\n", - "\n", - "def total_control(selection, target):\n", - " \"\"\"Toggle bits in `target` depending on `selection`.\"\"\"\n", - " print(f\"Selection is {selection}\")\n", - " \n", - " for n, trial in enumerate(itertools.product((0, 1), repeat=len(selection))):\n", - " print(f\"Step {n}, apply total control: {trial}\")\n", - " target[n] ^= slb.And(*[slb.Xnor(s, t) for s, t in zip(selection, trial)])\n", - " \n", - " if target[n] == S.true:\n", - " print(f\" -> At this stage, {n}= and our output bit is set\")\n", - "\n", - " \n", - "selection = [0, 0, 0]\n", - "target = [False]*8\n", - "total_control(selection, target) \n", - "print()\n", - "print(\"Target:\")\n", - "print(target)" - ] - }, - { - "cell_type": "markdown", - "id": "e572a31d", - "metadata": {}, - "source": [ - "Note that our target register shows we have indeed applied $X_\\mathrm{0b010}$. Try changing `selection` to other bit patterns and notice how it changes." - ] - }, - { - "cell_type": "markdown", - "id": "a4a75f61", - "metadata": {}, - "source": [ - "Of course, we don't know what state the selection register will be in. We can use sympy's support for symbolic boolean logic to verify our gadget for all possible selection inputs." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5df67d45", - "metadata": {}, - "outputs": [], - "source": [ - "selection = [S.Symbol(f's{i}') for i in range(3)]\n", - "target = [S.false for i in range(2**len(selection)) ]\n", - "total_control(selection, target)\n", - "\n", - "print()\n", - "print(\"Target:\")\n", - "for n, t in enumerate(target):\n", - " print(f'{n}= {t}')\n", - " \n", - "tc_target = target.copy()" - ] - }, - { - "cell_type": "markdown", - "id": "deab0553", - "metadata": {}, - "source": [ - "As expected, the \"not pattern\" (where `~` is boolean not) matches the binary representations of `n`." - ] - }, - { - "cell_type": "markdown", - "id": "81b69e70", - "metadata": {}, - "source": [ - "## Unary Iteration with segment trees\n", - "\n", - "A [segment tree](https://en.wikipedia.org/wiki/Segment_tree) is a data structure that allows logrithmic-time querying of intervals. We use a segment tree where each interval is length 1 and comprises all the `n` integers we may select.\n", - "\n", - "It is defined recursively by dividing the input interval into two half-size intervals until the left limit meets the right limit." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ab998aa4", - "metadata": {}, - "outputs": [], - "source": [ - "def segtree(ctrl, selection, target, depth, left, right):\n", - " \"\"\"Toggle bits in `target` depending on `selection` using a recursive segment tree.\"\"\"\n", - " print(f'depth={depth} left={left} right={right}', end=' ')\n", - " \n", - " if left == (right - 1):\n", - " # Leaf of the recusion.\n", - " print(f'n={n} ctrl={ctrl}')\n", - " target[left] ^= ctrl\n", - " return \n", - " print()\n", - " \n", - " assert depth < len(selection)\n", - " mid = (left + right) >> 1\n", - " \n", - " # Recurse left interval\n", - " new_ctrl = slb.And(ctrl, slb.Not(selection[depth]))\n", - " segtree(ctrl=new_ctrl, selection=selection, target=target, depth=depth+1, left=left, right=mid)\n", - " \n", - " # Recurse right interval\n", - " new_ctrl = slb.And(ctrl, selection[depth])\n", - " segtree(ctrl=new_ctrl, selection=selection, target=target, depth=depth+1, left=mid, right=right)\n", - " \n", - " # Quantum note:\n", - " # instead of throwing away the first value of `new_ctrl` and re-anding\n", - " # with selection, we can just invert the first one (but only if `ctrl` is active)\n", - " # new_ctrl ^= ctrl" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6a514ee6", - "metadata": {}, - "outputs": [], - "source": [ - "selection = [S.Symbol(f's{i}') for i in range(3)]\n", - "target = [S.false for i in range(2**len(selection)) ]\n", - "segtree(S.true, selection, target, 0, 0, 2**len(selection))\n", - "\n", - "print()\n", - "print(\"Target:\")\n", - "for n, t in enumerate(target):\n", - " print(f'n={n} {slb.simplify_logic(t)}')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "23d91438", - "metadata": {}, - "outputs": [], - "source": [ - "print(f\"{'n':3s} | {'segtree':18s} | {'total control':18s} | same?\")\n", - "for n, (t1, t2) in enumerate(zip(target, tc_target)):\n", - " t1 = slb.simplify_logic(t1)\n", - " print(f'{n:3d} | {str(t1):18s} | {str(t2):18s} | {str(t1==t2)}')" - ] - }, - { - "cell_type": "markdown", - "id": "e39448e6", - "metadata": {}, - "source": [ - "## Quantum Circuit\n", - "\n", - "We can translate the boolean logic to reversible, quantum logic. It is instructive to start from the suboptimal total control quantum circuit for comparison purposes. We can build this as in the sympy boolean-logic case by adding controlled X operations to the target signature, with the controls on the selection signature toggled on or off according to the binary representation of the selection index.\n", - "\n", - "Let us first build a GateWithRegisters object to implement the circuit" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6b37d717", - "metadata": {}, - "outputs": [], - "source": [ - "import cirq\n", - "from functools import cached_property\n", - "from qualtran import Signature, GateWithRegisters, QUInt\n", - "\n", - "class TotallyControlledNot(GateWithRegisters):\n", - " \n", - " def __init__(self, selection_bitsize: int, target_bitsize: int, control_bitsize: int = 1):\n", - " self._selection_bitsize = selection_bitsize\n", - " self._target_bitsize = target_bitsize\n", - " self._control_bitsize = control_bitsize\n", - "\n", - " @cached_property\n", - " def signature(self) -> Signature:\n", - " return Signature(\n", - " [\n", - " *Signature.build(control=self._control_bitsize),\n", - " *Signature.build(selection=self._selection_bitsize),\n", - " *Signature.build(target=self._target_bitsize)\n", - " ]\n", - " )\n", - "\n", - " def decompose_from_registers(self, **qubit_regs: Sequence[cirq.Qid]) -> cirq.OP_TREE:\n", - " num_controls = self._control_bitsize + self._selection_bitsize\n", - " for target_bit in range(self._target_bitsize):\n", - " bit_pattern = QUInt(self._selection_bitsize).to_bits(target_bit)\n", - " control_values = [1]*self._control_bitsize + list(bit_pattern)\n", - " yield cirq.X.controlled(\n", - " num_controls=num_controls,\n", - " control_values=control_values\n", - " ).on(\n", - " *qubit_regs[\"control\"], \n", - " *qubit_regs[\"selection\"],\n", - " qubit_regs[\"target\"][-(target_bit+1)])\n", - " " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1f7b6758", - "metadata": {}, - "outputs": [], - "source": [ - "import qualtran.cirq_interop.testing as cq_testing\n", - "tc_not = TotallyControlledNot(3, 5)\n", - "tc = cq_testing.GateHelper(tc_not)\n", - "cirq.Circuit((cirq.decompose_once(tc.operation)))\n", - "SVGCircuit(cirq.Circuit(cirq.decompose_once(tc.operation)))" - ] - }, - { - "cell_type": "markdown", - "id": "7b28663a", - "metadata": {}, - "source": [ - "## Tests for Correctness\n", - "\n", - "We can use a full statevector simulation to compare the desired statevector to the one generated by the unary iteration circuit for each basis state." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "574c5058", - "metadata": {}, - "outputs": [], - "source": [ - "selection_bitsize = 3\n", - "target_bitsize = 5\n", - "for n in range(target_bitsize):\n", - " # Initial qubit values\n", - " qubit_vals = {q: 0 for q in tc.all_qubits}\n", - " # All controls 'on' to activate circuit\n", - " qubit_vals.update({c: 1 for c in tc.quregs['control']})\n", - " # Set selection according to `n`\n", - " qubit_vals.update(zip(tc.quregs['selection'], QUInt(selection_bitsize).to_bits(n)))\n", - "\n", - " initial_state = [qubit_vals[x] for x in tc.all_qubits]\n", - " final_state = [qubit_vals[x] for x in tc.all_qubits]\n", - " final_state[-(n+1)] = 1\n", - " cq_testing.assert_circuit_inp_out_cirqsim(\n", - " tc.circuit, tc.all_qubits, initial_state, final_state\n", - " )\n", - " print(f'n={n} checked!')" - ] - }, - { - "cell_type": "markdown", - "id": "d76fcf8f", - "metadata": {}, - "source": [ - "## Towards a segment tree \n", - "\n", - "Next let's see how we can reduce the circuit to the observe the tree structure.\n", - "First let's recall what we are trying to do with the controlled not. Given a\n", - "selection integer (say 3 = 011), we want to toggle the bit in the target\n", - "register to on if the qubit 1 and 2 are set to on in the selection register." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3aca2666", - "metadata": {}, - "outputs": [], - "source": [ - "# The selection bits [1-3] are set according to binary representation of the number 3 (011)\n", - "initial_state = [1, 0, 1, 1, 0, 0, 0, 0, 0]\n", - "final_state = [1, 0, 1, 1, 0, 1, 0, 0, 0]\n", - "actual, should_be = cq_testing.get_circuit_inp_out_cirqsim(\n", - " tc.circuit, tc.all_qubits, initial_state, final_state\n", - " )\n", - "print(\"simulated: \", actual)\n", - "print(\"expected : \", should_be)\n" - ] - }, - { - "cell_type": "markdown", - "id": "4640eeed", - "metadata": {}, - "source": [ - "Now what is important to note is that we can remove many repeated controlled operations by using ancilla qubits to flag what part of the circuit we need to apply, this works because we know the bit pattern of nearby integers is very similar. \n", - "\n", - "A circuit demonstrating this for our example is given below." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ef853ae7", - "metadata": {}, - "outputs": [], - "source": [ - "from qualtran.bloqs.mcmt import And\n", - "\n", - "selection_bitsize = 2\n", - "target_bitsize = 4\n", - "qubits = cirq.LineQubit(0).range(1 + selection_bitsize * 2 + target_bitsize)\n", - "circuit = cirq.Circuit()\n", - "circuit.append(\n", - " [\n", - " And(1, 0).on(qubits[0], qubits[1], qubits[2]),\n", - " And(1, 0).on(qubits[2], qubits[3], qubits[4]),\n", - " cirq.CX(qubits[4], qubits[8]),\n", - " cirq.CNOT(qubits[2], qubits[4]),\n", - " cirq.CX(qubits[4], qubits[7]),\n", - " And().adjoint().on(qubits[2], qubits[3], qubits[4]),\n", - " cirq.CNOT(qubits[0], qubits[2]),\n", - " And(1, 0).on(qubits[2], qubits[3], qubits[4]),\n", - " cirq.CX(qubits[4], qubits[6]),\n", - " cirq.CNOT(qubits[2], qubits[4]),\n", - " cirq.CX(qubits[4], qubits[5]),\n", - " And().adjoint().on(qubits[2], qubits[3], qubits[4]),\n", - " And().adjoint().on(qubits[0], qubits[1], qubits[2]),\n", - " ]\n", - ")\n", - "\n", - "SVGCircuit(circuit)" - ] - }, - { - "cell_type": "markdown", - "id": "b9d45d52", - "metadata": {}, - "source": [ - "Reading from left to right we first check the control is set to on and the selection qubit is off, if both these conditions are met then the ancilla qubit is now set to 1. The next control checks if the previous condition was met and likewise the second selection index is also off. At this point if both these conditions are met we must be indexing 0 as the first two qubits are set to off (00), otherwise we know that we want to apply X to qubit 1 so we perform a CNOT operation to flip the bit value in the second ancilla qubit, before returning back up the circuit. Now if the left half of the circuit was not applied (i.e. the first selection register was set to 1) then the CNOT between the control qubit and the first ancilla qubit causes the ancilla qubit to toggle on. This triggers the right side of the circuit, which now performs the previously described operations to figure out if the lowest bit is set. Combining these two then yields the expected controlled X operation. \n", - "\n", - "Below we check the circuit is giving the expected behaviour." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "83d1287d", - "metadata": {}, - "outputs": [], - "source": [ - "initial_state = [1, 0, 0, 0, 0, 0, 0, 0, 0]\n", - "target_indx = 3\n", - "sel_bits = QUInt(selection_bitsize).to_bits(target_indx)\n", - "sel_indices = [i for i in range(1, 2*selection_bitsize+1, 2)]\n", - "initial_state[sel_indices[0]] = sel_bits[0]\n", - "initial_state[sel_indices[1]] = sel_bits[1]\n", - "result = cirq.Simulator(dtype=np.complex128).simulate(\n", - " circuit, initial_state=initial_state\n", - ")\n", - "actual = result.dirac_notation(decimals=2)[1:-1]\n", - "print(\"simulated: {}, index set in string {}\".format(actual, len(qubits)-1-target_indx))" - ] - }, - { - "cell_type": "markdown", - "id": "a86e0d42", - "metadata": {}, - "source": [ - "Extending the above idea to larger ranges of integers is relatively straightforward. For example consider the next simplest case of $L=8 = 2^3$. The circuit above takes care of the last two bits and can be duplicated. For the extra bit we just need to add a additional `AND` operations, and a CNOT to switch between the original range `[0,3]` or the new range `[4,7]` depending on whether the new selection register is off or on respectively. This procedure can be repeated and we can begin to notice the recursive tree-like structure. \n", - "\n", - "This structure is just the segtree described previously for boolean logic and this gives is the basic idea of unary iteration, \n", - "which uses `L-1` `AND` operations. Below the `ApplyXToLthQubit` builds the controlled Not operation using the `UnaryIterationGate` as a base class which defines the `decompose_from_registers` method appropriately to recursively construct the unary iteration circuit.\n", - "\n", - "Note below a different ordering of ancilla and selection qubits is taken to what was used in the simpler `L=4` example." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9cba52b1", - "metadata": {}, - "outputs": [], - "source": [ - "from qualtran import QAny, Register, Register, BQUInt\n", - "from qualtran.bloqs.multiplexers.unary_iteration_bloq import UnaryIterationGate\n", - "from functools import cached_property\n", - "\n", - "\n", - "\n", - "class ApplyXToLthQubit(UnaryIterationGate):\n", - " def __init__(self, selection_bitsize: int, target_bitsize: int, control_bitsize: int = 1):\n", - " self._selection_bitsize = selection_bitsize\n", - " self._target_bitsize = target_bitsize\n", - " self._control_bitsize = control_bitsize\n", - "\n", - " @cached_property\n", - " def control_registers(self) -> Tuple[Register, ...]:\n", - " return (Register('control', QAny(self._control_bitsize)),)\n", - "\n", - " @cached_property\n", - " def selection_registers(self) -> Tuple[Register, ...]:\n", - " return (Register('selection', BQUInt(self._selection_bitsize, self._target_bitsize)),)\n", - "\n", - " @cached_property\n", - " def target_registers(self) -> Tuple[Register, ...]:\n", - " return (Register('target', QAny(self._target_bitsize)),)\n", - "\n", - " def nth_operation(\n", - " self, context, control: cirq.Qid, selection: int, target: Sequence[cirq.Qid]\n", - " ) -> cirq.OP_TREE:\n", - " return cirq.CNOT(control, target[-(selection + 1)])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a1e4bafa", - "metadata": {}, - "outputs": [], - "source": [ - "import qualtran.cirq_interop.testing as cq_testing\n", - "selection_bitsize = 3\n", - "target_bitsize = 5\n", - "\n", - "g = cq_testing.GateHelper(\n", - " ApplyXToLthQubit(selection_bitsize, target_bitsize))\n", - "SVGCircuit(cirq.Circuit(cirq.decompose_once(g.operation)))" - ] - }, - { - "cell_type": "markdown", - "id": "13773620", - "metadata": {}, - "source": [ - "## Tests for Correctness\n", - "\n", - "We can use a full statevector simulation to check again that the optimized circuit produces the expected result." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "32ae469b", - "metadata": {}, - "outputs": [], - "source": [ - "from qualtran import QUInt\n", - "\n", - "for n in range(target_bitsize):\n", - " # Initial qubit values\n", - " qubit_vals = {q: 0 for q in g.all_qubits}\n", - " # All controls 'on' to activate circuit\n", - " qubit_vals.update({c: 1 for c in g.quregs['control']})\n", - " # Set selection according to `n`\n", - " qubit_vals.update(zip(g.quregs['selection'], QUInt(selection_bitsize).to_bits(n)))\n", - "\n", - " initial_state = [qubit_vals[x] for x in g.all_qubits]\n", - " qubit_vals[g.quregs['target'][-(n + 1)]] = 1\n", - " final_state = [qubit_vals[x] for x in g.all_qubits]\n", - " cq_testing.assert_circuit_inp_out_cirqsim(\n", - " g.decomposed_circuit, g.all_qubits, initial_state, final_state\n", - " )\n", - " print(f'n={n} checked!')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "e2fa907b", + "metadata": {}, + "outputs": [], + "source": [ + "# Copyright 2023 Google LLC\n", + "#\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "id": "ebb4d29d", + "metadata": {}, + "source": [ + "# Select" + ] + }, + { + "cell_type": "markdown", + "id": "8b7c37c9", + "metadata": {}, + "source": [ + "The `SELECT` operation, defined by $$ SELECT \\left( | i \\rangle \\otimes | \\psi \\rangle \\right) = |i \\rangle \\otimes U_i | \\psi \\rangle $$ is a fundamental primitive in quantum computing. In recent years techniques such as Quantum Singular Value Transformations (QSVT) have brought about a unifying way to construct virtually all known quantum algorithms. Qualtran has a few different techniques for implementing these oracles; this article aims to explain how the basic construction underlying a few of the implementations, known as Unary Iteration, is done. Towards the end, variants such as SELECT-SWAP or QROAM, as well as pointers to specific implementations for physical systems such as chemical systems or Hubbard Models are mentioned. " + ] + }, + { + "cell_type": "markdown", + "id": "49b5e1e6", + "metadata": {}, + "source": [ + "## Unary Iteration" + ] + }, + { + "cell_type": "markdown", + "id": "347de8f3", + "metadata": {}, + "source": [ + "Throughout the following discussion we will use the universal gate set of Clifford + T gates, with the goal of minimizing the number of T gates (and therefore Toffoli gates). We also assume that the user has a control register `ctrl` that contains the state to be \"selected on\" and a system register `sys` that the target unitaries are applied to. We will be very pedagogical and start off with a two qubit `ctrl` register (a 1 qubit `ctrl` is just a controlled version of your bloq), show how to add in additional register, and lastly explain how to move to an arbitrary `ctrl` register using segment trees. Throughout we will demonstrate how to build these bloqs from scratch using Qualtran, so lets import the necessary code now." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "756a61d0", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import numpy as np\n", + "from qualtran import BloqBuilder, QUInt, QAny, QBit, Bloq, Signature, CompositeBloq, Soquet\n", + "from qualtran.bloqs.basic_gates.rotation import CZPowGate\n", + "from qualtran.bloqs.basic_gates.cnot import CNOT\n", + "from qualtran.bloqs.mcmt.and_bloq import And\n", + "from qualtran.bloqs.basic_gates.toffoli import Toffoli\n", + "from qualtran.bloqs.basic_gates.x_basis import XGate\n", + "from qualtran.bloqs.basic_gates.identity import Identity\n", + "from qualtran.drawing.musical_score import draw_musical_score, get_musical_score_data\n", + "from qualtran.drawing import get_musical_score_data, draw_musical_score\n", + "import attrs\n", + "from typing import Dict, List\n", + "from qualtran import BloqBuilder, Register\n", + "from qualtran._infra.composite_bloq import SoquetT\n", + "\n", + "\n", + "def int_to_bool_list(num, bitsize):\n", + " \"\"\"converts a given `num` as an integer to a list of booleans in big endian\n", + " Ex: `assert int_to_bool_list(11, 4) == [True, False, True, True]` \"\"\"\n", + " x = [bool(num & (1< 1 and 1 -> 0) using X, store the Toffoli of these two into `anc` so that `anc = ~ctrl[0] * ~ctrl[1]`, meaning `anc = 1` if and only if both `ctrl` qubits start out in the 0 state. After this we do our controlled $U_0$, which we will use a controlled Z rotation with the index as the rotation angle for demonstration purposes, and then uncompute. The Qualtran code for this is" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "baaa3142", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAAGuCAYAAACk4JpHAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABCuUlEQVR4nO3deViVdf7/8dfhAMqiEZUwpaNDpeYWpmNpmQuM5II6tmnpTyc1TSJ1rMxSa7S+aRul+Q1NzRaNmjGXwWVSyGzSSVtIU1ErNXctFQVBBT6/P7w4XxlBWc52n/N8XNe58tzL534f+tznvl/n3mzGGCMAAAAAACwqwNMFAAAAAABQHQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWzhFRYsWKDp06d7ugwAAABU0PTp07VgwQJPlwFIkmzGGOPpIoBrr71WBw8e1OTJkzV+/HhPlwMAAIBLeP755zVhwgT97ne/04EDBzxdDsARW3iPmJgYTZgwQc8//7ynSwEAAEA5SkJtTEyMp0sBHAI9XQBQYtCgQZKkCRMmSBJHbgEAALxMSaidNGmSJOmtt97ycEXAeQRbeJWSUEu4BQAA8C4XhtoJEyZo8uTJni4JcCDYwiO2bt2q7du3O97n5+c7/k24BQAA8C7/HWpL5Ofna9GiRY73jRo1UpMmTTxRIvwcwRZuV1BQoFtvvVW5ubmOYTabTTfffLPjPeEWAADAO5QXam+++Wbl5OSoT58+jmG1atXSkSNHVLNmTU+UCj9GsIXbFRYWKjc3V2+//bb+/Oc/S5LsdrsiIiJKTUe4BQAA8KzyQq0k9ezZU8eOHVNRUZEkadGiRRo6dKgKCws9USr8HMEWHlOrVi1dddVVl5yGcAsAAOAZlwq1JS48MFGrVi03VQZcjGALr0e4BQAAcK+KhFrAmxBsYQmEWwAAAPcg1MKKCLawDMItAACAaxFqYVUEW1gK4RYAAMA1CLWwsgBPF+Bur7/+ug4dOnTJaRo0aKCsrCzH+yNHjqhp06aSpLNnz2rs2LG64YYbdNNNN6lZs2aaM2fORW189tlnstlsev/9951W++OPP660tDRJUlpammJjY9WsWTM1a9ZMr776qmO6TZs2qWvXrk5brre58Av3+eef93Q5AAAAluevoZZs4Dv87ojt66+/ro4dOyo6OvqiccXFxWXOs2TJEvXs2VOSNGjQIJ05c0bff/+9wsLCtHv3bnXt2lVnz57VI4884phnzpw5iouL05w5czRgwIBq171//34tX75cL7/8siSpXr16WrlypaKjo5WTk6NWrVqpVatW6tixo1q0aKEaNWooMzNTnTt3rvayvRFHbgEAAJzDX0OtRDbwJT4dbNevX68nnnhCp06dkjFGd999tw4cOKD7779fISEhmjdvnhYvXqzNmzcrNzdXe/fu1apVqy5qZ/HixXr22We1c+dOLV68WHv37lVYWJik87/gvPrqqxoyZIij8544cULLli3Ttm3b1KJFC/3444+64YYbJJ3v/DVq1NCPP/6ovXv3qlmzZkpLS1NxcbEaNGigjRs3ql69epKkp59+WkVFRZo6darmzp2ru+++WzabTZJ0++23O+q74oor1LhxY+3evdsxrF+/fpo5c6ZPd17CLQAAQPX4U6glG/h2NpDxUb/99pupU6eOWbt2rTHGmKKiIvPbb7+Z+vXrm++++84x3bPPPmt+97vfmUOHDjmGXTjNyZMnTUxMjCkuLjYfffSRadGixUXLOnbsmJHkaGPGjBnm/vvvN8YYM3r0aDNu3DjHtAMHDjRt2rQxeXl5prCw0LRr184sWLDAGGPM008/7Zi2oKDAREdHm927dxtjjOncubP55z//WeZn3bJli4mMjDR79+51DNuzZ4+56qqrLvk3ys3N9cjr0KFDRpJJS0u7ZH0VNWnSJCPJTJ482SntAQAA+IPJkycbSWbSpElOaS8tLc2xT+yp/czykA0unw2szmeP2K5fv16NGjVS+/btJUkBAQGKjIwsc9pu3bopKiqqzHErVqzQXXfd5fg15FJCQkIknT/V4MUXX5QkPfTQQ0pISNDkyZNlt9slSX/+858VGhoqSWrTpo1++uknSdKIESPUpk0bPfvss/r73/+uNm3aqH79+pKkffv2lVnjvn371KtXL6Wmpqpu3bqO4dHR0frtt99UUFCgmjVrlllveHj4ZT+TFUyYMEFff/21JkyYoDFjxjj+PwAAAKBs+fn5mjBhgnr27On0I7VlndbrLsaYMoeTDS6fDazO724eVZZLBbxFixapd+/ekqSWLVtq586d+u2330pNs379ejVt2lS1a9dWVlaWNm3apKFDh6pBgwbq0aOHfv31V61YscIx/YWdyW63q7CwUJJ03XXX6c4779RHH32kGTNm6NFHH3VMFxoaqoKCglLLPXDggOLj4zV+/Hjde++9pcYVFBTIbrcrODi4cn8MC5o5c6aWLl2q5ORkn11RAQAAnKlmzZpKTk7W0qVLNWvWLE+X41XIBtbks0ds27Vrp507d+qLL75Q+/btVVxcrBMnTqh27drKycmpUBtnz57V+vXr9d5770mSbrzxRiUmJurhhx/W+++/r9DQUO3evVtjx4513Hlszpw5GjNmjKZMmeJo56233tKcOXPUo0ePyy5z5MiRuvfeexUeHq74+HjH8BYtWmj79u2OX5kOHjyouLg4jR07VgMHDryonW3btqlZs2YKCCj/t4vc3NwK/R2cLTc312m/5M2cOVPDhw9XcnKy3njjjQr9egYAAODvbDab3njjDUnSsGHDJEkPP/ywU9o+dOiQ150ZSDa4fDawOp8NtldeeaUWLVqkMWPG6NSpUwoICNDkyZP12GOPaejQoQoNDdW8efMu2UZmZqbuuOMOBQUFOYa99957mjBhgpo3b66AgADt2rVL6enpSkhIUEFBgebPn6/PP/+8VDv33XefHn/8cR0+fPiydd9222264oorNGzYsFIh7Z577tF7772nIUOGSJImTpyoX375RW+88YbjS2nkyJH6y1/+IklauXKl7rnnnksuq+Qid3cr7xSRyiLUAgAAVJ2rwm1YWJjH9jPLQza4fDawPE9f5OvNhg0bZv7+97+XO76oqMg88cQTJjY21vz2229OWea+fftMdHS0OXny5EXLatWqVamLwMtz5swZ07x5c3P06FGn1ORsp06dqvbNo1JTU40kk5ycbIqLi51YHQAAgH8pLi42ycnJRpKZOXNmldspuXnUqVOnnFid9yAbeDefPWLrDKmpqZccHxAQoJdeeslpy5s4caLmzp2rKVOmqFatWhcta+bMmdq9e3epC8HLsmvXLk2ZMkVXX32102rzJhypBQAAcB5XnpbsS8gG3s1mjJPOCwUqKDc3V7Vq1VJaWpruv//+Ss1LqAUAAHANY4xGjhyp6dOna+bMmZUOtx999JH69u2rU6dOed01tvB9HLGFZRBqAQAAXIcjt7Aygi0sgVALAADgeoRbWBXBFl6PUAsAAOA+hFtYEcEWXo1QCwAA4H6EW1gNN4+C2+Xn5ysiIkKFhYWOh0QHBQXpn//8p+Li4hzTEWoBAAA861I3lMrIyFBiYqLOnTsnSSouLlZgYKBOnDihkJAQT5UMP8URW7hdSEiIMjMztXnzZsewsWPHat26dY5gS6gFAADwvEsduV23bp2CgoL02muvOaZv0aIFoRYeQbCFR9x+++26/fbbHe8nTZrk+DehFgAAwHtcKtyGhYVp+PDhHqsNKEGwhVcpCbXDhw8n1AIAAHiJknB77tw5R7gFvAnX2MIrXHvttbrmmmu0adMmSeLB3gAAAF4oNzdXtWrVknT+tOOjR4/qwIEDHq4KkAI8XQBQoiTUSuJILQAAgBe6cB/twn03wNM4FRleYezYsdq/f79efvllT5cCAACACnjiiSd03XXXeboMQBKnIsOL5OXlOU4/zs3NVVhYmIcrAgAAwIXYX4O34lRkAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAH7t0KFDSk5OVkxMjGrUqKF69eopMTFRGRkZeu6552Sz2cp9/e1vfyvV1qBBg7R79+4yl1PeuDVr1uiWW25RjRo1dMMNN2jevHmXrXnTpk1q3769atasqXr16umll16qwicHAMB3EGwBAH5r9+7datWqlTIzM/Xyyy9r8+bNWrlypTp16qSkpCQ9/vjjOnjw4EWvQYMGKSIiQg888ICOHTumGTNmyBjjaPenn37S/PnzLzlOknbt2qXu3burU6dOysrK0qhRozRkyBD961//KrfmkydPqkuXLqpfv76++eYbvfzyy3ruuec0a9Ys1/2hAADwcoGeLgAAAE8ZMWKEbDabNmzYoLCwMMfwpk2b6qGHHlJ4eLjCw8NLzTN//ny9//77WrZsmW688UadPn1a+/fv11133aWioiKlpqbqyy+/1NSpU1WzZs1yx0lSamqq/vCHP+jVV1+VJN10003697//rZSUFCUkJJRZ8/z583X27FnNnTtXwcHBatq0qbKysvTaa6/p4YcfdtFfCgAA70awBQD4pWPHjmnlypV64YUXSoXaEhERERcN++abbzR06FBNmTLFETxDQ0P1P//zP1q+fLl69uypwsJCZWZmKigoSJIuOW79+vWKj48vtYyEhASNGjWq3LrXr1+vO++8U8HBwaXmmTp1qo4fP64rr7yysn8KAAAsj1ORAQB+6ccff5QxRo0bN67Q9EeOHNGf//xn3X333Xr88ccdwwsKCjRx4kS98cYb6tixo2677TbFx8drw4YNlxwnnb++NyoqqtRyoqKidPLkSeXn55dZR3nzlIwDAMAfEWwBAH7pwuteL+fcuXO65557FBUVpbfffrvUuNOnTysqKkorV65U3bp1NXz4cM2dO1c7duy45DgAAOA8nIoMAPBLN954o2w2m7Kzsy877WOPPaadO3dq48aNqlmzZqlxkZGRSkpKKjXs+uuv1/XXXy9JlxwXHR2tw4cPlxp/+PBh1a5dWyEhIWXWUt48JeMAAPBHHLEFAPilyMhIJSQkaMaMGcrLy7to/IkTJyRJs2bN0ty5c7Vw4ULVrVv3km3OmzdPDRo0qPC4tm3bKiMjo9SwVatWqW3btuUuo23btlq7dq3OnTtXap5GjRpxfS0AwG8RbAEAfmvGjBkqKipSmzZttHDhQu3cuVPbtm3TtGnT1LZtW3355ZdKTk7WxIkTFRMTo0OHDpV65eTkVGv5w4cP188//6wnn3xS2dnZ+t///V99/PHHGj16tGOaN998U3FxcY73DzzwgIKDgzV48GBt2bJFH330kd544w399a9/rVYtAABYmc1U5iIjwIXy8vIcj9XIzc0t8y6lAOBsBw8e1AsvvKD09HQdPHhQ11xzjVq1aqXRo0fr3Xff1bx588qdd+DAgZccXxFr1qzR6NGjtXXrVtWtW1cTJkzQoEGDHOOfe+45zZs3T7t373YM27Rpk5KSkrRx40ZdffXVSk5O1tixY6tVBwBUBPtr8FYEW3gNvigBAAC8G/tr8FacigwAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAMqVl5enadOmKS4uTtHR0QoODlZ0dLTi4uI0bdo0nT592tMler2CggIVFBR4ugyvR18DAFSHzRhjPF0EIJ3fqQkPD5ck5ebmKiwszMMVAf5t2bJlGjJkiA4dOiRJuuKKKxQZGaljx44pJydHkhQdHa3Zs2ere/funizVqxhjtG7dOqWmpmrhwoXKz8+XJIWEhOjee+/Vww8/rHbt2slms3m4Uu9BXwOsg/01eCuO2AIALjJr1iz17NlTR48e1ZAhQ/T9999r69at+uSTT7Rt2zZt2rRJQ4cO1dGjR9WzZ0/NmjXL0yV7hZ07d6pFixa64447lJaW5gi1kpSfn68FCxbojjvuUIsWLbRz504PVuo96GsAAGcg2AIASsnMzNSIESMUFhamlStX6u2331aLFi302muvqWXLlnrjjTfUvHlzzZo1SytXrlRoaKhGjBih1atXe7p0j8rKylLr1q2VnZ0tSSosLLxompJh2dnZat26tbKystxZotehrwEAnIVgCwBwKCwsVFJSkoqKijR//nzFx8dfcvr4+Hh9+OGHKioqUnJycplhzh8cOXJE8fHxysvLq9DfoLCwUHl5eYqPj9eRI0fcUKH3oa8BAJyJYOsEX3/9tbp27SpJysnJ0bBhwxQTE6NGjRqpVatWWrJkiWPaNWvWKCQkRLGxsYqNjVXTpk319ttvO8YPGTJEn332mSRp8eLF+s9//lNqWadPn1br1q116tQpSdK9996rdevWufojAvATGRkZys7OVmJiohITEys0T48ePZSYmKjs7GxlZma6uELvlJqaquPHj6uoqKjC8xQVFen48eOaOXOmCyvzXvQ1AL6KbOAZBFsnWLRokXr37i1jjLp166agoCDt2LFD27dv15w5c/TII49o+fLljukbNWqkrKwsZWVl6V//+pceffRRR2ecPXu2OnXqJKnszvvmm2+qV69eqlWrliTpmWee0VNPPeWmTwrA15Wc4jlgwIBKzde/f39J0qpVq5xek7c7e/aspk+fruLi4krPW1xcrGnTpuns2bMuqMy70dcA+CqygWcEeroAT3jwwQe1fft2nT17VvXq1dOcOXNUUFCg2NhYjRw5Uunp6crJydG0adPUrVs3SdL69ev1xBNP6NSpUzLGaPLkyerVq5ckaenSpVq1apUyMjK0Z88effbZZwoMPP+njY2N1fjx4zV58mRHWxc6efKkwsLCFBQUJEnq2LGjRo0apeDgYEe78+bN06OPPqohQ4Zo5syZ+vTTTx3zx8bG6ujRo9q2bZtuuummMj9vXl6eU/9+rnJhnVapGfA1u3btkiTVr19f1113nQ4cOHDRNFOnTtXUqVMd73//+9/r448/dszvb+vv8uXL9euvv1Z5/l9//VVLliwpcxvhy+hrgDVZdX/tUndv9rds4LOMHzpy5Ijj3y+++KIZNmyY2bVrl5Fk/vGPfxhjjFmxYoVp2LChMcaY3377zdSpU8esXbvWGGNMUVGR+e2334wxxuzYscO0a9fOGGPM1KlTTc+ePS9a3rfffmtq1qxpjDHms88+MzVr1jQ333yzuemmm0xwcLCZOXOmY9oOHTqYRYsWGWOMGThwoElJSXGM++WXX8xVV111Uft/+ctfzPTp08v9vJJ48eLFixcvXrx48fLbF9nA9/nlEdsFCxbo/fffV0FBgQoKCnT11VdLkmrWrKk+ffpIktq2bauffvpJ0vlfZBo1aqT27dtLkgICAhQZGSnp/041uJyQkBDHv0tON5Ckffv26fbbb1fr1q11yy23XLKNffv2KSoq6qLh0dHR2rdv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4ph2zZo1CgkJUWxsrGJjY9W0aVO9/fbbjvFDhgzRZ599JklavHix/vOf/5Ra1unTp9W6dWudOnVKknTvvfdq3bp1rv6IAIDLyMzMdITaynj55Zcd3/sAAOsjG3gOR2yradGiRerdu7eMMerWrZtatmypHTt2KDAwUFlZWerWrZuCgoLUrVs3SVKjRo2UlZUlSdq3b5+uv/569e3bV7Vq1Sp1k5HFixcrNjZWt912m2PYm2++qV69eqlWrVqSpGeeeUaPPfaY1q5d674PDAC4SEpKigIDAy95pLYsgYGBSklJUadOnVxUGQDAncgGnuOXR2zz8/N1//33q0mTJrr55pvVpUsX9ejRQwsWLHBM8+mnn+rWW2+VdP5Olk2aNFFsbKyaN2+ur776yjHd0qVL1atXL2VkZGjPnj167bXXFBh4/veC2NhYjR8/XpMnTy6zjpMnTyosLExBQUGSpI4dO2rx4sVavny5li5dqpdfflmxsbGOTj1z5kw98MADjvljY2N19OhRbdu2zbl/IABAhf38889atmxZpUOtdP605PT0dP38888uqAwAUBFkA9/gl0dsV65cqRMnTmjr1q2SpGPHjumbb77Rs88+6+gcM2bM0KOPPipJGjNmjLKzs/W73/1O586d05kzZyRJO3fuVO3atRUdHa333ntPrVq1UnBwcKlltW3bVmPGjHG83759u2JjY3X27Fn99NNPmj59umrWrFlqnm7duqlnz56KjY3VqFGjJEl79+5VTk6Orr/++ovaz8jI0E033VTmZ83Ly6viX8n9LqzVSnUD/oJ1tGyrV6+u1k2gjDHKyMhQVFSUE6uyNvoa4L2svH6GhYWVOdyfsoEv88tge/PNN2vbtm0aMWKEOnTooG7duulPf/qTRo0ape+++06RkZHasGGDPv74Y0lSXFycBgwYoMTERHXt2lUNGzaU9H+nGlxOSEiI49//fbrB7bffrtatW+uWW265ZBv79u0rc6cnOjpa+/btK3e+8PDwy9bnjdjBA7wb66hzPfzww3r44Yc9XYZXoq8B3stq62d5P0L6UzbwZX55KnJMTIy2bt2qu+66S19++aWaNWum48eP67HHHtP06dOVmpqqhx56SDVq1JAkLVy4UFOmTNG5c+fUrVs3paWlSTp/rntJ573lllv0zTff6Ny5c6WWtX79erVr167MOurWratb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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "bb = BloqBuilder()\n", + "ctrl = bb.add_register_from_dtype(\"ctrl\", QAny(2))\n", + "ctrls = bb.split(ctrl)\n", + "anc = bb.add_register_from_dtype(\"anc\", QBit())\n", + "sys = bb.add_register_from_dtype(\"sys\", QBit())\n", + "\n", + "ctrls[0] = bb.add(XGate(), q=ctrls[0])\n", + "ctrls[1] = bb.add(XGate(), q=ctrls[1])\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "\n", + "[anc, sys] = bb.add(CZPowGate(exponent=0.0), q=[anc, sys])\n", + "\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "ctrls[0] = bb.add(XGate(), q=ctrls[0])\n", + "ctrls[1] = bb.add(XGate(), q=ctrls[1])\n", + "\n", + "ctrl = bb.join(ctrls)\n", + "cbloq = bb.finalize(ctrl=ctrl, anc=anc, sys=sys)\n", + "\n", + "msd = get_musical_score_data(cbloq)\n", + "fig, ax = draw_musical_score(msd)\n", + "fig.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "d008f3ee", + "metadata": {}, + "source": [ + "Now that we have the 0 state taken care of, we can repeat this process for the remaining states 1, 2, and 3. The insight is that the X patterns dictate which state is \"selected\" on." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b2f2af19", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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cdvdy+k6Bm4iIiIiInInte9qXt0xdLsRAzcREREREamBwVs9DN1uwsBNRERERERqYvBWB0O3gwICAnD58mV4e3srU7t27Up94ToDNxERERERaUFlwTs/Px/t2rUrlW0MBgMWLlyoYsX6xxupOaioqAgff/wxCgoKAAB79uzBwoULcebMGYSEhDBwExERERGR5pR3c7WzZ88iNDQUo0ePRuvWrQEAvr6+GDx4MLy8vFSuWL94pttBXl5eGDp0KEaNGoVRo0ahR48eyjKz2Yxu3boxcBPOnDmDcePGoVmzZvD19UWjRo3Qq1cvpKWl4ZVXXoHBYKhwmj59eqltDRkyBMePHy/3eSpatn37drRt2xa+vr64/fbb8eGHH1ZZ8549e9C5c2f4+fmhUaNGePPNN+145UTVizPHOpG7qfm3au3atbj//vtRr149BAcH495778XmzZurrJl/q/RJzb727bffolOnTqhTpw78/f0RFhaGuXPnVlmzJ/a1G894d+/evdQZ7x49eij5ZujQoQzcDmLodpGSwP39998jMTGRgbsaO378ONq1a4f09HTMmTMHe/fuxaZNm9ClSxeMHTsWL7zwArKzs8tMQ4YMQa1atfDYY4/h4sWLWLBgAW68MOXo0aNYuXJlpcsA4NixY3jggQfQpUsXZGZm4tlnn8Xw4cMrPZi5fPkyunbtisaNG2PXrl2YM2cOXnnlFbz//vuuayginXPGWCdSi9p/q77++mvcf//9SElJwa5du9ClSxf06tULv/zyS4U182+VPqnd1wIDA/HMM8/g66+/RlZWFqZMmYIpU6ZU2m88ua9FRUUhMTERO3fuLBO8yYmEnCopKUkASJs2bcRkMgkAyc3NVbssUlGPHj2kQYMG5faDP//8s9zHrFixQkwmk2zatElERMxmsyQkJEjXrl0lLi5OJk2aJPfdd5989913lS4TEZk4caK0atWq1PYHDhwo3bp1q7Dm9957T2655RYpKChQ5k2aNElatGhh68snqjacMdaJ1KL236ry3HXXXTJ9+vQKl/NvlT5psa89/PDDMmjQoAqXe3pfy83NFQBiMpkkMjJSAEhSUpLaZXkUhm4nKwndNWrUEAAM3dXchQsXxGAwyGuvvWb1Y3766Sfx9/eXOXPmlFm2YcMGMZlMEh0dLYWFhVYt69y5s0yYMKHUusuWLZPg4OAK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vbQZWU8v+5TjQP/00m5a7mtaPS7SUpu5m6OZz1oGkQpu80cOMZvNqFGjBoDrdzWvjjff8ARmsxkffPAB1q1bh6+++goAUK9ePURERKBPnz546qmnVN+3WutrJW2WlJSEvXv34vz58wCA6Oho9O3bVxNtRp6DY9S+erTeZmQbPexTjgPPoId202pf0/JxkdbazBMxdLsIO69n0fL+ZG1E2u5rWq1Nq3WR/bS8T7Vam1br0jottxtrs51W6/IkLrl7ORERERERERExdBMRERERERG5DEM3ERERERERkYswdBMRERERERG5CEM3ERERERERkYswdBMRERERERG5CEM3ERERERERkYswdBMRERERERG5CEM3ERERERERkYswdBMRERERERG5CEM3ERERERERkYswdBMRERERERG5CEM3ERERERERkYswdBMRERERERG5CEM3ERERERERkYswdBMRERERERG5CEM3ERERERERkYswdBMRERERERG5CEM3ERERERERkYswdBMRERERERG5CEM3ERERERERkYswdBPpWEFBAZKSkpTf9+/fr2I1RHSznJwcfPzxx8rvp06dUrEaInVwHJC78LiItEoXofunn35Cjx49AFz/j3vkyJFo1qwZWrRogXbt2mH9+vXKutu3b4e/vz8iIyMRGRmJVq1aYcmSJcry4cOHY9u2bQCAxMREfP/996WeKy8vD+3bt8eVK1cAAI888gh27Njh6pdIZLPt27ejQYMGeOyxx5R599xzD3r27InLly+rWBkRAcCSJUtQv359jBkzRpkXFhaG5557DsXFxSpWRuQ+HAfkLjwu0j+PznyiAy+//LIsWrRIiouLJSoqSsaOHSvXrl0TEZFffvlFbr31VtmwYYOIiGzbtk3atGmjPPb3338XHx8fuXz5cpntPvnkkzJ37txS89544w2ZMWOG8vsvv/winTt3trnm3NxcASAAJDc31+bHk7ZobX8ePXpUfHx8xGg0KnWVTCaTSXr27Kl2iSKivXYjz6W1vpaUlFRmbJZMBoNBXnnlFbVL1FybkeO0tk85DjyX1tqNx0WO0Updesx81vJyVZi/evUqhgwZgr1798Lb2xshISHw8fHBY489prwDtWXLFvzrX//CDz/8gA8++AD//ve/4ePjA4vFgg8++AD33HMPACApKQmpqalIS0vDiRMnsG3b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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "bb = BloqBuilder()\n", + "ctrl = bb.add_register_from_dtype(\"ctrl\", QAny(2))\n", + "ctrls = bb.split(ctrl)\n", + "anc = bb.add_register_from_dtype(\"anc\", QBit())\n", + "sys = bb.add_register_from_dtype(\"sys\", QBit())\n", + "\n", + "# SELECT on 0 = 00\n", + "ctrls[0] = bb.add(XGate(), q=ctrls[0])\n", + "ctrls[1] = bb.add(XGate(), q=ctrls[1])\n", + "\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "[anc, sys] = bb.add(CZPowGate(exponent=0.0), q=[anc, sys])\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "\n", + "ctrls[0] = bb.add(XGate(), q=ctrls[0])\n", + "ctrls[1] = bb.add(XGate(), q=ctrls[1])\n", + "\n", + "# SELECT on 1 = 01\n", + "ctrls[0] = bb.add(XGate(), q=ctrls[0])\n", + "\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "[anc, sys] = bb.add(CZPowGate(exponent=1.0), q=[anc, sys])\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "\n", + "ctrls[0] = bb.add(XGate(), q=ctrls[0])\n", + "\n", + "# SELECT on 2 = 10\n", + "ctrls[1] = bb.add(XGate(), q=ctrls[1])\n", + "\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "[anc, sys] = bb.add(CZPowGate(exponent=2.0), q=[anc, sys])\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "\n", + "ctrls[1] = bb.add(XGate(), q=ctrls[1])\n", + "\n", + "# SELECT on 3 = 11\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "[anc, sys] = bb.add(CZPowGate(exponent=3.0), q=[anc, sys])\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "\n", + "ctrl = bb.join(ctrls)\n", + "cbloq = bb.finalize(ctrl=ctrl, anc=anc, sys=sys)\n", + "\n", + "msd = get_musical_score_data(cbloq)\n", + "fig, ax = draw_musical_score(msd)\n", + "fig.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "ad85af61", + "metadata": {}, + "source": [ + "Now the above cirquit is correct (TODO: Should we add in testing?), but there is a lot of redundancy. For example, the two X gates done back to back on the `ctrls[0]` register can be cancelled. The second gate saving we could do is based on the identity that an X gate between two Toffoli's can be replaced with the same X gate and a CNot on the other control\n", + "$$\n", + "Toffoli \\cdot I \\otimes X \\otimes I \\cdot Toffoli = CNOT(1, 3) \\cdot I \\otimes X \\otimes I.\n", + "$$\n", + "Implementing these reductions gives the final unary iteration bloq for 0,...,3 as" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e9286106", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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2RPLli1DRkaGst57770HV1dX6PV61K5dG3/99RcAoH///li2bBlSU1Px3//+F7Vr10a5cuUAAHFxcVnWGBcXhzZt2mD+/PkoXbq0Mt/Lywvx8fFISUnJse4XVahQIfTt2xf379/HkSNHVH89IiIiIiIyvyNHjuD+/fvo06ePRU4pZ2ZTP7NZzd3L3d3dTS7buHEj3n33XQBAjRo1cP78ecTHxxutc+jQIVSrVg0eHh44duwYjh8/jt69e6N8+fJ455138M8//2DHjh3K+s7OzsrXer0e6enpAIBSpUrhX//6F9auXYs5c+bg448/VtZzdXXN9J9x/fp1NG7cGKNHj0b79u2NlqWkpECv18PJycnk9+bgYJ7/gg0bNqBXr17o0KEDunfvbpZtEhERERGRZXXv3h0dOnRAr169sGHDBrNs01yZI79mthelauiuX78+zp8/jwMHDgB4fMH9nTt34OHhgYSEhFxt49GjRzh06BACAwMBAJUrV0arVq3Qp08fJCcnAwAuXbqEESNGKHeiW7x4MYYPH47Lly/j0qVLuHTpEmbMmJHri/MHDx6M0NBQ3Lt3D40bN1bmV69eHX/++afy+MaNGwgKCsKIESOyDLpnzpyBr6+v2ZrclA0bNqBDhw54//33sXLlSjg6qn6pPhERERERqcDR0RErV67E+++/jw4dOpgteJvCzKZ+ZlM1DRYtWhQbN27EyJEjUb16ddSsWRO//PILBg0ahN69eyMgIMDoFvNZ2bNnD958800UKFBAmbd8+XJUqlQJfn5+qFy5Ml599VV8++23aNasGVJSUrBq1Sp07tzZaDvBwcH48ccfcevWrRzrrlu3LgoXLoz+/fsbnR7Rrl077Nq1S3k8ZswYXLlyBTNnzkRAQAACAgKwdOlSZfnOnTvRrl27HF/vRTBwExERERHZF0sGb2Y29TObTkRE1Vd4QR999BEaN25sckcYDAaMHDkSu3fvRmRkpMnrD/Li2rVrqFWrFs6dO2d0HYXBYEDt2rURERFhdB1AVh49eoRatWphz549KF68eJbrtG/fHvfv3zdqirxg4CYiIiIisl/p6eno0qULfvjhB6xduxZt27Z9ru00a9bsuTNHbthzZjMHqw/dljZmzBgsWbIEX375ZZanHxw5cgQPHz7Em2++me12/vzzT/z1119o2bKlyXVeJHQzcBMRERER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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "bb = BloqBuilder()\n", + "ctrl = bb.add_register_from_dtype(\"ctrl\", QAny(2))\n", + "ctrls = bb.split(ctrl)\n", + "anc = bb.add_register_from_dtype(\"anc\", QBit())\n", + "sys = bb.add_register_from_dtype(\"sys\", QBit())\n", + "\n", + "# SELECT on 0 = 0b00\n", + "ctrls[0] = bb.add(XGate(), q=ctrls[0])\n", + "ctrls[1] = bb.add(XGate(), q=ctrls[1])\n", + "\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "[anc, sys] = bb.add(CZPowGate(exponent=0.0), q=[anc, sys])\n", + "\n", + "ctrls[1] = bb.add(XGate(), q=ctrls[1])\n", + "\n", + "# SELECT on 1 = 0b01\n", + "\n", + "ctrls[0], anc = bb.add(CNOT(), ctrl=ctrls[0], target=anc)\n", + "[anc, sys] = bb.add(CZPowGate(exponent=1.0), q=[anc, sys])\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "\n", + "ctrls[0] = bb.add(XGate(), q=ctrls[0])\n", + "\n", + "# SELECT on 2 = 0b10\n", + "ctrls[1] = bb.add(XGate(), q=ctrls[1])\n", + "\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "[anc, sys] = bb.add(CZPowGate(exponent=2.0), q=[anc, sys])\n", + "\n", + "ctrls[1] = bb.add(XGate(), q=ctrls[1])\n", + "\n", + "# SELECT on 3 = 0b11\n", + "ctrls[0], anc = bb.add(CNOT(), ctrl=ctrls[0], target=anc)\n", + "[anc, sys] = bb.add(CZPowGate(exponent=3.0), q=[anc, sys])\n", + "[ctrls[0], ctrls[1]], anc = bb.add(Toffoli(), ctrl=[ctrls[0], ctrls[1]], target=anc)\n", + "\n", + "ctrl = bb.join(ctrls)\n", + "cbloq = bb.finalize(ctrl=ctrl, anc=anc, sys=sys)\n", + "\n", + "msd = get_musical_score_data(cbloq)\n", + "fig, ax = draw_musical_score(msd)\n", + "fig.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "f0bf41d7", + "metadata": {}, + "source": [ + "Now from these simplifications it is pretty clear that there are two groups of operations going on, the $U_0$ and $U_1$ half and the $U_2, U_3$ half. Since there are no more simplifications to make, we can now proceed to analyze what exactly the circuit is doing at each step, with the intent to generalize to larger control registers. The way we will go about this is by tracking the state of the ancilla qubit right before each CZ. We will do so using boolean algebra, if $x$ and $y$ are variables then $\\bar{x}$ denotes the logical NOT of $x$, $x \\oplus y$ the XOR operation, and $x \\otimes y$ the AND operation. We also use $c_0$ and $c_1$ to denote the state of the `ctrl` qubits 0 and 1 respectively.\n", + "\n", + "Before CZ**0.0, the state of the ancilla is just the output of the Toffoli, which is \n", + "$$|\\bar{c_0} \\otimes \\bar{c_1} \\rangle.$$ \n", + "\n", + "Now to get to the state before `CZ**1.0` we just have to do a CNOT with the control in the state $\\bar{c_0}$. This results in\n", + "$$ |\\bar{c_0} \\otimes \\bar{c_1} \\oplus \\bar{c_0} \\rangle = |\\bar{c_0} \\otimes (\\bar{c_1} \\oplus 1) \\rangle = |\\bar{c_0} \\otimes c_1 \\rangle.$$ \n", + "This is clearly only 1 if the control register is in 01 as we wanted.\n", + "\n", + "To get to the next `CZ` we have to perform two Toffoli gates. Instead of doing one after the other we can add into the ancilla the net result of both of them. As one Toffoli has the effect of adding in the logical multiplication of the two controls, the net effect of the two Toffoli's between `CZ**1.0` and `CZ**2.0` is to add in $\\bar{c_0} \\otimes c_1 \\oplus c_0 \\otimes \\bar{c_1}$. Adding this into the ancilla state from the previous expression gives us\n", + "$$ |\\bar{c_0} \\otimes c_1 \\oplus \\bar{c_0} \\otimes c_1 \\oplus c_0 \\otimes \\bar{c_1} \\rangle = |c_0 \\otimes \\bar{c_1} \\rangle $$\n", + "\n", + "By now it should be clear what the next state will be, the CNOT added into this last state gives us\n", + "$$ |c_0 \\otimes \\bar{c_1} \\oplus c_0 \\rangle = |c_0 \\otimes (\\bar{c_1} \\oplus 1) \\rangle = |c_0 \\otimes c_1\\rangle, $$\n", + "as it should. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c087600", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "7543b9a3", + "metadata": {}, + "source": [ + "## Beyond 2 Control Qubits with Segment Trees\n", + "\n", + "Now that we know the ins and outs of how the 2 qubit Unary Iteration works it is clear that if we wanted to do an arbitrary controlled qubit we could just extend this process: Compute the $n$-qubit AND of the `ctrl` qubits, using `n-1` ancillas, uncompute, and then flip the `ctrl` qubits to change which qubit we are selecting on. After this circuit is laid out, we can go back and perform cancellations to reduce the number of gates significantly. Although this would work, adding gates just to cancel them later is rather inefficient and it would be better if we could introduce structure which would get the correct gates from the start. The abstraction we will use which lets us do this is called a Segment Tree, or an interval tree, which we will develop a small example of now.\n", + "\n", + "A [Segment Tree](https://en.wikipedia.org/wiki/Segment_tree) is a way of organizing a collection of intervals (of the real line) in a binary tree structure that allows us to ask which intervals a given number falls in. The intervals we will use are single number intervals $[i, i+1)$ ranging over the possible values we would like to query `ctrl` for. Before we jump in and build a unary iterator for quantum registers, to illustrate how the datastructure works we will build one for classical bits. We are going to build this in a class `SegmentTree` in two stages: first we will implement the computation stage and second the uncomputation stage. \n", + "\n", + "In order for us to be able to query a `ctrl` register we need to store where our \"iterator\" is in the binary tree. This will be done using a `state`, which is a list of `True` and `False`. Think of this list of `True` or `False` as giving the position of a walker on a binary tree. If the walker always starts out at the root node, then `state[0]` will tell us if the walker should traverse the left half (`state[0] == False`) or the right half (`state[0] == True`) of the tree. By repeating this process an $n$ bit state vector will give us the position of a depth $n$ tree. Now the key point is that we can store information about the `ctrl` register while performing this navigation so that by the time we reach the bottom of the tree we then know if the `ctrl` register is in the same state as the walker or if the two are different. This information we store in ancilla bits, or `anc` for short. The invariant that we want to store is that `anc[i] = (ctrl[i + 1] == state[i + 1]) and anc[i - 1]`, and in order to break the recursion we use the convention that `anc[-1] = ctrl[0] == state[0]`. " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "bfa89b97", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "five\n" + ] + } + ], + "source": [ + "class SegmentTree():\n", + " ctrl_bitsize: int\n", + " def __init__(self, ctrl_bitsize: int, ops):\n", + " \"\"\"A segment tree with unit intervals that can iterate through all possible configurations of ctrl bits.\n", + " \"\"\"\n", + " # We need ctrl_bitsize - 1 ancillas, initialize them to 0\n", + " self.state = []\n", + " self.ancilla_bits = [False for ix in range(ctrl_bitsize - 1)]\n", + " self.ctrl_bitsize = int(ctrl_bitsize)\n", + " self.ops = ops\n", + "\n", + " def compute(self, query, ctrl):\n", + " for ix in range(len(self.state)):\n", + " assert self.state[ix] == query[ix]\n", + " if len(self.state) == len(query):\n", + " return\n", + " if len(self.state) == 0:\n", + " self.ancilla_bits[0] ^= (ctrl[0] == query[0]) and (ctrl[1] == query[1])\n", + " self.state.append(query[0])\n", + " self.state.append(query[1])\n", + " else:\n", + " ctrl_ix = len(self.state)\n", + " self.ancilla_bits[ctrl_ix - 1] ^= self.ancilla_bits[ctrl_ix - 2] and (ctrl[ctrl_ix] == query[ctrl_ix])\n", + " self.state.append(query[ctrl_ix])\n", + " self.compute(query, ctrl)\n", + "\n", + " def select(self, query, ctrl):\n", + " query_list = int_to_bool_list(query, self.ctrl_bitsize)\n", + " self.compute(query_list, ctrl)\n", + " if self.ancilla_bits[-1]:\n", + " print(self.ops[query])\n", + "\n", + "ops = {0: \"zero\", 1: \"one\", 2: \"two\", 5: \"five\"}\n", + "st = SegmentTree(3, ops)\n", + "ctrl = int_to_bool_list(5, 3)\n", + "st.select(5, ctrl)\n", + "st = SegmentTree(3, ops)\n", + "st.select(2, ctrl)" + ] + }, + { + "cell_type": "markdown", + "id": "7031a3e2", + "metadata": {}, + "source": [ + "So far the computing works, it correctly identifies when `ctrl` is in the same state as the given query. One major issue though is we can only use the object once, because otherwise the invariant for `compute` of `state` being a prefix of `query` is not met. To fix this we need to introduce `uncompute`, which walks *up* the binary segment tree so that `compute` can walk down. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a037a1d1", + "metadata": {}, + "outputs": [], + "source": [ + "class SegmentTree():\n", + " ctrl_bitsize: int\n", + " def __init__(self, ctrl, ops):\n", + " \"\"\"A segment tree with unit intervals that can iterate through all possible configurations of ctrl bits.\n", + " \"\"\"\n", + " # We need ctrl_bitsize - 1 ancillas, initialize them to 0\n", + " self.state = []\n", + " self.ctrl_bitsize = math.ceil(np.log2(ctrl))\n", + " self.ctrl = int_to_bool_list(ctrl, self.ctrl_bitsize)\n", + " self.ancilla_bits = [False for ix in range(self.ctrl_bitsize - 1)]\n", + " self.ops = ops\n", + "\n", + " def compute(self, query):\n", + " for ix in range(len(self.state)):\n", + " assert self.state[ix] == query[ix]\n", + " if len(self.state) == len(query):\n", + " return\n", + " if len(self.state) == 0:\n", + " self.ancilla_bits[0] ^= (self.ctrl[0] == query[0]) and (self.ctrl[1] == query[1])\n", + " self.state.append(query[0])\n", + " self.state.append(query[1])\n", + " else:\n", + " ctrl_ix = len(self.state)\n", + " self.ancilla_bits[ctrl_ix - 1] ^= self.ancilla_bits[ctrl_ix - 2] and (self.ctrl[ctrl_ix] == query[ctrl_ix])\n", + " self.state.append(query[ctrl_ix])\n", + " self.compute(query)\n", + " \n", + " def uncompute(self, query):\n", + " first_diff_ix = None\n", + " for ix in range(len(self.state)):\n", + " if self.state[ix] != query[ix]:\n", + " first_diff_ix = ix\n", + " break\n", + " if first_diff_ix is None:\n", + " # state is a prefix of query so we do not need to uncompute\n", + " return\n", + " if first_diff_ix < len(self.state) - 1:\n", + " # we have some extra bits we have to undo\n", + " if len(self.state) == 2 and first_diff_ix == 0:\n", + " # we are the bottom of the barrel\n", + " self.ancilla_bits[0] ^= (self.ctrl[0] == self.state[0]) and (self.ctrl[1] == self.state[1])\n", + " self.state.pop()\n", + " self.state.pop()\n", + " else:\n", + " self.ancilla_bits[len(self.state) - 2] ^= self.ancilla_bits[len(self.state) - 3] and (self.state[-1] == self.ctrl[len(self.state) - 1])\n", + " self.state.pop()\n", + " self.uncompute(query)\n", + " else:\n", + " # first_diff_ix is the last bit, so we just need to do the CNOT trick\n", + " if first_diff_ix == 1:\n", + " self.ancilla_bits[first_diff_ix - 1] ^= (self.ctrl[0] == self.state[0])\n", + " self.state[1] ^= True\n", + " else:\n", + " self.ancilla_bits[first_diff_ix - 1] ^= self.ancilla_bits[first_diff_ix - 1]\n", + " self.state[first_diff_ix] ^= True\n", + " return\n", + "\n", + " def select(self, query):\n", + " query_list = int_to_bool_list(query, self.ctrl_bitsize)\n", + " self.uncompute(query_list)\n", + " self.compute(query_list)\n", + " if self.ancilla_bits[-1]:\n", + " print(self.ops[query])\n", + " else:\n", + " print(\"Miss!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e267880d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Miss!\n", + "three\n", + "Miss!\n" + ] + } + ], + "source": [ + "ops = {0: \"Hit: zero\", 3: \"Hit: three\", 5: \"Hit: five\"}\n", + "st = SegmentTree(3, ops)\n", + "ctrl = int_to_bool_list(5, 3)\n", + "st.select(5)\n", + "st.select(3)\n", + "st.select(0)" + ] + }, + { + "cell_type": "markdown", + "id": "f8900632", + "metadata": {}, + "source": [ + "Now that we have all the reversible logic implemented and the iterative queries implemented we can finally move on to using this to build a bloq that acts on **quantum** registers! We now have to be able to query `ctrl` that is a quantum register using ancillas that are also qubits. This will essentially boil down to moving our AND and XOR logic to Toffolis and CNOTs. We start below with the computation step to verify these cirquits work" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d683c3b9", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'ops' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[2], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mclass\u001b[39;00m \u001b[38;5;21;01mUnaryIterator\u001b[39;00m(Bloq):\n\u001b[1;32m 2\u001b[0m ctrl_bitsize: \u001b[38;5;28mint\u001b[39m\n\u001b[1;32m 3\u001b[0m state \u001b[38;5;241m=\u001b[39m []\n", + "Cell \u001b[0;32mIn[2], line 5\u001b[0m, in \u001b[0;36mUnaryIterator\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m state \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 4\u001b[0m sys_bitsize: \u001b[38;5;28mint\u001b[39m\n\u001b[0;32m----> 5\u001b[0m \u001b[43mops\u001b[49m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;129m@property\u001b[39m\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21msignature\u001b[39m(\u001b[38;5;28mself\u001b[39m) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Signature:\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Signature([Register(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mctrl\u001b[39m\u001b[38;5;124m'\u001b[39m, QAny(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mctrl_bitsize)), Register(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124manc\u001b[39m\u001b[38;5;124m'\u001b[39m, QAny(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mctrl_bitsize \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m)), Register(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124msys\u001b[39m\u001b[38;5;124m'\u001b[39m, QAny(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msys_bitsize))])\n", + "\u001b[0;31mNameError\u001b[0m: name 'ops' is not defined" + ] + } + ], + "source": [ + "class UnaryIterator(Bloq):\n", + " ctrl_bitsize: int\n", + " state = []\n", + " sys_bitsize: int\n", + " ops\n", + "\n", + " @property\n", + " def signature(self) -> Signature:\n", + " return Signature([Register('ctrl', QAny(self.ctrl_bitsize)), Register('anc', QAny(self.ctrl_bitsize - 1)), Register('sys', QAny(self.sys_bitsize))])\n", + " \n", + " def set_ops(self, ops):\n", + " self.ops = ops\n", + "\n", + " def compute(self, query: List[bool], bb: BloqBuilder, ancs, ctrls):\n", + " for ix in range(len(self.state)):\n", + " assert self.state[ix] == query[ix]\n", + " if len(self.state) == len(query):\n", + " return\n", + " if len(self.state) == 0:\n", + " g0 = XGate() if query[0] == False else Identity()\n", + " g1 = XGate() if query[1] == False else Identity()\n", + " ctrls[0] = bb.add(g0, q=ctrls[0])\n", + " ctrls[1] = bb.add(g1, q=ctrls[1])\n", + " ctrls[:2], ancs[0] = bb.add(Toffoli(), ctrl=ctrls[:2], target=ancs[0])\n", + " ctrls[0] = bb.add(g0, q=ctrls[0])\n", + " ctrls[1] = bb.add(g1, q=ctrls[1])\n", + " self.state.append(query[0])\n", + " self.state.append(query[1])\n", + " else:\n", + " ctrl_ix = len(self.state)\n", + " g = XGate() if query[ctrl[ix]] == False else Identity()\n", + " ctrls[ctrl_ix] = bb.add(g, q=ctrls[ctrl_ix])\n", + " [ctrls[ctrl_ix], ancs[ctrl_ix - 2]], ancs[ctrl_ix - 1] = bb.add(Toffoli(), ctrl=[ctrls[ctrl_ix], ancs[ctrl_ix - 2]], target=ancs[ctrl_ix - 1])\n", + " ctrls[ctrl_ix] = bb.add(g, q=ctrls[ctrl_ix])\n", + " self.state.append(query[ctrl_ix])\n", + " self.compute(query, bb, ancs, ctrls)\n", + "\n", + " def build_composite_bloq(self, bb: BloqBuilder, ctrl: SoquetT, anc: SoquetT, sys: SoquetT) -> Dict[str, 'SoquetT']:\n", + " queries = list(self.ops.keys())\n", + " queries.sort()\n", + " ctrls = bb.split(ctrl)\n", + " ancs = bb.split(anc)\n", + " for q_int in [2]:\n", + " q_bools = int_to_bool_list(q_int, self.ctrl_bitsize)\n", + " self.compute(q_bools, bb, ancs, ctrls)\n", + " print(f\"calling ops[{q_int}]\")\n", + " [ancs[-1], sys] = bb.add(self.ops[q_int], q=[ancs[-1], sys])\n", + " ctrl = bb.join(ctrls)\n", + " anc = bb.join(ancs)\n", + " return {'ctrl': ctrl, 'sys': sys, 'anc': anc}\n", + "\n", + "cbloq = UnaryIterator()\n", + "cbloq.ctrl_bitsize = 3\n", + "cbloq.sys_bitsize = 1\n", + "ops = dict()\n", + "for ix in range(4):\n", + " ops[ix] = CZPowGate(exponent=float(ix))\n", + "cbloq.set_ops(ops)\n", + "msd = get_musical_score_data(cbloq.decompose_bloq())\n", + "fig, ax = draw_musical_score(msd)\n", + "fig.set_figwidth(18)\n", + "fig.set_figheight(7)" + ] + }, + { + "cell_type": "markdown", + "id": "9d860b01", + "metadata": {}, + "source": [ + "Now we just have to translate the uncompute function from classical to quantum" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "314793b8", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABrMAAAI7CAYAAABP3WAsAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAACDu0lEQVR4nOzdd5gV5fk/4GfZpSMqKqBYsRFURDEqGhSBgIogdmMkNqyIGGsSjSUmJiY2MDYUNHYTCyIqKC0aNWIBxAIqQUSkBSJKh935/eFv9+sK22D3nNnd+76uc13smXfOeXZ45z2z8znzTk6SJEkAAAAAAABACtXJdgEAAAAAAABQEmEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBUCHz5s2LAQMGROvWraN+/fqx3XbbRa9evWLs2LFx3XXXRU5OTomP66+/vthrnX766fH555+v931KWjZhwoTYd999o379+rHLLrvEgw8+WGbN77//fnTq1CkaNGgQ2223Xfz5z3/egN8carbK3LcBIC2yeez6zDPPxE9/+tPYaqutomnTptGxY8cYPXp0mTU7dqUmy+Y++a9//SsOPvjg2GKLLaJhw4bRpk2buO2228qs2T4JkA7CLADK7fPPP48OHTrEuHHj4i9/+UtMnTo1Ro0aFYcddlj0798/Lrvsspg7d+46j9NPPz0222yzOOWUU2Lx4sVx5513RpIkRa87Y8aMePTRR0tdFhExc+bM6NmzZxx22GExefLkuPjii6Nfv36lnhT45ptvonv37rHDDjvEu+++G3/5y1/iuuuuiyFDhlTdhoJqpjL2bQBIm2wfu7766qvx05/+NF588cV4991347DDDotevXrFpEmTSqzZsSs1Wbb3ycaNG8eFF14Yr776anz88cdx9dVXx9VXX13q/mWfBEiPnOT7IzwAlOLII4+M999/P6ZPnx6NGzcutuzrr7+OzTbbbJ11Hn300TjttNPihRdeiB49esTy5cvj97//fbz77ruRn58f++23X7z++utx0003Rfv27UtcdtBBB8WVV14ZL7zwQnzwwQdFr3/yySfH119/HaNGjVpvzXfffXdcddVVMW/evKhXr15ERPzqV7+K4cOHx7Rp0ypv40A1Vhn7NgCkTbaPXddnjz32iJNOOimuueaa9S537EpNlsZ98thjj43GjRvHww8/vN7l9kmAFEkAoBwWLVqU5OTkJDfeeGO513nnnXeShg0bJn/5y1/WWfbCCy8kubm5yaGHHpqsXr26XMs6deqUDBw4sFjbYcOGJU2bNi2xhr59+yZHH310sefGjRuXRESyePHicv8uUFNV9r4NAGmQhmPXH8rPz0+222675I477iixjWNXaqo07pPvvfde0qJFi+S+++4rsY19EiA9TDMIQLl89tlnkSRJtGnTplztFyxYEMccc0wcd9xxcdlllxU9v3Llyrjmmmti0KBB0blz5zjwwAOjW7duMXHixFKXRXw3v3qLFi2KvU+LFi3im2++iRUrVqy3jpLWKVwGtV1l7dsAkCZpOHb9oZtvvjmWLl0aJ554Yol1OHalpkrTPrnttttG/fr1Y7/99ov+/ftHv379SqzDPgmQHsIsAMolqcCstGvWrInjjz8+WrRoEffdd1+xZcuXL48WLVrEqFGjYtttt43zzjsvhg0bFp988kmpy4CqUVn7NgCkSdqOXR977LG4/vrr4+9//3s0b958o38/qG7StE++9tpr8c4778Q999wTt99+ezz++OOV8jsCULXysl0AANXDrrvuGjk5OeWaF/yiiy6KTz/9NN5+++1o0KBBsWXNmjWL/v37F3tu5513jp133jkiotRlLVu2jPnz5xdbPn/+/GjatGk0bNhwvbWUtE7hMqjtKmvfBoA0ScOxa6Ennngi+vXrF//4xz+iW7dupdbi2JWaKk375E477RQREXvttVfMnz8/rrvuuvjZz3623lrskwDp4cosAMqlWbNm0aNHj7jzzjtj2bJl6yz/+uuvIyJiyJAhMWzYsHj66adj2223LfU1H3zwwdhxxx3Lvaxjx44xduzYYs+98sor0bFjxxLfo2PHjvHqq6/GmjVriq2z++67x+abb15qfVAbVMW+DQDZloZj14iIxx9/PM4444x4/PHHo2fPnmXW7diVmiot++QPFRQUxKpVq0pcbp8ESA9hFgDlduedd0Z+fn7sv//+8fTTT8enn34aH3/8cQwePDg6duwYr7/+egwYMCCuueaaaN26dcybN6/YY8mSJRv1/uedd1785z//iSuuuCKmTZsWd911V/z973+PX/7yl0Vt/vrXv0bXrl2Lfj7llFOiXr16cdZZZ8WHH34YTz75ZAwaNCguueSSjaoFapJs79sAUBWy/fn22GOPxS9+8Yu45ZZb4oADDljv6zp2pTbJ9j555513xvPPPx+ffvppfPrppzF06NC4+eab49RTTy1qY58ESLEEACrgq6++Svr375/ssMMOSb169ZJWrVolvXv3TsaPH5+cfvrpSUSU+DjttNM2+v3Hjx+ftG/fPqlXr17SunXr5IEHHii2/Nprr0122GGHYs9NmTIl+clPfpLUr18/adWqVfKnP/1po+uAmibb+zYAVIVsfr4deuihZb6uY1dqm2zuk4MHD0722GOPpFGjRknTpk2TffbZJ7nrrruS/Pz8ojb2SYD0ykmSCtyBEQAAAAAAADLINIMAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCrBRZuHBhrFmzJttllGnu3LmRJEm2ywAAAAAAoJpZu3ZtLFiwINtllOnbb7+Nb7/9Nttl8P8Js1JiypQpse2228bJJ5+c6kBr/PjxsfPOO0ffvn2zXQoAAAAAANXMwQcfHO3atYuPPvoo26WUaMmSJfHTn/40WrVqFf/73/+yXQ4hzEqNpUuXxurVq+OZZ55JbaA1fvz46NmzZ6xYsSIWLVqU7XIAAAAAAKhmFi1aFPPnz48uXbqkMtBasmRJ9OjRI95666349ttvU3muvjYSZqXMn//853j++edTF2gVBlmdOnWKI444ItvlAAAAAABQTZ155pnRvHnz1AVahUHW9OnT49prr812OXyPMCtlevXqFU8//XSqAq3vB1nDhw+Phg0bZrskAAAAAACqqa222irGjh2bqkDr+0HWmDFjokOHDtkuie8RZqVQmgItQRYAAAAAAJUtTYGWICv98rJdQG01Y8aMOO6442Lp0qUREbFixYqIiMjNzY2I/wu0jjvuuDj55JPjiSeeiLp162a0xpKCrNzc3PjnP/8Zu+yyS6W8zzbbbBPDhw+PZs2aVcrrAQAAAABQOX54LntjzZo1q+g8eGGg1bVr1+jSpUuMGzcu2rZtWynvU14lBVmFNe6///6Rl/ddlLLNNtvEc889F5tvvnlGayQiJ0mSJNtF1EYPPPBAnHnmmXHFFVdETk5ORETsuuuucdZZZxVr9/zzz8dxxx0XvXr1ymigVdoVWf/5z39iyJAhlfI+ixYtivvvvz9effXV6NSpU6W8JlA9LV26NK677rqYMWNG7LzzznHddddFkyZNsl0W1Fr5+fnxl7/8JSZOnBhbbbVV/Pa3v41tt90222UBQGqMHj06/va3v0WSJHHqqadGz549s10S1GpTpkyJ2267LZYuXRqHH3549OvXL9slQY0xbNiwOOuss4qdy94Yubm5cemllxa7uGHhwoXRtWvXWLBgQUYDrdKuyMrPz49bb701Fi1aFBHfncu+7777MlIX6xJmZUlhmJWfnx916pQ+22OmA61MTi04ffr0aNOmjTALarmFCxfG4YcfHlOmTImCgoKoU6dO7L333jFq1KjYaqutsl0e1DorV66Mn//85/Hss89GRESdOnWiefPmMXbs2PjRj36U5eoAIPsefPDBYl9GTZIk7r333jj77LOzWBXUXhMmTIijjjoqVq5cGQUFBZEkSVx66aXx5z//uczzbkDZCsOs8pzL3hiZDrQqOrXg9OnTY/fdd6/SmiiZ0bwayOQ9tNwji8q2bNmyGDx4cHTt2jVatmwZ9erVi5YtW0bXrl1j8ODBsXz58myXSJYlSRK9evWKKVOmRH5+fiRJEvn5+fH+++9Hz549w3cuiDCWZNqFF14Yw4cPjyRJivbJBQsWxCGHHFJp00rwHX0b0sU+SXm88sorccYZZ0RBQUHRI0mSOOecc+KFF17IdnmkgLEks2bOnBndu3ePFStWFP1NGRFxyy23xM0335zl6moe/ZuqlMl7aLlHVjWUkBXDhg1LIiLJz88v9zojRoxI6tatmxx77LHJ6tWrK72mcePGJQ0bNky6d++eLF++vNJff32mTZuWRETy6quvZuT9yKyRI0cmLVu2TCIiiYhk0003TXbaaadk0003LXquZcuWyciRI7NdKln0/PPPF/WH9T2ee+65bJdIlhlLMuuTTz5JcnJy1rs/1qlTJ7nxxhuzXWKNoW9DutgnKY+CgoJk3333TXJzc9f5nMzNzU322GOPpKCgINtlkkXGksw7/fTTk7y8vPUevzZt2jRZsmRJtkusMfTv2mvo0KEVPpe9MRYsWJDstddeSYsWLZIPP/yw0l//66+/Tg444IBks802S955551yrzdt2rRKr4Xyc2VWNVKVV2iV54qszp07R05OTuTk5MTkyZPL9boPPvhg0ToXX3xxpdVL+g0ZMiR69+4dCxcujH79+sWUKVPio48+imeeeSY+/vjjeP/99+Pss8+OhQsXRu/evSvtPmxUP4MGDSq6oeYP5ebmxuDBgzNcEWliLMm8e+65p8RpIwoKCmLw4MGRn5+f4apqHn0b0sU+SXm9/fbb8d577633szA/Pz8+/PDDeP3117NQGWlgLMm8RYsWxaOPPhpr165d7/Jvv/02Hn/88QxXVTPp35SkKs4ZV+UVWq7IqsaynabVVhtyZVahyr5Cq7xXZB166KHJ2WefncydOzdZs2ZNkiRJMmvWrOTII49MGjZsmGy11VbJZZddVrQsSZJk+fLlydy5c5OOHTsmAwcOXOc1XZlVM40dOzbJzc1NNtlkk+SVV14pev7SSy9NIiK58sori5575ZVXkiZNmiS5ubnF2lI7LF68uMQrQL7/WLRoUbZLJQuMJdnx/W9alvR47bXXsl1mtaZvQ7rYJ6mISy+9tMQrQCIiycvLSy688MJsl0kWGEuy44EHHij1uDUnJyc55JBDsl1mtad/U9qVWVVxzrhQZV+htaFXZBVyZVZ2ZfzKrNtvvz3mzZtXapsdd9yxWIq7YMGC2GOPPSIiYvXq1XHllVfGLrvsEj/60Y9izz33jKFDh67zGuPHj4+cnJx4+OGHK632yy67LJ544omIiHjiiSeiffv2seeee8aee+4Zt9xyS1G7999/P4444ohKe98fqswrtCp6j6xGjRpFy5YtIy8vL/Lz86Nnz56xevXqeOONN+Jvf/tbPPjgg3HNNdcUtW/YsGHR/LnUDmvXro3+/ftHfn5+PProo9GtW7dS23fr1i0ef/zxyM/PjwEDBpT4bSpqpilTppTrnljl/WYPNYexJDsWLlxY5nFaTk5OTJo0KUMV1Tz6NqSLfZKKeuedd0r9f1+7dm288847GayINDCWZM97770XdevWLXF5kiQxadIk92LeCPo35VFV54wr8wqtbF6RJROpHKkKswpvnPpDzz33XPTu3TsiIk4//fT47LPPYsqUKfHxxx/HyJEj4+abb46777672DpDhw6Nrl27rvc/dUPMmTMnXnzxxTjppJMiImK77baLUaNGxQcffBCvv/563H333TFhwoSIiGjXrl3Ur18/xo0bVynvvT6VEWhVNMj6oZdffjk++uijeOSRR6J9+/ZxxBFHxA033BB33nlnrF69usL1UDOMHTs2pk2bFr169YpevXqVa52jjjoqevXqFdOmTavS/Yb0mTx5conTmRWqU6eOMKsWMpZkx5QpU8psk5uba5/cCPo2pIt9koooPClelilTpqz33AY1l7Eke959990yz4l9++238cUXX2SooppH/6aiKvuccWUEWtmeWlAmUjmqNMx688034yc/+Unsvffe0a5du7j++uvjq6++ipNOOinat28fkydPjuuuuy6OO+646NGjR+y5554xd+7cdV5n+PDhccwxx8Snn34aw4cPjyFDhkTjxo0j4rvE8pZbbokbbrihqP3XX38dL7zwQjzyyCPx0UcfxWeffVa07PTTT49zzz03unbtGrvttlsce+yxsXr16li5cmW0bNkyZs+eXdT2N7/5TVx55ZURETFs2LA47rjjIicnJyIiDj744GjZsmVERGy66abRpk2b+Pzzz4vW/dnPfhb33ntv5W3M9diYQGtjg6yI7/5/99prr2jRokXRcz169IhvvvkmPvzwwwq/HjXDmDFjIiKib9++FVrv1FNPjYiIV155pdJrIr0mTZpUrjDLVSC1j7EkOyZNmlTiPewKrV27NiZOnJihimoefRvSxT5JRcyePTu++eabMtutWLEiZsyYkYGKSAtjSXYUFBSU+0tW/qbccPo3FVUV54w3JtDKdJAlE6m6TCSvql548eLF0adPn3jqqaeiU6dOUVBQEF9//XU88MAD8eSTT0b79u0j4rv/lDfffDMmTZpUrIMX+vbbb2PatGnx4x//OP7xj3/ErrvuGltssUWxNh07doy5c+fG/Pnzo0WLFvHYY49Fjx49omXLlnHqqafGsGHD4sYbbyxqP3ny5Bg/fnzUr18/DjnkkHj66afjZz/7WZx11llx9913x4033hirVq2KBx54IP79739HRMSECRPil7/85Xp/148++ijefPPNuOeee4rV1L9//43djGUqDLSOO+64OPnkk+OJJ54o9fLqiMoJsiIi5s2bt87/WeHPZV02+UMrVqyIZcuWbVAdpMvMmTMjImKHHXaIVq1axVdffbVOm5tuuiluuummop+33377+Pvf/160vr5Qe8yaNavMKQfWrl0bs2bN0i9qGWNJdsyaNSvq1Kmz3pvaf9+cOXNs3w2kb0O62CepiIoEVDNmzIhtttmmCqshTYwl2fHtt9/G8uXLy2yXk5NjG28E/ZuIiFWrVpW7bWWeM/6+wkCra9eu0aVLlxg3bly0bdu21HUyHWTJRKo4E6mqm3GNHDky6dSp0zrP77DDDsmkSZOKfr722muTs846q8Q2Tz75ZHLBBRcU/btdu3brvObixYuTiEiWLFmSJEmS7Lvvvsno0aOTJEmSqVOnJttss02ydu3aJEmS5LTTTkv++Mc/Fq178cUXJzfccEOSJEny5ZdfJttss02ycuXK5OGHH0569+5d1G633XZLJk6cuM57z549O9lll12Sv//978WeX7VqVRIRyYoVK9a7fYYNG1biTfM2xIgRI5K6deuWeaPZzz77LGnYsGHSvXv3ZPny5RV6j0MPPbTYDfnOPvvs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7ZKoAYRZVMisWbPimmuuiYKCgoiIYjftKwy0cnJy4qSTToonn3xSoAUAAAAAUEvcfPPNcfnllxcFWd+/Nc2NN94Yw4YNi4iIRo0axU033RSbbbZZliqlujHNIBVyxx13xN///vf44osv4osvvogGDRrE1VdfXbQ8Ly8vHn744TjhhBPipJNOiqeffjqL1QIAAAAAkAklBVnbb799nH322bF69eqi88pDhgyJJ598MssVU524MosK22GHHeKf//xnicsLA62IcIUWAAAAAEANV9oVWXXr1o0hQ4YUa5+bm5vpEqnmXJlFlXCFFgAAAABAzVdakAWVRZhFlRFoAQAAAADUXIIsMkWYRZUSaAEAAAAA1DyCLDJJmEWVE2gBAAAAANQcgiwyrVqFWe+8804cccQRERGxZMmSOPfcc6N169ax++67R4cOHeK5555bZ50HHnggcnJy4rXXXqu0Ok444YR48803IyLihRdeiA4dOkT9+vXj4osvLtbur3/9a9x4442V9r7VmUALAAAAAKD6E2RlhjykuGoVZj377LPRp0+fSJIkjjzyyKhbt2588sknMX369Bg6dGicf/758eKLLxZbZ+jQodG1a9cYOnRopdQwceLEWLx4cXTs2DEiInbdddcYNmxYXH755eu0Peecc2Lo0KGxZMmSSnnv6k6gBQAAAABQfQmyMkceUlyVhlk///nPY7/99ot27dpFz549Y968efH555/HZpttFtdee2106NAhdtlll2Ib/M0334yf/OQnsffee0e7du2KpYsjRoyIo48+OsaOHRuzZs2KW2+9NfLy8iIion379nH11VfHDTfcUNR++vTpMXPmzHjooYdi+PDh8c033xQt69y5c1x22WXRqVOn2HnnneO8886LiIivvvoqWrRoEcuXLy9qe8opp8Tdd98dERH33ntvnHLKKUXLdtttt9h7772L6vi+evXqRffu3eOxxx7b2E1ZYwi0AAAAAACqH0FW6eQhVZuHVGmYdfvtt8c777wT77//fnTq1Cmuu+66iPjukrh27drFu+++G3/961/jl7/8ZURELF68OPr06RN//OMfY8qUKTF58uTo1KlTRER8+umn0bRp02jZsmW899570aFDh6hXr16x9+vYsWNMnjy56OehQ4dG3759Y5tttokuXbrEE088Uaz9jBkzYvz48fHBBx/E6NGj480334xtttkmunXrFo888khERMyfPz/GjBkTffv2jYiICRMmxAEHHFDubdCxY8cYO3ZsicuXLVu2zqO0Zdl+rFmzpty/e0kEWgAAAAAA1UdVBFmrVq3K+vnuip6fL408pOw8ZGOsG59VosceeywefvjhWLlyZaxcuTK23HLLiIho0KBBHHvssRHx3S83Y8aMiPguhdx9992L/sPq1KkTzZo1i4j/u6SuLA0bNoyIiLVr18ZDDz0U//znPyMi4swzz4wbbrghzjnnnKK2J510UuTl5UVeXl60b98+ZsyYER07doyBAwfG2WefHeecc07cd9998bOf/SyaNGkSERFffvlltGjRotzboGXLlvHll1+WuLzwddenIu+TSTvttNNGv0ZhoBXx3f/DiBEj4sgjj9zo1wUAAAAAoPLceeedVXJF1sCBA2PgwIGV8lpVYX3n55MkKbG9PKTsPGRjVNmVWf/6179i8ODB8eKLL8YHH3wQt956a6xcuTIiIurXr1/U4XNzcyM/P7/M1xs+fHjRf96+++4b77777jpXCb355ptx0EEHRUTEyJEj4+uvv44ePXrEjjvuGP3794/33nsvPvjgg6L2DRo0KPp3bm5urF27NiIi9t9//2jUqFGMHz8+hgwZEv379y9q16hRo6LfozxWrlxZ1KEoLi8vL84888zIz8+PiRMnZrscAAAAAAB+4I033ohGjRrFKaecYmrBEshDvlOVeUiVXZn1v//9LzbZZJPYYostYvXq1XHvvfeWuc5BBx0Un376abz22mvRqVOnKCgoiK+//jpWrVoVS5cujV133TUiIrp06RLbbbdd/PKXv4zbb7898vLyYvLkyXHbbbfFU089FRHfXVJ3++23F839GBFx5ZVXxtChQ+O2224rs5aBAwfGL37xi2jbtm3stttuRc+3a9cupk+fHtttt125tsPHH38ce++9d4nLly5dWuznZcuWFSWd8+fPj8aNG5frfTLlN7/5TYwePbpSXmv8+PFx9NFHR/fu3ePKK6+slNcEAAAAAKDy3H777TF16tTo0qVLjBs3Ltq2bVsprzto0KA466yzKuW1KsuGnp+Xh3ynrDxkY1RZmHX44YfHI488ErvvvntsscUW0a1bt5gzZ06p62y++ebx7LPPxqWXXhrffvtt1KlTJ2644YaYM2dO9O7du6hdnTp14qWXXorL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WBXUXiNHjozDDz88Vq1aVfTcHXfcEWeddVYWqwKAdFi4cGEceOCBxU6UT5s2LQ444ICYN29eFiuD2mnNmjXRvXv3GDlyZNFzixcvjoMOOijee++9LFYGQGUSZgGQVV9++WVceeWV613261//2tSDkGGrVq2Kc889N/Lz89e5EuvBBx+MCRMmZKcwAEiJP/zhDzFr1qxiU5rl5+fHl19+Gddff30WK4Pa6cEHH4zXXnut2LFrfn5+LF++PAYMGFDsS5MAVF/CLACyatCgQSVOXVZQUBC33357ZguCWu6JJ56Ir776ar1/9Ofm5sYf/vCHLFQFAOmwePHiuPvuu4sFWYXy8/Pj/vvvjwULFmShMqidCgoK4g9/+EPk5OSssyw/Pz/eeOONePPNN7NQGQCVTZgFQNasXbs27r///vWeDIj47o+PoUOHxtq1azNcGdRe9957b9Sps/5DxPz8/Bg7dmx88cUXGa4KANLh8ccfjzVr1pS4vKCgIB5++OEMVgS126uvvhqzZs0q8eqrvLy8uO+++zJcFQBVQZgFQNZMnz49vv7661LbLFmyJKZNm5aZgqCWW716dbz99tslXi0ZEZEkSbzxxhsZrAoA0uO1114r8Usf328DZMZrr70Wubm5JS5fu3atabIBaohqE2a98847ccQRR0TEdyc2zz333GjdunXsvvvu0aFDh3juuefWWeeBBx6InJycSj2QPOGEE4ouT37hhReiQ4cOUb9+/bj44ouLtfvrX/8aN954Y6W9L0BNNHny5HK1mzRpUtUWAkRExMcff1zmlZB169Yt974LADXN22+/XeKsAhHfXZn1zjvvZLAiqN0mTZpU5j2xZs2aFd9++22GKgKoHPKQdVWbMOvZZ5+NPn36RJIkceSRR0bdunXjk08+ienTp8fQoUPj/PPPjxdffLHYOkOHDo2uXbvG0KFDK6WGiRMnxuLFi6Njx44REbHrrrvGsGHD4vLLL1+n7TnnnBNDhw6NJUuWVMp7A9REkyZNirp165baxolzyJzyBMdr166Nd999NwPVAEC6LF26NGbOnFlmuzlz5sT//ve/DFQElDWrQMR3Mwu8//77GaoIoHLIQ9aVV2WvHBErVqyI008/PaZOnRp169aNFi1aRL169eKUU06JU045JSIiXn755fjtb38bb731Vtx///1x6623Rr169YpunHrAAQdERMSIESPilVdeibFjx8asWbNi/PjxkZf3Xfnt27ePq6++Om644YY48sgjI+K7qatmzpwZb7/9drRt2za++eabaNq0aUREdO7cOfbbb79466234quvvoqf/vSncc8998RXX30V++yzT8ycOTMaNWoUERGnnHJKdOrUKc4///y49957i+qOiNhtt90i4ruO9UP16tWL7t27x2OPPRbnn3/+erfPsmXLSn1ufcuhutK3WZ+pU6eWes+BiIg1a9bElClT9BsiwlhS1d5///2oW7duqftlkiQxdepU27+S6duQPvZLfmjKlCllXgFSaNKkSUXnM6jdjCVVZ+XKlfHll1+Wq+3kyZOjffv2VVtQLaNvU1OVp283btx4vc/LQ0rPQzZaUoWeeeaZpHv37kU/L1q0KHn55ZeTjh07Fj3Xu3fv5KGHHkqSJEmaNm2afPXVV0mSJMnq1auTb7/9NkmSJPnkk0+Sgw46KEmSJLnpppuS3r17r/Ne7733XtKgQYOiny+//PLkyiuvTJIkSY455pjk3nvvLVp26KGHJn369EnWrFmTLF++PNlxxx2TN954I0mSJDnllFOK2s6bNy/Zaqutiupo3bp1MnXq1HXe+9prr00GDhy4zvN/+9vfkuOOO67E7RMRHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7V5FESeUjpecjGqtJpBvfee+/4+OOP44ILLognn3wy6tatGz/96U9jyZIlMWnSpJg1a1ZMnDgxTjzxxIiI6Nq1a/Tt2zcGDRoUM2fOjCZNmkTE/11SV5aGDRtGxHfT3zz00ENxxhlnRETEmWeeuc6ldSeddFLk5eVFw4YNo3379jFjxoyIiBg4cGDceeedERFx3333xc9+9rOiOr788sto0aJFuX//li1blvsbIgAAAAAAQPUkD6naPKRKpxls3bp1fPTRRzFu3LgYM2ZMXHHFFTF58uS46KKL4o477ogWLVrEmWeeGfXr14+IiKeffjrefffdmDBhQhx55JHx+9//Pk4++eQYPnx4/O1vf4uIiH333TcGDx4ca9asKXaflTfffDMOOuigiIgYOXJkfP3119GjR4+IiEiSJL766qv44IMPYs8994yIiAYNGhStm5ubW3Sz8/333z8aNWoU48ePjyFDhsSYMWOK2jVq1ChWrlxZ7t9/5cqVRR1qfZYuXbrOc8uWLSvqIPPnzy/xkkWobvRt1qdHjx7x+uuvl9muY8eO8corr2SgItLOWFK1Lrvsshg6dGiZ039uuummMWfOnAxVVTvo25A+9kt+6I033oju3buXq+3IkSOjc+fOVVsQ1YKxpOp8/fXXse2225bZrk6dOnHTTTdV3bRXtZS+TU21MX1bHlJ6HrKxqjTM+vLLL2PzzTeP3r17x+GHHx7Dhw+P2bNnR9++feN3v/td5Ofnx9tvvx0R36WHn3/+eey3336x3377xX//+9+YOHFiHHroobF06dLYddddIyKiS5cusd1228Uvf/nLuP322yMvLy8mT54ct912Wzz11FMR8d2Nzm6//fY477zzimq58sorY+jQoXHbbbeVWffAgQPjF7/4RbRt27ZoHsiIiHbt2sX06dNju+22K9fv//HHH8fee+9d4vKydoTGjRv7IKBG0rcp1KxZs8jJySn13gM5OTmx+eab6zOsw1hS+Zo1a1audk2aNLHtq5C+DeljvyQionnz5uVuu9VWW+kzrMNYUrnq1q1b5t+TEREFBQWxxRZb2PZVSN+mpqpo35aHlJ6HbKwqnWZw6tSpcfDBB8fee+8d++yzT/Tt2zfatWsXjRo1imOPPTYOPvjgog2Rn58fZ555Zuy5557Rvn37ePfdd+OSSy6J5557Lnr37v1/BdepEy+99FKsWrUqdtttt2jdunV07Ngxnn766dh7773jq6++irFjx8YJJ5xQrJaf//zn8cgjj8Tq1avLrPv444+PpUuXxoUXXrjO86NHjy76eezYsbHtttvGrbfeGkOHDo1tt902RowYUbR81KhRcfzxx2/QtgOoDfbcc8+im1eWJC8vL/baa68MVQS12x577FHmVVk5OTlVenAKAGm1++67R506ZZ9GycnJiR/96EcZqAhqt3r16sVOO+1UrraFVyYAVCV5SNXmITlJWV9fqAL5+fnRoUOHuOOOO6JTp06ltj388MPj97//fey3337rXb5y5cro169fLFiwIEaMGFHscrkN9c4778Qpp5wS06ZNK3agunTp0jjooIPizTffLDOR/eijj+Lcc8+N1157rULvvWzZsqI5KZcuXepbDdQY+jbr8/e//z1OOumkMts98cQT5WpHzWcsqVofffRR7LHHHqW2ycvLi1/96ldxww03ZKiq2kHfhvSxX7I+bdq0ienTp5faZscdd4yZ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wrHk5ptvjsaNG2e4QrLNWJJZCxYsiIEDB8bzzz8fy5YtW2+bhg0bxlFHHRW33XZbtGrVKsMV1ixjx46Na6+9Nl5//fUS22y77bZx5plnxrXXXivUqmHuvPPOGDBgwAav/8Pz8zk5ObHddttFhEArG4RZVay0qQXPOOOM2G+//Ur8w7SifvzjH8cXX3xRKa8FlWnt2rXRv3//op8vuOCCuP/++33zD6iQtWvXxoUXXlj08/nnnx9Dhw41llAjDB8+vOjf++23X7z44oux0047Za8gsm7atGnRpUuX2HLLLeO2226LDh06RP369YuWJ0kS33zzTbzyyisxePDgmD59eowePdqYWEkmTpwYffr0Kfr5rbfeii5dumSvoBJcfPHFcccdd0S/fv2iT58+0bx582In4dauXRvTp0+P+++/v+gki5PQtcv06dPjsMMOiy222CJuvfVWY0mGvf3229ViLPnlL38ZgwYNirPPPrvMsaR3797x3HPPxZFHHpnFiquv//3vf3HYYYfFokWL4te//nUccsgh6wTIy5cvj3/9619xxx13xGGHHRavv/56bLXVVlmqeP2++eabOO6444p+/tOf/hS/+93vUndLlbFjx0bPnj1j3333jQceeCB+9KMfFRvfkiSJhQsXxogRI+L3v/99zJ07N4YMGZLFiqlsn3/+eeywww7xzDPPVMrrNW7cOHbfffeinwVaGZZQZe6///4kIpLzzz8/KSgoqPL3y83NTe65554qfx+oiBUrViRHHnlkkpOTk0REEhFJTk5OcvjhhyfLly/PdnlANWEsoSa75557ivXt3NzcZKuttkref//9bJdGFp1zzjnJtttumyxatKjMti+//HISEclLL72Ugcpqvpdffjlp0KBBkpubW7Rf1qtXL3nxxRezXVoxc+bMSXJycpKbbrqpzLarV69ODj300GT//ffPQGWkybnnnpu0atUq+e9//1tm28KxJG19vbp65ZVX1juWvPDCC9kurZivvvqqQmNJ586djSUb4Y477kjy8vKSadOmldl2xowZSYMGDZK//OUvGais/BYsWJC0a9euWN/O5PnPijjkkEOSjh07JqtWrSqz7aBBg5KISGbOnFn1hZExl156adKmTZsqf5+VK1cmRx55ZNKgQYNk9OjRVf5+tZXrJqtIaVdkkV3Lli2LwYMHR9euXaNly5ZRr169aNmyZXTt2jUGDx4cy5cvz3aJNcodd9wRL730UiRJUvRckiTx8ssvxx133JHFykqnn1AWfSSzShtLBg8enMXKSqefZE513dafffZZ9O/fv1jfzs/Pj8WLF0ffvn2LPZ8m1XV7VycjRoyIk08+OZo1a1Zm227dusUuu+wSzz33XAYqq9mWLVsWJ598cqxatarYPTXWrFkTp5xySnzzzTdZrK64kSNHRp06deLss88us23dunXj7LPPjokTJ8bcuXMzUB1pUTiWbLHFFmW2NZZUntLGkp///OfGklpsxIgR0a1bt2JXdpSkdevWcfjhh6dun/zVr34VH3744Tr3nrr77rtjxIgRWapqXYsXL47XXnst+vXrF/Xq1Suz/Zlnnhn169dP1e9A9VF4hVaXLl3i6KOPjpdffjnbJdVIwqwqIMhKrxdeeCF22WWXGDhwYIwbNy5WrlwZ2267baxcuTLGjRsXAwcOjJ133jleeOGFbJdaI6xcuTJuvPHG9Z6IKygoiBtvvDFWrlyZhcpKp59QFn0ks8oaS/74xz8Wu0FrWugnmVOdt/WNN9643mPF/Pz8mDJlSrz44otZqKp01Xl7VxcFBQUxb968cp3oivhu7v7dd9895syZU8WV1XxDhgyJ//3vf+t85iT/fyq2u+++O0uVrWvOnDnRokWL2HzzzcvVvk2bNhERTkDXIgUFBTF37twKjSVt2rQxllSC++67LxYvXlziWHLXXXdlqbJ1VXQsKexPX331VVWWVWPNmTOn3PtkRKTu8/2LL76IBx98cJ0gKyKiTp068dvf/jY1X8aaN29eJElS7u3dpEmTaNWqVaq2N9WLQKvqCbMqWXmCrM6dO0dOTk7k5OTE5MmTy/W6Dz74YNE6F198ceUWXUsMGTIkevfuHQsXLox+/frFlClT4qOPPopnnnkmPv7443j//ffj7LPPjoULF0bv3r3NkVsJnnnmmfj6669LXL5kyZJ46qmnMldQOegnlEUfyTxjCaWpztt6yZIl8dhjj5V4/9S8vLxUneyKqN7buzopKCiIiKjQPWvq1q273hNLlF+SJHHnnXeWuLygoCDuuuuuov+fbMvPz69wHylcj9qh8ISysSSzquNYkpeXV+72xpKNsyHbO03beujQoSV+ab+goCCmTp0a77zzToarWr/C7VadtzdVqyrO0Qu0qpYwqxJV5Iqss88+O+bOnRt77rlnRHz3zYaePXtGo0aNonnz5nH55ZcXO7Fx0kknxdy5c6Njx45V/nvUROPGjYsLLrggGjduHKNGjYr77rsv2rVrF7feemvss88+MWjQoNhrr71iyJAhMWrUqGjUqFFccMEFMWbMmGyXXq29/PLLpR405OXlxSuvvJLBikqnn1AWfSQ7XnnllVLHktzcXGNJLVXdt/Ubb7wRq1atKnH52rVrY9y4can5g7q6b++a4vTTT48+ffpku4waafbs2TFjxoxSv1H+xRdfxMyZMzNY1YbRTyiLPlJ1vvzyy/jss89KHUtmz54d//nPfzJY1YbRTzKnumzrF198sdRj07T9bVaS6rK9qXpVcY5eoFV1hFmVpKJTCzZq1ChatmwZeXl5kZ+fHz179ozVq1fHG2+8EX/729/iwQcfjGuuuaaofcOGDYvuR0DFrF27Nvr37x/5+fnx6KOPRrdu3Upt361bt3j88ccjPz8/BgwYUOK3pSnb22+/Xer2W7t2bbz99tsZrKhk+gll0Ueyp6yxJD8/31hSC9WEbT158uTIzc0ttc3KlSvj008/zVBFJasJ2xvKUt5v5Ja3HVA7GUuoqfLz8+ODDz4otU2SJDFp0qQMVQQbr6rO0Qu0qoYwqxJs7D2yXn755fjoo4/ikUceifbt28cRRxwRN9xwQ9x5552xevXqKqq69hg7dmxMmzYtevXqFb169SrXOkcddVT06tUrpk2bFuPGjaviCmumVatWxfTp08ts98knn5T6rfRM0U8oiz6SHatXry73WJKGe/DpJ5lTE7b1pEmTynVPgTScEKgJ2xvKMmnSpDKnIsrLy0vFPgmkl7GEmurTTz8t82+ugoKC1HzRECqqss/RC7QqnzBrI21skBUR8eabb8Zee+0VLVq0KHquR48e8c0338SHH35YmeXWSoVT2/Tt27dC65166qkREdXi8ug0mj59ermmRcrPz4+PP/44AxWVTj+hLPpIdkyfPr1cV3QUFBQYS2qZmrCt33vvvTLvl1G3bt14//33M1RRyWrC9oayTJ06tcx9Mj8/P6ZMmZKhioDqyFhCTTV16tRytZs1a1YsW7asiquBylcV5+gFWpWr/HfAYx2VEWRFRMybN6/YThIRRT/PmzevQq+1atUqHxg/UDin/Q477BCtWrWKr776ap02N910U9x0001FP2+//fbx97//vWh927Ti5s6dW+628+bNy/o21k8oiz6SHcYSSlITtvX//ve/crX773//m/Vaa8L2rk42dFrG/Px823kjLFy4sMwT0EmSpGKfjIgNnsVjxYoVqaifqreh91w0lmwcYwmlKc9V+etbJw3bev78+RVq+8NznZm2fPnyDVpvzZo1qdjeVI41a9aUu21lnqP/vsJA69hjj42jjz46nnvuuejevfsGv15tJszaQEuXLo1+/fpFr169NirIqkxJksTAgQNj4MCB2S4llQ444IByt/3iiy/iwAMPjIiIp59+Op5++umqKouIOOKII7JdQhH9hLLoI+l15JFHZruEIvpJ5tT0bb1mzZq4//774/777892KRFR87d3dTd69Oho0qRJtsuo8f7973+nZjtvs802FV7n0EMPrYJKqElefvnl1PTxmuytt95KzXbeeuutK7xO586dK7+QWqBu3boVXmfevHmp6SvltfPOO2e7hA2yevXqGDx4cAwePDjbpVCJWrdune0SigKt/fffP04//fT1fjmQsplmcAM1bNgwevXqFS+//HKMHj16o16rZcuW63y7ofDnli1bbtRrAwAAAAAAJavqc/QPPvhgvP/++3HSSSdt9GvVVq7M2kC5ubnx1FNPxfHHHx99+vSJ4cOHx+GHH75Br9WxY8f4wx/+EAsWLIjmzZtHxHf3FWjatGm0bdu23K+Tk5MTgwYNirPOOmuD6qipxowZE3369ImjjjoqnnjiiWLLfvOb38TgwYPjkksuid/97nfFlp188skxcuTIeO6556Jr166ZLLlGePvtt+Owww4rV9tx48bF/vvvX8UVlU4/oSz6SHa888475f7W59ixYyt01UhV0E8ypyZs61133bXMqTTr1q0b55xzTrHp+7KhJmzv6mTt2rWx2WabVXi9Hj16uAJuIxx77LHluo9B586dY+TIkRmoqHTXX3990VSeFfHPf/4zOnToUAUVkTb5+fmx6aabVni97t27xzPPPFMFFdUOxx13XLm+9HzooYfGCy+8kIGKSve73/1unc/28pgwYULst99+VVBRzbbvvvtWeJ2WLVum4v7Ajz32WJxzzjnlajt79uzYfPPNq7ii0n3wwQdFswSUV7169eKiiy6KG2+8sYqqItN+85vflPs+VZV1jn597r333jjvvPNiwIABceutt27Ua9VmwqyNUK9evUoJtLp37x5t27aNvn37xp///OeYN29eXH311dG/f/+oX79+hV6rfv360bhx4wrXUJP17Nkz2rRpEyNHjowJEyZEz549i5YVXt5dt27dYtvtxRdfjJEjR0abNm3iyCOPjLw8u0pFtWvXrkJts91v9RPKoo9kh7GEktSEbd22bdsyw6y1a9fGHnvsoW/XMht6z6zc3Nys95XqrG3btjF+/PhS761Qt27dVOyTEd/9PbohGjZsmIr6qXobes8sY8nGadu2bYwbN85YwnptyG1KcnJyUrGt99prr3K122yzzaJVq1ZZvyVLo0aNNmi9Hx7TUr1VZGrPyjxH/33fD7IGDRqU9X2jOjPN4EYqDLS6d+8effr0iVGjRlX4NXJzc2PkyJGRm5sbHTt2jFNPPTV+8YtfrPPNVjZMXl5e/PWvf43c3Nz42c9+FmPHji21/dixY+Pkk0+O3NzcuOOOO5x42UDNmjUr17zbLVu2jC222CIDFZVOP6Es+kh2bL755uW6H0iLFi1iyy23zEBFpdNPMqcmbOt99923zD+ukiSJ9u3bZ6agUtSE7Q1lad++fZk3CV+zZk0q9kkgvYwl1FR77bVXuU7C77vvvk7WUy1VxTl6QVblEmZVgsoItHbYYYd48cUXY/ny5bFw4cK4+eab/dFfibp27Rp33nlnLFu2LHr06BEXXHBBfPjhh9G/f/947bXX4rzzzouPPvoo+vfvHz169Ihly5bFnXfeGd26dct26dXaj3/846hTp+Rhpk6dOvHjH/84gxWVTj+hLPpIdhhLKEl139b77LNPmSe7cnJyKnSFYlWq7tu7pnjwwQdj+PDh2S6jRtpnn30qtV026SeURR+pOsYSNkR12NaNGzeO1q1bl9qmbt261WIq2+qwvcmOyjxHL8iqfNKSSlLRKQfvuuuuuP/+++PNN98s12W6jz76aJx77rmxYsUK397ZQOeee25su+22cdZZZ8Xdd98dd999d2y22WbRrFmzWLx4cXz99dcR8d23+4cOHVps+hw2TMeOHUu9n0BOTk507NgxgxWVTT+hLPpI5nXs2DGef/75EpcbS2q36ryty7rHW05OTrRt2zZV05xU5+0NZfnRj34UjRs3jmXLlpXYpkGDBrHHHntksCqgumnTpk00adIkli5dWmKbBg0axJ577pnBqqBydOrUKWbNmlXilMhr1qyp8H2qIJuq6hy9IKtquDKrEpX3Cq1HH300Pvroo5g8eXLsvvvu5Xrt3r17x+TJk2P69Olx9dVXV2bZtUrPnj1jxowZcfvtt0eXLl2iXr16MXv27KhXr1506dIlBg0aFDNmzHDipZL07du31OUFBQVltskG/YSy6COZdeqpp5a6vKCgIH7xi19kqJry008yp7pu69atW8dPfvKTyM3NLbHN2WefncGKyqe6bu/qqKCgoErasn5169aN0047rcRv3+bl5UXfvn2jQYMGGa6sZPoI5aGfZFZ5xpKf//znqRpLkiQpd1t9ZONV5+19xhlnlHpvz8022yx1x4DVeXtTtarqHL0gq+q4MquSlecKrVatWlX4dTfZZJPYZJNNKqvMWq1x48YxcODAGDhwYLZLqfFatWoVp5xySjzxxBPrHOzk5eXFiSeeGNtuu22WqiudfkJZ9JHMKWssOeGEE4wlVNttfeWVV0avXr3Wu6xp06ZxxhlnZLii8qmu27u6yMvLi4YNG8aCBQvKvc6CBQvKnPqHsl188cVxzz33rHdZfn5+XHzxxZktqBSbbrppLF68ONauXVuu6W8K+9Omm25a1aWRErm5udGoUaMKjyU77rhj1RVVS1x88cVx9913r3dZfn5+XHLJJRmuqGQVHUsWLlwYEd+FFlTcpptuWqF9cuHChana1p06dYp99903pkyZEvn5+cWW1alTJy655JKoX79+lqorrvDzrrzbu6CgIBYtWpSq7U3Vqopz9IKsquXKrCpQGffQgprihhtuiKZNmxb71nlubm5ssskm8fvf/z6LlQHVSeFY8v0/sI0l1AQ9e/aMnj17rve+cLfeems0bdo0C1WRBocddliMGDGiXN8mnjdvXkycODEOO+ywDFRWs+26665x+eWXr3fZxRdfHG3bts1wRSU77LDDYtmyZTFu3LhytX/uueeiVatWscsuu1RxZaRJRceSt956y1hSCXbZZZe44oor1rts4MCBqRxLxo4dW672xpKNc9hhh8WoUaNi1apVZbZdu3ZtvPDCC9G5c+eqL6yccnJy4o477oi8vLxix6+5ubmx0047pepLH9ttt120bt06nnvuuXK1/9e//hWLFy9O1famehFkVT1hVhURaMF3dtxxx3jrrbdim222KXpu6623jrfeeit22mmnLFYGVCc77rhjTJw4sdhYss0228Rbb73lSgSqtZycnHjmmWfi+OOPL3ouNzc3HnrooTjzzDOzWBnZ1q9fv3jjjTfioosuirlz5663TZIkMWnSpOjVq1dsttlm0adPn8wWWUP98Y9/jOuvv77Yc1dccUXccsstWapo/Tp06BDt27eP008/PV555ZV1viFf6Jtvvonbb7897r777jjrrLPWG55Tc/Xr1y/efPPNGDBgQJljSe/evWPTTTeNY445JsNV1kw33nhj/O53vyv23FVXXRW33nprlipav3333Tf22WefOOOMM+KVV14pcQq5b775JgYNGhR33313nHnmmcaSDXTqqafGt99+G8cff3x88sknJbabMWNGnHzyybFgwYLUTal+0EEHxbhx44rd17Vdu3bx73//O1WzSuXk5ES/fv1i2LBh8ec//7no3q4/lJ+fH+PHj4++fftGmzZt4qCDDspsodQIgqzMyEkqMnEoFbZ69eo4/vjj4+WXX15nysG33norTjvttHUOFFavXh0R3wViFTFjxoy47777ol+/fhtfOFSyL774InbYYYeIiPj888+L/g1QEYsWLYpXX301kiSJQw45JLbccstslwSV4ttvvy26CmvixInx4x//OMsVkQaDBw+OSy+9NPLz86NVq1bFpu1JkiSWLFkSixYtihYtWsRLL70U++yzTxarrXkmTJhQdIXK0qVLi520S4v58+dHjx49YsqUKbHJJpvElltuWewE85o1a2Lu3LmxZs2aOOecc+Luu+92AroWuuOOO+KSSy4xlmTJxIkT49NPP43WrVtHx44ds13OehlLMmvUqFFxwgknxNKlS6Nly5bRqFGjohPfSZLEihUrYu7cudGoUaN49NFHU/tllenTp0ebNm0i4rs+1Lx58yxXtK4kSeLiiy+OwYMHR926dWPrrbeOunXrFi0vKCiIxYsXx5IlS6Jt27bx8ssvb9DUc6TXlVdeGbfeemuFz0OWdH6+adOm8fTTTxf7gr4gK3OEWRlQUqB1zTXXxO233x79+/cvartmzZqib/xdeumlxQbYsjRq1Cguv/zyVN1EFAotW7YsmjRpEhHpPRkAANnic5KSLF68OJ5//vmYMWNGrFmzptiyRo0axQEHHBCHHXZYhf5uoHyqy36ZJEm8/fbbMW7cuFiyZEmxZbm5ubHNNttEr169YrvttstShaRBWWPJ/vvvH126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j8svvzx23XXXaNSo0Tpjd/PmzePEE0+MUaNGZbtUgEo3efLkOPfcc2O77baLBg0aFBv/GjRoEK1atYqzzjor3n777WyXmmqPPPJIhc6vl3V+fsWKFdGwYcOIEGhlQ162C6jpSrsi65prrokdd9wxCgoKKuW9rrjiinjjjTeiU6dOlfJ6UJkeeuihon//7W9/iwsuuCCL1UDtkiRJXHnllfGXv/wlDjnkkLj00ktj0003jZycnKI2q1evjvfeey9uvvnmePbZZ2Ps2LGxxRZbZLFqqF0+/vjjon9fdNFFcccdd0SjRo2yWFHNsXr16jj55JPj+eefj8MPPzyuvvrqdbbt8uXL41//+ldcccUVMXbs2Hjqqaeifv36WaqYNFiwYEGcf/75RT/Pnz8/WrduncWKoHb57LPPonPnzrFq1ao47rjjok2bNlG3bt2i5UmSxMKFC+P555+PI444Iu65554499xzs1gx1C5JksRNN91U9PMrr7wSffr0yV5BNcyrr74aRx55ZGy55ZZx4oknxg477BC5ublFy/Pz82P27NnxzDPPxGOPPRYjRoyIn/70p1msOL0mTJgQ22yzTVx22WWV8noNGzaM0047rejnwkCra9eu0aVLlxg3blzssccelfJerCsnSZIk20XUVJmeWnDLLbeMyy+/PK688soqfR+oiCRJ4ne/+11cd911xZ6/9tpr49prry12Mh2oGu+991506NAhbr755rj00ktLbfvhhx/GwQcfHOeee26xP06AqvPGG2/E4YcfHt9++21ERNSpUyd+/OMfx4svvhjNmjXLcnXV36OPPhqnnnpqjBw5Mnr27Flq29GjR8cRRxwRw4YNi9NPPz0zBZI6//nPf6Jr164xe/bsyM/Pj4iIbbfdNsaNGxe77rprlquD2uGYY46JKVOmxOuvvx5bb711ie2SJInzzz8/hg4dGvPnz/e5CRmwZs2aOOuss+Lhhx8uei43NzeGDRsWv/jFL7JYWc2QJEm0adMmmjdvHqNHjy71C24rV66Mo446Kj799NP4/PPPnWNbj379+sWHH34Yb775ZpW+z3//+9/o2rVrzJs3T6BVhUwzWEXcI4vKtmzZshg8eHB07do1WrZsGfXq1YuWLVtG165dY/DgwbF8+fJsl7heTz755DpBVkTE9ddfH0888UTmC6rBqmsfoeo9/fTT0axZs7jooovKbLvHHnvESSedFE899VQGKgMWL14cPXr0iGXLlhU9V1BQEO+8844wpZI8/fTTceCBB5YZZEVE9OjRIw4++GBjYCWorscl+fn5ceSRR8aXX35ZFGRFRMydOzeOOOKIWLt2bRarq3mqaz+hai1btixeeuml6N+/f6lBVkRETk5OXHvttZGfnx8jRozIUIVQu914443xyCOPFHsuPz8/TjvttHj33XezVFXN8eGHH8Ynn3wSv/71r8ucqaFBgwZx1VVXxRdffGG6wSwz5WBmCLOqgCCLyvbCCy/ELrvsEgMHDoxx48bFypUrY9ttt42VK1fGuHHjYuDAgbHzzjvHCy+8kO1SiykoKIirr7466tRZd6ipU6dOXH311ZU2zWZtV137CJnx6aefxr777ltsapbS7L///vGf//zHCTvIgNtuuy2WL1++zudhfn5+PP/8804IVIJPPvkkfvzjH5e7/QEHHBCffvppFVZU81Xn45J//OMfMX369HU+A/Pz82PGjBnx2GOPZamymqc69xOq1uzZs2PVqlXlHru33nrr2H777Y3dkAFLliyJP//5z7G+ib7y8vLit7/9bRaqqlkKx7L999+/XO0POOCAYuuRPQKtqifMqmTlCbI6d+5cdBO5yZMnl+t1J0yYULSOOWhrlyFDhkTv3r1j4cKF0a9fv5gyZUp89NFH8cwzz8THH38c77//fpx99tmxcOHC6N27dwwZMiTbJRcZN25czJgxY72BVUFBQfznP/+JsWPHZqGymqU69xEyY/Xq1RX6YkVh2zVr1lRVSUB8t2/eddddJX6xIy8vL+66664MV1XzbMgYuHr16iqsqGar7scld9xxx3q/iBXx3Zex7rjjjgxXVDNV935C1Socg43dkD6PPPJIrFixYr3L1q5dG6NGjYqZM2dmuKqapaJjYGE7Y2DFVcU5eoFW1RJmVaKKXJF19tlnx9y5c2PPPfeMiO9u9N2hQ4eoX79+tG/ffp32Bx10UMydOzdOPPHEqiqfFBo3blxccMEF0bhx4xg1alTcd9990a5du7j11ltjn332iUGDBsVee+0VQ4YMiVGjRkWjRo3iggsuiDFjxmS79IiIePbZZyMvL6/E5Xl5efHss89msKKap7r3EbLv9NNP9yUJyJJ///vfsXjx4hKXr127Np566qn1fvOVjWf8q3zV/bhk0aJF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UKTKBo+L58/v3bu3V+0vtuvNufX0L0FW//OUqp8ItOBf06dPj507d0Y2m42SkpIoKSmJbDYbO3fujHvuuafY5cGQ8Mgjj0RHR0esXLkyzp07d9V2uVwuduzYEc3NzbFo0aICVghDVzabjZaWlpg8eXKk0+nIZDIREbFixQovfeTJ4sWL4/jx4/Hyyy/3uC1OZ2dnvPrqq9Ha2moMHOKamppizpw5kUqlIpPJRCqVilmzZsWHH35Y7NJgSMhkMvHoo4/Gli1b4tChQz22PX36dKxcuTIqKyvj/vvvL1CFMLQ99thjsXXr1kvfk+l0OkaMGBEtLS1x2223Fbu8AW/06NExc+bM2LBhQ5w4caLHtidPnoz169dHXV1dTJo0qUAVciWCrMJI5Wys2a9OnjwZjY2NUVJSctmWg21tbfHiiy/mbW/TpqameOmll+K5557Ly/0gnz799NNoamqKXC4XCxcujIaGhmKXBEPKjh07YunSpVFeXh733ntvVFZWdlsxef78+fjss8+ira0tFi1aFO+9995VzwwB8u/PP/+M119/PX799deor6+PZcuWdfuM8t+88sorsWHDhrj55ptjxowZUV5efunnm8vl4u+//47Dhw/HL7/8Es8//3y88MILfv5D3IULF2Lr1q1x/PjxmDBhQjzzzDOXwmag/7W3t8e8efPi0KFDUVNTE7fffnu3z2BXV1f89ttvcfDgwchkMrFnz56YMWNGESuGoeejjz6KPXv2RHl5+aXdBsiPU6dORWNjY3z77bcxffr0GDt2bKTT6UvXOzs749SpU3H48OGoqqqK/fv3x/jx44tYcXItX748mpubY+7cuXm5X1lZWWzcuDFGjhx56e8EWYUjzCqAqwVaa9asie3bt+ftoX42m41t27b58gDgir7++utoamqKo0ePxpkzZ7pdy2QyMX78+Hj44Ydj9uzZ3SbKAIPBJ598Ek1NTfHFF19ctkq1tLQ07rzzzli4cGFMmzZNkAWQAOfOnYvdu3dHc3Nz/PDDD5etrr3xxhujsbExFi5ceNlZ5QAD3enTp6O5uTl2794dv//+e3R1dV26lkqlYtSoUfHAAw/EQw891C1YobsjR47E+vXr83a/gwcPxubNm2P16tURIcgqNGFWgVwp0FqzZk0cOHAgPv/882KXBwAAAAAAXEV5eXls2rQpVq9eLcgqAmdmFYgztAAAAAAAYGATZBWHMKuABFoAAAAAADAwHTt2TJBVJLYZLIKLWw5+9913UVNTY5tBAAAAAABIsPLy8jh79mzU19cLsoqgpNgFDEUXV2g1NjZGdXV1scsBAAAAAAB6UF1dHel0WpBVJFZmFdG5c+di+PDhxS4DAAAAAADoQS6Xi3/++SdKS0uLXcqQJMwCAAAAAAAgsYYVuwAAAAAAAAC4GmEWAAAAAAAAiSXMAgAAAAAAILGEWQAAAAAAACSWMAsAAAAAAIDEEmYBAAAAAACQWMIsAAAAAAAAEkuYBQAAAAAAQGIJswAAAAAAAEgsYRYAAAAAAACJJcwCAAAAAAAgsYRZAAAAAAAAJJYwCwAAAAAAgMQSZgEAAAAAAJBYwiwAAAAAAAASS5gFAAAAAABAYgmzAAAAAAAASCxhFgAAAAAAAIklzAIAAAAAACCxhFkAAAAAAAAkljALAAAAAACAxBJmAQAAAAAAkFjCLAAAAAAAABJLmAUAAAAAAEBiCbMAAAAAAABILGEWAAAAAAAAiSXMAgAAAAAAILGEWQAAAAAAACSWMAsAAAAAAIDEEmYBAAAAAACQWMIsAAAAAAAAEkuYBQAAAAAAQGIJswAAAAAAAEgsYRYAAAAAAACJJcwCAAAAAAAgsYRZAAAAAAAAJJYwCwAAAAAAgMQSZgEAAAAAAJBYwiwAAAAAAAASS5gFAAAAAABAYgmzAAAAAAAASKz/A0CXnGP+x3uvAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "class UnaryIterator(Bloq):\n", + " ctrl_bitsize: int\n", + " state = []\n", + " sys_bitsize: int\n", + "\n", + " @property\n", + " def signature(self) -> Signature:\n", + " return Signature([Register('ctrl', QAny(self.ctrl_bitsize)), Register('anc', QAny(self.ctrl_bitsize - 1)), Register('sys', QAny(self.sys_bitsize))])\n", + " \n", + " def set_ops(self, ops):\n", + " self.ops = ops\n", + "\n", + " def compute(self, query: List[bool], bb: BloqBuilder, ancs, ctrls):\n", + " for ix in range(len(self.state)):\n", + " assert self.state[ix] == query[ix]\n", + " if len(self.state) == len(query):\n", + " return\n", + " if len(self.state) == 0:\n", + " g0 = XGate() if query[0] == False else Identity()\n", + " g1 = XGate() if query[1] == False else Identity()\n", + " ctrls[0] = bb.add(g0, q=ctrls[0])\n", + " ctrls[1] = bb.add(g1, q=ctrls[1])\n", + " ctrls[:2], ancs[0] = bb.add(Toffoli(), ctrl=ctrls[:2], target=ancs[0])\n", + " ctrls[0] = bb.add(g0, q=ctrls[0])\n", + " ctrls[1] = bb.add(g1, q=ctrls[1])\n", + " self.state.append(query[0])\n", + " self.state.append(query[1])\n", + " else:\n", + " ctrl_ix = len(self.state)\n", + " g = XGate() if query[ctrl_ix] == False else Identity()\n", + " ctrls[ctrl_ix] = bb.add(g, q=ctrls[ctrl_ix])\n", + " [ctrls[ctrl_ix], ancs[ctrl_ix - 2]], ancs[ctrl_ix - 1] = bb.add(Toffoli(), ctrl=[ctrls[ctrl_ix], ancs[ctrl_ix - 2]], target=ancs[ctrl_ix - 1])\n", + " ctrls[ctrl_ix] = bb.add(g, q=ctrls[ctrl_ix])\n", + " self.state.append(query[ctrl_ix])\n", + " self.compute(query, bb, ancs, ctrls)\n", + "\n", + " def uncompute(self, query: List[bool], bb: BloqBuilder, ancs, ctrls):\n", + " first_diff_ix = None\n", + " if len(query) == 0:\n", + " first_diff_ix = 0\n", + " else: \n", + " for ix in range(len(self.state)):\n", + " if self.state[ix] != query[ix]:\n", + " first_diff_ix = ix\n", + " break\n", + " if first_diff_ix is None:\n", + " # state is a prefix of query so we do not need to uncompute\n", + " return\n", + " if first_diff_ix < len(self.state) - 1:\n", + " # we have some extra bits we have to undo\n", + " if len(self.state) == 2 and first_diff_ix == 0:\n", + " # we are the bottom of the barrel\n", + " g0 = XGate() if self.state[0] == False else Identity()\n", + " g1 = XGate() if self.state[1] == False else Identity()\n", + " ctrls[0] = bb.add(g0, q=ctrls[0])\n", + " ctrls[1] = bb.add(g1, q=ctrls[1])\n", + " ctrls[:2], ancs[0] = bb.add(Toffoli(), ctrl=ctrls[:2], target=ancs[0])\n", + " ctrls[0] = bb.add(g0, q=ctrls[0])\n", + " ctrls[1] = bb.add(g1, q=ctrls[1])\n", + " self.state.pop()\n", + " self.state.pop()\n", + " else:\n", + " ctrl_ix = len(self.state) - 1\n", + " g = XGate() if self.state[ctrl_ix] == False else Identity()\n", + " ctrls[ctrl_ix] = bb.add(g, q=ctrls[ctrl_ix])\n", + " [ctrls[ctrl_ix], ancs[ctrl_ix - 2]], ancs[ctrl_ix - 1] = bb.add(Toffoli(), ctrl=[ctrls[ctrl_ix], ancs[ctrl_ix - 2]], target = ancs[ctrl_ix - 1])\n", + " ctrls[ctrl_ix] = bb.add(g, q=ctrls[ctrl_ix])\n", + " self.state.pop()\n", + " self.uncompute(query, bb, ancs, ctrls)\n", + " elif len(self.state) == 0:\n", + " return\n", + " else:\n", + " # first_diff_ix is the last bit, so we just need to do the CNOT trick\n", + " if first_diff_ix == 1:\n", + " g = XGate() if self.state[0] == False else Identity()\n", + " ctrls[0] = bb.add(g, q=ctrls[0])\n", + " ctrls[0], ancs[0] = bb.add(CNOT(), ctrl=ctrls[0], target=ancs[0])\n", + " ctrls[0] = bb.add(g, q=ctrls[0])\n", + " self.state[1] ^= True\n", + " else:\n", + " ancs[first_diff_ix - 2], ancs[first_diff_ix - 1] = bb.add(CNOT(), ctrl=ancs[first_diff_ix - 2], target=ancs[first_diff_ix - 1])\n", + " self.state[first_diff_ix] ^= True\n", + " return\n", + "\n", + " def build_composite_bloq(self, bb: BloqBuilder, ctrl: SoquetT, anc: SoquetT, sys: SoquetT) -> Dict[str, 'SoquetT']:\n", + " queries = list(self.ops.keys())\n", + " queries.sort()\n", + " ctrls = bb.split(ctrl)\n", + " ancs = bb.split(anc)\n", + " for q_int in queries:\n", + " q_bools = int_to_bool_list(q_int, self.ctrl_bitsize)\n", + " self.uncompute(q_bools, bb, ancs, ctrls)\n", + " self.compute(q_bools, bb, ancs, ctrls)\n", + " [ancs[-1], sys] = bb.add(self.ops[q_int], q=[ancs[-1], sys])\n", + " self.uncompute([], bb, ancs, ctrls)\n", + " ctrl = bb.join(ctrls)\n", + " anc = bb.join(ancs)\n", + " return {'ctrl': ctrl, 'sys': sys, 'anc': anc}\n", + " \n", + "cbloq = UnaryIterator()\n", + "cbloq.ctrl_bitsize = 2\n", + "cbloq.sys_bitsize = 1\n", + "ops = dict()\n", + "for ix in range(4):\n", + " ops[ix] = CZPowGate(exponent=float(ix))\n", + "cbloq.set_ops(ops)\n", + "msd = get_musical_score_data(cbloq.decompose_bloq())\n", + "fig, ax = draw_musical_score(msd)\n", + "fig.set_figwidth(18)\n", + "fig.set_figheight(7)" + ] + }, + { + "cell_type": "markdown", + "id": "266e8149", + "metadata": {}, + "source": [ + "And we can see that we have replicated almost exactly the hand-crafted circuit from the beginning! The only difference is that we have extra factors of $X^2$ gates floating around, but since $X^2 = I$ these would be cancelled if we ran a circuit optimizer. The cool thing about this implementation is that now we can replicate more complicated circuits, such as Fig. 7 in [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/pdf/1805.03662) just by changing the bit sizes and operations used. Create a code block below and see what you can make, or if you can eliminate the duplicated Pauil X gates." + ] + }, + { + "cell_type": "markdown", + "id": "eac11000", + "metadata": {}, + "source": [ + "# Measurement Based Uncomputation\n", + "\n", + "Now that we have a recursive implementation of a unary iteration cirquit we can further reduce the number of Toffoli gates used by introducing a trick for the `uncompute` step called measurement based uncomputation. The key idea is that instead of using a Toffoli to uncompute an ancilla we can change the bit information of the ancilla into a relative phase, then measure the ancilla and apply a correction phase to the state if needed. We will walk through mathematically how this works, then we will implement a bloq that can act as a measurement and classically controlled operations based on the measurement outcome.\n", + "\n", + "To start with measurement based uncomputation, assume we have three qubits in the state $|a \\rangle |b\\rangle |0\\rangle$ and we apply a Toffoli gate to get $|a\\rangle |b\\rangle |a \\otimes b\\rangle$. Now to uncompute we apply a Hadamard gate to the third qubit. If $a \\otimes b$ is 0, then the Hadamard will put the third qubit in the state $|0\\rangle + |1\\rangle$ (up to normalization). If $a \\otimes b$ is 1, then we get $|0\\rangle - |1\\rangle$, so the third qubit is in the state $|0\\rangle + (-1)^{a \\otimes b} |1\\rangle$. After the Hadamard gate we then perform a measurement on the third qubit. If the measurement is 0 then we are good, however if the measurement is 1 then we have to correct for the phase of $(-1)^{a \\otimes b}$ that was introduced. This can be easily corrected by performing a CZ gate between $a$ and $b$, which takes 0 T gates to do.\n", + "\n", + "The rest of this notebook will walk through a mock-up of how to implement these ideas in Qualtran, starting with a a `Measure` bloq and followed by the classically controlled logic. We then will put this together in the unary iterator bloq we constructed earlier and see how many T-gates we can save. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c368cc72", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from qualtran import CompositeBloq\n", + "from qualtran import Side\n", + "from qualtran._infra.bloq import DecomposeTypeError\n", + "from qualtran.bloqs.basic_gates.hadamard import Hadamard\n", + "from qualtran.bloqs.basic_gates.z_basis import CZ, OneEffect, ZeroEffect, ZeroState\n", + "\n", + "class MeasurementUncomputation(Bloq):\n", + " outcome: None | bool\n", + "\n", + " @property\n", + " def signature(self) -> Signature:\n", + " return Signature([Register('ctrl', QBit()), Register('q1', QBit()), Register('q2', QBit())])\n", + " \n", + " def build_composite_bloq(self, bb: BloqBuilder, ctrl: SoquetT, q1: SoquetT, q2: SoquetT) -> Dict[str, 'SoquetT']:\n", + " ctrl = bb.add(Hadamard(), q=ctrl)\n", + " bb.add(OneEffect(), q=ctrl)\n", + " q1, q2 = bb.add(CZ(), q1=q1, q2=q2)\n", + " ctrl = bb.add(ZeroState())\n", + " return {'ctrl': ctrl, 'q1' : q1, 'q2': q2}\n", + " \n", + "meas = MeasurementUncomputation() \n", + "msd = get_musical_score_data(meas.decompose_bloq())\n", + "fig, ax = draw_musical_score(msd)\n", + "fig.set_figwidth(5)\n", + "fig.set_figheight(2)" + ] + }, + { + "cell_type": "markdown", + "id": "11ab54b6", + "metadata": {}, + "source": [ + "In an ideal world we would actually implement a measurement and use the outcome to determine if we perform the CZ gate, but since we are only interested in the worst-case Toffoli/T-gate analysis then always performing the CZ will suffice. Now we replace all the Toffoli's in the `uncompute` function for our `UnaryIterator` bloq below:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "28e68d3f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABrMAAAI7CAYAAABP3WAsAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAACDu0lEQVR4nOzdd5gV5fk/4GfZpSMqKqBYsRFURDEqGhSBgIogdmMkNqyIGGsSjSUmJiY2MDYUNHYTCyIqKC0aNWIBxAIqQUSkBSJKh935/eFv9+sK22D3nNnd+76uc13smXfOeXZ45z2z8znzTk6SJEkAAAAAAABACtXJdgEAAAAAAABQEmEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBUCHz5s2LAQMGROvWraN+/fqx3XbbRa9evWLs2LFx3XXXRU5OTomP66+/vthrnX766fH555+v931KWjZhwoTYd999o379+rHLLrvEgw8+WGbN77//fnTq1CkaNGgQ2223Xfz5z3/egN8carbK3LcBIC2yeez6zDPPxE9/+tPYaqutomnTptGxY8cYPXp0mTU7dqUmy+Y++a9//SsOPvjg2GKLLaJhw4bRpk2buO2228qs2T4JkA7CLADK7fPPP48OHTrEuHHj4i9/+UtMnTo1Ro0aFYcddlj0798/Lrvsspg7d+46j9NPPz0222yzOOWUU2Lx4sVx5513RpIkRa87Y8aMePTRR0tdFhExc+bM6NmzZxx22GExefLkuPjii6Nfv36lnhT45ptvonv37rHDDjvEu+++G3/5y1/iuuuuiyFDhlTdhoJqpjL2bQBIm2wfu7766qvx05/+NF588cV4991347DDDotevXrFpEmTSqzZsSs1Wbb3ycaNG8eFF14Yr776anz88cdx9dVXx9VXX13q/mWfBEiPnOT7IzwAlOLII4+M999/P6ZPnx6NGzcutuzrr7+OzTbbbJ11Hn300TjttNPihRdeiB49esTy5cvj97//fbz77ruRn58f++23X7z++utx0003Rfv27UtcdtBBB8WVV14ZL7zwQnzwwQdFr3/yySfH119/HaNGjVpvzXfffXdcddVVMW/evKhXr15ERPzqV7+K4cOHx7Rp0ypv40A1Vhn7NgCkTbaPXddnjz32iJNOOimuueaa9S537EpNlsZ98thjj43GjRvHww8/vN7l9kmAFEkAoBwWLVqU5OTkJDfeeGO513nnnXeShg0bJn/5y1/WWfbCCy8kubm5yaGHHpqsXr26XMs6deqUDBw4sFjbYcOGJU2bNi2xhr59+yZHH310sefGjRuXRESyePHicv8uUFNV9r4NAGmQhmPXH8rPz0+222675I477iixjWNXaqo07pPvvfde0qJFi+S+++4rsY19EiA9TDMIQLl89tlnkSRJtGnTplztFyxYEMccc0wcd9xxcdlllxU9v3Llyrjmmmti0KBB0blz5zjwwAOjW7duMXHixFKXRXw3v3qLFi2KvU+LFi3im2++iRUrVqy3jpLWKVwGtV1l7dsAkCZpOHb9oZtvvjmWLl0aJ554Yol1OHalpkrTPrnttttG/fr1Y7/99ov+/ftHv379SqzDPgmQHsIsAMolqcCstGvWrInjjz8+WrRoEffdd1+xZcuXL48WLVrEqFGjYtttt43zzjsvhg0bFp988kmpy4CqUVn7NgCkSdqOXR977LG4/vrr4+9//3s0b958o38/qG7StE++9tpr8c4778Q999wTt99+ezz++OOV8jsCULXysl0AANXDrrvuGjk5OeWaF/yiiy6KTz/9NN5+++1o0KBBsWXNmjWL/v37F3tu5513jp133jkiotRlLVu2jPnz5xdbPn/+/GjatGk0bNhwvbWUtE7hMqjtKmvfBoA0ScOxa6Ennngi+vXrF//4xz+iW7dupdbi2JWaKk375E477RQREXvttVfMnz8/rrvuuvjZz3623lrskwDp4cosAMqlWbNm0aNHj7jzzjtj2bJl6yz/+uuvIyJiyJAhMWzYsHj66adj2223LfU1H3zwwdhxxx3Lvaxjx44xduzYYs+98sor0bFjxxLfo2PHjvHqq6/GmjVriq2z++67x+abb15qfVAbVMW+DQDZloZj14iIxx9/PM4444x4/PHHo2fPnmXW7diVmiot++QPFRQUxKpVq0pcbp8ESA9hFgDlduedd0Z+fn7sv//+8fTTT8enn34aH3/8cQwePDg6duwYr7/+egwYMCCuueaaaN26dcybN6/YY8mSJRv1/uedd1785z//iSuuuCKmTZsWd911V/z973+PX/7yl0Vt/vrXv0bXrl2Lfj7llFOiXr16cdZZZ8WHH34YTz75ZAwaNCguueSSjaoFapJs79sAUBWy/fn22GOPxS9+8Yu45ZZb4oADDljv6zp2pTbJ9j555513xvPPPx+ffvppfPrppzF06NC4+eab49RTTy1qY58ESLEEACrgq6++Svr375/ssMMOSb169ZJWrVolvXv3TsaPH5+cfvrpSUSU+DjttNM2+v3Hjx+ftG/fPqlXr17SunXr5IEHHii2/Nprr0122GGHYs9NmTIl+clPfpLUr18/adWqVfKnP/1po+uAmibb+zYAVIVsfr4deuihZb6uY1dqm2zuk4MHD0722GOPpFGjRknTpk2TffbZJ7nrrruS/Pz8ojb2SYD0ykmSCtyBEQAAAAAAADLINIMAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCrBRZuHBhrFmzJttllGnu3LmRJEm2ywAAAAAAoJpZu3ZtLFiwINtllOnbb7+Nb7/9Nttl8P8Js1JiypQpse2228bJJ5+c6kBr/PjxsfPOO0ffvn2zXQoAAAAAANXMwQcfHO3atYuPPvoo26WUaMmSJfHTn/40WrVqFf/73/+yXQ4hzEqNpUuXxurVq+OZZ55JbaA1fvz46NmzZ6xYsSIWLVqU7XIAAAAAAKhmFi1aFPPnz48uXbqkMtBasmRJ9OjRI95666349ttvU3muvjYSZqXMn//853j++edTF2gVBlmdOnWKI444ItvlAAAAAABQTZ155pnRvHnz1AVahUHW9OnT49prr812OXyPMCtlevXqFU8//XSqAq3vB1nDhw+Phg0bZrskAAAAAACqqa222irGjh2bqkDr+0HWmDFjokOHDtkuie8RZqVQmgItQRYAAAAAAJUtTYGWICv98rJdQG01Y8aMOO6442Lp0qUREbFixYqIiMjNzY2I/wu0jjvuuDj55JPjiSeeiLp162a0xpKCrNzc3PjnP/8Zu+yyS6W8zzbbbBPDhw+PZs2aVcrrAQAAAABQOX54LntjzZo1q+g8eGGg1bVr1+jSpUuMGzcu2rZtWynvU14lBVmFNe6///6Rl/ddlLLNNtvEc889F5tvvnlGayQiJ0mSJNtF1EYPPPBAnHnmmXHFFVdETk5ORETsuuuucdZZZxVr9/zzz8dxxx0XvXr1ymigVdoVWf/5z39iyJAhlfI+ixYtivvvvz9effXV6NSpU6W8JlA9LV26NK677rqYMWNG7LzzznHddddFkyZNsl0W1Fr5+fnxl7/8JSZOnBhbbbVV/Pa3v41tt90222UBQGqMHj06/va3v0WSJHHqqadGz549s10S1GpTpkyJ2267LZYuXRqHH3549OvXL9slQY0xbNiwOOuss4qdy94Yubm5cemllxa7uGHhwoXRtWvXWLBgQUYDrdKuyMrPz49bb701Fi1aFBHfncu+7777MlIX6xJmZUlhmJWfnx916pQ+22OmA61MTi04ffr0aNOmjTALarmFCxfG4YcfHlOmTImCgoKoU6dO7L333jFq1KjYaqutsl0e1DorV66Mn//85/Hss89GRESdOnWiefPmMXbs2PjRj36U5eoAIPsefPDBYl9GTZIk7r333jj77LOzWBXUXhMmTIijjjoqVq5cGQUFBZEkSVx66aXx5z//uczzbkDZCsOs8pzL3hiZDrQqOrXg9OnTY/fdd6/SmiiZ0bwayOQ9tNwji8q2bNmyGDx4cHTt2jVatmwZ9erVi5YtW0bXrl1j8ODBsXz58myXSJYlSRK9evWKKVOmRH5+fiRJEvn5+fH+++9Hz549w3cuiDCWZNqFF14Yw4cPjyRJivbJBQsWxCGHHFJp00rwHX0b0sU+SXm88sorccYZZ0RBQUHRI0mSOOecc+KFF17IdnmkgLEks2bOnBndu3ePFStWFP1NGRFxyy23xM0335zl6moe/ZuqlMl7aLlHVjWUkBXDhg1LIiLJz88v9zojRoxI6tatmxx77LHJ6tWrK72mcePGJQ0bNky6d++eLF++vNJff32mTZuWRETy6quvZuT9yKyRI0cmLVu2TCIiiYhk0003TXbaaadk0003LXquZcuWyciRI7NdKln0/PPPF/WH9T2ee+65bJdIlhlLMuuTTz5JcnJy1rs/1qlTJ7nxxhuzXWKNoW9DutgnKY+CgoJk3333TXJzc9f5nMzNzU322GOPpKCgINtlkkXGksw7/fTTk7y8vPUevzZt2jRZsmRJtkusMfTv2mvo0KEVPpe9MRYsWJDstddeSYsWLZIPP/yw0l//66+/Tg444IBks802S955551yrzdt2rRKr4Xyc2VWNVKVV2iV54qszp07R05OTuTk5MTkyZPL9boPPvhg0ToXX3xxpdVL+g0ZMiR69+4dCxcujH79+sWUKVPio48+imeeeSY+/vjjeP/99+Pss8+OhQsXRu/evSvtPmxUP4MGDSq6oeYP5ebmxuDBgzNcEWliLMm8e+65p8RpIwoKCmLw4MGRn5+f4apqHn0b0sU+SXm9/fbb8d577633szA/Pz8+/PDDeP3117NQGWlgLMm8RYsWxaOPPhpr165d7/Jvv/02Hn/88QxXVTPp35SkKs4ZV+UVWq7IqsaynabVVhtyZVahyr5Cq7xXZB166KHJ2WefncydOzdZs2ZNkiRJMmvWrOTII49MGjZsmGy11VbJZZddVrQsSZJk+fLlydy5c5OOHTsmAwcOXOc1XZlVM40dOzbJzc1NNtlkk+SVV14pev7SSy9NIiK58sori5575ZVXkiZNmiS5ubnF2lI7LF68uMQrQL7/WLRoUbZLJQuMJdnx/W9alvR47bXXsl1mtaZvQ7rYJ6mISy+9tMQrQCIiycvLSy688MJsl0kWGEuy44EHHij1uDUnJyc55JBDsl1mtad/U9qVWVVxzrhQZV+htaFXZBVyZVZ2ZfzKrNtvvz3mzZtXapsdd9yxWIq7YMGC2GOPPSIiYvXq1XHllVfGLrvsEj/60Y9izz33jKFDh67zGuPHj4+cnJx4+OGHK632yy67LJ544omIiHjiiSeiffv2seeee8aee+4Zt9xyS1G7999/P4444ohKe98fqswrtCp6j6xGjRpFy5YtIy8vL/Lz86Nnz56xevXqeOONN+Jvf/tbPPjgg3HNNdcUtW/YsGHR/LnUDmvXro3+/ftHfn5+PProo9GtW7dS23fr1i0ef/zxyM/PjwEDBpT4bSpqpilTppTrnljl/WYPNYexJDsWLlxY5nFaTk5OTJo0KUMV1Tz6NqSLfZKKeuedd0r9f1+7dm288847GayINDCWZM97770XdevWLXF5kiQxadIk92LeCPo35VFV54wr8wqtbF6RJROpHKkKswpvnPpDzz33XPTu3TsiIk4//fT47LPPYsqUKfHxxx/HyJEj4+abb46777672DpDhw6Nrl27rvc/dUPMmTMnXnzxxTjppJMiImK77baLUaNGxQcffBCvv/563H333TFhwoSIiGjXrl3Ur18/xo0bVynvvT6VEWhVNMj6oZdffjk++uijeOSRR6J9+/ZxxBFHxA033BB33nlnrF69usL1UDOMHTs2pk2bFr169YpevXqVa52jjjoqevXqFdOmTavS/Yb0mTx5conTmRWqU6eOMKsWMpZkx5QpU8psk5uba5/cCPo2pIt9koooPClelilTpqz33AY1l7Eke959990yz4l9++238cUXX2SooppH/6aiKvuccWUEWtmeWlAmUjmqNMx688034yc/+Unsvffe0a5du7j++uvjq6++ipNOOinat28fkydPjuuuuy6OO+646NGjR+y5554xd+7cdV5n+PDhccwxx8Snn34aw4cPjyFDhkTjxo0j4rvE8pZbbokbbrihqP3XX38dL7zwQjzyyCPx0UcfxWeffVa07PTTT49zzz03unbtGrvttlsce+yxsXr16li5cmW0bNkyZs+eXdT2N7/5TVx55ZURETFs2LA47rjjIicnJyIiDj744GjZsmVERGy66abRpk2b+Pzzz4vW/dnPfhb33ntv5W3M9diYQGtjg6yI7/5/99prr2jRokXRcz169IhvvvkmPvzwwwq/HjXDmDFjIiKib9++FVrv1FNPjYiIV155pdJrIr0mTZpUrjDLVSC1j7EkOyZNmlTiPewKrV27NiZOnJihimoefRvSxT5JRcyePTu++eabMtutWLEiZsyYkYGKSAtjSXYUFBSU+0tW/qbccPo3FVUV54w3JtDKdJAlE6m6TCSvql548eLF0adPn3jqqaeiU6dOUVBQEF9//XU88MAD8eSTT0b79u0j4rv/lDfffDMmTZpUrIMX+vbbb2PatGnx4x//OP7xj3/ErrvuGltssUWxNh07doy5c+fG/Pnzo0WLFvHYY49Fjx49omXLlnHqqafGsGHD4sYbbyxqP3ny5Bg/fnzUr18/DjnkkHj66afjZz/7WZx11llx9913x4033hirVq2KBx54IP79739HRMSECRPil7/85Xp/148++ijefPPNuOeee4rV1L9//43djGUqDLSOO+64OPnkk+OJJ54o9fLqiMoJsiIi5s2bt87/WeHPZV02+UMrVqyIZcuWbVAdpMvMmTMjImKHHXaIVq1axVdffbVOm5tuuiluuummop+33377+Pvf/160vr5Qe8yaNavMKQfWrl0bs2bN0i9qGWNJdsyaNSvq1Kmz3pvaf9+cOXNs3w2kb0O62CepiIoEVDNmzIhtttmmCqshTYwl2fHtt9/G8uXLy2yXk5NjG28E/ZuIiFWrVpW7bWWeM/6+wkCra9eu0aVLlxg3bly0bdu21HUyHWTJRKo4E6mqm3GNHDky6dSp0zrP77DDDsmkSZOKfr722muTs846q8Q2Tz75ZHLBBRcU/btdu3brvObixYuTiEiWLFmSJEmS7Lvvvsno0aOTJEmSqVOnJttss02ydu3aJEmS5LTTTkv++Mc/Fq178cUXJzfccEOSJEny5ZdfJttss02ycuXK5OGHH0569+5d1G633XZLJk6cuM57z549O9lll12Sv//978WeX7VqVRIRyYoVK9a7fYYNG1biTfM2xIgRI5K6deuWeaPZzz77LGnYsGHSvXv3ZPny5RV6j0MPPbTYDfnOPvvspHv37sXaLFu2LImI5MUXXyx13ULTpk0r8ybzHh4eHh4eHh4eHh4eHh4eHh4eHh4e2X2s71x2VZwzLs2CBQuSvfbaK2nRokWydOnSUtv26NEj2WyzzZJ33nmnQu9RkmnTppW6XCZSeiaysTJ+z6z1adKkSYnLnn322ejTp09EROyzzz7x6aefxqJFi4q1efPNN2OPPfaIpk2bxuTJk+P999+Ps88+O3bcccc46qij4r///W+89NJLRe0bNGhQ9O/c3NyiqwJatWoVhxxySDz55JNx5513xoUXXljUrlGjRrFy5cpi7/vVV19Ft27d4uqrr44TTjih2LKVK1dGbm5umTexqyw5OTmRk5NT7htaFrbfGC1btoz58+cXe67w58LLDQEAAAAAqB2q+pxxRc6DJ0lSKefBq4JMpOKqbJrBgw46KD799NN47bXXil1S17Rp01iyZEm5XmP16tXx5ptvxkMPPRQREbvuumv06tUrzjnnnHj44YejUaNG8fnnn8eVV14Zt9xyS0R8d5OzSy+9NP70pz8Vvc7dd98dQ4cOjaOOOqrM9xw4cGCccMIJ0aRJk+jWrVvR8+3atYvp06dHp06dIiJi7ty50bVr17jyyivjtNNOW+d1Pv7449hzzz3LvBdMZRg5cmQcd9xx0bNnz7j11ltLbbvzzjvH888/H7169Yo+ffrE8OHDi3XkiujYsWP84Q9/iAULFkTz5s0j4rt5cJs2bVrmJZ4/NHr06Dj44IM3qA7SZcyYMdGnT5846qij4oknnih6ftmyZUWXzV544YXF9tGIiJNPPjlGjhwZzz33XHTt2jWjNZM9PXv2jH/+859ltuvUqVOxD2BqPmNJdlx55ZUxZMiQMu/DufnmmxebU5vy07chXUraJyMiLr/88qKbas+fP7/oHgUR9snaauLEidGlS5dytX3ppZeKzh9Q8xlLsuPbb7+Nrbfeusx2OTk5ccstt8Q555yTgapqHsevREQ89NBDccEFF5SrbWWeM/6h//73v9G1a9eYN29ejBs3rtiYuj5PPvlk9OjRI7p16xZjxoyJfffdd6PevywykarNRKoszNp8883j2WefjUsvvTS+/fbbqFOnTtxwww1x0UUXxdlnnx2NGjWKBx98sNTXGDduXPzkJz8pdg+ohx56KH7729/GXnvtFXXq1ImZM2fGyJEjo0ePHrFy5cp49NFH1zkxeuKJJ8Zll122TiK8PgceeGBsuummce655xZLbI8//vh46KGHol+/fhERcc0118QXX3wRgwYNikGDBkXEd//pZ5xxRkREjBo1Ko4//vhybauN8f0g64knnihX6tm1a9dKCbS6d+8ebdu2jb59+8af//znmDdvXlx99dXRv3//qF+/foVeq2HDhmUOPlQPPXv2jDZt2sTIkSNjwoQJ0bNnz3Xa1KtXr9j/94svvhgjR46MNm3axJFHHhl5eVU2NJEyO+ywQ+Tl5ZV636zc3NzYfvvtjRG1jLEkO3bYYYcoKCgos90222xjn9xA+jakS2n75Pf/Dm3cuHHRfmmfrL123nnncrdt3bq1z8paxFiSHQ0bNoxGjRqVed+sJElixx13tE9uIMevRESFzvVW5jnj7/thkLXHHnuUuc5mm20Wo0ePzligJROp4kykSiYvrCTnnntu8o9//KPE5fn5+cnll1+etG/fPlm0aFGlvOeXX36ZtGzZMvnmm2/Wea8OHToks2fPLvM1Vq1aley1117JwoULS2xTGffMev7555N69eolxxxzTLJq1aoKrz9mzJikYcOGSY8ePco1j+X65jD9/PPPkyOOOCJp2LBhsuWWWyaXXnppsmbNmnKtmyT/d8+sV199tcL1k15jxoxJcnNzk0022SQZM2ZMkiRJsnTp0qL5dS+55JJibTfZZJMkNzc3eeWVV7JVMlly6623JnXq1Cl1PuY6deokN998c7ZLJQuMJZn38ssvlzlHel5eXnLGGWdku9RqTd+GdFnfPpkkSXLRRRcV7ZeF92OwT9ZuBQUFySabbFLmZ2XDhg0r7f7YVB/Gkuzo2LFjue7zM3PmzGyXWq05fmXo0KHlvmdWkmz8OeMfWrhwYdKuXbukefPmyQcffFDh+v/3v/8l+++/f7L55psn7777boXXL1TWPbMqQ03ORDZWqsOsTPvtb3+btGrVKnnwwQfXu/ydd95JXnvttTJfZ9q0ackLL7xQapuNDbM2NsgqVJFAa0NuyFfWusKsmuuee+5J6tSpk+Tm5ibnn39+MnHixKKDnA8//DD58MMPkwsuuCDJzc1N6tSpk9xzzz3ZLpksGD9+fLn+8Bg7dmy2SyVLjCWZtWDBgjL3x5ycnGTw4MHZLrXa07chXX64T37wwQfJBx98ULRfvv322/ZJkiT57m/bsj4rDzzwwGyXSZYYSzJvwIABSd26dUvdJzfZZJOkoKAg26VWe45fa7eKhlnlVZ51NzbIKlQZgVYmwqxMy2QmsrGEWVmyMWFWZQVZhcobaB166KFJ3bp1k8aNGyfvv/9+uV77kUceSRo3bpzUqVNHmFULjRw5MmnRosU6B5KbbbZZ0b9btGiRjBw5MtulkiWLFy8uV5hVWd80oXoylmRWy5Yty9wny3MgS9n0bUiXH+6T398X7ZMUuvTSS5O8vLxSr2C+8MILs10mWWQsyazC82ulfRHrkEMOyXaZNYbj19qrrDCrss8ZF6qsIKvQxgZaNTHMqk5ykiRJgox74IEH4swzz4z8/PwK3RBtQ+6RVR5jx46NXr16xSGHHFLiPbTmzJkTK1asiIiI7bffvlzv/e233xbNy7nZZpvFlltuWWz59OnTo02bNvHqq6+6OW4NtWzZsrj//vvj2WefLZq7dauttoq99torjj766DjrrLPMW13L/fSnP43x48dHfn7+Ostyc3Pj0EMPjbFjx2ahMtLEWJI5l156aQwaNGi9+2RERMuWLWP27Nnmva8k+jakS+E+OWLEiJg6dWosXLgwIiIOPfTQOPbYY+2TxMSJE+OAAw4otY2/bzGWZM6iRYti6623jjVr1qx3eU5OTtx1111x3nnnZbiymsvxa+00bNiwOOuss9Z7LrsqzhlHbNg9ssrj66+/jh49esSnn35a4XtoTZ8+PXbfffdKqYOKE2ZlyYaEWVUVZBUqT6BV2YRZtceyZcuiSZMmERGxdOlSBzYUGTlyZPTq1avE5cOHD4+jjz46gxWRZsaSqvfpp5/G7rvvHus7RCy8ee1vfvObLFRWs+nbkD72S9YnSZLo0KFDvP/+++t88SM3NzfatGkTU6dOLXbzdGo3Y0nVO+OMM+KRRx6JtWvXrrNsk002iS+//DKaNm2ahcpqNn27diktzKoKVRVkFdrQQEuYlV1V3/OoFFUdZEVEdO3aNZ5//vl49dVXo0+fPrFy5cpKfw+AH+rZs2f8+Mc/Xucqj9zc3OjQoUP07t07S5VB7bTrrrvGmWeeuc4fKLm5ubH55pvHgAEDslQZAGRfTk5O3HTTTeu9gjk/Pz9uuukmQRZk2DXXXBM5OTnrPcF+1VVXCbKgmqnqICviu6vBRo8eHbvuumt069Yt3nvvvUp/DyqfMKsayESQVUigBWRaTk5OvPDCC7HXXntFbm5u0fN77LFHvPjii04GQBb89a9/jaOPPrrY/rflllvGq6++GptsskkWKwOA7PvpT38aw4YNK3biPCcnJ+69997o2bNnFiuD2mmnnXaK0aNHrzPD0CWXXBKXX355lqoCNkQmgqxCAq3qx80Osuyqq64qOlG06667xhlnnFFseSaDrEKFgVavXr2iT58+60w5OHPmzLjvvvsq5b0WLVpUKa8DVG9bbbVVvPrqq/Gb3/wm7rjjjoiIGD16dDRv3jzLlUHt1KBBg/jHP/4RN9xwQ1x//fUREfHPf/7TdAoA8P+dccYZsfnmm8cxxxwTERFPPvlknHDCCVmuCmqvww47LMaMGRMHHXRQREQMHjzYjAJQBb5/Lntj5ObmxiWXXBKbb7550XOZDLIKFQZaPXr0iG7duq0z5WB+fn7cfvvtReewFy1aFPfee2+V18X6uWdWlnz22Wdx3HHHxdKlSyMiYsWKFTF37tz45JNPYtddd42I7ARZ31fSPbROPPHEeP7552ObbbYpapskSXz55ZcREbHttttWaFDbZptt4rnnnotmzZpV7i9AqphLmfLQTyiLPpJZtnfm2NaQPvZLyqKPUB76SebY1plle9cuPzyXXR6lnS+eNWtWXHnllfGHP/whIrITZH1fSffQevHFF6Nnz56x/fbbF90ew7ns7HFlVpbssssuMWXKlKKfX3/99fjJT35SNO92toOsiJKv0MrPz4/OnTvHSy+9VNT2+x9gU6dO9QEGAAAAAFAD/PBcdnmUdr54l112KToPnu0gK6LkK7QKa3z77bfNHpQC7pmVQiNHjoxjjjkmevTokbUgq5B7aAEAAAAAUNnSEGQVKgy0dt555zjssMPcQyuFhFkpUxhk5efnx9/+9resBlmFBFoAAAAAAFSWNAVZhTbbbLMYPnx4LFu2TKCVQsKslLn88ssjPz8/kiRJRZBV6PuB1osvvpjtcgAAAAAAqKaGDh2aqiCr0GabbRb5+fmxbNmyuO6667JdDt8jzEqJxo0bR7169aJXr16RJEm2y1mvwkCrYcOGbnAHAAAAAECFNWvWLJo3b566IOv78vPzY7/99osmTZpE3bp1s10OEZGX7QL4Tvv27WP27NlRt27dVAdFXbt2jc8++yy23nrrbJcCAAAAAEA188Ybb8SiRYuiRYsW2S6lVCNGjIgmTZrEJptsku1SCFdmpUrz5s1TNbVgSbbZZpvIycnJdhkAAAAAAFQzeXl5qQ+yIiKaNm0qyEoRYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbPYaEmSZLsEAAAAAABSyjlkNpYwi43y6quvxs477xzDhw/PdikAAAAAAKTMoEGDYs8994xPPvkk26VQjeVluwCqt2nTpsXMmTPjtNNOy3YpAAAAAACkzFVXXRUREXPmzInddtsty9VQXbkyi0pxwgknRE5OTrbLAAAAAAAgRU455ZRsl0ANIMxio+Xk5MTDDz8cJ554YuTk5JhyEAAAAACgFhs0aFBEfHdV1vXXX5/laqgJTDNIpcjLy4tHHnkkIiJOO+20aNCgQRx33HFZrgoAAAAAgEy6+eab46qrroqrrroqbrjhhvjPf/6T7ZKoAYRZVMisWbPimmuuiYKCgoiIYjftKwy0cnJy4qSTToonn3xSoAUAAAAAUEvcfPPNcfnllxcFWd+/Nc2NN94Yw4YNi4iIRo0axU033RSbbbZZliqlujHNIBVyxx13xN///vf44osv4osvvogGDRrE1VdfXbQ8Ly8vHn744TjhhBPipJNOiqeffjqL1QIAAAAAkAklBVnbb799nH322bF69eqi88pDhgyJJ598MssVU524MosK22GHHeKf//xnicsLA62IcIUWAAAAAEANV9oVWXXr1o0hQ4YUa5+bm5vpEqnmXJlFlXCFFgAAAABAzVdakAWVRZhFlRFoAQAAAADUXIIsMkWYRZUSaAEAAAAA1DyCLDJJmEWVE2gBAAAAANQcgiwyrVqFWe+8804cccQRERGxZMmSOPfcc6N169ax++67R4cOHeK5555bZ50HHnggcnJy4rXXXqu0Ok444YR48803IyLihRdeiA4dOkT9+vXj4osvLtbur3/9a9x4442V9r7VmUALAAAAAKD6E2RlhjykuGoVZj377LPRp0+fSJIkjjzyyKhbt2588sknMX369Bg6dGicf/758eKLLxZbZ+jQodG1a9cYOnRopdQwceLEWLx4cXTs2DEiInbdddcYNmxYXH755eu0Peecc2Lo0KGxZMmSSnnv6k6gBQAAAABQfQmyMkceUlyVhlk///nPY7/99ot27dpFz549Y968efH555/HZpttFtdee2106NAhdtlll2Ib/M0334yf/OQnsffee0e7du2KpYsjRoyIo48+OsaOHRuzZs2KW2+9NfLy8iIion379nH11VfHDTfcUNR++vTpMXPmzHjooYdi+PDh8c033xQt69y5c1x22WXRqVOn2HnnneO8886LiIivvvoqWrRoEcuXLy9qe8opp8Tdd98dERH33ntvnHLKKUXLdtttt9h7772L6vi+evXqRffu3eOxxx7b2E1ZYwi0AAAAAACqH0FW6eQhVZuHVGmYdfvtt8c777wT77//fnTq1Cmuu+66iPjukrh27drFu+++G3/961/jl7/8ZURELF68OPr06RN//OMfY8qUKTF58uTo1KlTRER8+umn0bRp02jZsmW899570aFDh6hXr16x9+vYsWNMnjy56OehQ4dG3759Y5tttokuXbrEE088Uaz9jBkzYvz48fHBBx/E6NGj480334xtttkmunXrFo888khERMyfPz/GjBkTffv2jYiICRMmxAEHHFDubdCxY8cYO3ZsicuXLVu2zqO0Zdl+rFmzpty/e0kEWgAAAAAA1UdVBFmrVq3K+vnuip6fL408pOw8ZGOsG59VosceeywefvjhWLlyZaxcuTK23HLLiIho0KBBHHvssRHx3S83Y8aMiPguhdx9992L/sPq1KkTzZo1i4j/u6SuLA0bNoyIiLVr18ZDDz0U//znPyMi4swzz4wbbrghzjnnnKK2J510UuTl5UVeXl60b98+ZsyYER07doyBAwfG2WefHeecc07cd9998bOf/SyaNGkSERFffvlltGjRotzboGXLlvHll1+WuLzwddenIu+TSTvttNNGv0ZhoBXx3f/DiBEj4sgjj9zo1wUAAAAAoPLceeedVXJF1sCBA2PgwIGV8lpVYX3n55MkKbG9PKTsPGRjVNmVWf/6179i8ODB8eKLL8YHH3wQt956a6xcuTIiIurXr1/U4XNzcyM/P7/M1xs+fHjRf96+++4b77777jpXCb355ptx0EEHRUTEyJEj4+uvv44ePXrEjjvuGP3794/33nsvPvjgg6L2DRo0KPp3bm5urF27NiIi9t9//2jUqFGMHz8+hgwZEv379y9q16hRo6LfozxWrlxZ1KEoLi8vL84888zIz8+PiRMnZrscAAAAAAB+4I033ohGjRrFKaecYmrBEshDvlOVeUiVXZn1v//9LzbZZJPYYostYvXq1XHvvfeWuc5BBx0Un376abz22mvRqVOnKCgoiK+//jpWrVoVS5cujV133TUiIrp06RLbbbdd/PKXv4zbb7898vLyYvLkyXHbbbfFU089FRHfXVJ3++23F839GBFx5ZVXxtChQ+O2224rs5aBAwfGL37xi2jbtm3stttuRc+3a9cupk+fHtttt125tsPHH38ce++9d4nLly5dWuznZcuWFSWd8+fPj8aNG5frfTLlN7/5TYwePbpSXmv8+PFx9NFHR/fu3ePKK6+slNcEAAAAAKDy3H777TF16tTo0qVLjBs3Ltq2bVsprzto0KA466yzKuW1KsuGnp+Xh3ynrDxkY1RZmHX44YfHI488ErvvvntsscUW0a1bt5gzZ06p62y++ebx7LPPxqWXXhrffvtt1KlTJ2644YaYM2dO9O7du6hdnTp14qWXXorLL7+8aMPOnTs33nrrrWjXrl189dVXMXbs2HjwwQeLvf7Pf/7z6Nq1a9x0001l1n/88cfH+eefHxdeeOE6z48ePTq6desWERFjx46N0047Lb755ptIkiSeeuqpuOuuu4rqHTVqVLGbsP1QaTtD48aNUxdm1a1bt1JeZ/z48dGzZ8/o1KlTDB8+3NVrAAAAAAAptNVWW8XYsWOja9eulRpo1a9fP3Xnv7+vIufn5SHly0M2Rk5S2iSPKXH44YfH73//+9hvv/3Wu3zlypXRr1+/WLBgQYwYMaLY5XIb6p133olTTjklpk2bFnXq/N9sjEuXLo2DDjoo3nzzzTI78kcffRTnnntuvPbaa+V+32XLlhXNR7l06dLU7cyXXXZZjBw5MqZNm7bBryHIqp3S3rdJB/2EsugjmWV7Z45tDeljv6Qs+gjloZ9kjm2dWbZ37bRw4cLo2rVrLFiwYKMDrdzc3Ljrrrvi3HPPrcQKN14a+nZtykMqosquzKpMo0aNKnV5gwYN4pFHHqm09+vXr1+8/PLLcf/99xf7j4uIaNKkSdx2220xc+bM2HPPPUt9ndmzZ5frcsLaRJAFAAAAAFD9VNUVWhQnD1m/ahFmZdr9999f6vKuXbuW63V69OhRGeXUGIIsAAAAAIDqS6BV81SXPKRO2U1g4wmyAAAAAACqv8JAq3nz5tGlS5f46KOPsl0StYAwiyonyAIAAAAAqDkEWmSaMIsqJcgCAAAAAKh5BFpkkjCLKiPIAgAAAACouQRaZEpetgug+vn2229jxIgRRT+3bds2dtlll2JtBFkAAAAAADVfYaDVtWvX6NKlS4wbNy7atm1brM2UKVNi1qxZRT8nSZLpMqnmhFlUyN577x233HJLHH300UXPbbnllrFw4cKinwVZAAAAAAC1R2mB1rx58+KAAw6IVatWFbXPy8tbJ/CC0phmkArp27dvLFy4MObPnx/z58+Pv/zlL7Fo0aKi5YIsAAAAAIDap6QpB5ctWxarVq2Kp59+uui88oIFC6JTp05ZrpjqxJVZVNiWW25Z9O+mTZsW/VuQBQAAAABQe63vCq369etHRMTmm28ezZs3z3KFVFeuzKJSCLIAAAAAACjpCi3YGMIsNlqSJIIsAAAAAAAionigdfLJJ2e7HGoAYRaVQpAFAAAAAEChwkBr5513znYp1ADumcVGOe2002KrrbaKww8/XJAFAAAAAECRwkDrnXfeiUMPPTTb5VCNCbPYKPXr149jjjkm22UAAAAAAJBCW221VRxxxBHZLoNqzjSDAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAAAAAAACpJcwCAAAAAAAgtYRZAAAAAAAApJYwCwAAAAAAgNQSZgEAAAAAAJBawiwAAAAAAABSS5gFAAAAAABAagmzAAAAAAAASC1hFgAAAAAAAKklzAIAAAAAACC1hFkAAAAAAACkljALAAAAAACA1BJmAQAAAAAAkFrCLAAAAAAAAFJLmAUAAAAAAEBqCbMAAAAAAABILWEWAAAAAAAAqSXMAgAAAAAAILWEWQAAAAAAAKSWMAsAAAAAAIDUEmYBAAAAAACQWsIsAAAAAAAAUkuYBQAAAAAAQGoJswAAAAAAAEgtYRYAqTF79uxslwB8z8KFC7NdAgCk1sqVK4v+vWLFiixWAkRE5OfnF/17yZIlWawEgKogzAIgFe6///5o27Zt0c/Dhg3LYjXA2LFji+2Tv/71r6OgoCCLFQFAeixcuDC6du1a9HPnzp1j/vz5WawIareVK1dG3759i35u3759fPDBB1msCIDKJswCIOtuu+22OPvssyNJkqLnLrroorjllluyWBXUXiNHjozDDz88Vq1aVfTcHXfcEWeddVYWqwKAdFi4cGEceOCBxU6UT5s2LQ444ICYN29eFiuD2mnNmjXRvXv3GDlyZNFzixcvjoMOOijee++9LFYGQGUSZgGQVV9++WVceeWV613261//2tSDkGGrVq2Kc889N/Lz89e5EuvBBx+MCRMmZKcwAEiJP/zhDzFr1qxiU5rl5+fHl19+Gddff30WK4Pa6cEHH4zXXnut2LFrfn5+LF++PAYMGFDsS5MAVF/CLACyatCgQSVOXVZQUBC33357ZguCWu6JJ56Ir776ar1/9Ofm5sYf/vCHLFQFAOmwePHiuPvuu4sFWYXy8/Pj/vvvjwULFmShMqidCgoK4g9/+EPk5OSssyw/Pz/eeOONePPNN7NQGQCVTZgFQNasXbs27r///vWeDIj47o+PoUOHxtq1azNcGdRe9957b9Sps/5DxPz8/Bg7dmx88cUXGa4KANLh8ccfjzVr1pS4vKCgIB5++OEMVgS126uvvhqzZs0q8eqrvLy8uO+++zJcFQBVQZgFQNZMnz49vv7661LbLFmyJKZNm5aZgqCWW716dbz99tslXi0ZEZEkSbzxxhsZrAoA0uO1114r8Usf328DZMZrr70Wubm5JS5fu3atabIBaohqE2a98847ccQRR0TEdyc2zz333GjdunXsvvvu0aFDh3juuefWWeeBBx6InJycSj2QPOGEE4ouT37hhReiQ4cOUb9+/bj44ouLtfvrX/8aN954Y6W9L0BNNHny5HK1mzRpUtUWAkRExMcff1zmlZB169Yt974LADXN22+/XeKsAhHfXZn1zjvvZLAiqN0mTZpU5j2xZs2aFd9++22GKgKoHPKQdVWbMOvZZ5+NPn36RJIkceSRR0bdunXjk08+ienTp8fQoUPj/PPPjxdffLHYOkOHDo2uXbvG0KFDK6WGiRMnxuLFi6Njx44REbHrrrvGsGHD4vLLL1+n7TnnnBNDhw6NJUuWVMp7A9REkyZNirp165baxolzyJzyBMdr166Nd999NwPVAEC6LF26NGbOnFlmuzlz5sT//ve/DFQElDWrQMR3Mwu8//77GaoIoHLIQ9aVV2WvHBErVqyI008/PaZOnRp169aNFi1aRL169eKUU06JU045JSIiXn755fjtb38bb731Vtx///1x6623Rr169YpunHrAAQdERMSIESPilVdeibFjx8asWbNi/PjxkZf3Xfnt27ePq6++Om644YY48sgjI+K7qatmzpwZb7/9drRt2za++eabaNq0aUREdO7cOfbbb79466234quvvoqf/vSncc8998RXX30V++yzT8ycOTMaNWoUERGnnHJKdOrUKc4///y49957i+qOiNhtt90i4ruO9UP16tWL7t27x2OPPRbnn3/+erfPsmXLSn1ufcuhutK3WZ+pU6eWes+BiIg1a9bElClT9BsiwlhS1d5///2oW7duqftlkiQxdepU27+S6duQPvZLfmjKlCllXgFSaNKkSUXnM6jdjCVVZ+XKlfHll1+Wq+3kyZOjffv2VVtQLaNvU1OVp283btx4vc/LQ0rPQzZaUoWeeeaZpHv37kU/L1q0KHn55ZeTjh07Fj3Xu3fv5KGHHkqSJEmaNm2afPXVV0mSJMnq1auTb7/9NkmSJPnkk0+Sgw46KEmSJLnpppuS3r17r/Ne7733XtKgQYOiny+//PLkyiuvTJIkSY455pjk3nvvLVp26KGHJn369EnWrFmTLF++PNlxxx2TN954I0mSJDnllFOK2s6bNy/Zaqutiupo3bp1MnXq1HXe+9prr00GDhy4zvN/+9vfkuOOO67E7RMRHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7V5FESeUjpecjGqtJpBvfee+/4+OOP44ILLognn3wy6tatGz/96U9jyZIlMWnSpJg1a1ZMnDgxTjzxxIiI6Nq1a/Tt2zcGDRoUM2fOjCZNmkTE/11SV5aGDRtGxHfT3zz00ENxxhlnRETEmWeeuc6ldSeddFLk5eVFw4YNo3379jFjxoyIiBg4cGDceeedERFx3333xc9+9rOiOr788sto0aJFuX//li1blvsbIgAAAAAAQPUkD6naPKRKpxls3bp1fPTRRzFu3LgYM2ZMXHHFFTF58uS46KKL4o477ogWLVrEmWeeGfXr14+IiKeffjrefffdmDBhQhx55JHx+9//Pk4++eQYPnx4/O1vf4uIiH333TcGDx4ca9asKXaflTfffDMOOuigiIgYOXJkfP3119GjR4+IiEiSJL766qv44IMPYs8994yIiAYNGhStm5ubW3Sz8/333z8aNWoU48ePjyFDhsSYMWOK2jVq1ChWrlxZ7t9/5cqVRR1qfZYuXbrOc8uWLSvqIPPnzy/xkkWobvRt1qdHjx7x+uuvl9muY8eO8corr2SgItLOWFK1Lrvsshg6dGiZ039uuummMWfOnAxVVTvo25A+9kt+6I033oju3buXq+3IkSOjc+fOVVsQ1YKxpOp8/fXXse2225bZrk6dOnHTTTdV3bRXtZS+TU21MX1bHlJ6HrKxqjTM+vLLL2PzzTeP3r17x+GHHx7Dhw+P2bNnR9++feN3v/td5Ofnx9tvvx0R36WHn3/+eey3336x3377xX//+9+YOHFiHHroobF06dLYddddIyKiS5cusd1228Uvf/nLuP322yMvLy8mT54ct912Wzz11FMR8d2Nzm6//fY477zzimq58sorY+jQoXHbbbeVWffAgQPjF7/4RbRt27ZoHsiIiHbt2sX06dNju+22K9fv//HHH8fee+9d4vKydoTGjRv7IKBG0rcp1KxZs8jJySn13gM5OTmx+eab6zOsw1hS+Zo1a1audk2aNLHtq5C+DeljvyQionnz5uVuu9VWW+kzrMNYUrnq1q1b5t+TEREFBQWxxRZb2PZVSN+mpqpo35aHlJ6HbKwqnWZw6tSpcfDBB8fee+8d++yzT/Tt2zfatWsXjRo1imOPPTYOPvjgog2Rn58fZ555Zuy5557Rvn37ePfdd+OSSy6J5557Lnr37v1/BdepEy+99FKsWrUqdtttt2jdunV07Ngxnn766dh7773jq6++irFjx8YJJ5xQrJaf//zn8cgjj8Tq1avLrPv444+PpUuXxoUXXrjO86NHjy76eezYsbHtttvGrbfeGkOHDo1tt902RowYUbR81KhRcfzxx2/QtgOoDfbcc8+im1eWJC8vL/baa68MVQS12x577FHmVVk5OTlVenAKAGm1++67R506ZZ9GycnJiR/96EcZqAhqt3r16sVOO+1UrraFVyYAVCV5SNXmITlJWV9fqAL5+fnRoUOHuOOOO6JTp06ltj388MPj97//fey3337rXb5y5cro169fLFiwIEaMGFHscrkN9c4778Qpp5wS06ZNK3agunTp0jjooIPizTffLDOR/eijj+Lcc8+N1157rULvvWzZsqI5KZcuXepbDdQY+jbr8/e//z1OOumkMts98cQT5WpHzWcsqVofffRR7LHHHqW2ycvLi1/96ldxww03ZKiq2kHfhvSxX7I+bdq0ienTp5faZscdd4yZ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wrHk5ptvjsaNG2e4QrLNWJJZCxYsiIEDB8bzzz8fy5YtW2+bhg0bxlFHHRW33XZbtGrVKsMV1ixjx46Na6+9Nl5//fUS22y77bZx5plnxrXXXivUqmHuvPPOGDBgwAav/8Pz8zk5ObHddttFhEArG4RZVay0qQXPOOOM2G+//Ur8w7SifvzjH8cXX3xRKa8FlWnt2rXRv3//op8vuOCCuP/++33zD6iQtWvXxoUXXlj08/nnnx9Dhw41llAjDB8+vOjf++23X7z44oux0047Za8gsm7atGnRpUuX2HLLLeO2226LDh06RP369YuWJ0kS33zzTbzyyisxePDgmD59eowePdqYWEkmTpwYffr0Kfr5rbfeii5dumSvoBJcfPHFcccdd0S/fv2iT58+0bx582In4dauXRvTp0+P+++/v+gki5PQtcv06dPjsMMOiy222CJuvfVWY0mGvf3229ViLPnlL38ZgwYNirPPPrvMsaR3797x3HPPxZFHHpnFiquv//3vf3HYYYfFokWL4te//nUccsgh6wTIy5cvj3/9619xxx13xGGHHRavv/56bLXVVlmqeP2++eabOO6444p+/tOf/hS/+93vUndLlbFjx0bPnj1j3333jQceeCB+9KMfFRvfkiSJhQsXxogRI+L3v/99zJ07N4YMGZLFiqlsn3/+eeywww7xzDPPVMrrNW7cOHbfffeinwVaGZZQZe6///4kIpLzzz8/KSgoqPL3y83NTe65554qfx+oiBUrViRHHnlkkpOTk0REEhFJTk5OcvjhhyfLly/PdnlANWEsoSa75557ivXt3NzcZKuttkref//9bJdGFp1zzjnJtttumyxatKjMti+//HISEclLL72Ugcpqvpdffjlp0KBBkpubW7Rf1qtXL3nxxRezXVoxc+bMSXJycpKbbrqpzLarV69ODj300GT//ffPQGWkybnnnpu0atUq+e9//1tm28KxJG19vbp65ZVX1juWvPDCC9kurZivvvqqQmNJ586djSUb4Y477kjy8vKSadOmldl2xowZSYMGDZK//OUvGais/BYsWJC0a9euWN/O5PnPijjkkEOSjh07JqtWrSqz7aBBg5KISGbOnFn1hZExl156adKmTZsqf5+VK1cmRx55ZNKgQYNk9OjRVf5+tZXrJqtIaVdkkV3Lli2LwYMHR9euXaNly5ZRr169aNmyZXTt2jUGDx4cy5cvz3aJNcodd9wRL730UiRJUvRckiTx8ssvxx133JHFykqnn1AWfSSzShtLBg8enMXKSqefZE513dafffZZ9O/fv1jfzs/Pj8WLF0ffvn2LPZ8m1XV7VycjRoyIk08+OZo1a1Zm227dusUuu+wSzz33XAYqq9mWLVsWJ598cqxatarYPTXWrFkTp5xySnzzzTdZrK64kSNHRp06deLss88us23dunXj7LPPjokTJ8bcuXMzUB1pUTiWbLHFFmW2NZZUntLGkp///OfGklpsxIgR0a1bt2JXdpSkdevWcfjhh6dun/zVr34VH3744Tr3nrr77rtjxIgRWapqXYsXL47XXnst+vXrF/Xq1Suz/Zlnnhn169dP1e9A9VF4hVaXLl3i6KOPjpdffjnbJdVIwqwqIMhKrxdeeCF22WWXGDhwYIwbNy5WrlwZ2267baxcuTLGjRsXAwcOjJ133jleeOGFbJdaI6xcuTJuvPHG9Z6IKygoiBtvvDFWrlyZhcpKp59QFn0ks8oaS/74xz8Wu0FrWugnmVOdt/WNN9643mPF/Pz8mDJlSrz44otZqKp01Xl7VxcFBQUxb968cp3oivhu7v7dd9895syZU8WV1XxDhgyJ//3vf+t85iT/fyq2u+++O0uVrWvOnDnRokWL2HzzzcvVvk2bNhERTkDXIgUFBTF37twKjSVt2rQxllSC++67LxYvXlziWHLXXXdlqbJ1VXQsKexPX331VVWWVWPNmTOn3PtkRKTu8/2LL76IBx98cJ0gKyKiTp068dvf/jY1X8aaN29eJElS7u3dpEmTaNWqVaq2N9WLQKvqCbMqWXmCrM6dO0dOTk7k5OTE5MmTy/W6Dz74YNE6F198ceUWXUsMGTIkevfuHQsXLox+/frFlClT4qOPPopnnnkmPv7443j//ffj7LPPjoULF0bv3r3NkVsJnnnmmfj6669LXL5kyZJ46qmnMldQOegnlEUfyTxjCaWpztt6yZIl8dhjj5V4/9S8vLxUneyKqN7buzopKCiIiKjQPWvq1q273hNLlF+SJHHnnXeWuLygoCDuuuuuov+fbMvPz69wHylcj9qh8ISysSSzquNYkpeXV+72xpKNsyHbO03beujQoSV+ab+goCCmTp0a77zzToarWr/C7VadtzdVqyrO0Qu0qpYwqxJV5Iqss88+O+bOnRt77rlnRHz3zYaePXtGo0aNonnz5nH55ZcXO7Fx0kknxdy5c6Njx45V/nvUROPGjYsLLrggGjduHKNGjYr77rsv2rVrF7feemvss88+MWjQoNhrr71iyJAhMWrUqGjUqFFccMEFMWbMmGyXXq29/PLLpR405OXlxSuvvJLBikqnn1AWfSQ7XnnllVLHktzcXGNJLVXdt/Ubb7wRq1atKnH52rVrY9y4can5g7q6b++a4vTTT48+ffpku4waafbs2TFjxoxSv1H+xRdfxMyZMzNY1YbRTyiLPlJ1vvzyy/jss89KHUtmz54d//nPfzJY1YbRTzKnumzrF198sdRj07T9bVaS6rK9qXpVcY5eoFV1hFmVpKJTCzZq1ChatmwZeXl5kZ+fHz179ozVq1fHG2+8EX/729/iwQcfjGuuuaaofcOGDYvuR0DFrF27Nvr37x/5+fnx6KOPRrdu3Upt361bt3j88ccjPz8/BgwYUOK3pSnb22+/Xer2W7t2bbz99tsZrKhk+gll0Ueyp6yxJD8/31hSC9WEbT158uTIzc0ttc3KlSvj008/zVBFJasJ2xvKUt5v5Ja3HVA7GUuoqfLz8+ODDz4otU2SJDFp0qQMVQQbr6rO0Qu0qoYwqxJs7D2yXn755fjoo4/ikUceifbt28cRRxwRN9xwQ9x5552xevXqKqq69hg7dmxMmzYtevXqFb169SrXOkcddVT06tUrpk2bFuPGjaviCmumVatWxfTp08ts98knn5T6rfRM0U8oiz6SHatXry73WJKGe/DpJ5lTE7b1pEmTynVPgTScEKgJ2xvKMmnSpDKnIsrLy0vFPgmkl7GEmurTTz8t82+ugoKC1HzRECqqss/RC7QqnzBrI21skBUR8eabb8Zee+0VLVq0KHquR48e8c0338SHH35YmeXWSoVT2/Tt27dC65166qkREdXi8ug0mj59ermmRcrPz4+PP/44AxWVTj+hLPpIdkyfPr1cV3QUFBQYS2qZmrCt33vvvTLvl1G3bt14//33M1RRyWrC9oayTJ06tcx9Mj8/P6ZMmZKhioDqyFhCTTV16tRytZs1a1YsW7asiquBylcV5+gFWpWr/HfAYx2VEWRFRMybN6/YThIRRT/PmzevQq+1atUqHxg/UDin/Q477BCtWrWKr776ap02N910U9x0001FP2+//fbx97//vWh927Ti5s6dW+628+bNy/o21k8oiz6SHcYSSlITtvX//ve/crX773//m/Vaa8L2rk42dFrG/Px823kjLFy4sMwT0EmSpGKfjIgNnsVjxYoVqaifqreh91w0lmwcYwmlKc9V+etbJw3bev78+RVq+8NznZm2fPnyDVpvzZo1qdjeVI41a9aUu21lnqP/vsJA69hjj42jjz46nnvuuejevfsGv15tJszaQEuXLo1+/fpFr169NirIqkxJksTAgQNj4MCB2S4llQ444IByt/3iiy/iwAMPjIiIp59+Op5++umqKouIOOKII7JdQhH9hLLoI+l15JFHZruEIvpJ5tT0bb1mzZq4//774/777892KRFR87d3dTd69Oho0qRJtsuo8f7973+nZjtvs802FV7n0EMPrYJKqElefvnl1PTxmuytt95KzXbeeuutK7xO586dK7+QWqBu3boVXmfevHmp6SvltfPOO2e7hA2yevXqGDx4cAwePDjbpVCJWrdune0SigKt/fffP04//fT1fjmQsplmcAM1bNgwevXqFS+//HKMHj16o16rZcuW63y7ofDnli1bbtRrAwAAAAAAJavqc/QPPvhgvP/++3HSSSdt9GvVVq7M2kC5ubnx1FNPxfHHHx99+vSJ4cOHx+GHH75Br9WxY8f4wx/+EAsWLIjmzZtHxHf3FWjatGm0bdu23K+Tk5MTgwYNirPOOmuD6qipxowZE3369ImjjjoqnnjiiWLLfvOb38TgwYPjkksuid/97nfFlp188skxcuTIeO6556Jr166ZLLlGePvtt+Owww4rV9tx48bF/vvvX8UVlU4/oSz6SHa888475f7W59ixYyt01UhV0E8ypyZs61133bXMqTTr1q0b55xzTrHp+7KhJmzv6mTt2rWx2WabVXi9Hj16uAJuIxx77LHluo9B586dY+TIkRmoqHTXX3990VSeFfHPf/4zOnToUAUVkTb5+fmx6aabVni97t27xzPPPFMFFdUOxx13XLm+9HzooYfGCy+8kIGKSve73/1unc/28pgwYULst99+VVBRzbbvvvtWeJ2WLVum4v7Ajz32WJxzzjnlajt79uzYfPPNq7ii0n3wwQdFswSUV7169eKiiy6KG2+8sYqqItN+85vflPs+VZV1jn597r333jjvvPNiwIABceutt27Ua9VmwqyNUK9evUoJtLp37x5t27aNvn37xp///OeYN29eXH311dG/f/+oX79+hV6rfv360bhx4wrXUJP17Nkz2rRpEyNHjowJEyZEz549i5YVXt5dt27dYtvtxRdfjJEjR0abNm3iyCOPjLw8u0pFtWvXrkJts91v9RPKoo9kh7GEktSEbd22bdsyw6y1a9fGHnvsoW/XMht6z6zc3Nys95XqrG3btjF+/PhS761Qt27dVOyTEd/9PbohGjZsmIr6qXobes8sY8nGadu2bYwbN85YwnptyG1KcnJyUrGt99prr3K122yzzaJVq1ZZvyVLo0aNNmi9Hx7TUr1VZGrPyjxH/33fD7IGDRqU9X2jOjPN4EYqDLS6d+8effr0iVGjRlX4NXJzc2PkyJGRm5sbHTt2jFNPPTV+8YtfrPPNVjZMXl5e/PWvf43c3Nz42c9+FmPHji21/dixY+Pkk0+O3NzcuOOOO5x42UDNmjUr17zbLVu2jC222CIDFZVOP6Es+kh2bL755uW6H0iLFi1iyy23zEBFpdNPMqcmbOt99923zD+ukiSJ9u3bZ6agUtSE7Q1lad++fZk3CV+zZk0q9kkgvYwl1FR77bVXuU7C77vvvk7WUy1VxTl6QVblEmZVgsoItHbYYYd48cUXY/ny5bFw4cK4+eab/dFfibp27Rp33nlnLFu2LHr06BEXXHBBfPjhh9G/f/947bXX4rzzzouPPvoo+vfvHz169Ihly5bFnXfeGd26dct26dXaj3/846hTp+Rhpk6dOvHjH/84gxWVTj+hLPpIdhhLKEl139b77LNPmSe7cnJyKnSFYlWq7tu7pnjwwQdj+PDh2S6jRtpnn30qtV026SeURR+pOsYSNkR12NaNGzeO1q1bl9qmbt261WIq2+qwvcmOyjxHL8iqfNKSSlLRKQfvuuuuuP/+++PNN98s12W6jz76aJx77rmxYsUK397ZQOeee25su+22cdZZZ8Xdd98dd999d2y22WbRrFmzWLx4cXz99dcR8d23+4cOHVps+hw2TMeOHUu9n0BOTk507NgxgxWVTT+hLPpI5nXs2DGef/75EpcbS2q36ryty7rHW05OTrRt2zZV05xU5+0NZfnRj34UjRs3jmXLlpXYpkGDBrHHHntksCqgumnTpk00adIkli5dWmKbBg0axJ577pnBqqBydOrUKWbNmlXilMhr1qyp8H2qIJuq6hy9IKtquDKrEpX3Cq1HH300Pvroo5g8eXLsvvvu5Xrt3r17x+TJk2P69Olx9dVXV2bZtUrPnj1jxowZcfvtt0eXLl2iXr16MXv27KhXr1506dIlBg0aFDNmzHDipZL07du31OUFBQVltskG/YSy6COZdeqpp5a6vKCgIH7xi19kqJry008yp7pu69atW8dPfvKTyM3NLbHN2WefncGKyqe6bu/qqKCgoErasn5169aN0047rcRv3+bl5UXfvn2jQYMGGa6sZPoI5aGfZFZ5xpKf//znqRpLkiQpd1t9ZONV5+19xhlnlHpvz8022yx1x4DVeXtTtarqHL0gq+q4MquSlecKrVatWlX4dTfZZJPYZJNNKqvMWq1x48YxcODAGDhwYLZLqfFatWoVp5xySjzxxBPrHOzk5eXFiSeeGNtuu22WqiudfkJZ9JHMKWssOeGEE4wlVNttfeWVV0avXr3Wu6xp06ZxxhlnZLii8qmu27u6yMvLi4YNG8aCBQvKvc6CBQvKnPqHsl188cVxzz33rHdZfn5+XHzxxZktqBSbbrppLF68ONauXVuu6W8K+9Omm25a1aWRErm5udGoUaMKjyU77rhj1RVVS1x88cVx9913r3dZfn5+XHLJJRmuqGQVHUsWLlwYEd+FFlTcpptuWqF9cuHChana1p06dYp99903pkyZEvn5+cWW1alTJy655JKoX79+lqorrvDzrrzbu6CgIBYtWpSq7U3Vqopz9IKsquXKrCpQGffQgprihhtuiKZNmxb71nlubm5ssskm8fvf/z6LlQHVSeFY8v0/sI0l1AQ9e/aMnj17rve+cLfeems0bdo0C1WRBocddliMGDGiXN8mnjdvXkycODEOO+ywDFRWs+26665x+eWXr3fZxRdfHG3bts1wRSU77LDDYtmyZTFu3LhytX/uueeiVatWscsuu1RxZaRJRceSt956y1hSCXbZZZe44oor1rts4MCBqRxLxo4dW672xpKNc9hhh8WoUaNi1apVZbZdu3ZtvPDCC9G5c+eqL6yccnJy4o477oi8vLxix6+5ubmx0047pepLH9ttt120bt06nnvuuXK1/9e//hWLFy9O1famehFkVT1hVhURaMF3dtxxx3jrrbdim222KXpu6623jrfeeit22mmnLFYGVCc77rhjTJw4sdhYss0228Rbb73lSgSqtZycnHjmmWfi+OOPL3ouNzc3HnrooTjzzDOzWBnZ1q9fv3jjjTfioosuirlz5663TZIkMWnSpOjVq1dsttlm0adPn8wWWUP98Y9/jOuvv77Yc1dccUXccsstWapo/Tp06BDt27eP008/PV555ZV1viFf6Jtvvonbb7897r777jjrrLPWG55Tc/Xr1y/efPPNGDBgQJljSe/evWPTTTeNY445JsNV1kw33nhj/O53vyv23FVXXRW33nprlipav3333Tf22WefOOOMM+KVV14pcQq5b775JgYNGhR33313nHnmmcaSDXTqqafGt99+G8cff3x88sknJbabMWNGnHzyybFgwYLUTal+0EEHxbhx44rd17Vdu3bx73//O1WzSuXk5ES/fv1i2LBh8ec//7no3q4/lJ+fH+PHj4++fftGmzZt4qCDDspsodQIgqzMyEkqMnEoFbZ69eo4/vjj4+WXX15nysG33norTjvttHUOFFavXh0R3wViFTFjxoy47777ol+/fhtfOFSyL774InbYYYeIiPj888+L/g1QEYsWLYpXX301kiSJQw45JLbccstslwSV4ttvvy26CmvixInx4x//OMsVkQaDBw+OSy+9NPLz86NVq1bFpu1JkiSWLFkSixYtihYtWsRLL70U++yzTxarrXkmTJhQdIXK0qVLi520S4v58+dHjx49YsqUKbHJJpvElltuWewE85o1a2Lu3LmxZs2aOOecc+Luu+92AroWuuOOO+KSSy4xlmTJxIkT49NPP43WrVtHx44ds13OehlLMmvUqFFxwgknxNKlS6Nly5bRqFGjohPfSZLEihUrYu7cudGoUaN49NFHU/tllenTp0ebNm0i4rs+1Lx58yxXtK4kSeLiiy+OwYMHR926dWPrrbeOunXrFi0vKCiIxYsXx5IlS6Jt27bx8ssvb9DUc6TXlVdeGbfeemuFz0OWdH6+adOm8fTTTxf7gr4gK3OEWRlQUqB1zTXXxO233x79+/cvartmzZqib/xdeumlxQbYsjRq1Cguv/zyVN1EFAotW7YsmjRpEhHpPRkAANnic5KSLF68OJ5//vmYMWNGrFmzptiyRo0axQEHHBCHHXZYhf5uoHyqy36ZJEm8/fbbMW7cuFiyZEmxZbm5ubHNNttEr169YrvttstShaRBWWPJ/vvvH126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j8svvzx23XXXaNSo0Tpjd/PmzePEE0+MUaNGZbtUgEo3efLkOPfcc2O77baLBg0aFBv/GjRoEK1atYqzzjor3n777WyXmmqPPPJIhc6vl3V+fsWKFdGwYcOIEGhlQ162C6jpSrsi65prrokdd9wxCgoKKuW9rrjiinjjjTeiU6dOlfJ6UJkeeuihon//7W9/iwsuuCCL1UDtkiRJXHnllfGXv/wlDjnkkLj00ktj0003jZycnKI2q1evjvfeey9uvvnmePbZZ2Ps2LGxxRZbZLFqqF0+/vjjon9fdNFFcccdd0SjRo2yWFHNsXr16jj55JPj+eefj8MPPzyuvvrqdbbt8uXL41//+ldcccUVMXbs2Hjqqaeifv36WaqYNFiwYEGcf/75RT/Pnz8/WrduncWKoHb57LPPonPnzrFq1ao47rjjok2bNlG3bt2i5UmSxMKFC+P555+PI444Iu65554499xzs1gx1C5JksRNN91U9PMrr7wSffr0yV5BNcyrr74aRx55ZGy55ZZx4oknxg477BC5ublFy/Pz82P27NnxzDPPxGOPPRYjRoyIn/70p1msOL0mTJgQ22yzTVx22WWV8noNGzaM0047rejnwkCra9eu0aVLlxg3blzssccelfJerCsnSZIk20XUVJmeWnDLLbeMyy+/PK688soqfR+oiCRJ4ne/+11cd911xZ6/9tpr49prry12Mh2oGu+991506NAhbr755rj00ktLbfvhhx/GwQcfHOeee26xP06AqvPGG2/E4YcfHt9++21ERNSpUyd+/OMfx4svvhjNmjXLcnXV36OPPhqnnnpqjBw5Mnr27Flq29GjR8cRRxwRw4YNi9NPPz0zBZI6//nPf6Jr164xe/bsyM/Pj4iIbbfdNsaNGxe77rprlquD2uGYY46JKVOmxOuvvx5bb711ie2SJInzzz8/hg4dGvPnz/e5CRmwZs2aOOuss+Lhhx8uei43NzeGDRsWv/jFL7JYWc2QJEm0adMmmjdvHqNHjy71C24rV66Mo446Kj799NP4/PPPnWNbj379+sWHH34Yb775ZpW+z3//+9/o2rVrzJs3T6BVhUwzWEXcI4vKtmzZshg8eHB07do1WrZsGfXq1YuWLVtG165dY/DgwbF8+fJsl7heTz755DpBVkTE9ddfH0888UTmC6rBqmsfoeo9/fTT0axZs7jooovKbLvHHnvESSedFE899VQGKgMWL14cPXr0iGXLlhU9V1BQEO+8844wpZI8/fTTceCBB5YZZEVE9OjRIw4++GBjYCWorscl+fn5ceSRR8aXX35ZFGRFRMydOzeOOOKIWLt2bRarq3mqaz+hai1btixeeuml6N+/f6lBVkRETk5OXHvttZGfnx8jRozIUIVQu914443xyCOPFHsuPz8/TjvttHj33XezVFXN8eGHH8Ynn3wSv/71r8ucqaFBgwZx1VVXxRdffGG6wSwz5WBmCLOqgCCLyvbCCy/ELrvsEgMHDoxx48bFypUrY9ttt42VK1fGuHHjYuDAgbHzzjvHCy+8kO1SiykoKIirr7466tRZd6ipU6dOXH311ZU2zWZtV137CJnx6aefxr777ltsapbS7L///vGf//zHCTvIgNtuuy2WL1++zudhfn5+PP/8804IVIJPPvkkfvzjH5e7/QEHHBCffvppFVZU81Xn45J//OMfMX369HU+A/Pz82PGjBnx2GOPZamymqc69xOq1uzZs2PVqlXlHru33nrr2H777Y3dkAFLliyJP//5z7G+ib7y8vLit7/9bRaqqlkKx7L999+/XO0POOCAYuuRPQKtqifMqmTlCbI6d+5cdBO5yZMnl+t1J0yYULSOOWhrlyFDhkTv3r1j4cKF0a9fv5gyZUp89NFH8cwzz8THH38c77//fpx99tmxcOHC6N27dwwZMiTbJRcZN25czJgxY72BVUFBQfznP/+JsWPHZqGymqU69xEyY/Xq1RX6YkVh2zVr1lRVSUB8t2/eddddJX6xIy8vL+66664MV1XzbMgYuHr16iqsqGar7scld9xxx3q/iBXx3Zex7rjjjgxXVDNV935C1Socg43dkD6PPPJIrFixYr3L1q5dG6NGjYqZM2dmuKqapaJjYGE7Y2DFVcU5eoFW1RJmVaKKXJF19tlnx9y5c2PPPfeMiO9u9N2hQ4eoX79+tG/ffp32Bx10UMydOzdOPPHEqiqfFBo3blxccMEF0bhx4xg1alTcd9990a5du7j11ltjn332iUGDBsVee+0VQ4YMiVGjRkWjRo3iggsuiDFjxmS79IiIePbZZyMvL6/E5Xl5efHss89msKKap7r3EbLv9NNP9yUJyJJ///vfsXjx4hKXr127Np566qn1fvOVjWf8q3zV/bhk0aJF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UKTKBo+L58/v3bu3V+0vtuvNufX0L0FW//OUqp8ItOBf06dPj507d0Y2m42SkpIoKSmJbDYbO3fujHvuuafY5cGQ8Mgjj0RHR0esXLkyzp07d9V2uVwuduzYEc3NzbFo0aICVghDVzabjZaWlpg8eXKk0+nIZDIREbFixQovfeTJ4sWL4/jx4/Hyyy/3uC1OZ2dnvPrqq9Ha2moMHOKamppizpw5kUqlIpPJRCqVilmzZsWHH35Y7NJgSMhkMvHoo4/Gli1b4tChQz22PX36dKxcuTIqKyvj/vvvL1CFMLQ99thjsXXr1kvfk+l0OkaMGBEtLS1x2223Fbu8AW/06NExc+bM2LBhQ5w4caLHtidPnoz169dHXV1dTJo0qUAVciWCrMJI5Wys2a9OnjwZjY2NUVJSctmWg21tbfHiiy/mbW/TpqameOmll+K5557Ly/0gnz799NNoamqKXC4XCxcujIaGhmKXBEPKjh07YunSpVFeXh733ntvVFZWdlsxef78+fjss8+ira0tFi1aFO+9995VzwwB8u/PP/+M119/PX799deor6+PZcuWdfuM8t+88sorsWHDhrj55ptjxowZUV5efunnm8vl4u+//47Dhw/HL7/8Es8//3y88MILfv5D3IULF2Lr1q1x/PjxmDBhQjzzzDOXwmag/7W3t8e8efPi0KFDUVNTE7fffnu3z2BXV1f89ttvcfDgwchkMrFnz56YMWNGESuGoeejjz6KPXv2RHl5+aXdBsiPU6dORWNjY3z77bcxffr0GDt2bKTT6UvXOzs749SpU3H48OGoqqqK/fv3x/jx44tYcXItX748mpubY+7cuXm5X1lZWWzcuDFGjhx56e8EWYUjzCqAqwVaa9asie3bt+ftoX42m41t27b58gDgir7++utoamqKo0ePxpkzZ7pdy2QyMX78+Hj44Ydj9uzZ3SbKAIPBJ598Ek1NTfHFF19ctkq1tLQ07rzzzli4cGFMmzZNkAWQAOfOnYvdu3dHc3Nz/PDDD5etrr3xxhujsbExFi5ceNlZ5QAD3enTp6O5uTl2794dv//+e3R1dV26lkqlYtSoUfHAAw/EQw891C1YobsjR47E+vXr83a/gwcPxubNm2P16tURIcgqNGFWgVwp0FqzZk0cOHAgPv/882KXBwAAAAAAXEV5eXls2rQpVq9eLcgqAmdmFYgztAAAAAAAYGATZBWHMKuABFoAAAAAADAwHTt2TJBVJLYZLIKLWw5+9913UVNTY5tBAAAAAABIsPLy8jh79mzU19cLsoqgpNgFDEUXV2g1NjZGdXV1scsBAAAAAAB6UF1dHel0WpBVJFZmFdG5c+di+PDhxS4DAAAAAADoQS6Xi3/++SdKS0uLXcqQJMwCAAAAAAAgsYYVuwAAAAAAAAC4GmEWAAAAAAAAiSXMAgAAAAAAILGEWQAAAAAAACSWMAsAAAAAAIDEEmYBAAAAAACQWMIsAAAAAAAAEkuYBQAAAAAAQGIJswAAAAAAAEgsYRYAAAAAAACJJcwCAAAAAAAgsYRZAAAAAAAAJJYwCwAAAAAAgMQSZgEAAAAAAJBYwiwAAAAAAAASS5gFAAAAAABAYgmzAAAAAAAASCxhFgAAAAAAAIklzAIAAAAAACCxhFkAAAAAAAAkljALAAAAAACAxBJmAQAAAAAAkFjCLAAAAAAAABJLmAUAAAAAAEBiCbMAAAAAAABILGEWAAAAAAAAiSXMAgAAAAAAILGEWQAAAAAAACSWMAsAAAAAAIDEEmYBAAAAAACQWMIsAAAAAAAAEkuYBQAAAAAAQGIJswAAAAAAAEgsYRYAAAAAAACJJcwCAAAAAAAgsYRZAAAAAAAAJJYwCwAAAAAAgMQSZgEAAAAAAJBYwiwAAAAAAAASS5gFAAAAAABAYgmzAAAAAAAASKz/A0CXnGP+x3uvAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "class MBCUnaryIterator(Bloq):\n", + " ctrl_bitsize: int\n", + " state = []\n", + " sys_bitsize: int\n", + "\n", + " @property\n", + " def signature(self) -> Signature:\n", + " return Signature([Register('ctrl', QAny(self.ctrl_bitsize)), Register('anc', QAny(self.ctrl_bitsize - 1)), Register('sys', QAny(self.sys_bitsize))])\n", + " \n", + " def set_ops(self, ops):\n", + " self.ops = ops\n", + "\n", + " def compute(self, query: List[bool], bb: BloqBuilder, ancs, ctrls):\n", + " for ix in range(len(self.state)):\n", + " assert self.state[ix] == query[ix]\n", + " if len(self.state) == len(query):\n", + " return\n", + " if len(self.state) == 0:\n", + " g0 = XGate() if query[0] == False else Identity()\n", + " g1 = XGate() if query[1] == False else Identity()\n", + " ctrls[0] = bb.add(g0, q=ctrls[0])\n", + " ctrls[1] = bb.add(g1, q=ctrls[1])\n", + " ctrls[:2], ancs[0] = bb.add(Toffoli(), ctrl=ctrls[:2], target=ancs[0])\n", + " ctrls[0] = bb.add(g0, q=ctrls[0])\n", + " ctrls[1] = bb.add(g1, q=ctrls[1])\n", + " self.state.append(query[0])\n", + " self.state.append(query[1])\n", + " else:\n", + " ctrl_ix = len(self.state)\n", + " g = XGate() if query[ctrl_ix] == False else Identity()\n", + " ctrls[ctrl_ix] = bb.add(g, q=ctrls[ctrl_ix])\n", + " [ctrls[ctrl_ix], ancs[ctrl_ix - 2]], ancs[ctrl_ix - 1] = bb.add(Toffoli(), ctrl=[ctrls[ctrl_ix], ancs[ctrl_ix - 2]], target=ancs[ctrl_ix - 1])\n", + " ctrls[ctrl_ix] = bb.add(g, q=ctrls[ctrl_ix])\n", + " self.state.append(query[ctrl_ix])\n", + " self.compute(query, bb, ancs, ctrls)\n", + "\n", + " def uncompute(self, query: List[bool], bb: BloqBuilder, ancs, ctrls):\n", + " first_diff_ix = None\n", + " if len(query) == 0:\n", + " first_diff_ix = 0\n", + " else: \n", + " for ix in range(len(self.state)):\n", + " if self.state[ix] != query[ix]:\n", + " first_diff_ix = ix\n", + " break\n", + " if first_diff_ix is None:\n", + " # state is a prefix of query so we do not need to uncompute\n", + " return\n", + " if first_diff_ix < len(self.state) - 1:\n", + " # we have some extra bits we have to undo\n", + " if len(self.state) == 2 and first_diff_ix == 0:\n", + " # we are the bottom of the barrel\n", + " g0 = XGate() if self.state[0] == False else Identity()\n", + " g1 = XGate() if self.state[1] == False else Identity()\n", + " ctrls[0] = bb.add(g0, q=ctrls[0])\n", + " ctrls[1] = bb.add(g1, q=ctrls[1])\n", + " ancs[0], ctrls[0], ctrls[1] = bb.add(MeasurementUncomputation(), ctrl=ancs[0], q1=ctrls[0], q2=ctrls[1])\n", + " ctrls[0] = bb.add(g0, q=ctrls[0])\n", + " ctrls[1] = bb.add(g1, q=ctrls[1])\n", + " self.state.pop()\n", + " self.state.pop()\n", + " else:\n", + " ctrl_ix = len(self.state) - 1\n", + " g = XGate() if self.state[ctrl_ix] == False else Identity()\n", + " ctrls[ctrl_ix] = bb.add(g, q=ctrls[ctrl_ix])\n", + " ancs[ctrl_ix - 1], ctrls[ctrl_ix], ancs[ctrl_ix - 2] = bb.add(MeasurementUncomputation(), ctrl = ancs[ctrl_ix - 1], q1 = ctrls[ctrl_ix], q2 = ancs[ctrl_ix - 2])\n", + " ctrls[ctrl_ix] = bb.add(g, q=ctrls[ctrl_ix])\n", + " self.state.pop()\n", + " self.uncompute(query, bb, ancs, ctrls)\n", + " elif len(self.state) == 0:\n", + " return\n", + " else:\n", + " # first_diff_ix is the last bit, so we just need to do the CNOT trick\n", + " if first_diff_ix == 1:\n", + " g = XGate() if self.state[0] == False else Identity()\n", + " ctrls[0] = bb.add(g, q=ctrls[0])\n", + " ctrls[0], ancs[0] = bb.add(CNOT(), ctrl=ctrls[0], target=ancs[0])\n", + " ctrls[0] = bb.add(g, q=ctrls[0])\n", + " self.state[1] ^= True\n", + " else:\n", + " ancs[first_diff_ix - 2], ancs[first_diff_ix - 1] = bb.add(CNOT(), ctrl=ancs[first_diff_ix - 2], target=ancs[first_diff_ix - 1])\n", + " self.state[first_diff_ix] ^= True\n", + " return\n", + "\n", + " def build_composite_bloq(self, bb: BloqBuilder, ctrl: SoquetT, anc: SoquetT, sys: SoquetT) -> Dict[str, 'SoquetT']:\n", + " queries = list(self.ops.keys())\n", + " queries.sort()\n", + " ctrls = bb.split(ctrl)\n", + " ancs = bb.split(anc)\n", + " bb.add_register_allowed = True\n", + " for q_int in queries:\n", + " q_bools = int_to_bool_list(q_int, self.ctrl_bitsize)\n", + " self.uncompute(q_bools, bb, ancs, ctrls)\n", + " self.compute(q_bools, bb, ancs, ctrls)\n", + " [ancs[-1], sys] = bb.add(self.ops[q_int], q=[ancs[-1], sys])\n", + " self.uncompute([], bb, ancs, ctrls)\n", + " ctrl = bb.join(ctrls)\n", + " anc = bb.join(ancs)\n", + " return {'ctrl': ctrl, 'sys': sys, 'anc': anc}\n", + " \n", + "cbloq = UnaryIterator()\n", + "cbloq.ctrl_bitsize = 2\n", + "cbloq.sys_bitsize = 1\n", + "ops = dict()\n", + "for ix in range(4):\n", + " ops[ix] = CZPowGate(exponent=float(ix))\n", + "cbloq.set_ops(ops)\n", + "msd = get_musical_score_data(cbloq.decompose_bloq())\n", + "fig, ax = draw_musical_score(msd)\n", + "fig.set_figwidth(18)\n", + "fig.set_figheight(7)" + ] + }, + { + "cell_type": "markdown", + "id": "e4aa6e9a", + "metadata": {}, + "source": [ + "Now we can use Qualtran's resource estimation protocols to determine just how many T-gates the measurement based uncomputation has saved us." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "af387d37", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Toffoli based uncomputation:\n", + " T-count: 112\n", + "Rotations: 0\n", + "Cliffords: 64\n", + "\n", + "Measurement based uncomputation:\n", + " T-count: 56\n", + "Rotations: 0\n", + "Cliffords: 92\n", + "\n" + ] + } + ], + "source": [ + "cbloq = UnaryIterator()\n", + "cbloq.ctrl_bitsize = 4\n", + "cbloq.sys_bitsize = 1\n", + "ops = dict()\n", + "for ix in range(2**4):\n", + " ops[ix] = CZPowGate(exponent=float(ix))\n", + "cbloq.set_ops(ops)\n", + "mbc = MBCUnaryIterator()\n", + "mbc.ctrl_bitsize = 4\n", + "mbc.sys_bitsize = 1\n", + "mbc.set_ops(ops)\n", + "from qualtran.cirq_interop.t_complexity_protocol import t_complexity\n", + "print(\"Toffoli based uncomputation:\\n\", t_complexity(cbloq))\n", + "print(\"Measurement based uncomputation:\\n\", t_complexity(mbc))", + "from qualtran import QAny, Register, Register, BQUInt\n", + "from qualtran.bloqs.multiplexers.unary_iteration_bloq import UnaryIterationGate\n", + "from functools import cached_property\n", + "\n", + "\n", + "\n", + "class ApplyXToLthQubit(UnaryIterationGate):\n", + " def __init__(self, selection_bitsize: int, target_bitsize: int, control_bitsize: int = 1):\n", + " self._selection_bitsize = selection_bitsize\n", + " self._target_bitsize = target_bitsize\n", + " self._control_bitsize = control_bitsize\n", + "\n", + " @cached_property\n", + " def control_registers(self) -> Tuple[Register, ...]:\n", + " return (Register('control', QAny(self._control_bitsize)),)\n", + "\n", + " @cached_property\n", + " def selection_registers(self) -> Tuple[Register, ...]:\n", + " return (Register('selection', BQUInt(self._selection_bitsize, self._target_bitsize)),)\n", + "\n", + " @cached_property\n", + " def target_registers(self) -> Tuple[Register, ...]:\n", + " return (Register('target', QAny(self._target_bitsize)),)\n", + "\n", + " def nth_operation(\n", + " self, context, control: cirq.Qid, selection: int, target: Sequence[cirq.Qid]\n", + " ) -> cirq.OP_TREE:\n", + " return cirq.CNOT(control, target[-(selection + 1)])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a1e4bafa", + "metadata": {}, + "outputs": [], + "source": [ + "import qualtran.cirq_interop.testing as cq_testing\n", + "selection_bitsize = 3\n", + "target_bitsize = 5\n", + "\n", + "g = cq_testing.GateHelper(\n", + " ApplyXToLthQubit(selection_bitsize, target_bitsize))\n", + "SVGCircuit(cirq.Circuit(cirq.decompose_once(g.operation)))" + ] + }, + { + "cell_type": "markdown", + "id": "83e29013", + "metadata": {}, + "source": [ + "As we can see from the above we have cut our T-count in half! This makes sense as every Toffoli in the compute stage cannot be eliminated (we have to do an AND at some point), but the \"mirror\" Toffoli used to uncompute can be replaced with a measurement and Clifford gates. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file