From 110b29bbf6ddd40f923c81a396f9a9f2591c9676 Mon Sep 17 00:00:00 2001 From: Charles Yuan Date: Fri, 21 Jun 2024 15:08:58 -0700 Subject: [PATCH] Add `Unitary` and `TensorProduct` block encodings --- dev_tools/autogenerate-bloqs-notebooks-v2.py | 2 + qualtran/_infra/registers.py | 2 +- qualtran/_infra/registers_test.py | 2 +- qualtran/bloqs/block_encoding/__init__.py | 2 + .../bloqs/block_encoding/block_encoding.ipynb | 232 ++++++++++++++++++ .../block_encoding/block_encoding_base.py | 28 ++- .../bloqs/block_encoding/tensor_product.py | 152 ++++++++++++ .../block_encoding/tensor_product_test.py | 55 +++++ qualtran/bloqs/block_encoding/unitary.py | 111 +++++++++ qualtran/bloqs/block_encoding/unitary_test.py | 48 ++++ qualtran/conftest.py | 2 + qualtran/serialization/resolver_dict.py | 4 + 12 files changed, 637 insertions(+), 3 deletions(-) create mode 100644 qualtran/bloqs/block_encoding/tensor_product.py create mode 100644 qualtran/bloqs/block_encoding/tensor_product_test.py create mode 100644 qualtran/bloqs/block_encoding/unitary.py create mode 100644 qualtran/bloqs/block_encoding/unitary_test.py diff --git a/dev_tools/autogenerate-bloqs-notebooks-v2.py b/dev_tools/autogenerate-bloqs-notebooks-v2.py index 5946ed062..4358844cc 100644 --- a/dev_tools/autogenerate-bloqs-notebooks-v2.py +++ b/dev_tools/autogenerate-bloqs-notebooks-v2.py @@ -570,6 +570,8 @@ qualtran.bloqs.block_encoding.lcu_block_encoding._LCU_BLOCK_ENCODING_DOC, qualtran.bloqs.block_encoding.lcu_block_encoding._LCU_ZERO_STATE_BLOCK_ENCODING_DOC, qualtran.bloqs.block_encoding.chebyshev_polynomial._CHEBYSHEV_BLOQ_DOC, + qualtran.bloqs.block_encoding.unitary._UNITARY_DOC, + qualtran.bloqs.block_encoding.tensor_product._TENSOR_PRODUCT_DOC, ], directory=f'{SOURCE_DIR}/bloqs/block_encoding/', ), diff --git a/qualtran/_infra/registers.py b/qualtran/_infra/registers.py index 8d440deef..2d3529f29 100644 --- a/qualtran/_infra/registers.py +++ b/qualtran/_infra/registers.py @@ -162,7 +162,7 @@ def build_from_dtypes(cls, **registers: QDType) -> 'Signature': registers: keyword arguments mapping register name to QDType. All registers will be 0-dimensional and THRU. """ - return cls(Register(name=k, dtype=v) for k, v in registers.items() if v.num_qubits) + return cls(Register(name=k, dtype=v) for k, v in registers.items()) def lefts(self) -> Iterable[Register]: """Iterable over all registers that appear on the LEFT as input.""" diff --git a/qualtran/_infra/registers_test.py b/qualtran/_infra/registers_test.py index cb4c8380a..f6dc7e537 100644 --- a/qualtran/_infra/registers_test.py +++ b/qualtran/_infra/registers_test.py @@ -130,7 +130,7 @@ def test_signature_build(): sig1 = Signature([Register("r1", QInt(7)), Register("r2", QBit())]) sig2 = Signature.build_from_dtypes(r1=QInt(7), r2=QBit()) assert sig1 == sig2 - sig1 = Signature([Register("r1", QInt(7))]) + sig1 = Signature([Register("r1", QInt(7)), Register("r2", QAny(0))]) sig2 = Signature.build_from_dtypes(r1=QInt(7), r2=QAny(0)) assert sig1 == sig2 diff --git a/qualtran/bloqs/block_encoding/__init__.py b/qualtran/bloqs/block_encoding/__init__.py index 22a74ba08..7b0523790 100644 --- a/qualtran/bloqs/block_encoding/__init__.py +++ b/qualtran/bloqs/block_encoding/__init__.py @@ -21,3 +21,5 @@ LCUBlockEncoding, LCUBlockEncodingZeroState, ) +from qualtran.bloqs.block_encoding.tensor_product import TensorProduct +from qualtran.bloqs.block_encoding.unitary import Unitary diff --git a/qualtran/bloqs/block_encoding/block_encoding.ipynb b/qualtran/bloqs/block_encoding/block_encoding.ipynb index e4fbb63dd..6accb2205 100644 --- a/qualtran/bloqs/block_encoding/block_encoding.ipynb +++ b/qualtran/bloqs/block_encoding/block_encoding.ipynb @@ -594,6 +594,238 @@ "show_call_graph(chebyshev_poly_g)\n", "show_counts_sigma(chebyshev_poly_sigma)" ] + }, + { + "cell_type": "markdown", + "id": "6836a23d", + "metadata": { + "cq.autogen": "Unitary.bloq_doc.md" + }, + "source": [ + "## `Unitary`\n", + "Trivial block encoding of a unitary operator.\n", + "\n", + "Builds the block encoding as\n", + "$\n", + " B[U] = U\n", + "$\n", + "where $U$ is a unitary operator. Here, $B[U]$ is a $(1, 0, 0)$-block encoding of $U$.\n", + "\n", + "#### Parameters\n", + " - `U`: The unitary operator to block-encode.\n", + " - `dtype`: The quantum data type of the system `U` operates over. \n", + "\n", + "#### Registers\n", + " - `system`: The system register.\n", + " - `ancilla`: The ancilla register (size 0).\n", + " - `resource`: The resource register (size 0).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10bda229", + "metadata": { + "cq.autogen": "Unitary.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.block_encoding import Unitary" + ] + }, + { + "cell_type": "markdown", + "id": "da23b898", + "metadata": { + "cq.autogen": "Unitary.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63966178", + "metadata": { + "cq.autogen": "Unitary.unitary_block_encoding" + }, + "outputs": [], + "source": [ + "from qualtran import QBit\n", + "from qualtran.bloqs.basic_gates import TGate\n", + "\n", + "unitary_block_encoding = Unitary(TGate(), dtype=QBit())" + ] + }, + { + "cell_type": "markdown", + "id": "670e3692", + "metadata": { + "cq.autogen": "Unitary.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97b4f383", + "metadata": { + "cq.autogen": "Unitary.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([unitary_block_encoding],\n", + " ['`unitary_block_encoding`'])" + ] + }, + { + "cell_type": "markdown", + "id": "91e9171c", + "metadata": { + "cq.autogen": "Unitary.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0042bd79", + "metadata": { + "cq.autogen": "Unitary.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "unitary_block_encoding_g, unitary_block_encoding_sigma = unitary_block_encoding.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(unitary_block_encoding_g)\n", + "show_counts_sigma(unitary_block_encoding_sigma)" + ] + }, + { + "cell_type": "markdown", + "id": "278a14ac", + "metadata": { + "cq.autogen": "TensorProduct.bloq_doc.md" + }, + "source": [ + "## `TensorProduct`\n", + "Tensor product of a sequence of block encodings.\n", + "\n", + "Builds the block encoding as\n", + "$$\n", + " B[U_1 ⊗ U_2 ⊗ \\cdots ⊗ U_n] = B[U_1] ⊗ B[U_2] ⊗ \\cdots ⊗ B[U_n]\n", + "$$\n", + "\n", + "When each $B[U_i]$ is a $(\\alpha_i, a_i, \\epsilon_i)$-block encoding of $U_i$, we have that\n", + "$B[U_1 ⊗ \\cdots ⊗ U_n]$ is a $(\\prod_i \\alpha_i, \\sum_i a_i, \\sum_i \\alpha_i \\epsilon_i)$-block\n", + "encoding of $U_1 ⊗ \\cdots ⊗ U_n$.\n", + "\n", + "#### Parameters\n", + " - `U`: A sequence of block encodings. \n", + "\n", + "#### Registers\n", + " - `system`: The system register.\n", + " - `ancilla`: The ancilla register.\n", + " - `resource`: The resource register. \n", + "\n", + "#### References\n", + " - [Quantum algorithms: A survey of applications and end-to-end complexities](https://arxiv.org/abs/2310.03011). Dalzell et al. (2023). Ch. 10.2.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f1e952b", + "metadata": { + "cq.autogen": "TensorProduct.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.block_encoding import TensorProduct" + ] + }, + { + "cell_type": "markdown", + "id": "004fde81", + "metadata": { + "cq.autogen": "TensorProduct.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1478a7ac", + "metadata": { + "cq.autogen": "TensorProduct.tensor_product_block_encoding" + }, + "outputs": [], + "source": [ + "from qualtran import QBit\n", + "from qualtran.bloqs.basic_gates import Hadamard, TGate\n", + "from qualtran.bloqs.block_encoding.unitary import Unitary\n", + "\n", + "tensor_product_block_encoding = TensorProduct(\n", + " [Unitary(TGate(), dtype=QBit()), Unitary(Hadamard(), dtype=QBit())]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "611fefc9", + "metadata": { + "cq.autogen": "TensorProduct.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f34e93d7", + "metadata": { + "cq.autogen": "TensorProduct.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([tensor_product_block_encoding],\n", + " ['`tensor_product_block_encoding`'])" + ] + }, + { + "cell_type": "markdown", + "id": "53df4024", + "metadata": { + "cq.autogen": "TensorProduct.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0883c9f0", + "metadata": { + "cq.autogen": "TensorProduct.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "tensor_product_block_encoding_g, tensor_product_block_encoding_sigma = tensor_product_block_encoding.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(tensor_product_block_encoding_g)\n", + "show_counts_sigma(tensor_product_block_encoding_sigma)" + ] } ], "metadata": { diff --git a/qualtran/bloqs/block_encoding/block_encoding_base.py b/qualtran/bloqs/block_encoding/block_encoding_base.py index 85e821ad8..c70c5de81 100644 --- a/qualtran/bloqs/block_encoding/block_encoding_base.py +++ b/qualtran/bloqs/block_encoding/block_encoding_base.py @@ -14,8 +14,9 @@ import abc from typing import Tuple -from qualtran import Bloq, BloqDocSpec, Register +from qualtran import Bloq, BloqDocSpec, QDType, Register from qualtran.bloqs.block_encoding.lcu_select_and_prepare import PrepareOracle +from qualtran.symbolics import SymbolicFloat class BlockEncoding(Bloq): @@ -67,6 +68,31 @@ class `BlockEncoding` bloq, which expects values for $\alpha$, $\epsilon$, def pretty_name(self) -> str: return 'B[H]' + @property + def dtype(self) -> QDType: + """The type of the system being block encoded.""" + raise NotImplementedError + + @property + def alpha(self) -> SymbolicFloat: + """The normalization constant.""" + raise NotImplementedError + + @property + def num_ancillas(self) -> int: + """The number of ancilla qubits.""" + raise NotImplementedError + + @property + def num_resource(self) -> int: + """The number of resource qubits not counted in ancillas.""" + raise NotImplementedError + + @property + def epsilon(self) -> SymbolicFloat: + """The precision to which the block encoding is to be prepared.""" + raise NotImplementedError + @property @abc.abstractmethod def selection_registers(self) -> Tuple[Register, ...]: diff --git a/qualtran/bloqs/block_encoding/tensor_product.py b/qualtran/bloqs/block_encoding/tensor_product.py new file mode 100644 index 000000000..cab8d43bc --- /dev/null +++ b/qualtran/bloqs/block_encoding/tensor_product.py @@ -0,0 +1,152 @@ +# 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 functools import cached_property, reduce +from typing import Dict, Sequence, Tuple + +from attrs import evolve, field, frozen + +from qualtran import ( + bloq_example, + BloqBuilder, + BloqDocSpec, + QAny, + QDType, + Register, + Signature, + SoquetT, +) +from qualtran.bloqs.block_encoding import BlockEncoding +from qualtran.bloqs.block_encoding.lcu_select_and_prepare import PrepareOracle +from qualtran.bloqs.bookkeeping import Partition +from qualtran.symbolics import SymbolicFloat + + +@frozen +class TensorProduct(BlockEncoding): + r"""Tensor product of a sequence of block encodings. + + Builds the block encoding as + $$ + B[U_1 ⊗ U_2 ⊗ \cdots ⊗ U_n] = B[U_1] ⊗ B[U_2] ⊗ \cdots ⊗ B[U_n] + $$ + + When each $B[U_i]$ is a $(\alpha_i, a_i, \epsilon_i)$-block encoding of $U_i$, we have that + $B[U_1 ⊗ \cdots ⊗ U_n]$ is a $(\prod_i \alpha_i, \sum_i a_i, \sum_i \alpha_i \epsilon_i)$-block + encoding of $U_1 ⊗ \cdots ⊗ U_n$. + + Args: + U: A sequence of block encodings. + + Registers: + system: The system register. + ancilla: The ancilla register. + resource: The resource register. + + References: + [Quantum algorithms: A survey of applications and end-to-end complexities](https://arxiv.org/abs/2310.03011). Dalzell et al. (2023). Ch. 10.2. + """ + + U: Sequence[BlockEncoding] = field(converter=lambda x: x if isinstance(x, tuple) else tuple(x)) + + @cached_property + def signature(self) -> Signature: + return Signature.build_from_dtypes( + system=self.dtype, ancilla=QAny(self.num_ancillas), resource=QAny(self.num_resource) + ) + + @cached_property + def dtype(self) -> QDType: + return QAny(bitsize=sum(u.dtype.num_qubits for u in self.U)) + + def pretty_name(self) -> str: + return f"B[{'⊗'.join(u.pretty_name()[2:-1] for u in self.U)}]" + + @cached_property + def alpha(self) -> SymbolicFloat: + return reduce(lambda a, b: a * b.alpha, self.U, 1.0) + + @cached_property + def num_ancillas(self) -> int: + return sum(u.num_ancillas for u in self.U) + + @cached_property + def num_resource(self) -> int: + return sum(u.num_resource for u in self.U) + + @cached_property + def epsilon(self) -> SymbolicFloat: + return sum(u.alpha * u.epsilon for u in self.U) + + @property + def target_registers(self) -> Tuple[Register, ...]: + return (self.signature.get_right("system"),) + + @property + def junk_registers(self) -> Tuple[Register, ...]: + return (self.signature.get_right("resource"),) + + @property + def selection_registers(self) -> Tuple[Register, ...]: + return (self.signature.get_right("ancilla"),) + + @property + def signal_state(self) -> PrepareOracle: + raise NotImplementedError + + def build_composite_bloq( + self, bb: BloqBuilder, system: SoquetT, ancilla: SoquetT, resource: SoquetT + ) -> Dict[str, SoquetT]: + transpose = lambda x: zip(*x) + sys_regs, anc_regs, res_regs = transpose( + [evolve(r, name=f"{r.name}{i}") for r in u.signature.lefts()] + for i, u in enumerate(self.U) + ) + sys_part = Partition(self.dtype.num_qubits, regs=sys_regs) + anc_part = Partition(self.num_ancillas, regs=anc_regs) + res_part = Partition(self.num_resource, regs=res_regs) + sys_out_regs = list(bb.add_t(sys_part, x=system)) + anc_out_regs = list(bb.add_t(anc_part, x=ancilla)) + res_out_regs = list(bb.add_t(res_part, x=resource)) + for i, u in enumerate(self.U): + sys_out_regs[i], anc_out_regs[i], res_out_regs[i] = bb.add_t( + u, system=sys_out_regs[i], ancilla=anc_out_regs[i], resource=res_out_regs[i] + ) + system = bb.add(sys_part.adjoint(), **{r.name: sp for r, sp in zip(sys_regs, sys_out_regs)}) + ancilla = bb.add( + anc_part.adjoint(), **{r.name: ap for r, ap in zip(anc_regs, anc_out_regs)} + ) + resource = bb.add( + res_part.adjoint(), **{r.name: ap for r, ap in zip(res_regs, res_out_regs)} + ) + return {"system": system, "ancilla": ancilla, "resource": resource} + + +@bloq_example +def _tensor_product_block_encoding() -> TensorProduct: + from qualtran import QBit + from qualtran.bloqs.basic_gates import Hadamard, TGate + from qualtran.bloqs.block_encoding.unitary import Unitary + + tensor_product_block_encoding = TensorProduct( + [Unitary(TGate(), dtype=QBit()), Unitary(Hadamard(), dtype=QBit())] + ) + return tensor_product_block_encoding + + +_TENSOR_PRODUCT_DOC = BloqDocSpec( + bloq_cls=TensorProduct, + import_line="from qualtran.bloqs.block_encoding import TensorProduct", + examples=[_tensor_product_block_encoding], +) diff --git a/qualtran/bloqs/block_encoding/tensor_product_test.py b/qualtran/bloqs/block_encoding/tensor_product_test.py new file mode 100644 index 000000000..9f5b18274 --- /dev/null +++ b/qualtran/bloqs/block_encoding/tensor_product_test.py @@ -0,0 +1,55 @@ +# 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 numpy as np +from attrs import evolve + +from qualtran import QAny, QBit, Register, Signature +from qualtran.bloqs.basic_gates import CNOT, Hadamard, TGate +from qualtran.bloqs.block_encoding.tensor_product import ( + _tensor_product_block_encoding, + TensorProduct, +) +from qualtran.bloqs.block_encoding.unitary import Unitary + + +def test_tensor_product(bloq_autotester): + bloq_autotester(_tensor_product_block_encoding) + + +def test_tensor_product_signature(): + assert _tensor_product_block_encoding().signature == Signature( + [Register("system", QAny(2)), Register("ancilla", QAny(0)), Register("resource", QAny(0))] + ) + + +def test_tensor_product_params(): + u1 = evolve( + Unitary(TGate(), dtype=QBit()), alpha=0.5, num_ancillas=2, num_resource=1, epsilon=0.01 + ) + u2 = evolve( + Unitary(CNOT(), dtype=QAny(2)), alpha=0.5, num_ancillas=1, num_resource=1, epsilon=0.1 + ) + bloq = TensorProduct([u1, u2]) + assert bloq.dtype == QAny(1 + 2) + assert bloq.alpha == 0.5 * 0.5 + assert bloq.epsilon == 0.5 * 0.01 + 0.5 * 0.1 + assert bloq.num_ancillas == 2 + 1 + assert bloq.num_resource == 1 + 1 + + +def test_tensor_product_tensors(): + from_gate = np.kron(TGate().tensor_contract(), Hadamard().tensor_contract()) + from_tensors = _tensor_product_block_encoding().tensor_contract() + np.testing.assert_allclose(from_gate, from_tensors) diff --git a/qualtran/bloqs/block_encoding/unitary.py b/qualtran/bloqs/block_encoding/unitary.py new file mode 100644 index 000000000..5a34606d1 --- /dev/null +++ b/qualtran/bloqs/block_encoding/unitary.py @@ -0,0 +1,111 @@ +# 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 functools import cached_property +from typing import Dict, Tuple + +from attrs import frozen + +from qualtran import ( + Bloq, + bloq_example, + BloqBuilder, + BloqDocSpec, + QAny, + QDType, + Register, + Signature, + SoquetT, +) +from qualtran.bloqs.block_encoding import BlockEncoding +from qualtran.bloqs.block_encoding.lcu_select_and_prepare import PrepareOracle +from qualtran.symbolics import SymbolicFloat + + +@frozen +class Unitary(BlockEncoding): + r"""Trivial block encoding of a unitary operator. + + Builds the block encoding as + $ + B[U] = U + $ + where $U$ is a unitary operator. Here, $B[U]$ is a $(1, 0, 0)$-block encoding of $U$. + + Args: + U: The unitary operator to block-encode. + dtype: The quantum data type of the system `U` operates over. + + Registers: + system: The system register. + ancilla: The ancilla register (size 0). + resource: The resource register (size 0). + """ + + U: Bloq + dtype: QDType + alpha: SymbolicFloat = 1 + num_ancillas: int = 0 + num_resource: int = 0 + epsilon: SymbolicFloat = 0 + + def __attrs_post_init__(self): + assert self.U.signature == self.U.signature.adjoint() + assert self.dtype.num_qubits == sum(r.bitsize for r in self.U.signature.rights()) + + @cached_property + def signature(self) -> Signature: + return Signature.build_from_dtypes(system=self.dtype, ancilla=QAny(0), resource=QAny(0)) + + def pretty_name(self) -> str: + return f"B[{self.U.pretty_name()}]" + + @property + def target_registers(self) -> Tuple[Register, ...]: + return tuple(self.signature.rights()) + + junk_registers: Tuple[Register, ...] = () + selection_registers: Tuple[Register, ...] = () + + @property + def signal_state(self) -> PrepareOracle: + raise NotImplementedError + + def build_composite_bloq( + self, bb: BloqBuilder, system: SoquetT, ancilla: SoquetT, resource: SoquetT + ) -> Dict[str, SoquetT]: + partitions = [ + (self.signature.get_left("system"), tuple(r.name for r in self.U.signature.lefts())) + ] + return { + "system": bb.add_and_partition(self.U, partitions=partitions, system=system), + "ancilla": ancilla, + "resource": resource, + } + + +@bloq_example +def _unitary_block_encoding() -> Unitary: + from qualtran import QBit + from qualtran.bloqs.basic_gates import TGate + + unitary_block_encoding = Unitary(TGate(), dtype=QBit()) + return unitary_block_encoding + + +_UNITARY_DOC = BloqDocSpec( + bloq_cls=Unitary, + import_line="from qualtran.bloqs.block_encoding import Unitary", + examples=[_unitary_block_encoding], +) diff --git a/qualtran/bloqs/block_encoding/unitary_test.py b/qualtran/bloqs/block_encoding/unitary_test.py new file mode 100644 index 000000000..13dd1d748 --- /dev/null +++ b/qualtran/bloqs/block_encoding/unitary_test.py @@ -0,0 +1,48 @@ +# 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 numpy as np +import pytest + +from qualtran import QAny, QBit, Register, Signature +from qualtran.bloqs.basic_gates import IntState, TGate +from qualtran.bloqs.block_encoding.unitary import _unitary_block_encoding, Unitary + + +def test_unitary(bloq_autotester): + bloq_autotester(_unitary_block_encoding) + + +def test_unitary_signature(): + assert _unitary_block_encoding().signature == Signature( + [Register("system", QBit()), Register("ancilla", QAny(0)), Register("resource", QAny(0))] + ) + with pytest.raises(AssertionError): + _ = Unitary(TGate(), dtype=QAny(2)) + with pytest.raises(AssertionError): + _ = Unitary(IntState(55, bitsize=8), dtype=QAny(8)) + + +def test_unitary_params(): + bloq = _unitary_block_encoding() + assert bloq.alpha == 1 + assert bloq.epsilon == 0 + assert bloq.num_ancillas == 0 + assert bloq.num_resource == 0 + + +def test_unitary_tensors(): + from_gate = TGate().tensor_contract() + from_tensors = _unitary_block_encoding().tensor_contract() + np.testing.assert_allclose(from_gate, from_tensors) diff --git a/qualtran/conftest.py b/qualtran/conftest.py index 98f360fd0..2fac47099 100644 --- a/qualtran/conftest.py +++ b/qualtran/conftest.py @@ -92,6 +92,8 @@ def assert_bloq_example_serializes_for_pytest(bloq_ex: BloqExample): 'symbolic_hamsim_by_gqsp', 'gqsp_1d_ising', 'auto_partition', + 'unitary_block_encoding', + 'tensor_product_block_encoding', ]: pytest.xfail("Skipping serialization test for bloq examples that cannot yet be serialized.") diff --git a/qualtran/serialization/resolver_dict.py b/qualtran/serialization/resolver_dict.py index 6885fc420..8a039515f 100644 --- a/qualtran/serialization/resolver_dict.py +++ b/qualtran/serialization/resolver_dict.py @@ -34,6 +34,8 @@ import qualtran.bloqs.basic_gates.z_basis import qualtran.bloqs.block_encoding import qualtran.bloqs.block_encoding.lcu_select_and_prepare +import qualtran.bloqs.block_encoding.tensor_product +import qualtran.bloqs.block_encoding.unitary import qualtran.bloqs.bookkeeping import qualtran.bloqs.chemistry.black_boxes import qualtran.bloqs.chemistry.df.double_factorization @@ -188,6 +190,8 @@ "qualtran.bloqs.block_encoding.lcu_block_encoding.BlackBoxPrepare": qualtran.bloqs.block_encoding.lcu_block_encoding.BlackBoxPrepare, "qualtran.bloqs.block_encoding.lcu_block_encoding.BlackBoxSelect": qualtran.bloqs.block_encoding.lcu_block_encoding.BlackBoxSelect, "qualtran.bloqs.block_encoding.chebyshev_polynomial.ChebyshevPolynomial": qualtran.bloqs.block_encoding.chebyshev_polynomial.ChebyshevPolynomial, + "qualtran.bloqs.block_encoding.unitary.Unitary": qualtran.bloqs.block_encoding.unitary.Unitary, + "qualtran.bloqs.block_encoding.tensor_product.TensorProduct": qualtran.bloqs.block_encoding.tensor_product.TensorProduct, "qualtran.bloqs.bookkeeping.allocate.Allocate": qualtran.bloqs.bookkeeping.allocate.Allocate, "qualtran.bloqs.bookkeeping.arbitrary_clifford.ArbitraryClifford": qualtran.bloqs.bookkeeping.arbitrary_clifford.ArbitraryClifford, "qualtran.bloqs.bookkeeping.auto_partition.AutoPartition": qualtran.bloqs.bookkeeping.auto_partition.AutoPartition,