From 370ccc47840acfd042ac8ddcb4f11b1f7dbbd57c Mon Sep 17 00:00:00 2001 From: skushnir123 Date: Fri, 15 Mar 2024 16:38:47 -0400 Subject: [PATCH 1/2] Implementation of an Approximate QFT algorithm (#773) * Create approximate_qft.py * started to add test * save here * added complete tests * Update approximate_qft.py * Update approximate_qft.py * testing updates * Update approximate_qft.py * added auto-doc * symbolic expressions * Minor improvements and fix merge commit * Fix pylint * Another bugfix --------- Co-authored-by: Tanuj Khattar --- dev_tools/autogenerate-bloqs-notebooks-v2.py | 6 + qualtran/_infra/gate_with_registers.ipynb | 2 +- qualtran/bloqs/arithmetic/multiplication.py | 6 +- qualtran/bloqs/qft/approximate_qft.ipynb | 219 ++++++++++++++++++ qualtran/bloqs/qft/approximate_qft.py | 167 +++++++++++++ qualtran/bloqs/qft/approximate_qft_test.py | 121 ++++++++++ .../symbolic_counting_utils.py | 4 + 7 files changed, 523 insertions(+), 2 deletions(-) create mode 100644 qualtran/bloqs/qft/approximate_qft.ipynb create mode 100644 qualtran/bloqs/qft/approximate_qft.py create mode 100644 qualtran/bloqs/qft/approximate_qft_test.py diff --git a/dev_tools/autogenerate-bloqs-notebooks-v2.py b/dev_tools/autogenerate-bloqs-notebooks-v2.py index 669199d52..5a45fe7ee 100644 --- a/dev_tools/autogenerate-bloqs-notebooks-v2.py +++ b/dev_tools/autogenerate-bloqs-notebooks-v2.py @@ -68,6 +68,7 @@ import qualtran.bloqs.mcmt.and_bloq import qualtran.bloqs.multiplexers.apply_gate_to_lth_target import qualtran.bloqs.phase_estimation.lp_resource_state +import qualtran.bloqs.qft.approximate_qft import qualtran.bloqs.qft.two_bit_ffft import qualtran.bloqs.reflection import qualtran.bloqs.rotations.phasing_via_cost_function @@ -326,6 +327,11 @@ module=qualtran.bloqs.qft.two_bit_ffft, bloq_specs=[qualtran.bloqs.qft.two_bit_ffft._TWO_BIT_FFFT_DOC], ), + NotebookSpecV2( + title='Approximate QFT', + module=qualtran.bloqs.qft.approximate_qft, + bloq_specs=[qualtran.bloqs.qft.approximate_qft._CC_AQFT_DOC], + ), # -------------------------------------------------------------------------- # ----- Phase Estimation ----------------------------------------------------- # -------------------------------------------------------------------------- diff --git a/qualtran/_infra/gate_with_registers.ipynb b/qualtran/_infra/gate_with_registers.ipynb index f4a2cf6ef..108c73c15 100644 --- a/qualtran/_infra/gate_with_registers.ipynb +++ b/qualtran/_infra/gate_with_registers.ipynb @@ -241,4 +241,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/qualtran/bloqs/arithmetic/multiplication.py b/qualtran/bloqs/arithmetic/multiplication.py index 2fed4abff..55861a9fe 100644 --- a/qualtran/bloqs/arithmetic/multiplication.py +++ b/qualtran/bloqs/arithmetic/multiplication.py @@ -29,8 +29,9 @@ Side, Signature, ) -from qualtran.bloqs.basic_gates import Toffoli +from qualtran.bloqs.basic_gates import TGate, Toffoli from qualtran.cirq_interop.t_complexity_protocol import TComplexity +from qualtran.resource_counting.symbolic_counting_utils import smax if TYPE_CHECKING: from qualtran import SoquetT @@ -103,6 +104,9 @@ def add_my_tensors( self, self.signature, self.short_name(), tn, tag, incoming=incoming, outgoing=outgoing ) + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']: + return {(TGate(), 8 * smax(self.a_bitsize, self.b_bitsize) ** 2)} + @bloq_example def _plus_equal_product() -> PlusEqualProduct: diff --git a/qualtran/bloqs/qft/approximate_qft.ipynb b/qualtran/bloqs/qft/approximate_qft.ipynb new file mode 100644 index 000000000..3c82703be --- /dev/null +++ b/qualtran/bloqs/qft/approximate_qft.ipynb @@ -0,0 +1,219 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a51ebafc", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# Approximate QFT" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01b76cf0", + "metadata": { + "cq.autogen": "top_imports" + }, + "outputs": [], + "source": [ + "from qualtran import Bloq, CompositeBloq, BloqBuilder, Signature, Register\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": "7aba7270", + "metadata": { + "cq.autogen": "ApproximateQFT.bloq_doc.md" + }, + "source": [ + "## `ApproximateQFT`\n", + "An approximate QFT in which phase shifts smaller than a certain threshold are deleted.\n", + "\n", + "Given a b-bit phase gradient state $|\\phi\\rangle$ prepared as\n", + "\n", + "$$\n", + " |\\phi\\rangle = \\frac{1}{\\sqrt{2^{b}}} \\sum_{k=0}^{2^{b} - 1} \\omega_{b}^{-k} |k\\rangle\n", + "$$\n", + "\n", + "Phase gradient rotations can be synthesized via additions into the phase gradient register.\n", + "This leads to significant reductions in T/Toffoli complexity and requires 0 arbitrary\n", + "rotations (given a one-time cost to prepare the gradient register). See the linked reference\n", + "for more details.\n", + "\n", + "The QFT uses exponentially small z-power gates. In practice, it is often sufficient to perform\n", + "an approximate qft, where z-power gates smaller than a certain threshold are dropped. When using\n", + "the \"add into phase-gradient trick\", this amounts to doing smaller additions with a smaller\n", + "phase gradient register.\n", + "\n", + "\n", + "#### Parameters\n", + " - `bitsize`: Size of input register to apply QFT on.\n", + " - `phase_bitsize`: The size of the phase gradient register. Defaults to being math.ceil(math.log2(bitsize)).\n", + " - `with_reverse`: Whether or not to include the swaps at the end of the circuit decomposition that reverse the order of the qubits. If True, the swaps are inserted. Defaults to True. These are technically necessary in order to perform the correct effect, but can almost always be optimized away by just performing later operations on different qubits. \n", + "\n", + "#### References\n", + "1. [Turning Gradients into Additions into QFTs](https://algassert.com/post/1620)\n", + "2. [Approximation Errors](https://arxiv.org/pdf/quant-ph/0008056.pdf)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33e7066d", + "metadata": { + "cq.autogen": "ApproximateQFT.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.qft.approximate_qft import ApproximateQFT" + ] + }, + { + "cell_type": "markdown", + "id": "1a671529", + "metadata": { + "cq.autogen": "ApproximateQFT.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf4ea035", + "metadata": { + "cq.autogen": "ApproximateQFT.approximate_qft_small" + }, + "outputs": [], + "source": [ + "approximate_qft_small = ApproximateQFT(6, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ceb3153", + "metadata": { + "cq.autogen": "ApproximateQFT.approximate_qft_from_epsilon" + }, + "outputs": [], + "source": [ + "epsilon = 1e-5\n", + "approximate_qft_from_epsilon = ApproximateQFT.from_epsilon(50, epsilon)" + ] + }, + { + "cell_type": "markdown", + "id": "37c030d1", + "metadata": { + "cq.autogen": "ApproximateQFT.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "809692b7", + "metadata": { + "cq.autogen": "ApproximateQFT.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([approximate_qft_small, approximate_qft_from_epsilon],\n", + " ['`approximate_qft_small`', '`approximate_qft_from_epsilon`'])" + ] + }, + { + "cell_type": "markdown", + "id": "23fc1278", + "metadata": { + "cq.autogen": "ApproximateQFT.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc42b5b7", + "metadata": { + "cq.autogen": "ApproximateQFT.call_graph.py" + }, + "outputs": [], + "source": [ + "approximate_qft_small_g, approximate_qft_small_sigma = approximate_qft_small.call_graph()\n", + "show_call_graph(approximate_qft_small_g)\n", + "show_counts_sigma(approximate_qft_small_sigma)" + ] + }, + { + "cell_type": "markdown", + "id": "c87a398e", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Approximate QFT Cost analysis\n", + "### T-Count Expression for `ApproximateQFT`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ddccb6b7", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.t_counts_from_sigma import t_counts_from_sigma\n", + "\n", + "def get_t_counts_aqft(n, eps):\n", + " _, sigma = ApproximateQFT.from_epsilon(n, eps).call_graph()\n", + " return t_counts_from_sigma(sigma)\n", + "\n", + "get_t_counts_aqft(*sympy.symbols('n, \\epsilon'))" + ] + } + ], + "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.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/qualtran/bloqs/qft/approximate_qft.py b/qualtran/bloqs/qft/approximate_qft.py new file mode 100644 index 000000000..f6fedecb1 --- /dev/null +++ b/qualtran/bloqs/qft/approximate_qft.py @@ -0,0 +1,167 @@ +# Copyright 2023 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. +from collections import defaultdict +from functools import cached_property +from typing import Set, TYPE_CHECKING + +import attrs +import cirq +import numpy as np +import sympy +from attr import field +from numpy.typing import NDArray + +from qualtran import bloq_example, BloqDocSpec, GateWithRegisters, QFxp, QUInt, Signature +from qualtran.bloqs.arithmetic.multiplication import PlusEqualProduct +from qualtran.bloqs.basic_gates import Hadamard, TwoBitSwap +from qualtran.resource_counting.symbolic_counting_utils import ( + ceil, + is_symbolic, + log2, + SymbolicFloat, + SymbolicInt, +) + +if TYPE_CHECKING: + from qualtran.resource_counting import BloqCountT, SympySymbolAllocator + + +@attrs.frozen +class ApproximateQFT(GateWithRegisters): + r"""An approximate QFT in which phase shifts smaller than a certain threshold are deleted. + + Given a b-bit phase gradient state $|\phi\rangle$ prepared as + + $$ + |\phi\rangle = \frac{1}{\sqrt{2^{b}}} \sum_{k=0}^{2^{b} - 1} \omega_{b}^{-k} |k\rangle + $$ + + Phase gradient rotations can be synthesized via additions into the phase gradient register. + This leads to significant reductions in T/Toffoli complexity and requires 0 arbitrary + rotations (given a one-time cost to prepare the gradient register). See the linked reference + for more details. + + The QFT uses exponentially small z-power gates. In practice, it is often sufficient to perform + an approximate qft, where z-power gates smaller than a certain threshold are dropped. When using + the "add into phase-gradient trick", this amounts to doing smaller additions with a smaller + phase gradient register. + + + Args: + bitsize: Size of input register to apply QFT on. + phase_bitsize: The size of the phase gradient register. Defaults to being math.ceil(math.log2(bitsize)). + with_reverse: Whether or not to include the swaps at the end + of the circuit decomposition that reverse the order of the + qubits. If True, the swaps are inserted. Defaults to True. + These are technically necessary in order to perform the + correct effect, but can almost always be optimized away by just + performing later operations on different qubits. + + References: + 1. [Turning Gradients into Additions into QFTs](https://algassert.com/post/1620) + 2. [Approximation Errors](https://arxiv.org/pdf/quant-ph/0008056.pdf) + """ + + bitsize: SymbolicInt + phase_bitsize: SymbolicInt = field() + with_reverse: bool = True + + @phase_bitsize.default + def ceiling_of_log_bitsize(self): + return ceil(log2(self.bitsize)) + + @classmethod + def from_epsilon(cls, n: SymbolicInt, eps: SymbolicFloat) -> 'ApproximateQFT': + """Builds an ApproximateQFT instance using total tolerable error `eps`. + + From a given error threshold, epsilon, calculates what size the + phase register should be and returns an ApproximateQFT instance. + + Args: + n: The size of the input register. + eps: The error threshold. + + Returns: + An ApproximateQFT instance. + """ + + # solving for k in quant-ph/0008056 (12) + # This equation is transcendental because it involves both + # algebraic and exponential terms in k. Therefore, I upper-bounded + # the error by replacing (n - k) with n. + num = sympy.pi * sympy.sqrt(2) if is_symbolic(n, eps) else np.pi * np.sqrt(2) + phase_bitsize = ceil(log2((n * num) / eps)) + return cls(n, phase_bitsize) + + @cached_property + def signature(self) -> 'Signature': + return Signature.build_from_dtypes( + q=QUInt(self.bitsize), phase_grad=QFxp(self.phase_bitsize, self.phase_bitsize) + ) + + def decompose_from_registers( + self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid] + ) -> cirq.OP_TREE: + if self.bitsize == 1: + yield cirq.H(*quregs['q']) + return + q, phase_grad = quregs['q'], quregs['phase_grad'] + for i in range(len(q)): + if i == 0: + yield cirq.H(q[i]) + continue + addition_bitsize = min(i, len(phase_grad) - 1) + addition_start_index = i - addition_bitsize + a, b = q[addition_start_index:i], phase_grad[: addition_bitsize + 1] + yield PlusEqualProduct(addition_bitsize, 1, addition_bitsize + 1).on_registers( + a=a[::-1], b=q[i], result=b + ) + yield cirq.H(q[i]) + + if self.with_reverse: + for i in range(self.bitsize // 2): + yield cirq.SWAP(q[i], q[-i - 1]) + + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']: + phase_dict = defaultdict(int) + if is_symbolic(self.bitsize, self.phase_bitsize): + phase_dict[PlusEqualProduct(self.phase_bitsize, 1, self.phase_bitsize)] = self.bitsize + else: + for i in range(1, self.bitsize): + b = min(i, self.phase_bitsize - 1) + phase_dict[PlusEqualProduct(b, 1, b + 1)] += 1 + ret = {(Hadamard(), self.bitsize), *phase_dict.items()} + if self.with_reverse: + ret |= {(TwoBitSwap(), self.bitsize // 2)} + return ret + + +@bloq_example +def _approximate_qft_small() -> ApproximateQFT: + approximate_qft_small = ApproximateQFT(6, 5) + return approximate_qft_small + + +@bloq_example +def _approximate_qft_from_epsilon() -> ApproximateQFT: + epsilon = 1e-5 + approximate_qft_from_epsilon = ApproximateQFT.from_epsilon(50, epsilon) + return approximate_qft_from_epsilon + + +_CC_AQFT_DOC = BloqDocSpec( + bloq_cls=ApproximateQFT, + import_line='from qualtran.bloqs.qft.approximate_qft import ApproximateQFT', + examples=(_approximate_qft_small, _approximate_qft_from_epsilon), +) diff --git a/qualtran/bloqs/qft/approximate_qft_test.py b/qualtran/bloqs/qft/approximate_qft_test.py new file mode 100644 index 000000000..1f9497487 --- /dev/null +++ b/qualtran/bloqs/qft/approximate_qft_test.py @@ -0,0 +1,121 @@ +# Copyright 2023 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 math +from typing import Dict, TYPE_CHECKING + +import attrs +import cirq +import numpy as np +import pytest + +from qualtran import GateWithRegisters, Signature +from qualtran.bloqs.arithmetic.multiplication import PlusEqualProduct +from qualtran.bloqs.qft.approximate_qft import ( + _approximate_qft_from_epsilon, + _approximate_qft_small, + ApproximateQFT, +) +from qualtran.bloqs.rotations.phase_gradient import PhaseGradientState +from qualtran.cirq_interop.testing import assert_decompose_is_consistent_with_t_complexity +from qualtran.testing import assert_valid_bloq_decomposition + +if TYPE_CHECKING: + from qualtran import BloqBuilder, SoquetT + + +@attrs.frozen +class TestApproximateQFT(GateWithRegisters): + bitsize: int + phase_bitsize: int + with_reverse: bool + + @property + def signature(self) -> 'Signature': + return Signature.build(q=self.bitsize) + + def build_composite_bloq(self, bb: 'BloqBuilder', *, q: 'SoquetT') -> Dict[str, 'SoquetT']: + phase_grad = bb.add(PhaseGradientState(self.phase_bitsize, exponent=-1)) + + q, phase_grad = bb.add( + ApproximateQFT(self.bitsize, self.phase_bitsize, self.with_reverse), + q=q, + phase_grad=phase_grad, + ) + bb.add(PhaseGradientState(self.phase_bitsize).adjoint(), phase_grad=phase_grad) + return {'q': q} + + +def test_approximate_qft_small_auto(bloq_autotester): + bloq_autotester(_approximate_qft_small) + + +def test_approximate_qft_from_epsilon_auto(bloq_autotester): + bloq_autotester(_approximate_qft_from_epsilon) + + +@pytest.mark.parametrize('n', [2, 3, 4, 5]) +@pytest.mark.parametrize('without_reverse', [True, False]) +def test_approximate_qft_exact(n: int, without_reverse: bool): + qft_bloq = TestApproximateQFT(n, n, not without_reverse) + qft_cirq = cirq.QuantumFourierTransformGate(n, without_reverse=without_reverse) + np.testing.assert_allclose(cirq.unitary(qft_bloq), cirq.unitary(qft_cirq)) + np.testing.assert_allclose(cirq.unitary(qft_bloq**-1), cirq.unitary(qft_cirq**-1)) + + assert_valid_bloq_decomposition(qft_bloq) + + +@pytest.mark.slow +def test_approximate_qft(): + num_qubits = 7 + phase_bitsize = 6 + qft_bloq = TestApproximateQFT(num_qubits, phase_bitsize, True) + qft_cirq = cirq.QuantumFourierTransformGate(num_qubits, without_reverse=False) + np.testing.assert_allclose(cirq.unitary(qft_bloq), cirq.unitary(qft_cirq), rtol=1e-2, atol=1e-2) + + assert_valid_bloq_decomposition(qft_bloq) + + +@pytest.mark.parametrize('n', [1000, 2000, 10000]) +@pytest.mark.parametrize('bits_of_precision', [4, 7, 10]) +def test_approximate_qft_with_eps(n: int, bits_of_precision: int): + epsilon = 2 ** (-1 * bits_of_precision) + phase_bitsize_for_single_bit_precision = ApproximateQFT.from_epsilon(n, 2**-1).phase_bitsize + + # factor of 2 takes care of the roughness of the upper-bound + assert phase_bitsize_for_single_bit_precision < 2 * math.log2(n) + approximate_qft = ApproximateQFT.from_epsilon(n, epsilon) + + # for each extra bit of precision, we only need 1 more bit in the phase register + assert ( + approximate_qft.phase_bitsize + == phase_bitsize_for_single_bit_precision + bits_of_precision - 1 + ) + + +@pytest.mark.parametrize('n', [10, 123]) +@pytest.mark.parametrize('with_reverse', [True, False]) +def test_approximate_qft_t_complexity(n: int, with_reverse: bool): + qft_bloq = ApproximateQFT(n, with_reverse=with_reverse) + + def f(n, b): + t_complexity = 0 + for i in range(1, n): + t_complexity += PlusEqualProduct(min(i, b - 1), 1, min(i, b - 1) + 1).t_complexity().t + return t_complexity + + qft_t_complexity = qft_bloq.t_complexity() + assert_decompose_is_consistent_with_t_complexity(qft_bloq) + b = math.ceil(math.log2(n)) + assert qft_t_complexity.t == f(n, b) <= 8 * n * (math.log2(n) ** 2) + assert qft_t_complexity.rotations == 0 diff --git a/qualtran/resource_counting/symbolic_counting_utils.py b/qualtran/resource_counting/symbolic_counting_utils.py index 14f47f741..5ffa3dc50 100644 --- a/qualtran/resource_counting/symbolic_counting_utils.py +++ b/qualtran/resource_counting/symbolic_counting_utils.py @@ -24,6 +24,10 @@ document(SymbolicFloat, """A floating point value or a sympy expression.""") +def is_symbolic(*args) -> bool: + return any(isinstance(x, sympy.Basic) for x in args) + + def log2(x: SymbolicFloat) -> SymbolicFloat: from sympy.codegen.cfunctions import log2 From 0db37a978a499d4d32a2ecde1066e7fd45ae3389 Mon Sep 17 00:00:00 2001 From: Tanuj Khattar Date: Fri, 15 Mar 2024 16:20:07 -0700 Subject: [PATCH 2/2] Improvements to `autogenerate-bloqs-notebooks-v2.py` (#796) * Improvements to autogenerate-bloqs-notebooks-v2.py * Revert swap_network.ipynb * Revert changes to jupyter_autogen_v2.py --- dev_tools/autogenerate-bloqs-notebooks-v2.py | 13 +- qualtran/bloqs/basic_gates/hadamard.ipynb | 146 ++++++++++++++++++ qualtran/bloqs/basic_gates/hadamard.py | 4 +- qualtran/bloqs/mcmt/and_bloq.ipynb | 2 +- .../mcmt/multi_control_multi_target_pauli.py | 4 +- .../multiplexers/apply_gate_to_lth_target.py | 2 +- .../prepare_uniform_superposition.py | 2 +- 7 files changed, 161 insertions(+), 12 deletions(-) create mode 100644 qualtran/bloqs/basic_gates/hadamard.ipynb diff --git a/dev_tools/autogenerate-bloqs-notebooks-v2.py b/dev_tools/autogenerate-bloqs-notebooks-v2.py index 5a45fe7ee..531a5d41c 100644 --- a/dev_tools/autogenerate-bloqs-notebooks-v2.py +++ b/dev_tools/autogenerate-bloqs-notebooks-v2.py @@ -96,6 +96,11 @@ module=qualtran.bloqs.basic_gates.toffoli, bloq_specs=[qualtran.bloqs.basic_gates.toffoli._TOFFOLI_DOC], ), + NotebookSpecV2( + title='Hadamard', + module=qualtran.bloqs.basic_gates.hadamard, + bloq_specs=[qualtran.bloqs.basic_gates.hadamard._HADAMARD_DOC], + ), NotebookSpecV2( title='Swap Network', module=qualtran.bloqs.swap_network, @@ -124,13 +129,11 @@ bloq_specs=[ qualtran.bloqs.state_preparation.prepare_uniform_superposition._PREP_UNIFORM_DOC ], - directory=f'{SOURCE_DIR}/bloqs/', ), NotebookSpecV2( title='Apply to Lth Target', module=qualtran.bloqs.multiplexers.apply_gate_to_lth_target, bloq_specs=[qualtran.bloqs.multiplexers.apply_gate_to_lth_target._APPLYLTH_DOC], - directory=f'{SOURCE_DIR}/bloqs/', ), NotebookSpecV2( title='QROM', @@ -200,7 +203,8 @@ qualtran.bloqs.chemistry.trotter.grid_ham.potential._PAIR_POTENTIAL, qualtran.bloqs.chemistry.trotter.grid_ham.potential._POTENTIAL_ENERGY, ], - directory=f'{SOURCE_DIR}/bloqs/chemistry/trotter', + directory=f'{SOURCE_DIR}/bloqs/chemistry/trotter/grid_ham/', + path_stem=f'{SOURCE_DIR}/bloqs/chemistry/trotter/grid_ham/trotter', ), NotebookSpecV2( title='Tensor Hypercontraction', @@ -219,7 +223,6 @@ qualtran.bloqs.mcmt.and_bloq._AND_DOC, qualtran.bloqs.mcmt.and_bloq._MULTI_AND_DOC, ], - directory=f'{SOURCE_DIR}/bloqs/', ), NotebookSpecV2( title='Block Encoding', @@ -243,7 +246,7 @@ qualtran.bloqs.mcmt.multi_control_multi_target_pauli._C_MULTI_NOT_DOC, qualtran.bloqs.mcmt.multi_control_multi_target_pauli._CC_PAULI_DOC, ], - directory=f'{SOURCE_DIR}/bloqs/', + directory=f'{SOURCE_DIR}/bloqs/mcmt/', ), # -------------------------------------------------------------------------- # ----- Arithmetic ----------------------------------------------------- diff --git a/qualtran/bloqs/basic_gates/hadamard.ipynb b/qualtran/bloqs/basic_gates/hadamard.ipynb new file mode 100644 index 000000000..af07c53f6 --- /dev/null +++ b/qualtran/bloqs/basic_gates/hadamard.ipynb @@ -0,0 +1,146 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "269cd713", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# Hadamard" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a181241c", + "metadata": { + "cq.autogen": "top_imports" + }, + "outputs": [], + "source": [ + "from qualtran import Bloq, CompositeBloq, BloqBuilder, Signature, Register\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": "51e3fa62", + "metadata": { + "cq.autogen": "Hadamard.bloq_doc.md" + }, + "source": [ + "## `Hadamard`\n", + "The Hadamard gate\n", + "\n", + "This converts between the X and Z basis.\n", + "\n", + "$$\\begin{aligned}\n", + "H |0\\rangle = |+\\rangle \\\\\n", + "H |-\\rangle = |1\\rangle\n", + "\\end{aligned}$$\n", + "\n", + "#### Registers\n", + " - `q`: The qubit\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d05cfd73", + "metadata": { + "cq.autogen": "Hadamard.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.basic_gates import Hadamard" + ] + }, + { + "cell_type": "markdown", + "id": "35b53a72", + "metadata": { + "cq.autogen": "Hadamard.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39ebe976", + "metadata": { + "cq.autogen": "Hadamard.hadamard" + }, + "outputs": [], + "source": [ + "hadamard = Hadamard()" + ] + }, + { + "cell_type": "markdown", + "id": "f19223de", + "metadata": { + "cq.autogen": "Hadamard.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92b60b87", + "metadata": { + "cq.autogen": "Hadamard.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([hadamard],\n", + " ['`hadamard`'])" + ] + }, + { + "cell_type": "markdown", + "id": "6dc303ab", + "metadata": { + "cq.autogen": "Hadamard.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19f5aeb1", + "metadata": { + "cq.autogen": "Hadamard.call_graph.py" + }, + "outputs": [], + "source": [ + "hadamard_g, hadamard_sigma = hadamard.call_graph()\n", + "show_call_graph(hadamard_g)\n", + "show_counts_sigma(hadamard_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/basic_gates/hadamard.py b/qualtran/bloqs/basic_gates/hadamard.py index 8552f19b0..1d954bdfd 100644 --- a/qualtran/bloqs/basic_gates/hadamard.py +++ b/qualtran/bloqs/basic_gates/hadamard.py @@ -46,10 +46,10 @@ class Hadamard(Bloq): This converts between the X and Z basis. - $$ + $$\begin{aligned} H |0\rangle = |+\rangle \\ H |-\rangle = |1\rangle - $$ + \end{aligned}$$ Registers: q: The qubit diff --git a/qualtran/bloqs/mcmt/and_bloq.ipynb b/qualtran/bloqs/mcmt/and_bloq.ipynb index d30806ce9..d7b0669d9 100644 --- a/qualtran/bloqs/mcmt/and_bloq.ipynb +++ b/qualtran/bloqs/mcmt/and_bloq.ipynb @@ -192,7 +192,7 @@ }, "outputs": [], "source": [ - "multi_and = MultiAnd(cvs=(1,) * 4)" + "multi_and = MultiAnd(cvs=(1, 0, 1, 0, 1, 0))" ] }, { diff --git a/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.py b/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.py index fda266e24..4a6e418a5 100644 --- a/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.py +++ b/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.py @@ -93,7 +93,7 @@ def _c_multi_not() -> MultiTargetCNOT: _C_MULTI_NOT_DOC = BloqDocSpec( bloq_cls=MultiTargetCNOT, - import_line='from qualtran.bloqs.multi_control_multi_target_pauli import MultiTargetCNOT', + import_line='from qualtran.bloqs.mcmt import MultiTargetCNOT', examples=(_c_multi_not_symb, _c_multi_not), ) @@ -204,7 +204,7 @@ def _ccpauli() -> MultiControlPauli: _CC_PAULI_DOC = BloqDocSpec( bloq_cls=MultiControlPauli, - import_line='from qualtran.bloqs.multi_control_multi_target_pauli import MultiControlPauli', + import_line='from qualtran.bloqs.mcmt import MultiControlPauli', examples=(_ccpauli,), ) diff --git a/qualtran/bloqs/multiplexers/apply_gate_to_lth_target.py b/qualtran/bloqs/multiplexers/apply_gate_to_lth_target.py index 91922ed6b..87060fb9c 100644 --- a/qualtran/bloqs/multiplexers/apply_gate_to_lth_target.py +++ b/qualtran/bloqs/multiplexers/apply_gate_to_lth_target.py @@ -125,6 +125,6 @@ def _z_to_odd(n: int): _APPLYLTH_DOC = BloqDocSpec( bloq_cls=ApplyGateToLthQubit, - import_line='from qualtran.bloqs.apply_gate_to_lth_target import ApplyGateToLthQubit', + import_line='from qualtran.bloqs.multiplexers.apply_gate_to_lth_target import ApplyGateToLthQubit', examples=(_apply_z_to_odd,), ) diff --git a/qualtran/bloqs/state_preparation/prepare_uniform_superposition.py b/qualtran/bloqs/state_preparation/prepare_uniform_superposition.py index ed1ac4f93..6356f1c21 100644 --- a/qualtran/bloqs/state_preparation/prepare_uniform_superposition.py +++ b/qualtran/bloqs/state_preparation/prepare_uniform_superposition.py @@ -127,6 +127,6 @@ def _c_prep_uniform() -> PrepareUniformSuperposition: _PREP_UNIFORM_DOC = BloqDocSpec( bloq_cls=PrepareUniformSuperposition, - import_line='from qualtran.bloqs.prepare_uniform_superposition import PrepareUniformSuperposition', + import_line='from qualtran.bloqs.state_preparation import PrepareUniformSuperposition', examples=(_prep_uniform, _c_prep_uniform), )