From ec85731ec6e0e498797b8412b39f7bdd75bc20f0 Mon Sep 17 00:00:00 2001 From: Noureldin Date: Mon, 12 Aug 2024 17:40:25 -0700 Subject: [PATCH] Improve support for CAdd (#1277) --- dev_tools/autogenerate-bloqs-notebooks-v2.py | 6 + docs/bloqs/index.rst | 1 + qualtran/bloqs/arithmetic/__init__.py | 1 + .../arithmetic/controlled_addition.ipynb | 163 ++++++++++++++++ .../bloqs/arithmetic/controlled_addition.py | 184 +++++++----------- .../arithmetic/controlled_addition_test.py | 109 ++++++----- qualtran/serialization/resolver_dict.py | 2 + 7 files changed, 303 insertions(+), 163 deletions(-) create mode 100644 qualtran/bloqs/arithmetic/controlled_addition.ipynb diff --git a/dev_tools/autogenerate-bloqs-notebooks-v2.py b/dev_tools/autogenerate-bloqs-notebooks-v2.py index 5ef5ecf18..cb675a426 100644 --- a/dev_tools/autogenerate-bloqs-notebooks-v2.py +++ b/dev_tools/autogenerate-bloqs-notebooks-v2.py @@ -56,6 +56,7 @@ import qualtran.bloqs.arithmetic.bitwise import qualtran.bloqs.arithmetic.comparison import qualtran.bloqs.arithmetic.controlled_add_or_subtract +import qualtran.bloqs.arithmetic.controlled_addition import qualtran.bloqs.arithmetic.conversions import qualtran.bloqs.arithmetic.multiplication import qualtran.bloqs.arithmetic.negate @@ -387,6 +388,11 @@ qualtran.bloqs.arithmetic.addition._ADD_K_DOC, ], ), + NotebookSpecV2( + title='Controlled Addition', + module=qualtran.bloqs.arithmetic.controlled_addition, + bloq_specs=[qualtran.bloqs.arithmetic.controlled_addition._CADD_DOC], + ), NotebookSpecV2( title='Negation', module=qualtran.bloqs.arithmetic.negate, diff --git a/docs/bloqs/index.rst b/docs/bloqs/index.rst index 6012e5868..256f60daf 100644 --- a/docs/bloqs/index.rst +++ b/docs/bloqs/index.rst @@ -62,6 +62,7 @@ Bloqs Library :caption: Arithmetic: arithmetic/addition.ipynb + arithmetic/controlled_addition.ipynb arithmetic/negate.ipynb arithmetic/subtraction.ipynb arithmetic/controlled_add_or_subtract.ipynb diff --git a/qualtran/bloqs/arithmetic/__init__.py b/qualtran/bloqs/arithmetic/__init__.py index bbdebc30b..68f726779 100644 --- a/qualtran/bloqs/arithmetic/__init__.py +++ b/qualtran/bloqs/arithmetic/__init__.py @@ -23,6 +23,7 @@ LessThanEqual, SingleQubitCompare, ) +from qualtran.bloqs.arithmetic.controlled_addition import CAdd from qualtran.bloqs.arithmetic.conversions import ( SignedIntegerToTwosComplement, SignExtend, diff --git a/qualtran/bloqs/arithmetic/controlled_addition.ipynb b/qualtran/bloqs/arithmetic/controlled_addition.ipynb new file mode 100644 index 000000000..5b4fd4a49 --- /dev/null +++ b/qualtran/bloqs/arithmetic/controlled_addition.ipynb @@ -0,0 +1,163 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "064ac84d", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# Controlled Addition" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a283d3fb", + "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": "d85b6f41", + "metadata": { + "cq.autogen": "CAdd.bloq_doc.md" + }, + "source": [ + "## `CAdd`\n", + "An n-bit controlled-addition gate.\n", + "\n", + "#### Parameters\n", + " - `a_dtype`: Quantum datatype used to represent the integer a.\n", + " - `b_dtype`: Quantum datatype used to represent the integer b. Must be large enough to hold the result in the output register of a + b, or else it simply drops the most significant bits. If not specified, b_dtype is set to a_dtype.\n", + " - `cv`: When controlled=0, this bloq is active when the ctrl register is 0. When controlled=1, this bloq is active when the ctrl register is 1. \n", + "\n", + "#### Registers\n", + " - `ctrl`: the control bit for the addition\n", + " - `a`: A a_dtype.bitsize-sized input register (register a above).\n", + " - `b`: A b_dtype.bitsize-sized input/output register (register b above). \n", + "\n", + "#### References\n", + " - [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e441ac2", + "metadata": { + "cq.autogen": "CAdd.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.arithmetic import CAdd" + ] + }, + { + "cell_type": "markdown", + "id": "801cf1e1", + "metadata": { + "cq.autogen": "CAdd.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "854ba572", + "metadata": { + "cq.autogen": "CAdd.cadd_small" + }, + "outputs": [], + "source": [ + "cadd_small = CAdd(QUInt(3))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a4d89b2", + "metadata": { + "cq.autogen": "CAdd.cadd_large" + }, + "outputs": [], + "source": [ + "cadd_large = CAdd(QUInt(1000), QUInt(1000))" + ] + }, + { + "cell_type": "markdown", + "id": "25c1ed97", + "metadata": { + "cq.autogen": "CAdd.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03b6c6a6", + "metadata": { + "cq.autogen": "CAdd.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([cadd_small, cadd_large],\n", + " ['`cadd_small`', '`cadd_large`'])" + ] + }, + { + "cell_type": "markdown", + "id": "3bd7842e", + "metadata": { + "cq.autogen": "CAdd.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80c50e8b", + "metadata": { + "cq.autogen": "CAdd.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "cadd_small_g, cadd_small_sigma = cadd_small.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(cadd_small_g)\n", + "show_counts_sigma(cadd_small_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/arithmetic/controlled_addition.py b/qualtran/bloqs/arithmetic/controlled_addition.py index 15dd23100..ec50ad4b3 100644 --- a/qualtran/bloqs/arithmetic/controlled_addition.py +++ b/qualtran/bloqs/arithmetic/controlled_addition.py @@ -11,23 +11,33 @@ # 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 itertools -import math -from typing import Any, Dict, Iterator, Set, TYPE_CHECKING, Union -import cirq +from typing import Dict, Set, TYPE_CHECKING, Union + import numpy as np import sympy from attrs import field, frozen -from numpy.typing import NDArray -from qualtran import Bloq, CompositeBloq, QBit, QInt, QUInt, Register, Signature, Soquet, SoquetT +from qualtran import ( + Bloq, + bloq_example, + BloqBuilder, + BloqDocSpec, + QBit, + QInt, + QUInt, + Register, + Signature, + Soquet, + SoquetT, +) from qualtran._infra.data_types import QMontgomeryUInt -from qualtran.bloqs.basic_gates import CNOT -from qualtran.bloqs.mcmt import MultiControlX +from qualtran.bloqs.arithmetic.addition import Add +from qualtran.bloqs.bookkeeping import Cast from qualtran.bloqs.mcmt.and_bloq import And -from qualtran.cirq_interop import decompose_from_cirq_style_method from qualtran.cirq_interop.t_complexity_protocol import TComplexity +from qualtran.resource_counting.generalizers import ignore_split_join +from qualtran.simulation.classical_sim import add_ints if TYPE_CHECKING: import quimb.tensor as qtn @@ -91,50 +101,21 @@ def signature(self): [Register("ctrl", QBit()), Register("a", self.a_dtype), Register("b", self.b_dtype)] ) - def add_my_tensors( - self, - tn: 'qtn.TensorNetwork', - tag: Any, - *, - incoming: Dict[str, 'SoquetT'], - outgoing: Dict[str, 'SoquetT'], - ): - import quimb.tensor as qtn - - if isinstance(self.a_dtype, QInt) or isinstance(self.b_dtype, QInt): - raise TypeError("Tensor contraction for addition is only supported for unsigned ints.") - N_a = 2**self.a_dtype.bitsize - N_b = 2**self.b_dtype.bitsize - inds = ( - incoming['ctrl'], - incoming['a'], - incoming['b'], - outgoing['ctrl'], - outgoing['a'], - outgoing['b'], - ) - unitary = np.zeros((2, N_a, N_b, 2, N_a, N_b), dtype=np.complex128) - for c, a, b in itertools.product(range(2), range(N_a), range(N_b)): - if c == self.cv: - unitary[c, a, b, c, a, int(math.fmod(a + b, N_b))] = 1 - else: - unitary[c, a, b, c, a, b] = 1 - - tn.add(qtn.Tensor(data=unitary, inds=inds, tags=[self.short_name(), tag])) - - def decompose_bloq(self) -> 'CompositeBloq': - return decompose_from_cirq_style_method(self) - def on_classical_vals(self, **kwargs) -> Dict[str, 'ClassicalValT']: a, b = kwargs['a'], kwargs['b'] - unsigned = isinstance(self.a_dtype, (QUInt, QMontgomeryUInt)) - b_bitsize = self.b_dtype.bitsize - N = 2**b_bitsize if unsigned else 2 ** (b_bitsize - 1) ctrl = kwargs['ctrl'] if ctrl != self.cv: return {'ctrl': ctrl, 'a': a, 'b': b} else: - return {'ctrl': ctrl, 'a': a, 'b': int(math.fmod(a + b, N))} + if not isinstance(self.b_dtype.bitsize, int): + raise ValueError(f'classical simulation is not supported for symbolic bloq {self}') + return { + 'ctrl': ctrl, + 'a': a, + 'b': add_ints( + a, b, num_bits=self.b_dtype.bitsize, is_signed=isinstance(self.b_dtype, QInt) + ), + } def short_name(self) -> str: return "a+b" @@ -151,68 +132,29 @@ def wire_symbol(self, soq: 'Soquet') -> 'WireSymbol': else: raise ValueError() - def _left_building_block(self, inp, out, anc, depth): - if depth == self.b_dtype.bitsize - 1: - return - else: - if depth < 1: - raise ValueError(f"{depth=} is not a positive integer") - if depth < len(inp): - yield CNOT().on(anc[depth - 1], inp[depth]) - control = inp[depth] - else: - # If inp[depth] doesn't exist, we treat it as a |0>, - # and therefore applying CNOT().on(anc[depth - 1], inp[depth]) - # essentially "copies" anc[depth - 1] into inp[depth] - # in the classical basis. So therefore, on future operations, - # we can use anc[depth - 1] in its place. - control = anc[depth - 1] - yield CNOT().on(anc[depth - 1], out[depth]) - yield And().on(control, out[depth], anc[depth]) - yield CNOT().on(anc[depth - 1], anc[depth]) - yield from self._left_building_block(inp, out, anc, depth + 1) - - def _right_building_block(self, inp, out, anc, control, depth): - if depth == 0: - return - yield CNOT().on(anc[depth - 1], anc[depth]) - if depth < len(inp): - yield And().adjoint().on(inp[depth], out[depth], anc[depth]) - yield MultiControlX((1, 1)).on(control, inp[depth], out[depth]) - yield CNOT().on(anc[depth - 1], inp[depth]) - else: - yield And().adjoint().on(anc[depth - 1], out[depth], anc[depth]) - yield MultiControlX((1, 1)).on(control, anc[depth - 1], out[depth]) - yield CNOT().on(anc[depth - 1], out[depth]) - yield from self._right_building_block(inp, out, anc, control, depth - 1) - - def decompose_from_registers( - self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid] # type: ignore[type-var] - ) -> Iterator[cirq.OP_TREE]: - # reverse the order of qubits for big endian-ness. - input_bits = quregs['a'][::-1] - output_bits = quregs['b'][::-1] - ancillas = context.qubit_manager.qalloc(self.b_dtype.bitsize - 1)[::-1] - control = quregs['ctrl'][0] - if self.cv == 0: - yield cirq.X(control) - # Start off the addition by anding into the ancilla - yield And().on(input_bits[0], output_bits[0], ancillas[0]) - # Left part of Fig.4 - yield from self._left_building_block(input_bits, output_bits, ancillas, 1) - yield CNOT().on(ancillas[-1], output_bits[-1]) - if len(input_bits) == len(output_bits): - yield MultiControlX((1, 1)).on(control, input_bits[-1], output_bits[-1]) - yield CNOT().on(ancillas[-1], output_bits[-1]) - # right part of Fig.4 - yield from self._right_building_block( - input_bits, output_bits, ancillas, control, self.b_dtype.bitsize - 2 - ) - yield And().adjoint().on(input_bits[0], output_bits[0], ancillas[0]) - yield MultiControlX((1, 1)).on(control, input_bits[0], output_bits[0]) - if self.cv == 0: - yield cirq.X(control) - context.qubit_manager.qfree(ancillas) + def build_composite_bloq( + self, bb: 'BloqBuilder', ctrl: 'Soquet', a: 'Soquet', b: 'Soquet' + ) -> Dict[str, 'SoquetT']: + a_arr = bb.split(a) + ctrl_q = bb.split(ctrl)[0] + ancilla_arr = [] + for i in range(len(a_arr)): + [ctrl_q, a_arr[i]], target = bb.add(And(self.cv, 1), ctrl=np.array([ctrl_q, a_arr[i]])) + ancilla_arr.append(target) + ancilla = bb.join(np.array(ancilla_arr), QUInt(len(ancilla_arr))) + ancilla = bb.add(Cast(QUInt(len(ancilla_arr)), self.a_dtype), reg=ancilla) + + ancilla, b = bb.add(Add(self.a_dtype, self.b_dtype), a=ancilla, b=b) + ancilla_arr = bb.split(ancilla).tolist() + + for i in reversed(range(len(a_arr))): + ctrl_q, a_arr[i] = bb.add( + And(self.cv, 1).adjoint(), ctrl=np.array([ctrl_q, a_arr[i]]), target=ancilla_arr[i] + ) + + a = bb.join(a_arr, self.a_dtype) + ctrl = bb.join(np.array([ctrl_q])) + return {'ctrl': ctrl, 'a': a, 'b': b} def _t_complexity_(self): n = self.b_dtype.bitsize @@ -221,11 +163,23 @@ def _t_complexity_(self): return TComplexity(t=4 * num_and, clifford=num_clifford) def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']: - n = self.b_dtype.bitsize - n_cnot = (n - 2) * 6 + 2 return { - (MultiControlX((1, 1)), n), - (And(), n - 1), - (And().adjoint(), n - 1), - (CNOT(), n_cnot), + (And(self.cv, 1), self.a_dtype.bitsize), + (Add(self.a_dtype, self.b_dtype), 1), + (And(self.cv, 1).adjoint(), self.a_dtype.bitsize), } + + +@bloq_example(generalizer=ignore_split_join) +def _cadd_small() -> CAdd: + cadd_small = CAdd(QUInt(3)) + return cadd_small + + +@bloq_example(generalizer=ignore_split_join) +def _cadd_large() -> CAdd: + cadd_large = CAdd(QUInt(1000), QUInt(1000)) + return cadd_large + + +_CADD_DOC = BloqDocSpec(bloq_cls=CAdd, examples=[_cadd_small, _cadd_large]) diff --git a/qualtran/bloqs/arithmetic/controlled_addition_test.py b/qualtran/bloqs/arithmetic/controlled_addition_test.py index abfa4a1c2..aec983854 100644 --- a/qualtran/bloqs/arithmetic/controlled_addition_test.py +++ b/qualtran/bloqs/arithmetic/controlled_addition_test.py @@ -12,54 +12,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -import cirq -import numpy as np import pytest +import sympy import qualtran.testing as qlt_testing -from qualtran import QUInt -from qualtran.bloqs.arithmetic.controlled_addition import CAdd -from qualtran.cirq_interop.testing import assert_circuit_inp_out_cirqsim -from qualtran.resource_counting.generalizers import ignore_split_join - - -def iter_bits(x, w): - return [int(b) for b in np.binary_repr(x, width=w)] - - -@pytest.mark.parametrize('a', [1, 2]) -@pytest.mark.parametrize('b', [1, 2, 3]) -@pytest.mark.parametrize('num_bits_a', [2, 3]) -@pytest.mark.parametrize('num_bits_b', [5]) -@pytest.mark.parametrize('controlled_on', [0, 1]) -@pytest.mark.parametrize('control', [0, 1]) -def test_controlled_addition(a, b, num_bits_a, num_bits_b, controlled_on, control): - num_anc = num_bits_b - 1 - gate = CAdd(QUInt(num_bits_a), QUInt(num_bits_b), cv=controlled_on) - qubits = cirq.LineQubit.range(num_bits_a + num_bits_b + 1) - op = gate.on_registers(ctrl=qubits[0], a=qubits[1 : num_bits_a + 1], b=qubits[num_bits_a + 1 :]) - greedy_mm = cirq.GreedyQubitManager(prefix="_a", maximize_reuse=True) - context = cirq.DecompositionContext(greedy_mm) - circuit = cirq.Circuit(cirq.decompose_once(op, context=context)) - circuit0 = cirq.Circuit(op) - ancillas = sorted(circuit.all_qubits())[-num_anc:] - initial_state = [0] * (num_bits_a + num_bits_b + num_anc + 1) - initial_state[0] = control - initial_state[1 : num_bits_a + 1] = list(iter_bits(a, num_bits_a)) - initial_state[num_bits_a + 1 : num_bits_a + num_bits_b + 1] = list(iter_bits(b, num_bits_b)) - final_state = [0] * (num_bits_a + num_bits_b + num_anc + 1) - final_state[0] = control - final_state[1 : num_bits_a + 1] = list(iter_bits(a, num_bits_a)) - if control == controlled_on: - final_state[num_bits_a + 1 : num_bits_a + num_bits_b + 1] = list( - iter_bits(a + b, num_bits_b) - ) - else: - final_state[num_bits_a + 1 : num_bits_a + num_bits_b + 1] = list(iter_bits(b, num_bits_b)) - assert_circuit_inp_out_cirqsim(circuit, qubits + ancillas, initial_state, final_state) - assert_circuit_inp_out_cirqsim( - circuit0, qubits, initial_state[:-num_anc], final_state[:-num_anc] - ) +from qualtran import QInt, QMontgomeryUInt, QUInt +from qualtran.bloqs.arithmetic.controlled_addition import _cadd_large, _cadd_small, CAdd +from qualtran.resource_counting import GateCounts, QECGatesCost, query_costs +from qualtran.resource_counting.generalizers import ignore_alloc_free, ignore_split_join + + +def test_examples(bloq_autotester): + bloq_autotester(_cadd_small) + bloq_autotester(_cadd_large) + + +@pytest.mark.notebook +def test_notebook(): + qlt_testing.execute_notebook('controlled_addition') + + +@pytest.mark.parametrize('control', range(2)) +@pytest.mark.parametrize('dtype', [QUInt, QMontgomeryUInt]) +@pytest.mark.parametrize(['a_bits', 'b_bits'], [(a, b) for a in range(1, 5) for b in range(a, 5)]) +def test_decomposition(control, dtype, a_bits, b_bits): + b = CAdd(dtype(a_bits), dtype(b_bits), cv=control) + qlt_testing.assert_valid_bloq_decomposition(b) + qlt_testing.assert_equivalent_bloq_counts(b, [ignore_alloc_free, ignore_split_join]) @pytest.mark.parametrize("n", [*range(3, 10)]) @@ -67,8 +46,42 @@ def test_addition_gate_counts_controlled(n: int): add = CAdd(QUInt(n), cv=1) num_and = 2 * n - 1 t_count = 4 * num_and - - qlt_testing.assert_valid_bloq_decomposition(add) - assert add.t_complexity() == add.decompose_bloq().t_complexity() assert add.bloq_counts() == add.decompose_bloq().bloq_counts(generalizer=ignore_split_join) assert add.t_complexity().t == t_count + + +@pytest.mark.parametrize('control', range(2)) +@pytest.mark.parametrize('dtype', [QUInt, QMontgomeryUInt]) +@pytest.mark.parametrize(['a_bits', 'b_bits'], [(a, b) for a in range(1, 5) for b in range(a, 5)]) +def test_classical_action_unsigned(control, dtype, a_bits, b_bits): + b = CAdd(dtype(a_bits), dtype(b_bits), cv=control) + cb = b.decompose_bloq() + for c in range(2): + for x in range(2**a_bits): + for y in range(2**b_bits): + assert b.call_classically(ctrl=c, a=x, b=y) == cb.call_classically( + ctrl=c, a=x, b=y + ), f'{c=} {x=} {y=}' + + +@pytest.mark.parametrize('control', range(2)) +@pytest.mark.parametrize(['a_bits', 'b_bits'], [(a, b) for a in range(2, 5) for b in range(a, 5)]) +def test_classical_action_signed(control, a_bits, b_bits): + b = CAdd(QInt(a_bits), QInt(b_bits), cv=control) + cb = b.decompose_bloq() + for c in range(2): + for x in range(-(2 ** (a_bits - 1)), 2 ** (a_bits - 1)): + for y in range(-(2 ** (b_bits - 1)), 2 ** (b_bits - 1)): + assert b.call_classically(ctrl=c, a=x, b=y) == cb.call_classically( + ctrl=c, a=x, b=y + ), f'{c=} {x=} {y=}' + + +@pytest.mark.parametrize('control', range(2)) +@pytest.mark.parametrize('dtype', [QInt, QUInt, QMontgomeryUInt]) +def test_symbolic_cost(control, dtype): + n, m = sympy.symbols('n m') + b = CAdd(dtype(n), dtype(m), control) + target_cost = QECGatesCost() + gate_count: GateCounts = query_costs(b, [target_cost])[b][target_cost] + assert gate_count.total_t_count() == 4 * (m + n - 1) diff --git a/qualtran/serialization/resolver_dict.py b/qualtran/serialization/resolver_dict.py index b28de18ef..b66e5b8ff 100644 --- a/qualtran/serialization/resolver_dict.py +++ b/qualtran/serialization/resolver_dict.py @@ -18,6 +18,7 @@ import qualtran.bloqs.arithmetic.bitwise import qualtran.bloqs.arithmetic.comparison import qualtran.bloqs.arithmetic.controlled_add_or_subtract +import qualtran.bloqs.arithmetic.controlled_addition import qualtran.bloqs.arithmetic.conversions.contiguous_index import qualtran.bloqs.arithmetic.conversions.ones_complement_to_twos_complement import qualtran.bloqs.arithmetic.conversions.sign_extension @@ -192,6 +193,7 @@ "qualtran.bloqs.arithmetic.sorting.ParallelComparators": qualtran.bloqs.arithmetic.sorting.ParallelComparators, "qualtran.bloqs.arithmetic.subtraction.Subtract": qualtran.bloqs.arithmetic.subtraction.Subtract, "qualtran.bloqs.arithmetic.subtraction.SubtractFrom": qualtran.bloqs.arithmetic.subtraction.SubtractFrom, + "qualtran.bloqs.arithmetic.controlled_addition.CAdd": qualtran.bloqs.arithmetic.controlled_addition.CAdd, "qualtran.bloqs.arithmetic.trigonometric.arcsin.ArcSin": qualtran.bloqs.arithmetic.trigonometric.arcsin.ArcSin, "qualtran.bloqs.arithmetic.trigonometric.arctan.ArcTan": qualtran.bloqs.arithmetic.trigonometric.arctan.ArcTan, "qualtran.bloqs.basic_gates.cnot.CNOT": qualtran.bloqs.basic_gates.cnot.CNOT,