diff --git a/dev_tools/autogenerate-bloqs-notebooks-v2.py b/dev_tools/autogenerate-bloqs-notebooks-v2.py index ec374e39b..2106de5d4 100644 --- a/dev_tools/autogenerate-bloqs-notebooks-v2.py +++ b/dev_tools/autogenerate-bloqs-notebooks-v2.py @@ -90,6 +90,7 @@ import qualtran.bloqs.factoring.mod_exp import qualtran.bloqs.hamiltonian_simulation.hamiltonian_simulation_by_gqsp import qualtran.bloqs.mcmt.and_bloq +import qualtran.bloqs.mcmt.controlled_via_and import qualtran.bloqs.mcmt.ctrl_spec_and import qualtran.bloqs.mod_arithmetic.mod_addition import qualtran.bloqs.multiplexers.apply_gate_to_lth_target @@ -224,6 +225,11 @@ module=qualtran.bloqs.mcmt.ctrl_spec_and, bloq_specs=[qualtran.bloqs.mcmt.ctrl_spec_and._CTRLSPEC_AND_DOC], ), + NotebookSpecV2( + title='Multi control bloq via single control bloq and `And` ladder', + module=qualtran.bloqs.mcmt.controlled_via_and, + bloq_specs=[qualtran.bloqs.mcmt.controlled_via_and._CONTROLLED_VIA_AND_DOC], + ), ] diff --git a/docs/bloqs/index.rst b/docs/bloqs/index.rst index 742252ead..4d62fc0dd 100644 --- a/docs/bloqs/index.rst +++ b/docs/bloqs/index.rst @@ -39,6 +39,7 @@ Bloqs Library basic_gates/identity.ipynb bookkeeping/bookkeeping.ipynb mcmt/ctrl_spec_and.ipynb + mcmt/controlled_via_and.ipynb .. toctree:: :maxdepth: 2 diff --git a/qualtran/bloqs/mcmt/__init__.py b/qualtran/bloqs/mcmt/__init__.py index 2b7ffcb2f..2082a5d86 100644 --- a/qualtran/bloqs/mcmt/__init__.py +++ b/qualtran/bloqs/mcmt/__init__.py @@ -13,6 +13,7 @@ # limitations under the License. from qualtran.bloqs.mcmt.and_bloq import And, MultiAnd +from qualtran.bloqs.mcmt.controlled_via_and import ControlledViaAnd from qualtran.bloqs.mcmt.ctrl_spec_and import CtrlSpecAnd from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import ( MultiControlPauli, diff --git a/qualtran/bloqs/mcmt/controlled_via_and.ipynb b/qualtran/bloqs/mcmt/controlled_via_and.ipynb new file mode 100644 index 000000000..1785c5512 --- /dev/null +++ b/qualtran/bloqs/mcmt/controlled_via_and.ipynb @@ -0,0 +1,170 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fa539e78", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# Multi control bloq via single control bloq and `And` ladder" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bcb7aec6", + "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": "0653bdfc", + "metadata": { + "cq.autogen": "ControlledViaAnd.bloq_doc.md" + }, + "source": [ + "## `ControlledViaAnd`\n", + "Reduces a generic controlled bloq to a singly-controlled bloq using an And ladder.\n", + "\n", + "Implements a generic controlled version of the subbloq, by first reducing the\n", + "arbitrary control to a single qubit, and then using a single-qubit-controlled\n", + "variant of the subbloq.\n", + "\n", + "For signature, see :class:`Controlled`.\n", + "\n", + "#### Parameters\n", + " - `subbloq`: The bloq we are controlling.\n", + " - `ctrl_spec`: The specification for how to control the bloq.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cdeee9e0", + "metadata": { + "cq.autogen": "ControlledViaAnd.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.mcmt import ControlledViaAnd" + ] + }, + { + "cell_type": "markdown", + "id": "63037b5b", + "metadata": { + "cq.autogen": "ControlledViaAnd.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "789e7f88", + "metadata": { + "cq.autogen": "ControlledViaAnd.controlled_via_and_ints" + }, + "outputs": [], + "source": [ + "from qualtran import CtrlSpec, QInt, QUInt\n", + "from qualtran.bloqs.basic_gates import Hadamard\n", + "\n", + "controlled_via_and_ints = ControlledViaAnd(\n", + " Hadamard(),\n", + " CtrlSpec(\n", + " qdtypes=(QUInt(4), QInt(4)), cvs=(np.array([0, 1, 2, 3]), np.array([0, 1, -1, -2]))\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7140523f", + "metadata": { + "cq.autogen": "ControlledViaAnd.controlled_via_and_qbits" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.basic_gates import Hadamard\n", + "\n", + "controlled_via_and_qbits = ControlledViaAnd(Hadamard(), CtrlSpec(cvs=(np.array([0, 1, 1, 0]),)))" + ] + }, + { + "cell_type": "markdown", + "id": "27d4d6e1", + "metadata": { + "cq.autogen": "ControlledViaAnd.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45a0473b", + "metadata": { + "cq.autogen": "ControlledViaAnd.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([controlled_via_and_ints, controlled_via_and_qbits],\n", + " ['`controlled_via_and_ints`', '`controlled_via_and_qbits`'])" + ] + }, + { + "cell_type": "markdown", + "id": "b0301e73", + "metadata": { + "cq.autogen": "ControlledViaAnd.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9af47394", + "metadata": { + "cq.autogen": "ControlledViaAnd.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "controlled_via_and_ints_g, controlled_via_and_ints_sigma = controlled_via_and_ints.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(controlled_via_and_ints_g)\n", + "show_counts_sigma(controlled_via_and_ints_sigma)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/qualtran/bloqs/mcmt/controlled_via_and.py b/qualtran/bloqs/mcmt/controlled_via_and.py index 879018c42..8fd751336 100644 --- a/qualtran/bloqs/mcmt/controlled_via_and.py +++ b/qualtran/bloqs/mcmt/controlled_via_and.py @@ -18,7 +18,7 @@ import numpy as np from attrs import frozen -from qualtran import Bloq, Controlled, CtrlSpec +from qualtran import Bloq, bloq_example, BloqDocSpec, Controlled, CtrlSpec from qualtran.bloqs.basic_gates import XGate from qualtran.bloqs.mcmt.ctrl_spec_and import CtrlSpecAnd @@ -125,3 +125,30 @@ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> set['BloqCountT']: counts[ctrl.adjoint()] += 1 return set(counts.items()) + + +@bloq_example +def _controlled_via_and_qbits() -> ControlledViaAnd: + from qualtran.bloqs.basic_gates import Hadamard + + controlled_via_and_qbits = ControlledViaAnd(Hadamard(), CtrlSpec(cvs=(np.array([0, 1, 1, 0]),))) + return controlled_via_and_qbits + + +@bloq_example +def _controlled_via_and_ints() -> ControlledViaAnd: + from qualtran import CtrlSpec, QInt, QUInt + from qualtran.bloqs.basic_gates import Hadamard + + controlled_via_and_ints = ControlledViaAnd( + Hadamard(), + CtrlSpec( + qdtypes=(QUInt(4), QInt(4)), cvs=(np.array([0, 1, 2, 3]), np.array([0, 1, -1, -2])) + ), + ) + return controlled_via_and_ints + + +_CONTROLLED_VIA_AND_DOC = BloqDocSpec( + bloq_cls=ControlledViaAnd, examples=[_controlled_via_and_ints, _controlled_via_and_qbits] +) diff --git a/qualtran/bloqs/mcmt/controlled_via_and_test.py b/qualtran/bloqs/mcmt/controlled_via_and_test.py new file mode 100644 index 000000000..981d7396f --- /dev/null +++ b/qualtran/bloqs/mcmt/controlled_via_and_test.py @@ -0,0 +1,49 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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 numpy as np +import pytest + +from qualtran import Controlled, CtrlSpec, QInt, QUInt +from qualtran.bloqs.for_testing.matrix_gate import MatrixGate +from qualtran.bloqs.mcmt.controlled_via_and import ( + _controlled_via_and_ints, + _controlled_via_and_qbits, + ControlledViaAnd, +) + + +def test_examples(bloq_autotester): + bloq_autotester(_controlled_via_and_qbits) + bloq_autotester(_controlled_via_and_ints) + + +@pytest.mark.parametrize( + "ctrl_spec", + [ + CtrlSpec(QInt(4), [1, -2]), + CtrlSpec(QUInt(4), [2, 5]), + CtrlSpec((QInt(2), QUInt(2)), cvs=([1, -2], [2, 3])), + ], +) +def test_tensor_against_naive_controlled(ctrl_spec: CtrlSpec): + rs = np.random.RandomState(42) + subbloq = MatrixGate.random(2, random_state=rs) + + cbloq = ControlledViaAnd(subbloq, ctrl_spec) + naive_cbloq = Controlled(subbloq, ctrl_spec) + + expected_tensor = naive_cbloq.tensor_contract() + actual_tensor = cbloq.tensor_contract() + + np.testing.assert_allclose(expected_tensor, actual_tensor)