diff --git a/dev_tools/autogenerate-bloqs-notebooks-v2.py b/dev_tools/autogenerate-bloqs-notebooks-v2.py index a86602eec..c98c6cd3e 100644 --- a/dev_tools/autogenerate-bloqs-notebooks-v2.py +++ b/dev_tools/autogenerate-bloqs-notebooks-v2.py @@ -217,10 +217,19 @@ ], ), NotebookSpecV2( - title='Swap Network', - module=qualtran.bloqs.swap_network, + title='Basic Swaps', + module=qualtran.bloqs.basic_gates.swap, bloq_specs=[ + qualtran.bloqs.basic_gates.swap._TWO_BIT_SWAP_DOC, + qualtran.bloqs.basic_gates.swap._TWO_BIT_CSWAP_DOC, + qualtran.bloqs.basic_gates.swap._SWAP_DOC, qualtran.bloqs.basic_gates.swap._CSWAP_DOC, + ], + ), + NotebookSpecV2( + title='Swap Networks', + module=qualtran.bloqs.swap_network, + bloq_specs=[ qualtran.bloqs.swap_network.cswap_approx._APPROX_CSWAP_DOC, qualtran.bloqs.swap_network.swap_with_zero._SWZ_DOC, qualtran.bloqs.swap_network.multiplexed_cswap._MULTIPLEXED_CSWAP_DOC, diff --git a/docs/bloqs/index.rst b/docs/bloqs/index.rst index 05a75dd4d..34df92ddb 100644 --- a/docs/bloqs/index.rst +++ b/docs/bloqs/index.rst @@ -34,6 +34,7 @@ Bloqs Library basic_gates/y_gate.ipynb mcmt/and_bloq.ipynb basic_gates/states_and_effects.ipynb + basic_gates/swap.ipynb swap_network/swap_network.ipynb basic_gates/global_phase.ipynb basic_gates/identity.ipynb diff --git a/qualtran/bloqs/basic_gates/swap.ipynb b/qualtran/bloqs/basic_gates/swap.ipynb new file mode 100644 index 000000000..bb9fa0cf2 --- /dev/null +++ b/qualtran/bloqs/basic_gates/swap.ipynb @@ -0,0 +1,493 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "14e2b0ae", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# Basic Swaps" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f05e9c2", + "metadata": { + "cq.autogen": "top_imports" + }, + "outputs": [], + "source": [ + "from qualtran import Bloq, CompositeBloq, BloqBuilder, Signature, Register\n", + "from qualtran import QBit, QInt, QUInt, QAny\n", + "from qualtran.drawing import show_bloq, show_call_graph, show_counts_sigma\n", + "from typing import *\n", + "import numpy as np\n", + "import sympy\n", + "import cirq" + ] + }, + { + "cell_type": "markdown", + "id": "e2cbcfe8", + "metadata": { + "cq.autogen": "TwoBitSwap.bloq_doc.md" + }, + "source": [ + "## `TwoBitSwap`\n", + "Swap two bits.\n", + "\n", + "This is a Clifford operation.\n", + "\n", + "#### Registers\n", + " - `x`: the first bit\n", + " - `y`: the second bit\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "daf1d7ba", + "metadata": { + "cq.autogen": "TwoBitSwap.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.basic_gates import TwoBitSwap" + ] + }, + { + "cell_type": "markdown", + "id": "358f64f0", + "metadata": { + "cq.autogen": "TwoBitSwap.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bff96ba3", + "metadata": { + "cq.autogen": "TwoBitSwap.swap_bit" + }, + "outputs": [], + "source": [ + "swap_bit = TwoBitSwap()" + ] + }, + { + "cell_type": "markdown", + "id": "c9889dd6", + "metadata": { + "cq.autogen": "TwoBitSwap.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ae8b94b4", + "metadata": { + "cq.autogen": "TwoBitSwap.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([swap_bit],\n", + " ['`swap_bit`'])" + ] + }, + { + "cell_type": "markdown", + "id": "1b73e8f3", + "metadata": { + "cq.autogen": "TwoBitCSwap.bloq_doc.md" + }, + "source": [ + "## `TwoBitCSwap`\n", + "Swap two bits controlled on a control bit.\n", + "\n", + "This is sometimes known as the [Fredkin Gate](https://en.wikipedia.org/wiki/Fredkin_gate).\n", + "\n", + "#### Registers\n", + " - `ctrl`: the control bit\n", + " - `x`: the first bit\n", + " - `y`: the second bit \n", + "\n", + "#### References\n", + " - [An algorithm for the T-count](https://arxiv.org/abs/1308.4134). Gosset et. al. 2013. Figure 5.2.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16bea751", + "metadata": { + "cq.autogen": "TwoBitCSwap.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.basic_gates import TwoBitCSwap" + ] + }, + { + "cell_type": "markdown", + "id": "86e8d4e0", + "metadata": { + "cq.autogen": "TwoBitCSwap.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd0cd0f4", + "metadata": { + "cq.autogen": "TwoBitCSwap.cswap_bit" + }, + "outputs": [], + "source": [ + "cswap_bit = TwoBitCSwap()" + ] + }, + { + "cell_type": "markdown", + "id": "883806ba", + "metadata": { + "cq.autogen": "TwoBitCSwap.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "191155bc", + "metadata": { + "cq.autogen": "TwoBitCSwap.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([cswap_bit],\n", + " ['`cswap_bit`'])" + ] + }, + { + "cell_type": "markdown", + "id": "2e20fd3d-54fb-4762-a4e2-9498553a3f74", + "metadata": {}, + "source": [ + "### Clifford+T circuit" + ] + }, + { + "cell_type": "markdown", + "id": "892102fa-8192-48a6-a5d9-51e47f1fa3c5", + "metadata": {}, + "source": [ + "In Qualtran, this bloq is treated as atomic because its implementation is architecture-dependent. A clifford+T compilation is provided via a Cirq circuit on the `to_clifford_t_circuit()` method" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa390bfc-816b-4623-b341-614a6dd30dbf", + "metadata": {}, + "outputs": [], + "source": [ + "cswap_bit.to_clifford_t_circuit()" + ] + }, + { + "cell_type": "markdown", + "id": "883ca296", + "metadata": { + "cq.autogen": "Swap.bloq_doc.md" + }, + "source": [ + "## `Swap`\n", + "Swap two registers\n", + "\n", + "This corresponds to a qubitwise `TwoBitSwap` on the two registers.\n", + "\n", + "#### Parameters\n", + " - `bitsize`: The bitsize of each of the two registers being swapped. \n", + "\n", + "#### Registers\n", + " - `x`: the first register\n", + " - `y`: the second register\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fba2eaf0", + "metadata": { + "cq.autogen": "Swap.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.basic_gates import Swap" + ] + }, + { + "cell_type": "markdown", + "id": "37b0df8c", + "metadata": { + "cq.autogen": "Swap.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bbfa7691", + "metadata": { + "cq.autogen": "Swap.swap" + }, + "outputs": [], + "source": [ + "n = sympy.Symbol('n', positive=True, integer=True)\n", + "swap = Swap(bitsize=n)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6fc3b608", + "metadata": { + "cq.autogen": "Swap.swap_small" + }, + "outputs": [], + "source": [ + "swap_small = Swap(bitsize=4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b16f9c34", + "metadata": { + "cq.autogen": "Swap.swap_large" + }, + "outputs": [], + "source": [ + "swap_large = Swap(bitsize=64)" + ] + }, + { + "cell_type": "markdown", + "id": "ec258365", + "metadata": { + "cq.autogen": "Swap.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b327687f", + "metadata": { + "cq.autogen": "Swap.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([swap, swap_small, swap_large],\n", + " ['`swap`', '`swap_small`', '`swap_large`'])" + ] + }, + { + "cell_type": "markdown", + "id": "fe3030df", + "metadata": { + "cq.autogen": "Swap.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c773542", + "metadata": { + "cq.autogen": "Swap.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "swap_g, swap_sigma = swap.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(swap_g)\n", + "show_counts_sigma(swap_sigma)" + ] + }, + { + "cell_type": "markdown", + "id": "b893cfae", + "metadata": { + "cq.autogen": "CSwap.bloq_doc.md" + }, + "source": [ + "## `CSwap`\n", + "Swap two registers controlled on a control bit.\n", + "\n", + "This decomposes into a qubitwise `TwoBitCSwap` on the two target registers,\n", + "and takes $n$ TwoBitCSwap gates.\n", + "\n", + "#### Parameters\n", + " - `bitsize`: The bitsize of each of the two registers being swapped. \n", + "\n", + "#### Registers\n", + " - `ctrl`: the control bit\n", + " - `x`: the first register\n", + " - `y`: the second register\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3282a474", + "metadata": { + "cq.autogen": "CSwap.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.basic_gates import CSwap" + ] + }, + { + "cell_type": "markdown", + "id": "2b72b8d4", + "metadata": { + "cq.autogen": "CSwap.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a532894", + "metadata": { + "cq.autogen": "CSwap.cswap" + }, + "outputs": [], + "source": [ + "n = sympy.Symbol('n', positive=True, integer=True)\n", + "cswap = CSwap(bitsize=n)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1a9b594", + "metadata": { + "cq.autogen": "CSwap.cswap_small" + }, + "outputs": [], + "source": [ + "# A small version on four bits.\n", + "cswap_small = CSwap(bitsize=4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6045b697", + "metadata": { + "cq.autogen": "CSwap.cswap_large" + }, + "outputs": [], + "source": [ + "# A large version that swaps 64-bit registers.\n", + "cswap_large = CSwap(bitsize=64)" + ] + }, + { + "cell_type": "markdown", + "id": "d5265723", + "metadata": { + "cq.autogen": "CSwap.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4bf1115f", + "metadata": { + "cq.autogen": "CSwap.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([cswap, cswap_small, cswap_large],\n", + " ['`cswap`', '`cswap_small`', '`cswap_large`'])" + ] + }, + { + "cell_type": "markdown", + "id": "34028742", + "metadata": { + "cq.autogen": "CSwap.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5cf02307", + "metadata": { + "cq.autogen": "CSwap.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "cswap_g, cswap_sigma = cswap.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(cswap_g)\n", + "show_counts_sigma(cswap_sigma)" + ] + } + ], + "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.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/qualtran/bloqs/basic_gates/swap.py b/qualtran/bloqs/basic_gates/swap.py index 3242a1abb..9df8148a3 100644 --- a/qualtran/bloqs/basic_gates/swap.py +++ b/qualtran/bloqs/basic_gates/swap.py @@ -13,7 +13,7 @@ # limitations under the License. from functools import cached_property -from typing import Dict, Iterable, Iterator, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union +from typing import Dict, Iterable, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union import cirq import numpy as np @@ -35,15 +35,11 @@ Soquet, SoquetT, ) -from qualtran.cirq_interop import CirqQuregT, decompose_from_cirq_style_method +from qualtran.cirq_interop import CirqQuregT from qualtran.cirq_interop.t_complexity_protocol import TComplexity from qualtran.drawing import Circle, Text, TextBox, WireSymbol from qualtran.resource_counting.generalizers import ignore_split_join -from .cnot import CNOT -from .hadamard import Hadamard -from .t_gate import TGate - if TYPE_CHECKING: import quimb.tensor as qtn @@ -67,6 +63,8 @@ def _controlled_swap_matrix(): class TwoBitSwap(Bloq): """Swap two bits. + This is a Clifford operation. + Registers: x: the first bit y: the second bit @@ -76,6 +74,9 @@ class TwoBitSwap(Bloq): def signature(self) -> Signature: return Signature.build(x=1, y=1) + def decompose_bloq(self) -> 'CompositeBloq': + raise DecomposeTypeError(f"{self} is atomic.") + def as_cirq_op( self, qubit_manager: 'cirq.QubitManager', x: 'CirqQuregT', y: 'CirqQuregT' # type: ignore[type-var] ) -> Tuple['cirq.Operation', Dict[str, 'CirqQuregT']]: # type: ignore[type-var] @@ -120,6 +121,15 @@ def adder( return cswap, adder +@bloq_example +def _swap_bit() -> TwoBitSwap: + swap_bit = TwoBitSwap() + return swap_bit + + +_TWO_BIT_SWAP_DOC = BloqDocSpec(bloq_cls=TwoBitSwap, examples=[_swap_bit], call_graph_example=None) + + @frozen class TwoBitCSwap(Bloq): """Swap two bits controlled on a control bit. @@ -141,40 +151,30 @@ def signature(self) -> Signature: return Signature.build(ctrl=1, x=1, y=1) def decompose_bloq(self) -> 'CompositeBloq': - return decompose_from_cirq_style_method(self) - - def decompose_from_registers( - self, - *, - context: cirq.DecompositionContext, - ctrl: NDArray[cirq.Qid], # type: ignore[type-var] - x: NDArray[cirq.Qid], # type: ignore[type-var] - y: NDArray[cirq.Qid], # type: ignore[type-var] - ) -> Iterator[cirq.OP_TREE]: - (ctrl,) = ctrl - (x,) = x - (y,) = y - yield [cirq.CNOT(y, x)] - yield [cirq.CNOT(ctrl, x), cirq.H(y)] - yield [cirq.T(ctrl), cirq.T(x) ** -1, cirq.T(y)] - yield [cirq.CNOT(y, x)] - yield [cirq.CNOT(ctrl, y), cirq.T(x)] - yield [cirq.CNOT(ctrl, x), cirq.T(y) ** -1] - yield [cirq.T(x) ** -1, cirq.CNOT(ctrl, y)] - yield [cirq.CNOT(y, x)] - yield [cirq.T(x), cirq.H(y)] - yield [cirq.CNOT(y, x)] + raise DecomposeTypeError(f"{self} is atomic.") + + def to_clifford_t_circuit(self) -> 'cirq.FrozenCircuit': + ctrl = cirq.NamedQubit('ctrl') + x = cirq.NamedQubit('x') + y = cirq.NamedQubit('y') + circuit = cirq.Circuit() + circuit += [cirq.CNOT(y, x)] + circuit += [cirq.CNOT(ctrl, x), cirq.H(y)] + circuit += [cirq.T(ctrl), cirq.T(x) ** -1, cirq.T(y)] + circuit += [cirq.CNOT(y, x)] + circuit += [cirq.CNOT(ctrl, y), cirq.T(x)] + circuit += [cirq.CNOT(ctrl, x), cirq.T(y) ** -1] + circuit += [cirq.T(x) ** -1, cirq.CNOT(ctrl, y)] + circuit += [cirq.CNOT(y, x)] + circuit += [cirq.T(x), cirq.H(y)] + circuit += [cirq.CNOT(y, x)] + return circuit.freeze() def my_tensors( self, incoming: Dict[str, 'ConnectionT'], outgoing: Dict[str, 'ConnectionT'] ) -> List['qtn.Tensor']: import quimb.tensor as qtn - # TODO: https://github.com/quantumlib/Qualtran/issues/873. Since this bloq - # has a decomposition, it will be used (by default) whenever `bloq.tensor_contract()` - # is called on a bloq containing a TwoBitCSwap instead of this implementation. - # When this becomes a leaf bloq, this explicit tensor will be used. - matrix = _controlled_swap_matrix() out_inds = [(outgoing['ctrl'], 0), (outgoing['x'], 0), (outgoing['y'], 0)] in_inds = [(incoming['ctrl'], 0), (incoming['x'], 0), (incoming['y'], 0)] @@ -189,12 +189,6 @@ def on_classical_vals( return {'ctrl': 1, 'x': y, 'y': x} raise ValueError("Bad control value for TwoBitCSwap classical simulation.") - def _t_complexity_(self) -> 'TComplexity': - return TComplexity(t=7, clifford=10) - - def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': - return {TGate(): 7, CNOT(): 8, Hadamard(): 2} - def adjoint(self) -> 'Bloq': return self @@ -207,10 +201,23 @@ def wire_symbol(self, reg: Optional['Register'], idx: Tuple[int, ...] = ()) -> ' return TextBox('×') +@bloq_example +def _cswap_bit() -> TwoBitCSwap: + cswap_bit = TwoBitCSwap() + return cswap_bit + + +_TWO_BIT_CSWAP_DOC = BloqDocSpec( + bloq_cls=TwoBitCSwap, examples=[_cswap_bit], call_graph_example=None +) + + @frozen class Swap(Bloq): """Swap two registers + This corresponds to a qubitwise `TwoBitSwap` on the two registers. + Args: bitsize: The bitsize of each of the two registers being swapped. @@ -275,19 +282,34 @@ def adder( return cswap, adder +@bloq_example +def _swap() -> Swap: + n = sympy.Symbol('n', positive=True, integer=True) + swap = Swap(bitsize=n) + return swap + + @bloq_example(generalizer=ignore_split_join) def _swap_small() -> Swap: swap_small = Swap(bitsize=4) return swap_small +@bloq_example +def _swap_large() -> Swap: + swap_large = Swap(bitsize=64) + return swap_large + + +_SWAP_DOC = BloqDocSpec(bloq_cls=Swap, examples=[_swap, _swap_small, _swap_large]) + + @frozen class CSwap(GateWithRegisters): """Swap two registers controlled on a control bit. - Implements a multi-target controlled swap unitary $CSWAP_n = |0><0| I + |1><1| SWAP_n$. - - This decomposes into a qubitwise SWAP on the two target registers, and takes $14n$ T-gates. + This decomposes into a qubitwise `TwoBitCSwap` on the two target registers, + and takes $n$ TwoBitCSwap gates. Args: bitsize: The bitsize of each of the two registers being swapped. @@ -359,12 +381,10 @@ def adjoint(self) -> 'Bloq': @bloq_example -def _cswap_symb() -> CSwap: - # A symbolic version. The bitsize is the symbol 'n'. - from sympy import sympify - - cswap_symb = CSwap(bitsize=sympify('n')) - return cswap_symb +def _cswap() -> CSwap: + n = sympy.Symbol('n', positive=True, integer=True) + cswap = CSwap(bitsize=n) + return cswap @bloq_example(generalizer=ignore_split_join) @@ -381,4 +401,4 @@ def _cswap_large() -> CSwap: return cswap_large -_CSWAP_DOC = BloqDocSpec(bloq_cls=CSwap, examples=(_cswap_symb, _cswap_small, _cswap_large)) +_CSWAP_DOC = BloqDocSpec(bloq_cls=CSwap, examples=(_cswap, _cswap_small, _cswap_large)) diff --git a/qualtran/bloqs/basic_gates/swap_test.py b/qualtran/bloqs/basic_gates/swap_test.py index 60a61994b..4ddb35587 100644 --- a/qualtran/bloqs/basic_gates/swap_test.py +++ b/qualtran/bloqs/basic_gates/swap_test.py @@ -31,9 +31,10 @@ ) from qualtran.bloqs.basic_gates.swap import ( _controlled_swap_matrix, + _cswap, _cswap_large, _cswap_small, - _cswap_symb, + _swap, _swap_matrix, _swap_small, Swap, @@ -104,9 +105,6 @@ def _set_ctrl_two_bit_swap(ctrl_bit): def test_two_bit_cswap(): cswap = TwoBitCSwap() np.testing.assert_allclose(cswap.tensor_contract(), cirq.unitary(cirq.CSWAP), atol=1e-8) - np.testing.assert_allclose( - cswap.decompose_bloq().tensor_contract(), cirq.unitary(cirq.CSWAP), atol=1e-8 - ) # Zero ctrl -- it's identity np.testing.assert_allclose(np.eye(4), _set_ctrl_two_bit_swap(0).tensor_contract(), atol=1e-8) @@ -226,6 +224,12 @@ def test_swap_small(bloq_autotester): bloq_autotester(_swap_small) +def test_swap_symb(bloq_autotester): + if bloq_autotester.check_name == 'serialize': + pytest.skip("Sympy equality with assumptions.") + bloq_autotester(_swap) + + def test_cswap_small(bloq_autotester): bloq_autotester(_cswap_small) @@ -235,4 +239,6 @@ def test_cswap_large(bloq_autotester): def test_cswap_symb(bloq_autotester): - bloq_autotester(_cswap_symb) + if bloq_autotester.check_name == 'serialize': + pytest.skip("Sympy equality with assumptions.") + bloq_autotester(_cswap) diff --git a/qualtran/bloqs/swap_network/multiplexed_cswap_test.py b/qualtran/bloqs/swap_network/multiplexed_cswap_test.py index 9a98c4196..5d0ff34eb 100644 --- a/qualtran/bloqs/swap_network/multiplexed_cswap_test.py +++ b/qualtran/bloqs/swap_network/multiplexed_cswap_test.py @@ -18,9 +18,9 @@ import pytest from qualtran import BQUInt, QUInt, Register -from qualtran.bloqs.basic_gates import TGate from qualtran.bloqs.swap_network.multiplexed_cswap import _multiplexed_cswap, MultiplexedCSwap from qualtran.cirq_interop.testing import assert_circuit_inp_out_cirqsim, GateHelper +from qualtran.resource_counting import GateCounts, get_cost_value, QECGatesCost from qualtran.testing import assert_valid_bloq_decomposition random.seed(12345) @@ -75,9 +75,15 @@ def test_multiplexed_cswap_t_counts(selection_bitsize, iteration_length, target_ Register('selection', BQUInt(selection_bitsize, iteration_length)), target_bitsize=target_bitsize, ) - expected = 4 * (iteration_length - 2) + 7 * (iteration_length * target_bitsize) - assert bloq.t_complexity().t == expected - assert bloq.call_graph()[1][TGate()] == expected + expected_t = 4 * (iteration_length - 2) + 7 * (iteration_length * target_bitsize) + assert bloq.t_complexity().t == expected_t + gc = get_cost_value(bloq, QECGatesCost()) + assert gc == GateCounts( + and_bloq=iteration_length - 2, + measurement=iteration_length - 2, # and^dag, + cswap=iteration_length * target_bitsize, + clifford=gc.clifford, # don't test this + ) def test_multiplexed_cswap(bloq_autotester): diff --git a/qualtran/bloqs/swap_network/swap_network.ipynb b/qualtran/bloqs/swap_network/swap_network.ipynb index 442247992..32a164e47 100644 --- a/qualtran/bloqs/swap_network/swap_network.ipynb +++ b/qualtran/bloqs/swap_network/swap_network.ipynb @@ -7,7 +7,7 @@ "cq.autogen": "title_cell" }, "source": [ - "# Swap Network\n", + "# Swap Networks\n", "\n", "Functionality for moving data between registers (swapping)." ] @@ -30,141 +30,6 @@ "import cirq" ] }, - { - "cell_type": "markdown", - "id": "8e668982", - "metadata": { - "cq.autogen": "CSwap.bloq_doc.md" - }, - "source": [ - "## `CSwap`\n", - "Swap two registers controlled on a control bit.\n", - "\n", - "Implements a multi-target controlled swap unitary $CSWAP_n = |0><0| I + |1><1| SWAP_n$.\n", - "\n", - "This decomposes into a qubitwise SWAP on the two target registers, and takes $14n$ T-gates.\n", - "\n", - "#### Parameters\n", - " - `bitsize`: The bitsize of each of the two registers being swapped. \n", - "\n", - "#### Registers\n", - " - `ctrl`: the control bit\n", - " - `x`: the first register\n", - " - `y`: the second register\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7486a640", - "metadata": { - "cq.autogen": "CSwap.bloq_doc.py" - }, - "outputs": [], - "source": [ - "from qualtran.bloqs.basic_gates import CSwap" - ] - }, - { - "cell_type": "markdown", - "id": "fa8b3b02", - "metadata": { - "cq.autogen": "CSwap.example_instances.md" - }, - "source": [ - "### Example Instances" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6b9600e8", - "metadata": { - "cq.autogen": "CSwap.cswap_symb" - }, - "outputs": [], - "source": [ - "# A symbolic version. The bitsize is the symbol 'n'.\n", - "from sympy import sympify\n", - "\n", - "cswap_symb = CSwap(bitsize=sympify('n'))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "745afb32", - "metadata": { - "cq.autogen": "CSwap.cswap_small" - }, - "outputs": [], - "source": [ - "# A small version on four bits.\n", - "cswap_small = CSwap(bitsize=4)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a5bac02b", - "metadata": { - "cq.autogen": "CSwap.cswap_large" - }, - "outputs": [], - "source": [ - "# A large version that swaps 64-bit registers.\n", - "cswap_large = CSwap(bitsize=64)" - ] - }, - { - "cell_type": "markdown", - "id": "70b14690", - "metadata": { - "cq.autogen": "CSwap.graphical_signature.md" - }, - "source": [ - "#### Graphical Signature" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c4ad9f12", - "metadata": { - "cq.autogen": "CSwap.graphical_signature.py" - }, - "outputs": [], - "source": [ - "from qualtran.drawing import show_bloqs\n", - "show_bloqs([cswap_symb, cswap_small, cswap_large],\n", - " ['`cswap_symb`', '`cswap_small`', '`cswap_large`'])" - ] - }, - { - "cell_type": "markdown", - "id": "615155b0", - "metadata": { - "cq.autogen": "CSwap.call_graph.md" - }, - "source": [ - "### Call Graph" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7c1215b2", - "metadata": { - "cq.autogen": "CSwap.call_graph.py" - }, - "outputs": [], - "source": [ - "from qualtran.resource_counting.generalizers import ignore_split_join\n", - "cswap_symb_g, cswap_symb_sigma = cswap_symb.call_graph(max_depth=1, generalizer=ignore_split_join)\n", - "show_call_graph(cswap_symb_g)\n", - "show_counts_sigma(cswap_symb_sigma)" - ] - }, { "cell_type": "markdown", "id": "c4d14510", diff --git a/qualtran/resource_counting/t_counts_from_sigma.py b/qualtran/resource_counting/t_counts_from_sigma.py index 07ffc8f8e..3eaf867cf 100644 --- a/qualtran/resource_counting/t_counts_from_sigma.py +++ b/qualtran/resource_counting/t_counts_from_sigma.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import warnings from typing import Mapping import cirq @@ -21,11 +22,14 @@ def t_counts_from_sigma(sigma: Mapping['Bloq', SymbolicInt]) -> SymbolicInt: """Aggregates T-counts from a sigma dictionary by summing T-costs for all rotation bloqs.""" - from qualtran.bloqs.basic_gates import TGate + warnings.warn("This function is deprecated. Use `get_cost_value`.", DeprecationWarning) + from qualtran.bloqs.basic_gates import TGate, Toffoli, TwoBitCSwap from qualtran.cirq_interop.t_complexity_protocol import TComplexity from qualtran.resource_counting.classify_bloqs import bloq_is_rotation ret = sigma.get(TGate(), 0) + sigma.get(TGate().adjoint(), 0) + ret += sigma.get(Toffoli(), 0) * 4 + ret += sigma.get(TwoBitCSwap(), 0) * 7 for bloq, counts in sigma.items(): if bloq_is_rotation(bloq) and not cirq.has_stabilizer_effect(bloq): if isinstance(bloq, Controlled): diff --git a/qualtran/resource_counting/t_counts_from_sigma_test.py b/qualtran/resource_counting/t_counts_from_sigma_test.py index e374e408c..38d64b9c7 100644 --- a/qualtran/resource_counting/t_counts_from_sigma_test.py +++ b/qualtran/resource_counting/t_counts_from_sigma_test.py @@ -50,6 +50,7 @@ def test_t_counts_from_sigma(): } expected_t_count = ( +100 + + 200 * 4 + 1 * TComplexity.rotation_cost(z_eps1) + 5 * TComplexity.rotation_cost(z_eps2) + 9 * TComplexity.rotation_cost(x_eps)