From e10560cebb9b3c3005a3e560a40b712db207f301 Mon Sep 17 00:00:00 2001 From: Noureldin Date: Wed, 13 Nov 2024 09:16:20 -0800 Subject: [PATCH 01/12] Fix bug in KaliskiStep3 and add tests for all steps (#1496) * Fix bug in KaliskiStep3 and add tests for all steps * cost --- qualtran/bloqs/arithmetic/comparison.py | 19 ++--- qualtran/bloqs/factoring/ecc/ec_add_test.py | 2 +- qualtran/bloqs/mod_arithmetic/mod_division.py | 23 +++--- .../bloqs/mod_arithmetic/mod_division_test.py | 82 ++++++++++++++++++- 4 files changed, 99 insertions(+), 27 deletions(-) diff --git a/qualtran/bloqs/arithmetic/comparison.py b/qualtran/bloqs/arithmetic/comparison.py index b28269ae4..fff30d5fc 100644 --- a/qualtran/bloqs/arithmetic/comparison.py +++ b/qualtran/bloqs/arithmetic/comparison.py @@ -1462,20 +1462,17 @@ def on_classical_vals( c: Optional['ClassicalValT'] = None, target: Optional['ClassicalValT'] = None, ) -> Dict[str, 'ClassicalValT']: + if self._op_symbol in ('>', '<='): + c_val = add_ints(-int(a), int(b), num_bits=self.dtype.bitsize + 1, is_signed=False) + else: + c_val = add_ints(int(a), -int(b), num_bits=self.dtype.bitsize + 1, is_signed=False) if self.uncompute: - assert c == add_ints( - int(a), - int(b), - num_bits=int(self.dtype.bitsize), - is_signed=isinstance(self.dtype, QInt), - ) + assert c == c_val assert target == self._classical_comparison(a, b) return {'a': a, 'b': b} - if self._op_symbol in ('>', '<='): - c = add_ints(-int(a), int(b), num_bits=self.dtype.bitsize + 1, is_signed=False) - else: - c = add_ints(int(a), -int(b), num_bits=self.dtype.bitsize + 1, is_signed=False) - return {'a': a, 'b': b, 'c': c, 'target': int(self._classical_comparison(a, b))} + assert c is None + assert target is None + return {'a': a, 'b': b, 'c': c_val, 'target': int(self._classical_comparison(a, b))} def _compute(self, bb: 'BloqBuilder', a: 'Soquet', b: 'Soquet') -> Dict[str, 'SoquetT']: if self._op_symbol in ('>', '<='): diff --git a/qualtran/bloqs/factoring/ecc/ec_add_test.py b/qualtran/bloqs/factoring/ecc/ec_add_test.py index 37c397707..7f7439d2d 100644 --- a/qualtran/bloqs/factoring/ecc/ec_add_test.py +++ b/qualtran/bloqs/factoring/ecc/ec_add_test.py @@ -418,7 +418,7 @@ def test_ec_add_symbolic_cost(): # toffoli cost for Kaliski Mod Inverse, n extra toffolis in ModNeg, 2n extra toffolis to do n # 3-controlled toffolis in step 2. The expression is written with rationals because sympy # comparison fails with floats. - assert total_toff == sympy.Rational(253, 2) * n**2 + sympy.Rational(391, 2) * n - 31 + assert total_toff == sympy.Rational(253, 2) * n**2 + sympy.Rational(407, 2) * n - 31 def test_ec_add(bloq_autotester): diff --git a/qualtran/bloqs/mod_arithmetic/mod_division.py b/qualtran/bloqs/mod_arithmetic/mod_division.py index c099c7562..06f643525 100644 --- a/qualtran/bloqs/mod_arithmetic/mod_division.py +++ b/qualtran/bloqs/mod_arithmetic/mod_division.py @@ -72,8 +72,6 @@ def signature(self) -> 'Signature': def on_classical_vals( self, v: int, m: int, f: int, is_terminal: int ) -> Dict[str, 'ClassicalValT']: - print('here') - assert False m ^= f & (v == 0) assert is_terminal == 0 is_terminal ^= m @@ -101,10 +99,10 @@ def build_composite_bloq( def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': if is_symbolic(self.bitsize): - cvs: Union[HasLength, List[int]] = HasLength(self.bitsize) + cvs: Union[HasLength, List[int]] = HasLength(self.bitsize + 1) else: - cvs = [0] * int(self.bitsize) - return {MultiAnd(cvs=cvs): 1, MultiAnd(cvs=cvs).adjoint(): 1, CNOT(): 2} + cvs = [0] * int(self.bitsize) + [1] + return {MultiAnd(cvs=cvs): 1, MultiAnd(cvs=cvs).adjoint(): 1, CNOT(): 3} @frozen @@ -197,11 +195,11 @@ def on_classical_vals( def build_composite_bloq( self, bb: 'BloqBuilder', u: Soquet, v: Soquet, b: Soquet, a: Soquet, m: Soquet, f: Soquet ) -> Dict[str, 'SoquetT']: - u, v, junk, greater_than = bb.add( + u, v, junk_c, greater_than = bb.add( LinearDepthHalfGreaterThan(QMontgomeryUInt(self.bitsize)), a=u, b=v ) - (greater_than, f, b), junk, ctrl = bb.add( + (greater_than, f, b), junk_m, ctrl = bb.add( MultiAnd(cvs=(1, 1, 0)), ctrl=(greater_than, f, b) ) @@ -209,13 +207,13 @@ def build_composite_bloq( ctrl, m = bb.add(CNOT(), ctrl=ctrl, target=m) greater_than, f, b = bb.add( - MultiAnd(cvs=(1, 1, 0)).adjoint(), ctrl=(greater_than, f, b), junk=junk, target=ctrl + MultiAnd(cvs=(1, 1, 0)).adjoint(), ctrl=(greater_than, f, b), junk=junk_m, target=ctrl ) u, v = bb.add( LinearDepthHalfGreaterThan(QMontgomeryUInt(self.bitsize)).adjoint(), a=u, b=v, - c=junk, + c=junk_c, target=greater_than, ) return {'u': u, 'v': v, 'b': b, 'a': a, 'm': m, 'f': f} @@ -391,7 +389,7 @@ def build_composite_bloq( def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': return { - CNOT(): 4, + CNOT(): 3, XGate(): 2, ModDbl(QMontgomeryUInt(self.bitsize), self.mod): 1, CSwapApprox(self.bitsize): 2, @@ -475,7 +473,7 @@ def on_classical_vals( of `f` and `m`. """ assert m == 0 - is_terminal = f == 1 and v == 0 + is_terminal = int(f == 1 and v == 0) if f == 0: # When `f = 0` this means that the algorithm is nearly over and that we just need to # double the value of `r`. @@ -489,7 +487,8 @@ def on_classical_vals( f = 0 r = (r << 1) % self.mod else: - m = (u % 2 == 1) & (v % 2 == 0) + m = ((u % 2 == 1) & (v % 2 == 0)) or (u % 2 == 1 and v % 2 == 1 and u > v) + m = int(m) # Kaliski iteration as described in Fig7 of https://arxiv.org/pdf/2001.09580. swap = (u % 2 == 0 and v % 2 == 1) or (u % 2 == 1 and v % 2 == 1 and u > v) if swap: diff --git a/qualtran/bloqs/mod_arithmetic/mod_division_test.py b/qualtran/bloqs/mod_arithmetic/mod_division_test.py index 093f0908f..934a26967 100644 --- a/qualtran/bloqs/mod_arithmetic/mod_division_test.py +++ b/qualtran/bloqs/mod_arithmetic/mod_division_test.py @@ -19,6 +19,7 @@ import qualtran.testing as qlt_testing from qualtran import QMontgomeryUInt +from qualtran.bloqs.mod_arithmetic import mod_division from qualtran.bloqs.mod_arithmetic.mod_division import _kaliskimodinverse_example, KaliskiModInverse from qualtran.resource_counting import get_cost_value, QECGatesCost from qualtran.resource_counting.generalizers import ignore_alloc_free, ignore_split_join @@ -36,7 +37,7 @@ def test_kaliski_mod_inverse_classical_action(bitsize, mod): continue x_montgomery = dtype.uint_to_montgomery(x, mod) res = blq.call_classically(x=x_montgomery) - print(x, x_montgomery) + assert res == cblq.call_classically(x=x_montgomery) assert len(res) == 2 assert res[0] == dtype.montgomery_inverse(x_montgomery, mod) @@ -85,11 +86,11 @@ def test_kaliski_symbolic_cost(): # construction this is just $n-1$ (BitwiseNot -> Add(p+1)). # - The cost of an iteration in Litinski $13n$ since they ignore constants. # Our construction is exactly the same but we also count the constants - # which amout to $3$. for a total cost of $13n + 3$. + # which amout to $3$. for a total cost of $13n + 4$. # For example the cost of ModDbl is 2n+1. In their figure 8, they report # it as just $2n$. ModDbl gets executed within the 2n loop so its contribution # to the overal cost should be 4n^2 + 2n instead of just 4n^2. - assert total_toff == 26 * n**2 + 7 * n - 1 + assert total_toff == 26 * n**2 + 9 * n - 1 def test_kaliskimodinverse_example(bloq_autotester): @@ -99,3 +100,78 @@ def test_kaliskimodinverse_example(bloq_autotester): @pytest.mark.notebook def test_notebook(): qlt_testing.execute_notebook('mod_division') + + +def test_kaliski_iteration_decomposition(): + mod = 7 + bitsize = 5 + b = mod_division._KaliskiIteration(bitsize, mod) + cb = b.decompose_bloq() + for x in range(mod): + u = mod + v = x + r = 0 + s = 1 + f = 1 + + for _ in range(2 * bitsize): + inputs = {'u': u, 'v': v, 'r': r, 's': s, 'm': 0, 'f': f, 'is_terminal': 0} + res = b.call_classically(**inputs) + assert res == cb.call_classically(**inputs), f'{inputs=}' + u, v, r, s, _, f, _ = res # type: ignore + + qlt_testing.assert_valid_bloq_decomposition(b) + qlt_testing.assert_equivalent_bloq_counts(b, generalizer=(ignore_alloc_free, ignore_split_join)) + + +def test_kaliski_steps(): + bitsize = 5 + mod = 7 + steps = [ + mod_division._KaliskiIterationStep1(bitsize), + mod_division._KaliskiIterationStep2(bitsize), + mod_division._KaliskiIterationStep3(bitsize), + mod_division._KaliskiIterationStep4(bitsize), + mod_division._KaliskiIterationStep5(bitsize), + mod_division._KaliskiIterationStep6(bitsize, mod), + ] + csteps = [b.decompose_bloq() for b in steps] + + # check decomposition is valid. + for step in steps: + qlt_testing.assert_valid_bloq_decomposition(step) + qlt_testing.assert_equivalent_bloq_counts( + step, generalizer=(ignore_alloc_free, ignore_split_join) + ) + + # check that for all inputs all 2n iteration work when excuted directly on the 6 steps + # and their decompositions. + for x in range(mod): + u, v, r, s, f = mod, x, 0, 1, 1 + + for _ in range(2 * bitsize): + a = b = m = is_terminal = 0 + + res = steps[0].call_classically(v=v, m=m, f=f, is_terminal=is_terminal) + assert res == csteps[0].call_classically(v=v, m=m, f=f, is_terminal=is_terminal) + v, m, f, is_terminal = res # type: ignore + + res = steps[1].call_classically(u=u, v=v, b=b, a=a, m=m, f=f) + assert res == csteps[1].call_classically(u=u, v=v, b=b, a=a, m=m, f=f) + u, v, b, a, m, f = res # type: ignore + + res = steps[2].call_classically(u=u, v=v, b=b, a=a, m=m, f=f) + assert res == csteps[2].call_classically(u=u, v=v, b=b, a=a, m=m, f=f) + u, v, b, a, m, f = res # type: ignore + + res = steps[3].call_classically(u=u, v=v, r=r, s=s, a=a) + assert res == csteps[3].call_classically(u=u, v=v, r=r, s=s, a=a) + u, v, r, s, a = res # type: ignore + + res = steps[4].call_classically(u=u, v=v, r=r, s=s, b=b, f=f) + assert res == csteps[4].call_classically(u=u, v=v, r=r, s=s, b=b, f=f) + u, v, r, s, b, f = res # type: ignore + + res = steps[5].call_classically(u=u, v=v, r=r, s=s, b=b, a=a, m=m, f=f) + assert res == csteps[5].call_classically(u=u, v=v, r=r, s=s, b=b, a=a, m=m, f=f) + u, v, r, s, b, a, m, f = res # type: ignore From 6c8e772a31dc4e86865b449a8ceb3fca53332dae Mon Sep 17 00:00:00 2001 From: Frankie Papa Date: Mon, 18 Nov 2024 10:19:22 -0800 Subject: [PATCH 02/12] Fix symbolic call graphs for factoring phase estimates (#1497) * Add some symbolic decomp type errors to build call graphs * Fixed call graph for FindECCPrivateKey and added typechecking for symbolics * fix comparison bitsize error --- qualtran/bloqs/arithmetic/comparison.py | 8 ++ .../bloqs/arithmetic/controlled_addition.py | 5 + qualtran/bloqs/factoring/_factoring_shims.py | 39 +++++++- qualtran/bloqs/factoring/ecc/ec_add.py | 94 ++++++++++--------- qualtran/bloqs/factoring/ecc/ec_add_r.py | 10 +- .../factoring/ecc/ec_phase_estimate_r.py | 7 +- .../factoring/ecc/find_ecc_private_key.py | 6 +- qualtran/bloqs/mod_arithmetic/mod_division.py | 9 ++ .../mod_arithmetic/mod_multiplication.py | 4 + 9 files changed, 120 insertions(+), 62 deletions(-) diff --git a/qualtran/bloqs/arithmetic/comparison.py b/qualtran/bloqs/arithmetic/comparison.py index fff30d5fc..a47d95cff 100644 --- a/qualtran/bloqs/arithmetic/comparison.py +++ b/qualtran/bloqs/arithmetic/comparison.py @@ -988,6 +988,9 @@ def wire_symbol(self, reg: Optional[Register], idx: Tuple[int, ...] = tuple()) - def build_composite_bloq( self, bb: 'BloqBuilder', x: 'Soquet', y: 'Soquet', target: 'Soquet' ) -> Dict[str, 'SoquetT']: + if is_symbolic(self.bitsize): + raise DecomposeTypeError(f"Cannot decompose {self} with symbolic `bitsize`.") + cvs: Union[list[int], HasLength] if isinstance(self.bitsize, int): cvs = [0] * self.bitsize @@ -1151,6 +1154,8 @@ def wire_symbol( def build_composite_bloq( self, bb: 'BloqBuilder', ctrl: 'Soquet', a: 'Soquet', b: 'Soquet', target: 'Soquet' ) -> Dict[str, 'SoquetT']: + if is_symbolic(self.dtype.bitsize): + raise DecomposeTypeError(f"Cannot decompose {self} with symbolic `bitsize`.") if isinstance(self.dtype, QInt): a = bb.add(SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)), x=a) @@ -1360,6 +1365,9 @@ def build_composite_bloq( c: Optional['Soquet'] = None, target: Optional['Soquet'] = None, ) -> Dict[str, 'SoquetT']: + if is_symbolic(self.dtype.bitsize): + raise DecomposeTypeError(f"Cannot decompose {self} with symbolic `bitsize`.") + if self.uncompute: # Uncompute assert c is not None diff --git a/qualtran/bloqs/arithmetic/controlled_addition.py b/qualtran/bloqs/arithmetic/controlled_addition.py index cfe4bcc5d..dc6751d23 100644 --- a/qualtran/bloqs/arithmetic/controlled_addition.py +++ b/qualtran/bloqs/arithmetic/controlled_addition.py @@ -23,6 +23,7 @@ bloq_example, BloqBuilder, BloqDocSpec, + DecomposeTypeError, QBit, QInt, QUInt, @@ -37,6 +38,7 @@ from qualtran.bloqs.mcmt.and_bloq import And from qualtran.resource_counting.generalizers import ignore_split_join from qualtran.simulation.classical_sim import add_ints +from qualtran.symbolics.types import is_symbolic if TYPE_CHECKING: import quimb.tensor as qtn @@ -134,6 +136,9 @@ def wire_symbol(self, soq: 'Soquet') -> 'WireSymbol': def build_composite_bloq( self, bb: 'BloqBuilder', ctrl: 'Soquet', a: 'Soquet', b: 'Soquet' ) -> Dict[str, 'SoquetT']: + if is_symbolic(self.a_dtype.bitsize, self.b_dtype.bitsize): + raise DecomposeTypeError(f"Cannot decompose {self} with symbolic `bitsize`.") + a_arr = bb.split(a) ctrl_q = bb.split(ctrl)[0] ancilla_arr = [] diff --git a/qualtran/bloqs/factoring/_factoring_shims.py b/qualtran/bloqs/factoring/_factoring_shims.py index 896e103b8..1bc32a5c6 100644 --- a/qualtran/bloqs/factoring/_factoring_shims.py +++ b/qualtran/bloqs/factoring/_factoring_shims.py @@ -13,13 +13,29 @@ # limitations under the License. from functools import cached_property -from typing import Optional, Tuple +from typing import Dict, Optional, Tuple +import numpy as np +import sympy from attrs import frozen -from qualtran import Bloq, CompositeBloq, DecomposeTypeError, QBit, Register, Side, Signature +from qualtran import ( + Bloq, + BloqBuilder, + DecomposeTypeError, + QBit, + QUInt, + Register, + Side, + Signature, + Soquet, + SoquetT, +) +from qualtran.bloqs.basic_gates._shims import Measure +from qualtran.bloqs.qft import QFTTextBook from qualtran.drawing import RarrowTextBox, Text, WireSymbol -from qualtran.symbolics import SymbolicInt +from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator +from qualtran.symbolics.types import SymbolicInt @frozen @@ -30,8 +46,21 @@ class MeasureQFT(Bloq): def signature(self) -> 'Signature': return Signature([Register('x', QBit(), shape=(self.n,), side=Side.LEFT)]) - def decompose_bloq(self) -> 'CompositeBloq': - raise DecomposeTypeError('MeasureQFT is a placeholder, atomic bloq.') + def build_composite_bloq(self, bb: 'BloqBuilder', x: Soquet) -> Dict[str, 'SoquetT']: + if isinstance(self.n, sympy.Expr): + raise DecomposeTypeError("Cannot decompose symbolic `n`.") + + x = bb.join(np.array(x), dtype=QUInt(self.n)) + x = bb.add(QFTTextBook(self.n), q=x) + x = bb.split(x) + + for i in range(self.n): + bb.add(Measure(), q=x[i]) + + return {} + + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': + return {QFTTextBook(self.n): 1, Measure(): self.n} def wire_symbol( self, reg: Optional['Register'], idx: Tuple[int, ...] = tuple() diff --git a/qualtran/bloqs/factoring/ecc/ec_add.py b/qualtran/bloqs/factoring/ecc/ec_add.py index 572e47673..f764c70cb 100644 --- a/qualtran/bloqs/factoring/ecc/ec_add.py +++ b/qualtran/bloqs/factoring/ecc/ec_add.py @@ -49,7 +49,7 @@ ) from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator from qualtran.simulation.classical_sim import ClassicalValT -from qualtran.symbolics.types import HasLength, is_symbolic +from qualtran.symbolics.types import HasLength, is_symbolic, SymbolicInt from .ec_point import ECPoint @@ -80,8 +80,8 @@ class _ECAddStepOne(Bloq): Fig 10. """ - n: int - mod: int + n: 'SymbolicInt' + mod: 'SymbolicInt' @cached_property def signature(self) -> 'Signature': @@ -214,9 +214,9 @@ class _ECAddStepTwo(Bloq): Fig 10. """ - n: int - mod: int - window_size: int = 1 + n: 'SymbolicInt' + mod: 'SymbolicInt' + window_size: 'SymbolicInt' = 1 @cached_property def signature(self) -> 'Signature': @@ -251,7 +251,9 @@ def on_classical_vals( f1 = 0 else: lam = QMontgomeryUInt(self.n).montgomery_product( - int(y), QMontgomeryUInt(self.n).montgomery_inverse(int(x), self.mod), self.mod + int(y), + QMontgomeryUInt(self.n).montgomery_inverse(int(x), int(self.mod)), + int(self.mod), ) # TODO(https://github.com/quantumlib/Qualtran/issues/1461): Fix bug in circuit # which flips f1 when lam and lam_r are equal. @@ -299,7 +301,7 @@ def build_composite_bloq( # If ctrl = 1 and x != a: lam = (y - b) / (x - a) % p. z4_split = bb.split(z4) lam_split = bb.split(lam) - for i in range(self.n): + for i in range(int(self.n)): ctrls = [f1, ctrl, z4_split[i]] ctrls, lam_split[i] = bb.add( MultiControlX(cvs=[0, 1, 1]), controls=ctrls, target=lam_split[i] @@ -311,7 +313,7 @@ def build_composite_bloq( # If ctrl = 1 and x = a: lam = lam_r. lam_r_split = bb.split(lam_r) - for i in range(self.n): + for i in range(int(self.n)): ctrls = [f1, ctrl, lam_r_split[i]] ctrls, lam_split[i] = bb.add( MultiControlX(cvs=[1, 1, 1]), controls=ctrls, target=lam_split[i] @@ -383,9 +385,9 @@ class _ECAddStepThree(Bloq): Fig 10. """ - n: int - mod: int - window_size: int = 1 + n: 'SymbolicInt' + mod: 'SymbolicInt' + window_size: 'SymbolicInt' = 1 @cached_property def signature(self) -> 'Signature': @@ -455,7 +457,7 @@ def build_composite_bloq( z1 = bb.add(IntState(bitsize=self.n, val=0)) a_split = bb.split(a) z1_split = bb.split(z1) - for i in range(self.n): + for i in range(int(self.n)): a_split[i], z1_split[i] = bb.add(CNOT(), ctrl=a_split[i], target=z1_split[i]) a = bb.join(a_split, QMontgomeryUInt(self.n)) z1 = bb.join(z1_split, QMontgomeryUInt(self.n)) @@ -472,7 +474,7 @@ def build_composite_bloq( z1 = bb.add(ModDbl(QMontgomeryUInt(self.n), mod=self.mod).adjoint(), x=z1) a_split = bb.split(a) z1_split = bb.split(z1) - for i in range(self.n): + for i in range(int(self.n)): a_split[i], z1_split[i] = bb.add(CNOT(), ctrl=a_split[i], target=z1_split[i]) a = bb.join(a_split, QMontgomeryUInt(self.n)) z1 = bb.join(z1_split, QMontgomeryUInt(self.n)) @@ -520,9 +522,9 @@ class _ECAddStepFour(Bloq): Fig 10. """ - n: int - mod: int - window_size: int = 1 + n: 'SymbolicInt' + mod: 'SymbolicInt' + window_size: 'SymbolicInt' = 1 @cached_property def signature(self) -> 'Signature': @@ -538,10 +540,10 @@ def on_classical_vals( self, x: 'ClassicalValT', y: 'ClassicalValT', lam: 'ClassicalValT' ) -> Dict[str, 'ClassicalValT']: x = ( - x - QMontgomeryUInt(self.n).montgomery_product(int(lam), int(lam), self.mod) + x - QMontgomeryUInt(self.n).montgomery_product(int(lam), int(lam), int(self.mod)) ) % self.mod if lam > 0: - y = QMontgomeryUInt(self.n).montgomery_product(int(x), int(lam), self.mod) + y = QMontgomeryUInt(self.n).montgomery_product(int(x), int(lam), int(self.mod)) return {'x': x, 'y': y, 'lam': lam} def build_composite_bloq( @@ -554,7 +556,7 @@ def build_composite_bloq( z4 = bb.add(IntState(bitsize=self.n, val=0)) lam_split = bb.split(lam) z4_split = bb.split(z4) - for i in range(self.n): + for i in range(int(self.n)): lam_split[i], z4_split[i] = bb.add(CNOT(), ctrl=lam_split[i], target=z4_split[i]) lam = bb.join(lam_split, QMontgomeryUInt(self.n)) z4 = bb.join(z4_split, QMontgomeryUInt(self.n)) @@ -584,7 +586,7 @@ def build_composite_bloq( ) lam_split = bb.split(lam) z4_split = bb.split(z4) - for i in range(self.n): + for i in range(int(self.n)): lam_split[i], z4_split[i] = bb.add(CNOT(), ctrl=lam_split[i], target=z4_split[i]) lam = bb.join(lam_split, QMontgomeryUInt(self.n)) z4 = bb.join(z4_split, QMontgomeryUInt(self.n)) @@ -602,7 +604,7 @@ def build_composite_bloq( # y = y_r + b % p. z3_split = bb.split(z3) y_split = bb.split(y) - for i in range(self.n): + for i in range(int(self.n)): z3_split[i], y_split[i] = bb.add(CNOT(), ctrl=z3_split[i], target=y_split[i]) z3 = bb.join(z3_split, QMontgomeryUInt(self.n)) y = bb.join(y_split, QMontgomeryUInt(self.n)) @@ -659,9 +661,9 @@ class _ECAddStepFive(Bloq): Fig 10. """ - n: int - mod: int - window_size: int = 1 + n: 'SymbolicInt' + mod: 'SymbolicInt' + window_size: 'SymbolicInt' = 1 @cached_property def signature(self) -> 'Signature': @@ -720,7 +722,7 @@ def build_composite_bloq( # If ctrl: lam = 0. z4_split = bb.split(z4) lam_split = bb.split(lam) - for i in range(self.n): + for i in range(int(self.n)): ctrls = [ctrl, z4_split[i]] ctrls, lam_split[i] = bb.add( MultiControlX(cvs=[1, 1]), controls=ctrls, target=lam_split[i] @@ -801,8 +803,8 @@ class _ECAddStepSix(Bloq): Fig 10. """ - n: int - mod: int + n: 'SymbolicInt' + mod: 'SymbolicInt' @cached_property def signature(self) -> 'Signature': @@ -866,7 +868,7 @@ def build_composite_bloq( # Set (x, y) to (a, b) if f4 is set. a_split = bb.split(a) x_split = bb.split(x) - for i in range(self.n): + for i in range(int(self.n)): toff_ctrl = [f4, a_split[i]] toff_ctrl, x_split[i] = bb.add(Toffoli(), ctrl=toff_ctrl, target=x_split[i]) f4 = toff_ctrl[0] @@ -875,7 +877,7 @@ def build_composite_bloq( x = bb.join(x_split, QMontgomeryUInt(self.n)) b_split = bb.split(b) y_split = bb.split(y) - for i in range(self.n): + for i in range(int(self.n)): toff_ctrl = [f4, b_split[i]] toff_ctrl, y_split[i] = bb.add(Toffoli(), ctrl=toff_ctrl, target=y_split[i]) f4 = toff_ctrl[0] @@ -888,11 +890,11 @@ def build_composite_bloq( xy = bb.join(np.concatenate([bb.split(x), bb.split(y)]), dtype=QMontgomeryUInt(2 * self.n)) ab, xy, f4 = bb.add(Equals(QMontgomeryUInt(2 * self.n)), x=ab, y=xy, target=f4) ab_split = bb.split(ab) - a = bb.join(ab_split[: self.n], dtype=QMontgomeryUInt(self.n)) - b = bb.join(ab_split[self.n :], dtype=QMontgomeryUInt(self.n)) + a = bb.join(ab_split[: int(self.n)], dtype=QMontgomeryUInt(self.n)) + b = bb.join(ab_split[int(self.n) :], dtype=QMontgomeryUInt(self.n)) xy_split = bb.split(xy) - x = bb.join(xy_split[: self.n], dtype=QMontgomeryUInt(self.n)) - y = bb.join(xy_split[self.n :], dtype=QMontgomeryUInt(self.n)) + x = bb.join(xy_split[: int(self.n)], dtype=QMontgomeryUInt(self.n)) + y = bb.join(xy_split[int(self.n) :], dtype=QMontgomeryUInt(self.n)) # Unset f3 if (a, b) = (0, 0). ab_arr = np.concatenate([bb.split(a), bb.split(b)]) @@ -1000,9 +1002,9 @@ class ECAdd(Bloq): Litinski. 2023. Fig 5. """ - n: int - mod: int - window_size: int = 1 + n: 'SymbolicInt' + mod: 'SymbolicInt' + window_size: 'SymbolicInt' = 1 @cached_property def signature(self) -> 'Signature': @@ -1070,20 +1072,20 @@ def build_composite_bloq( def on_classical_vals(self, a, b, x, y, lam_r) -> Dict[str, Union['ClassicalValT', sympy.Expr]]: curve_a = ( - QMontgomeryUInt(self.n).montgomery_to_uint(lam_r, self.mod) + QMontgomeryUInt(self.n).montgomery_to_uint(lam_r, int(self.mod)) * 2 - * QMontgomeryUInt(self.n).montgomery_to_uint(b, self.mod) - - (3 * QMontgomeryUInt(self.n).montgomery_to_uint(a, self.mod) ** 2) + * QMontgomeryUInt(self.n).montgomery_to_uint(b, int(self.mod)) + - (3 * QMontgomeryUInt(self.n).montgomery_to_uint(a, int(self.mod)) ** 2) ) % self.mod p1 = ECPoint( - QMontgomeryUInt(self.n).montgomery_to_uint(a, self.mod), - QMontgomeryUInt(self.n).montgomery_to_uint(b, self.mod), + QMontgomeryUInt(self.n).montgomery_to_uint(a, int(self.mod)), + QMontgomeryUInt(self.n).montgomery_to_uint(b, int(self.mod)), mod=self.mod, curve_a=curve_a, ) p2 = ECPoint( - QMontgomeryUInt(self.n).montgomery_to_uint(x, self.mod), - QMontgomeryUInt(self.n).montgomery_to_uint(y, self.mod), + QMontgomeryUInt(self.n).montgomery_to_uint(x, int(self.mod)), + QMontgomeryUInt(self.n).montgomery_to_uint(y, int(self.mod)), mod=self.mod, curve_a=curve_a, ) @@ -1091,8 +1093,8 @@ def on_classical_vals(self, a, b, x, y, lam_r) -> Dict[str, Union['ClassicalValT return { 'a': a, 'b': b, - 'x': QMontgomeryUInt(self.n).uint_to_montgomery(result.x, self.mod), - 'y': QMontgomeryUInt(self.n).uint_to_montgomery(result.y, self.mod), + 'x': QMontgomeryUInt(self.n).uint_to_montgomery(result.x, int(self.mod)), + 'y': QMontgomeryUInt(self.n).uint_to_montgomery(result.y, int(self.mod)), 'lam_r': lam_r, } diff --git a/qualtran/bloqs/factoring/ecc/ec_add_r.py b/qualtran/bloqs/factoring/ecc/ec_add_r.py index 9ced3a9f1..6bf020659 100644 --- a/qualtran/bloqs/factoring/ecc/ec_add_r.py +++ b/qualtran/bloqs/factoring/ecc/ec_add_r.py @@ -36,7 +36,7 @@ from qualtran.drawing import Circle, Text, TextBox, WireSymbol from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator from qualtran.simulation.classical_sim import ClassicalValT -from qualtran.symbolics import is_symbolic, Shaped +from qualtran.symbolics import is_symbolic, Shaped, SymbolicInt from .ec_add import ECAdd from .ec_point import ECPoint @@ -75,7 +75,7 @@ class ECAddR(Bloq): """ - n: int + n: 'SymbolicInt' R: ECPoint @cached_property @@ -144,10 +144,10 @@ class ECWindowAddR(Bloq): Litinski. 2013. Section 1, eq. (3) and (4). """ - n: int + n: 'SymbolicInt' R: ECPoint - add_window_size: int - mul_window_size: int = 1 + add_window_size: 'SymbolicInt' + mul_window_size: 'SymbolicInt' = 1 @cached_property def signature(self) -> 'Signature': diff --git a/qualtran/bloqs/factoring/ecc/ec_phase_estimate_r.py b/qualtran/bloqs/factoring/ecc/ec_phase_estimate_r.py index d56400d20..eb991c19a 100644 --- a/qualtran/bloqs/factoring/ecc/ec_phase_estimate_r.py +++ b/qualtran/bloqs/factoring/ecc/ec_phase_estimate_r.py @@ -34,6 +34,7 @@ ) from qualtran.bloqs.basic_gates import PlusState from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator +from qualtran.symbolics.types import SymbolicInt from .._factoring_shims import MeasureQFT from .ec_add_r import ECAddR, ECWindowAddR @@ -58,10 +59,10 @@ class ECPhaseEstimateR(Bloq): mul_window_size: The number of bits in the modular multiplication window. """ - n: int + n: 'SymbolicInt' point: ECPoint - add_window_size: int = 1 - mul_window_size: int = 1 + add_window_size: 'SymbolicInt' = 1 + mul_window_size: 'SymbolicInt' = 1 @cached_property def signature(self) -> 'Signature': diff --git a/qualtran/bloqs/factoring/ecc/find_ecc_private_key.py b/qualtran/bloqs/factoring/ecc/find_ecc_private_key.py index 05785939f..f6129af80 100644 --- a/qualtran/bloqs/factoring/ecc/find_ecc_private_key.py +++ b/qualtran/bloqs/factoring/ecc/find_ecc_private_key.py @@ -75,11 +75,11 @@ class FindECCPrivateKey(Bloq): Litinski. 2023. Figure 4 (a). """ - n: int + n: 'SymbolicInt' base_point: ECPoint public_key: ECPoint - add_window_size: int = 1 - mul_window_size: int = 1 + add_window_size: 'SymbolicInt' = 1 + mul_window_size: 'SymbolicInt' = 1 @cached_property def signature(self) -> 'Signature': diff --git a/qualtran/bloqs/mod_arithmetic/mod_division.py b/qualtran/bloqs/mod_arithmetic/mod_division.py index 06f643525..d3472103b 100644 --- a/qualtran/bloqs/mod_arithmetic/mod_division.py +++ b/qualtran/bloqs/mod_arithmetic/mod_division.py @@ -136,6 +136,9 @@ def on_classical_vals( def build_composite_bloq( self, bb: 'BloqBuilder', u: Soquet, v: Soquet, b: Soquet, a: Soquet, m: Soquet, f: Soquet ) -> Dict[str, 'SoquetT']: + if is_symbolic(self.bitsize): + raise DecomposeTypeError(f"Cannot decompose {self} with symbolic `bitsize`.") + u_arr = bb.split(u) v_arr = bb.split(v) @@ -542,6 +545,9 @@ def build_composite_bloq( f: Soquet, terminal_condition: Soquet, ) -> Dict[str, 'SoquetT']: + if is_symbolic(self.bitsize): + raise DecomposeTypeError(f"Cannot decompose {self} with symbolic `bitsize`.") + f = bb.add(XGate(), q=f) u = bb.add(XorK(QMontgomeryUInt(self.bitsize), self.mod), x=u) s = bb.add(XorK(QMontgomeryUInt(self.bitsize), 1), x=s) @@ -664,6 +670,9 @@ def signature(self) -> 'Signature': def build_composite_bloq( self, bb: 'BloqBuilder', x: Soquet, junk: Optional[Soquet] = None ) -> Dict[str, 'SoquetT']: + if is_symbolic(self.bitsize): + raise DecomposeTypeError(f"Cannot decompose {self} with symbolic `bitsize`.") + u = bb.allocate(self.bitsize, QMontgomeryUInt(self.bitsize)) r = bb.allocate(self.bitsize, QMontgomeryUInt(self.bitsize)) s = bb.allocate(self.bitsize, QMontgomeryUInt(self.bitsize)) diff --git a/qualtran/bloqs/mod_arithmetic/mod_multiplication.py b/qualtran/bloqs/mod_arithmetic/mod_multiplication.py index e02e60712..0e8f3ca0b 100644 --- a/qualtran/bloqs/mod_arithmetic/mod_multiplication.py +++ b/qualtran/bloqs/mod_arithmetic/mod_multiplication.py @@ -29,6 +29,7 @@ BloqBuilder, BloqDocSpec, DecomposeNotImplementedError, + DecomposeTypeError, QBit, QInt, QMontgomeryUInt, @@ -91,6 +92,9 @@ def on_classical_vals(self, x: 'ClassicalValT') -> Dict[str, 'ClassicalValT']: return {'x': x} def build_composite_bloq(self, bb: 'BloqBuilder', x: Soquet) -> Dict[str, 'SoquetT']: + if is_symbolic(self.dtype.bitsize): + raise DecomposeTypeError(f'symbolic decomposition is not supported for {self}') + # Allocate ancilla bits for sign and double. lower_bit = bb.allocate(n=1) sign = bb.allocate(n=1) From d92730e121f2a3540d0c595654a0df9c54bac53d Mon Sep 17 00:00:00 2001 From: Anurudh Peduri <7265746+anurudhp@users.noreply.github.com> Date: Wed, 27 Nov 2024 09:31:38 -0800 Subject: [PATCH 03/12] Deprecate `MultiControlPauli` (#1492) * make `MultiControlPauli` a hidden class, always use `MultiControlX` or `MultiControlZ` instead * add `DeprecationWarning` instead of renaming bloq --------- Co-authored-by: Tanuj Khattar --- qualtran/bloqs/mcmt/__init__.py | 2 +- .../multi_control_multi_target_pauli.ipynb | 106 +++++------ qualtran/bloqs/mcmt/multi_control_pauli.py | 172 ++++++++++-------- .../bloqs/mcmt/multi_control_pauli_test.py | 21 ++- .../qubitization_walk_operator_test.py | 91 ++++----- .../reflection_using_prepare_test.py | 120 ++++++------ 6 files changed, 265 insertions(+), 247 deletions(-) diff --git a/qualtran/bloqs/mcmt/__init__.py b/qualtran/bloqs/mcmt/__init__.py index a3b2b0073..23b8caf45 100644 --- a/qualtran/bloqs/mcmt/__init__.py +++ b/qualtran/bloqs/mcmt/__init__.py @@ -15,5 +15,5 @@ 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_pauli import MultiControlPauli, MultiControlX, MultiControlZ +from qualtran.bloqs.mcmt.multi_control_pauli import MultiControlX, MultiControlZ from qualtran.bloqs.mcmt.multi_target_cnot import MultiTargetCNOT diff --git a/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.ipynb b/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.ipynb index 6fb6eeeef..65993bfff 100644 --- a/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.ipynb +++ b/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "5c75973b", + "id": "8533d106", "metadata": { "cq.autogen": "title_cell" }, @@ -13,7 +13,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c0d3d2b2", + "id": "b5831fa4", "metadata": { "cq.autogen": "top_imports" }, @@ -30,7 +30,7 @@ }, { "cell_type": "markdown", - "id": "ac237fd0", + "id": "b85b5ac2", "metadata": { "cq.autogen": "MultiTargetCNOT.bloq_doc.md" }, @@ -48,7 +48,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a9e53831", + "id": "387c77c3", "metadata": { "cq.autogen": "MultiTargetCNOT.bloq_doc.py" }, @@ -59,7 +59,7 @@ }, { "cell_type": "markdown", - "id": "70036306", + "id": "cb66ffb6", "metadata": { "cq.autogen": "MultiTargetCNOT.example_instances.md" }, @@ -70,7 +70,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40a9ec07", + "id": "e4d9154a", "metadata": { "cq.autogen": "MultiTargetCNOT.c_multi_not_symb" }, @@ -83,7 +83,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ecddf931", + "id": "ca47c43a", "metadata": { "cq.autogen": "MultiTargetCNOT.c_multi_not" }, @@ -94,7 +94,7 @@ }, { "cell_type": "markdown", - "id": "251ca68a", + "id": "a44ef0b1", "metadata": { "cq.autogen": "MultiTargetCNOT.graphical_signature.md" }, @@ -105,7 +105,7 @@ { "cell_type": "code", "execution_count": null, - "id": "bff75a03", + "id": "baafd2bf", "metadata": { "cq.autogen": "MultiTargetCNOT.graphical_signature.py" }, @@ -118,7 +118,7 @@ }, { "cell_type": "markdown", - "id": "bd425677", + "id": "60bc96ef", "metadata": { "cq.autogen": "MultiTargetCNOT.call_graph.md" }, @@ -129,7 +129,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a0082f8f", + "id": "59e8162e", "metadata": { "cq.autogen": "MultiTargetCNOT.call_graph.py" }, @@ -141,57 +141,46 @@ "show_counts_sigma(c_multi_not_symb_sigma)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "5457e35b", - "metadata": {}, - "outputs": [], - "source": [ - "from qualtran.drawing import get_musical_score_data, draw_musical_score\n", - "\n", - "draw_musical_score(get_musical_score_data(c_multi_not.decompose_bloq()))" - ] - }, { "cell_type": "markdown", - "id": "aba024ec", + "id": "2de64cfb", "metadata": { - "cq.autogen": "MultiControlPauli.bloq_doc.md" + "cq.autogen": "MultiControlX.bloq_doc.md" }, "source": [ - "## `MultiControlPauli`\n", - "Implements multi-control, single-target C^{n}P gate.\n", + "## `MultiControlX`\n", + "Implements multi-control, single-target X gate.\n", "\n", - "Implements $C^{n}P = (1 - |1^{n}><1^{n}|) I + |1^{n}><1^{n}| P^{n}$ using $n-1$\n", - "clean ancillas using a multi-controlled `AND` gate. Uses the Toffoli ladder\n", - "construction described in \"n−2 Ancilla Bits\" section of Ref[1] but uses an\n", - "$\\text{AND} / \\text{AND}^\\dagger$ ladder instead for computing / uncomputing\n", - "using clean ancillas instead of the Toffoli ladder. The measurement based\n", - "uncomputation of $\\text{AND}$ does not consume any magic states and thus has\n", - "better constant factors.\n", + "Reduces multiple controls to a single control using an `And` ladder.\n", + "See class `ControlledViaAnd` for details on construction.\n", "\n", - "#### References\n", - " - [Constructing Large Controlled Nots](https://algassert.com/circuits/2015/06/05/Constructing-Large-Controlled-Nots.html). \n" + "Alternatively, one can directly use `XGate().controlled(CtrlSpec(cvs=cvs))`\n", + "\n", + "#### Parameters\n", + " - `cvs`: a tuple of `n` control bits, or a `HasLength(n)` to control by `n` 1s. \n", + "\n", + "#### Registers\n", + " - `controls`: control register of type `QBit` and shape `(n,)`.\n", + " - `target`: single qubit target register.\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "bf4f6c5a", + "id": "9b43be2a", "metadata": { - "cq.autogen": "MultiControlPauli.bloq_doc.py" + "cq.autogen": "MultiControlX.bloq_doc.py" }, "outputs": [], "source": [ - "from qualtran.bloqs.mcmt import MultiControlPauli" + "from qualtran.bloqs.mcmt import MultiControlX" ] }, { "cell_type": "markdown", - "id": "a3deef58", + "id": "f9fbb578", "metadata": { - "cq.autogen": "MultiControlPauli.example_instances.md" + "cq.autogen": "MultiControlX.example_instances.md" }, "source": [ "### Example Instances" @@ -200,20 +189,20 @@ { "cell_type": "code", "execution_count": null, - "id": "610dfdf5", + "id": "050b0f8d", "metadata": { - "cq.autogen": "MultiControlPauli.ccpauli" + "cq.autogen": "MultiControlX.ccpauli" }, "outputs": [], "source": [ - "ccpauli = MultiControlPauli(cvs=(1, 0, 1, 0, 1), target_gate=cirq.X)" + "ccpauli = MultiControlX(cvs=(1, 0, 1, 0, 1))" ] }, { "cell_type": "markdown", - "id": "29045880", + "id": "ccf7ef61", "metadata": { - "cq.autogen": "MultiControlPauli.graphical_signature.md" + "cq.autogen": "MultiControlX.graphical_signature.md" }, "source": [ "#### Graphical Signature" @@ -222,9 +211,9 @@ { "cell_type": "code", "execution_count": null, - "id": "df830018", + "id": "4fb51559", "metadata": { - "cq.autogen": "MultiControlPauli.graphical_signature.py" + "cq.autogen": "MultiControlX.graphical_signature.py" }, "outputs": [], "source": [ @@ -235,9 +224,9 @@ }, { "cell_type": "markdown", - "id": "6849b86b", + "id": "358ff8ae", "metadata": { - "cq.autogen": "MultiControlPauli.call_graph.md" + "cq.autogen": "MultiControlX.call_graph.md" }, "source": [ "### Call Graph" @@ -246,9 +235,9 @@ { "cell_type": "code", "execution_count": null, - "id": "dd1f8a53", + "id": "216d9bdf", "metadata": { - "cq.autogen": "MultiControlPauli.call_graph.py" + "cq.autogen": "MultiControlX.call_graph.py" }, "outputs": [], "source": [ @@ -261,21 +250,12 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "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.10.9" + "name": "python" } }, "nbformat": 4, diff --git a/qualtran/bloqs/mcmt/multi_control_pauli.py b/qualtran/bloqs/mcmt/multi_control_pauli.py index 27300e739..79616f971 100644 --- a/qualtran/bloqs/mcmt/multi_control_pauli.py +++ b/qualtran/bloqs/mcmt/multi_control_pauli.py @@ -11,14 +11,14 @@ # 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 functools import cached_property -from typing import Dict, Tuple, TYPE_CHECKING, Union +from typing import Dict, Optional, Tuple, TYPE_CHECKING, Union -import cirq import numpy as np import sympy from attrs import field, frozen +from numpy.typing import NDArray from qualtran import ( Bloq, @@ -33,14 +33,16 @@ Signature, SoquetT, ) -from qualtran.bloqs.basic_gates import XGate -from qualtran.bloqs.mcmt.and_bloq import _to_tuple_or_has_length, is_symbolic +from qualtran.bloqs.basic_gates import XGate, ZGate +from qualtran.bloqs.mcmt.and_bloq import _to_tuple_or_has_length from qualtran.bloqs.mcmt.controlled_via_and import ControlledViaAnd -from qualtran.symbolics import HasLength, SymbolicInt +from qualtran.drawing import Circle, TextBox, WireSymbol +from qualtran.symbolics import HasLength, is_symbolic, Shaped, slen, SymbolicInt if TYPE_CHECKING: + import cirq + from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator - from qualtran.simulation.classical_sim import ClassicalValT @frozen @@ -60,7 +62,16 @@ class MultiControlPauli(GateWithRegisters): """ cvs: Union[HasLength, Tuple[int, ...]] = field(converter=_to_tuple_or_has_length) - target_gate: cirq.Pauli + target_bloq: Bloq + + def __attrs_post_init__(self): + warnings.warn( + "`MultiControlPauli` is deprecated. Use `bloq.controlled(...)` which now defaults" + "to reducing controls using an `And` ladder." + "For the same signature as `MultiControlPauli(cvs, target_bloq)`," + "use `target_bloq.controlled(CtrlSpec(cvs=cvs))`.", + DeprecationWarning, + ) @cached_property def signature(self) -> 'Signature': @@ -72,7 +83,7 @@ def signature(self) -> 'Signature': @property def n_ctrls(self) -> SymbolicInt: - return self.cvs.n if isinstance(self.cvs, HasLength) else len(self.cvs) + return slen(self.cvs) @property def concrete_cvs(self) -> Tuple[int, ...]: @@ -80,15 +91,12 @@ def concrete_cvs(self) -> Tuple[int, ...]: raise ValueError(f"{self.cvs} is symbolic") return self.cvs - @property - def target_bloq(self) -> Bloq: - from qualtran.cirq_interop import cirq_gate_to_bloq - - return cirq_gate_to_bloq(self.target_gate) - @property def _multi_ctrl_bloq(self) -> ControlledViaAnd: - ctrl_spec = CtrlSpec(cvs=(np.array(self.concrete_cvs),)) + cvs: Union[NDArray[np.integer], Shaped] = ( + Shaped((self.n_ctrls,)) if is_symbolic(self.n_ctrls) else np.array(self.concrete_cvs) + ) + ctrl_spec = CtrlSpec(cvs=(cvs,)) return ControlledViaAnd(self.target_bloq, ctrl_spec) def build_composite_bloq(self, bb: 'BloqBuilder', **soqs: 'SoquetT') -> Dict[str, 'SoquetT']: @@ -109,52 +117,46 @@ def build_composite_bloq(self, bb: 'BloqBuilder', **soqs: 'SoquetT') -> Dict[str return {'controls': out_soqs[ctrl_reg_name], 'target': out_soqs[target_reg_name]} + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': + if self.n_ctrls == 0: + return {self.target_bloq: 1} + return {self._multi_ctrl_bloq: 1} + def __str__(self) -> str: n = self.n_ctrls ctrl = f'C^{n}' if is_symbolic(n) or n > 2 else ['', 'C', 'CC'][int(n)] - return f'{ctrl}{self.target_gate!s}' + return f'{ctrl}{self.target_bloq!s}' - def _circuit_diagram_info_(self, _) -> cirq.CircuitDiagramInfo: - wire_symbols = ["@" if b else "@(0)" for b in self.concrete_cvs] - wire_symbols += [str(self.target_gate)] - return cirq.CircuitDiagramInfo(wire_symbols=wire_symbols) + def wire_symbol(self, reg: Optional[Register], idx: Tuple[int, ...] = tuple()) -> 'WireSymbol': + if reg is None: + return TextBox(str(self)) + if reg.name == 'target': + (target_reg,) = tuple(self.target_bloq.signature) + return self.target_bloq.wire_symbol(target_reg) - def on_classical_vals(self, **vals: 'ClassicalValT') -> Dict[str, 'ClassicalValT']: - controls, target = vals.get('controls', np.array([])), vals.get('target', 0) - if self.target_gate not in (cirq.X, XGate()): - raise NotImplementedError(f"{self} is not classically simulatable.") + (i,) = idx + cv = self.concrete_cvs[i] + return Circle(filled=(cv == 1)) - if np.all(self.concrete_cvs == controls): - target = (target + 1) % 2 + def _circuit_diagram_info_(self, args) -> 'cirq.CircuitDiagramInfo': + from qualtran.cirq_interop._bloq_to_cirq import _wire_symbol_to_cirq_diagram_info - return {'controls': controls, 'target': target} + return _wire_symbol_to_cirq_diagram_info(self, args) - def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': - if self.n_ctrls == 0: - return {self.target_bloq: 1} - - if is_symbolic(self.cvs): - # TODO CtrlSpec does not support symbolic cvs yet. - # remove this case once support is added. - # https://github.com/quantumlib/Qualtran/issues/1168 - from qualtran.bloqs.mcmt.and_bloq import And, MultiAnd - - if self.n_ctrls == 1: - return {self.target_bloq.controlled(): 1} - elif self.n_ctrls == 2: - and_bloq = And(ssa.new_symbol('cv1'), ssa.new_symbol('cv2')) - return {self.target_bloq.controlled(): 1, and_bloq: 1, and_bloq.adjoint(): 1} - else: - m_and_bloq = MultiAnd(self.cvs) - return {self.target_bloq.controlled(): 1, m_and_bloq: 1, m_and_bloq.adjoint(): 1} + def _apply_unitary_(self, args: 'cirq.ApplyUnitaryArgs') -> np.ndarray: + import cirq - return {self._multi_ctrl_bloq: 1} + from qualtran.cirq_interop import BloqAsCirqGate - def _apply_unitary_(self, args: 'cirq.ApplyUnitaryArgs') -> np.ndarray: + target_gate = ( + self.target_bloq + if isinstance(self.target_bloq, cirq.Gate) + else BloqAsCirqGate(self.target_bloq) + ) cpauli = ( - self.target_gate.controlled(control_values=self.concrete_cvs) + target_gate.controlled(control_values=self.concrete_cvs) if self.n_ctrls - else self.target_gate + else target_gate ) return cirq.apply_unitary(cpauli, args) @@ -162,46 +164,72 @@ def _has_unitary_(self) -> bool: return not is_symbolic(self.n_ctrls) +@frozen +class MultiControlX(MultiControlPauli): + r"""Implements multi-control, single-target X gate. + + Reduces multiple controls to a single control using an `And` ladder. + See class `ControlledViaAnd` for details on construction. + + Alternatively, one can directly use `XGate().controlled(CtrlSpec(cvs=cvs))` + + Args: + cvs: a tuple of `n` control bits, or a `HasLength(n)` to control by `n` 1s. + + Registers: + controls: control register of type `QBit` and shape `(n,)`. + target: single qubit target register. + """ + + target_bloq: Bloq = field(init=False) + + @target_bloq.default + def _X(self): + return XGate() + + def __attrs_post_init__(self): + pass + + @bloq_example -def _ccpauli() -> MultiControlPauli: - ccpauli = MultiControlPauli(cvs=(1, 0, 1, 0, 1), target_gate=cirq.X) +def _ccpauli() -> MultiControlX: + ccpauli = MultiControlX(cvs=(1, 0, 1, 0, 1)) return ccpauli @bloq_example -def _ccpauli_symb() -> MultiControlPauli: +def _ccpauli_symb() -> MultiControlX: from qualtran.symbolics import HasLength - ccpauli_symb = MultiControlPauli(cvs=HasLength(sympy.Symbol("n")), target_gate=cirq.X) + ccpauli_symb = MultiControlX(cvs=HasLength(sympy.Symbol("n"))) return ccpauli_symb -_CC_PAULI_DOC = BloqDocSpec(bloq_cls=MultiControlPauli, examples=(_ccpauli,)) +_CC_PAULI_DOC = BloqDocSpec(bloq_cls=MultiControlX, examples=(_ccpauli,)) @frozen -class MultiControlX(MultiControlPauli): - r"""Implements multi-control, single-target X gate. - - See :class:`MultiControlPauli` for implementation and costs. - """ +class MultiControlZ(MultiControlPauli): + r"""Implements multi-control, single-target Z gate. - target_gate: cirq.Pauli = field(init=False) + Reduces multiple controls to a single control using an `And` ladder. + See class `ControlledViaAnd` for details on construction. - @target_gate.default - def _X(self): - return cirq.X + Alternatively, one can directly use `ZGate().controlled(CtrlSpec(cvs=cvs))` + Args: + cvs: a tuple of `n` control bits, or a `HasLength(n)` to control by `n` 1s. -@frozen -class MultiControlZ(MultiControlPauli): - r"""Implements multi-control, single-target Z gate. - - See :class:`MultiControlPauli` for implementation and costs. + Registers: + controls: control register of type `QBit` and shape `(n,)`. + target: single qubit target register. """ - target_gate: cirq.Pauli = field(init=False) + target_bloq: Bloq = field(init=False) - @target_gate.default + @target_bloq.default def _Z(self): - return cirq.Z + return ZGate() + + def __attrs_post_init__(self): + pass diff --git a/qualtran/bloqs/mcmt/multi_control_pauli_test.py b/qualtran/bloqs/mcmt/multi_control_pauli_test.py index 4386cf9bc..eed8610ca 100644 --- a/qualtran/bloqs/mcmt/multi_control_pauli_test.py +++ b/qualtran/bloqs/mcmt/multi_control_pauli_test.py @@ -17,6 +17,8 @@ import pytest import qualtran.testing as qlt_testing +from qualtran import Bloq +from qualtran.bloqs.basic_gates import XGate, YGate, ZGate from qualtran.bloqs.mcmt.multi_control_pauli import ( _ccpauli, _ccpauli_symb, @@ -35,10 +37,10 @@ def test_ccpauli_symb(): @pytest.mark.parametrize("num_controls", [0, 1, 2, *range(7, 17)]) -@pytest.mark.parametrize("pauli", [cirq.X, cirq.Y, cirq.Z]) +@pytest.mark.parametrize("pauli", [XGate(), YGate(), ZGate()]) @pytest.mark.parametrize('cv', [0, 1]) -def test_t_complexity_mcp(num_controls: int, pauli: cirq.Pauli, cv: int): - gate = MultiControlPauli([cv] * num_controls, target_gate=pauli) +def test_t_complexity_mcp(num_controls: int, pauli: Bloq, cv: int): + gate = MultiControlPauli([cv] * num_controls, target_bloq=pauli) qlt_testing.assert_valid_bloq_decomposition(gate) qlt_testing.assert_equivalent_bloq_counts(gate) @@ -47,8 +49,10 @@ def test_t_complexity_mcp(num_controls: int, pauli: cirq.Pauli, cv: int): @pytest.mark.parametrize("pauli", [cirq.X, cirq.Y, cirq.Z]) @pytest.mark.parametrize('cv', [0, 1]) def test_mcp_unitary(num_controls: int, pauli: cirq.Pauli, cv: int): + from qualtran.cirq_interop import cirq_gate_to_bloq + cvs = (cv,) * num_controls - gate = MultiControlPauli(cvs, target_gate=pauli) + gate = MultiControlPauli(cvs, target_bloq=cirq_gate_to_bloq(pauli)) cpauli = pauli.controlled(control_values=cvs) if num_controls else pauli np.testing.assert_allclose(gate.tensor_contract(), cirq.unitary(cpauli)) @@ -70,8 +74,8 @@ def test_multi_control_x(cvs): ((), 0, (), 1), ], ) -def test_classical_multi_control_pauli_target_x(cvs, x, ctrls, result): - bloq = MultiControlPauli(cvs=cvs, target_gate=cirq.X) +def test_classical_multi_control_X_target_x(cvs, x, ctrls, result): + bloq = MultiControlX(cvs=cvs) cbloq = bloq.decompose_bloq() kwargs = {'target': x} | ({'controls': ctrls} if ctrls else {}) bloq_classical = bloq.call_classically(**kwargs) @@ -105,3 +109,8 @@ def test_classical_multi_control_x(cvs, x, ctrls, result): np.testing.assert_array_equal(bloq_classical[i], cbloq_classical[i]) assert bloq_classical[-1] == result + + +@pytest.mark.notebook +def test_notebook(): + qlt_testing.execute_notebook('multi_control_multi_target_pauli') diff --git a/qualtran/bloqs/qubitization/qubitization_walk_operator_test.py b/qualtran/bloqs/qubitization/qubitization_walk_operator_test.py index 69b8da1d5..37cf3dab0 100644 --- a/qualtran/bloqs/qubitization/qubitization_walk_operator_test.py +++ b/qualtran/bloqs/qubitization/qubitization_walk_operator_test.py @@ -16,10 +16,9 @@ import numpy as np import pytest -from qualtran import Adjoint -from qualtran.bloqs.basic_gates import Power, XGate +from qualtran import Adjoint, Controlled +from qualtran.bloqs.basic_gates import Power, XGate, ZGate from qualtran.bloqs.chemistry.ising.walk_operator import get_walk_operator_for_1d_ising_model -from qualtran.bloqs.mcmt import MultiControlPauli from qualtran.bloqs.multiplexers.select_pauli_lcu import SelectPauliLCU from qualtran.bloqs.qubitization.qubitization_walk_operator import ( _thc_walk_op, @@ -174,10 +173,12 @@ def test_qubitization_walk_operator_diagrams(): # 4. Diagram for $Ctrl-W = Ctrl-SELECT.Ctrl-R_{L}$ in terms of $Ctrl-SELECT$ and $PREPARE$. def pred(binst): - bloqs_to_keep = (SelectPauliLCU, StatePreparationAliasSampling, MultiControlPauli, XGate) + bloqs_to_keep = (SelectPauliLCU, StatePreparationAliasSampling) keep = binst.bloq_is(bloqs_to_keep) if binst.bloq_is(Adjoint): keep |= isinstance(binst.bloq.subbloq, bloqs_to_keep) + if binst.bloq_is(Controlled) and isinstance(binst.bloq.subbloq, (XGate, ZGate)): + keep = True return not keep greedy_mm = cirq.GreedyQubitManager(prefix="ancilla", maximize_reuse=True) @@ -186,51 +187,51 @@ def pred(binst): cirq.testing.assert_has_diagram( circuit, ''' - ┌──────────────────────────────┐ -ancilla_0: ─────────────────────sigma_mu─────────────────────────────────sigma_mu───────────────────────── - │ │ -ancilla_1: ─────────────────────sigma_mu─────────────────────────────────sigma_mu───────────────────────── - │ │ -ancilla_2: ─────────────────────sigma_mu─────────────────────────────────sigma_mu───────────────────────── - │ │ -ancilla_3: ─────────────────────sigma_mu─────────────────────────────────sigma_mu───────────────────────── - │ │ -ancilla_4: ─────────────────────sigma_mu─────────────────────────────────sigma_mu───────────────────────── - │ │ -ancilla_5: ─────────────────────alt──────────────────────────────────────alt────────────────────────────── - │ │ -ancilla_6: ─────────────────────alt──────────────────────────────────────alt────────────────────────────── - │ │ -ancilla_7: ─────────────────────alt──────────────────────────────────────alt────────────────────────────── - │ │ -ancilla_8: ─────────────────────keep─────────────────────────────────────keep───────────────────────────── - │ │ -ancilla_9: ─────────────────────keep─────────────────────────────────────keep───────────────────────────── - │ │ -ancilla_10: ────────────────────keep─────────────────────────────────────keep───────────────────────────── - │ │ -ancilla_11: ────────────────────keep─────────────────────────────────────keep───────────────────────────── - │ │ -ancilla_12: ────────────────────keep─────────────────────────────────────keep───────────────────────────── - │ │ -ancilla_13: ────────────────────less_than_equal──────────────────────────less_than_equal────────────────── - │ │ -ctrl: ─────────@────────────────┼───────────────────────────────Z───────Z┼──────────────────────────────── - │ │ │ │ -selection0: ───In───────────────StatePreparationAliasSampling───@(0)─────StatePreparationAliasSampling──── - │ │ │ │ -selection1: ───In───────────────selection───────────────────────@(0)─────selection──────────────────────── - │ │ │ │ -selection2: ───In───────────────selection^-1────────────────────@(0)─────selection──────────────────────── + ┌──────────────────────────────┐ +ancilla_0: ─────────────────────sigma_mu────────────────────────────────sigma_mu───────────────────────── + │ │ +ancilla_1: ─────────────────────sigma_mu────────────────────────────────sigma_mu───────────────────────── + │ │ +ancilla_2: ─────────────────────sigma_mu────────────────────────────────sigma_mu───────────────────────── + │ │ +ancilla_3: ─────────────────────sigma_mu────────────────────────────────sigma_mu───────────────────────── + │ │ +ancilla_4: ─────────────────────sigma_mu────────────────────────────────sigma_mu───────────────────────── + │ │ +ancilla_5: ─────────────────────alt─────────────────────────────────────alt────────────────────────────── + │ │ +ancilla_6: ─────────────────────alt─────────────────────────────────────alt────────────────────────────── + │ │ +ancilla_7: ─────────────────────alt─────────────────────────────────────alt────────────────────────────── + │ │ +ancilla_8: ─────────────────────keep────────────────────────────────────keep───────────────────────────── + │ │ +ancilla_9: ─────────────────────keep────────────────────────────────────keep───────────────────────────── + │ │ +ancilla_10: ────────────────────keep────────────────────────────────────keep───────────────────────────── + │ │ +ancilla_11: ────────────────────keep────────────────────────────────────keep───────────────────────────── + │ │ +ancilla_12: ────────────────────keep────────────────────────────────────keep───────────────────────────── + │ │ +ancilla_13: ────────────────────less_than_equal─────────────────────────less_than_equal────────────────── + │ │ +ctrl: ─────────@────────────────┼───────────────────────────────Z──────Z┼──────────────────────────────── + │ │ │ │ +selection0: ───In───────────────StatePreparationAliasSampling───(0)─────StatePreparationAliasSampling──── + │ │ │ │ +selection1: ───In───────────────selection───────────────────────(0)─────selection──────────────────────── + │ │ │ │ +selection2: ───In───────────────selection^-1────────────────────(0)─────selection──────────────────────── │ -target0: ──────SelectPauliLCU───────────────────────────────────────────────────────────────────────────── +target0: ──────SelectPauliLCU──────────────────────────────────────────────────────────────────────────── │ -target1: ──────SelectPauliLCU───────────────────────────────────────────────────────────────────────────── +target1: ──────SelectPauliLCU──────────────────────────────────────────────────────────────────────────── │ -target2: ──────SelectPauliLCU───────────────────────────────────────────────────────────────────────────── +target2: ──────SelectPauliLCU──────────────────────────────────────────────────────────────────────────── │ -target3: ──────SelectPauliLCU───────────────────────────────────────────────────────────────────────────── - └──────────────────────────────┘ +target3: ──────SelectPauliLCU──────────────────────────────────────────────────────────────────────────── + └──────────────────────────────┘ ''', ) # pylint: enable=line-too-long diff --git a/qualtran/bloqs/reflections/reflection_using_prepare_test.py b/qualtran/bloqs/reflections/reflection_using_prepare_test.py index 30830e6d3..502f6e435 100644 --- a/qualtran/bloqs/reflections/reflection_using_prepare_test.py +++ b/qualtran/bloqs/reflections/reflection_using_prepare_test.py @@ -25,7 +25,7 @@ from qualtran.bloqs.arithmetic import LessThanConstant, LessThanEqual from qualtran.bloqs.basic_gates import ZPowGate from qualtran.bloqs.basic_gates.swap import CSwap -from qualtran.bloqs.mcmt import MultiControlPauli, MultiTargetCNOT +from qualtran.bloqs.mcmt import MultiControlX, MultiTargetCNOT from qualtran.bloqs.mcmt.and_bloq import And from qualtran.bloqs.reflections.prepare_identity import PrepareIdentity from qualtran.bloqs.reflections.reflection_using_prepare import ( @@ -49,7 +49,7 @@ LessThanEqual, CSwap, MultiTargetCNOT, - MultiControlPauli, + MultiControlX, cirq.H, cirq.CCNOT, cirq.CNOT, @@ -151,25 +151,25 @@ def test_reflection_using_prepare_diagram(): cirq.testing.assert_has_diagram( circuit, ''' -ancilla_0: ────X───────────────────────────────Z──────X─────────────────────────────── +ancilla_0: ────X───────────────────────────────Z─────X─────────────────────────────── │ -ancilla_1: ────sigma_mu────────────────────────┼──────sigma_mu──────────────────────── - │ │ │ -ancilla_2: ────alt─────────────────────────────┼──────alt───────────────────────────── - │ │ │ -ancilla_3: ────alt─────────────────────────────┼──────alt───────────────────────────── - │ │ │ -ancilla_4: ────alt─────────────────────────────┼──────alt───────────────────────────── - │ │ │ -ancilla_5: ────keep────────────────────────────┼──────keep──────────────────────────── - │ │ │ -ancilla_6: ────less_than_equal─────────────────┼──────less_than_equal───────────────── - │ │ │ -selection0: ───StatePreparationAliasSampling───@(0)───StatePreparationAliasSampling─── - │ │ │ -selection1: ───selection───────────────────────@(0)───selection─────────────────────── - │ │ │ -selection2: ───selection^-1────────────────────@(0)───selection─────────────────────── +ancilla_1: ────sigma_mu────────────────────────┼─────sigma_mu──────────────────────── + │ │ │ +ancilla_2: ────alt─────────────────────────────┼─────alt───────────────────────────── + │ │ │ +ancilla_3: ────alt─────────────────────────────┼─────alt───────────────────────────── + │ │ │ +ancilla_4: ────alt─────────────────────────────┼─────alt───────────────────────────── + │ │ │ +ancilla_5: ────keep────────────────────────────┼─────keep──────────────────────────── + │ │ │ +ancilla_6: ────less_than_equal─────────────────┼─────less_than_equal───────────────── + │ │ │ +selection0: ───StatePreparationAliasSampling───(0)───StatePreparationAliasSampling─── + │ │ │ +selection1: ───selection───────────────────────(0)───selection─────────────────────── + │ │ │ +selection2: ───selection^-1────────────────────(0)───selection─────────────────────── ''', ) @@ -179,25 +179,25 @@ def test_reflection_using_prepare_diagram(): cirq.testing.assert_has_diagram( circuit, ''' -ancilla_0: ────sigma_mu───────────────────────────────sigma_mu──────────────────────── - │ │ -ancilla_1: ────alt────────────────────────────────────alt───────────────────────────── - │ │ -ancilla_2: ────alt────────────────────────────────────alt───────────────────────────── - │ │ -ancilla_3: ────alt────────────────────────────────────alt───────────────────────────── - │ │ -ancilla_4: ────keep───────────────────────────────────keep──────────────────────────── - │ │ -ancilla_5: ────less_than_equal────────────────────────less_than_equal───────────────── - │ │ -control: ──────┼───────────────────────────────Z──────┼─────────────────────────────── - │ │ │ -selection0: ───StatePreparationAliasSampling───@(0)───StatePreparationAliasSampling─── - │ │ │ -selection1: ───selection───────────────────────@(0)───selection─────────────────────── - │ │ │ -selection2: ───selection^-1────────────────────@(0)───selection─────────────────────── +ancilla_0: ────sigma_mu──────────────────────────────sigma_mu──────────────────────── + │ │ +ancilla_1: ────alt───────────────────────────────────alt───────────────────────────── + │ │ +ancilla_2: ────alt───────────────────────────────────alt───────────────────────────── + │ │ +ancilla_3: ────alt───────────────────────────────────alt───────────────────────────── + │ │ +ancilla_4: ────keep──────────────────────────────────keep──────────────────────────── + │ │ +ancilla_5: ────less_than_equal───────────────────────less_than_equal───────────────── + │ │ +control: ──────┼───────────────────────────────Z─────┼─────────────────────────────── + │ │ │ +selection0: ───StatePreparationAliasSampling───(0)───StatePreparationAliasSampling─── + │ │ │ +selection1: ───selection───────────────────────(0)───selection─────────────────────── + │ │ │ +selection2: ───selection^-1────────────────────(0)───selection─────────────────────── ''', ) @@ -207,27 +207,27 @@ def test_reflection_using_prepare_diagram(): cirq.testing.assert_has_diagram( circuit, ''' - ┌──────────────────────────────┐ ┌──────────────────────────────┐ -ancilla_0: ─────sigma_mu───────────────────────────────────sigma_mu───────────────────────── - │ │ -ancilla_1: ─────alt────────────────────────────────────────alt────────────────────────────── - │ │ -ancilla_2: ─────alt────────────────────────────────────────alt────────────────────────────── - │ │ -ancilla_3: ─────alt────────────────────────────────────────alt────────────────────────────── - │ │ -ancilla_4: ─────keep───────────────────────────────────────keep───────────────────────────── - │ │ -ancilla_5: ─────less_than_equal────────────────────────────less_than_equal────────────────── - │ │ -control: ───────┼────────────────────────────X────Z───────X┼──────────────────────────────── - │ │ │ -selection0: ────StatePreparationAliasSampling─────@(0)─────StatePreparationAliasSampling──── - │ │ │ -selection1: ────selection─────────────────────────@(0)─────selection──────────────────────── - │ │ │ -selection2: ────selection^-1──────────────────────@(0)─────selection──────────────────────── - └──────────────────────────────┘ └──────────────────────────────┘ + ┌──────────────────────────────┐ ┌──────────────────────────────┐ +ancilla_0: ─────sigma_mu──────────────────────────────────sigma_mu───────────────────────── + │ │ +ancilla_1: ─────alt───────────────────────────────────────alt────────────────────────────── + │ │ +ancilla_2: ─────alt───────────────────────────────────────alt────────────────────────────── + │ │ +ancilla_3: ─────alt───────────────────────────────────────alt────────────────────────────── + │ │ +ancilla_4: ─────keep──────────────────────────────────────keep───────────────────────────── + │ │ +ancilla_5: ─────less_than_equal───────────────────────────less_than_equal────────────────── + │ │ +control: ───────┼────────────────────────────X────Z──────X┼──────────────────────────────── + │ │ │ +selection0: ────StatePreparationAliasSampling─────(0)─────StatePreparationAliasSampling──── + │ │ │ +selection1: ────selection─────────────────────────(0)─────selection──────────────────────── + │ │ │ +selection2: ────selection^-1──────────────────────(0)─────selection──────────────────────── + └──────────────────────────────┘ └──────────────────────────────┘ ''', ) From cb0d3c8d59fbb650739915d4016caef30eed62fc Mon Sep 17 00:00:00 2001 From: Anurudh Peduri <7265746+anurudhp@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:42:26 -0800 Subject: [PATCH 04/12] Self adjoint for `MCX` and `MCZ` (#1502) * self adjoint for `MCX` and `MCZ` * fix deprecated call graph in `SparseMatrixHermitian` --- qualtran/bloqs/block_encoding/sparse_matrix_hermitian.py | 9 +++++---- qualtran/bloqs/mcmt/multi_control_pauli.py | 6 ++++++ qualtran/bloqs/mod_arithmetic/mod_subtraction.py | 3 +-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/qualtran/bloqs/block_encoding/sparse_matrix_hermitian.py b/qualtran/bloqs/block_encoding/sparse_matrix_hermitian.py index 43ff03547..0097a2bbf 100644 --- a/qualtran/bloqs/block_encoding/sparse_matrix_hermitian.py +++ b/qualtran/bloqs/block_encoding/sparse_matrix_hermitian.py @@ -45,7 +45,8 @@ from qualtran.bloqs.state_preparation.prepare_uniform_superposition import ( PrepareUniformSuperposition, ) -from qualtran.resource_counting import BloqCountT, SympySymbolAllocator +from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator +from qualtran.resource_counting.generalizers import ignore_split_join from qualtran.symbolics import is_symbolic, SymbolicFloat, SymbolicInt from qualtran.symbolics.math_funcs import bit_length @@ -186,7 +187,7 @@ def diffusion(self): ], ) - def build_call_graph(self, ssa: SympySymbolAllocator) -> set[BloqCountT]: + def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT: counts = Counter[Bloq]() counts[self.diffusion] += 1 @@ -202,7 +203,7 @@ def build_call_graph(self, ssa: SympySymbolAllocator) -> set[BloqCountT]: counts[self.col_oracle.adjoint()] += 1 counts[self.diffusion.adjoint()] += 1 - return set(counts.items()) + return counts def build_composite_bloq( self, bb: BloqBuilder, system: SoquetT, ancilla: SoquetT, **soqs @@ -282,7 +283,7 @@ def build_composite_bloq( return soqs -@bloq_example +@bloq_example(generalizer=ignore_split_join) def _sparse_matrix_hermitian_block_encoding() -> SparseMatrixHermitian: from qualtran.bloqs.block_encoding.sparse_matrix import TopLeftRowColumnOracle from qualtran.bloqs.block_encoding.sparse_matrix_hermitian import UniformSqrtEntryOracle diff --git a/qualtran/bloqs/mcmt/multi_control_pauli.py b/qualtran/bloqs/mcmt/multi_control_pauli.py index 79616f971..aceff0c99 100644 --- a/qualtran/bloqs/mcmt/multi_control_pauli.py +++ b/qualtran/bloqs/mcmt/multi_control_pauli.py @@ -190,6 +190,9 @@ def _X(self): def __attrs_post_init__(self): pass + def adjoint(self) -> 'Bloq': + return self + @bloq_example def _ccpauli() -> MultiControlX: @@ -233,3 +236,6 @@ def _Z(self): def __attrs_post_init__(self): pass + + def adjoint(self) -> 'Bloq': + return self diff --git a/qualtran/bloqs/mod_arithmetic/mod_subtraction.py b/qualtran/bloqs/mod_arithmetic/mod_subtraction.py index c4da9e861..e0d0fbf81 100644 --- a/qualtran/bloqs/mod_arithmetic/mod_subtraction.py +++ b/qualtran/bloqs/mod_arithmetic/mod_subtraction.py @@ -190,8 +190,7 @@ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': else: cvs = HasLength(self.dtype.bitsize) return { - MultiControlX(cvs): 1, - MultiControlX(cvs).adjoint(): 1, + MultiControlX(cvs): 2, And(self.cv, 1): 1, And(self.cv, 1).adjoint(): 1, MultiTargetCNOT(self.dtype.bitsize): 1, From 859d9e8bc1ff25a555d5370dd36f7b614d456c73 Mon Sep 17 00:00:00 2001 From: Anurudh Peduri <7265746+anurudhp@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:21:01 -0800 Subject: [PATCH 05/12] notebooks for `ZPowConstViaPhaseGradient` and `RzViaPhaseGradient` (#1507) --- .../qualtran_dev_tools/notebook_specs.py | 14 ++ docs/bloqs/index.rst | 2 + qualtran/bloqs/rotations/__init__.py | 2 + .../rotations/rz_via_phase_gradient.ipynb | 168 ++++++++++++++++ .../bloqs/rotations/rz_via_phase_gradient.py | 6 + .../rotations/rz_via_phase_gradient_test.py | 6 + .../rotations/zpow_via_phase_gradient.ipynb | 188 ++++++++++++++++++ .../rotations/zpow_via_phase_gradient.py | 15 +- .../rotations/zpow_via_phase_gradient_test.py | 9 +- qualtran/serialization/resolver_dict.py | 2 + 10 files changed, 407 insertions(+), 5 deletions(-) create mode 100644 qualtran/bloqs/rotations/rz_via_phase_gradient.ipynb create mode 100644 qualtran/bloqs/rotations/zpow_via_phase_gradient.ipynb diff --git a/dev_tools/qualtran_dev_tools/notebook_specs.py b/dev_tools/qualtran_dev_tools/notebook_specs.py index a35c468ce..f0b156b58 100644 --- a/dev_tools/qualtran_dev_tools/notebook_specs.py +++ b/dev_tools/qualtran_dev_tools/notebook_specs.py @@ -119,6 +119,8 @@ import qualtran.bloqs.rotations.phasing_via_cost_function import qualtran.bloqs.rotations.programmable_rotation_gate_array import qualtran.bloqs.rotations.quantum_variable_rotation +import qualtran.bloqs.rotations.rz_via_phase_gradient +import qualtran.bloqs.rotations.zpow_via_phase_gradient import qualtran.bloqs.state_preparation.black_box_prepare import qualtran.bloqs.state_preparation.prepare_base import qualtran.bloqs.state_preparation.prepare_uniform_superposition @@ -634,6 +636,18 @@ qualtran.bloqs.rotations.hamming_weight_phasing._HAMMING_WEIGHT_PHASING_VIA_PHASE_GRADIENT_DOC, ], ), + NotebookSpecV2( + title='ZPow Rotation via Phase Gradient', + module=qualtran.bloqs.rotations.zpow_via_phase_gradient, + bloq_specs=[ + qualtran.bloqs.rotations.zpow_via_phase_gradient._ZPOW_CONST_VIA_PHASE_GRADIENT_DOC + ], + ), + NotebookSpecV2( + title='Rz Rotation via Phase Gradient', + module=qualtran.bloqs.rotations.rz_via_phase_gradient, + bloq_specs=[qualtran.bloqs.rotations.rz_via_phase_gradient._RZ_VIA_PHASE_GRADIENT_DOC], + ), NotebookSpecV2( title='Programmable Rotation Gate Array', module=qualtran.bloqs.rotations.programmable_rotation_gate_array, diff --git a/docs/bloqs/index.rst b/docs/bloqs/index.rst index d827dfaad..3ed5dfe35 100644 --- a/docs/bloqs/index.rst +++ b/docs/bloqs/index.rst @@ -108,6 +108,8 @@ Bloqs Library rotations/phasing_via_cost_function.ipynb rotations/phase_gradient.ipynb rotations/hamming_weight_phasing.ipynb + rotations/zpow_via_phase_gradient.ipynb + rotations/rz_via_phase_gradient.ipynb rotations/programmable_rotation_gate_array.ipynb qft/two_bit_ffft.ipynb qft/approximate_qft.ipynb diff --git a/qualtran/bloqs/rotations/__init__.py b/qualtran/bloqs/rotations/__init__.py index 48c79d877..e6c63bdf4 100644 --- a/qualtran/bloqs/rotations/__init__.py +++ b/qualtran/bloqs/rotations/__init__.py @@ -24,3 +24,5 @@ from qualtran.bloqs.rotations.phasing_via_cost_function import PhasingViaCostFunction from qualtran.bloqs.rotations.programmable_rotation_gate_array import ProgrammableRotationGateArray from qualtran.bloqs.rotations.quantum_variable_rotation import QvrPhaseGradient, QvrZPow +from qualtran.bloqs.rotations.rz_via_phase_gradient import RzViaPhaseGradient +from qualtran.bloqs.rotations.zpow_via_phase_gradient import ZPowConstViaPhaseGradient diff --git a/qualtran/bloqs/rotations/rz_via_phase_gradient.ipynb b/qualtran/bloqs/rotations/rz_via_phase_gradient.ipynb new file mode 100644 index 000000000..75e37a757 --- /dev/null +++ b/qualtran/bloqs/rotations/rz_via_phase_gradient.ipynb @@ -0,0 +1,168 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bbc3afcf", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# Rz Rotation via Phase Gradient" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6ca3000", + "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": "4d0db53e", + "metadata": { + "cq.autogen": "RzViaPhaseGradient.bloq_doc.md" + }, + "source": [ + "## `RzViaPhaseGradient`\n", + "Apply a controlled-Rz using a phase gradient state.\n", + "\n", + "Implements the following unitary action:\n", + "\n", + "$$\n", + " |\\psi\\rangle \\otimes |x\\rangle \\mapsto \\text{Rz}(4 \\pi x) |\\psi\\rangle \\otimes |x\\rangle\n", + "$$\n", + "\n", + "for every state $|\\psi\\rangle$ and every $x$, or equivalently\n", + "\n", + "$$\n", + " |b\\rangle|x\\rangle \\mapsto |b\\rangle e^{- (-1)^b i x/2} |x\\rangle\n", + "$$\n", + "\n", + "for every $b \\in \\{0, 1\\}$ and every $x$.\n", + "\n", + "To apply an $\\text{Rz}(\\theta) = e^{-i Z \\theta/2}$, the angle register $x$ should store $\\theta/(4\\pi)$.\n", + "\n", + "#### Parameters\n", + " - `angle_dtype`: Data type for the `angle_data` register.\n", + " - `phasegrad_dtype`: Data type for the phase gradient register. \n", + "\n", + "#### Registers\n", + " - `q`: The qubit to apply Rz on.\n", + " - `angle`: The rotation angle in radians.\n", + " - `phase_grad`: The phase gradient register of sufficient width. \n", + "\n", + "#### References\n", + " - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391). Section II-C: Oracles for phasing by cost function. Appendix A: Addition for controlled rotations.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88ae8ba0", + "metadata": { + "cq.autogen": "RzViaPhaseGradient.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.rotations import RzViaPhaseGradient" + ] + }, + { + "cell_type": "markdown", + "id": "f3d5ab59", + "metadata": { + "cq.autogen": "RzViaPhaseGradient.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16a874c9", + "metadata": { + "cq.autogen": "RzViaPhaseGradient.rz_via_phase_gradient" + }, + "outputs": [], + "source": [ + "from qualtran import QFxp\n", + "\n", + "rz_via_phase_gradient = RzViaPhaseGradient(angle_dtype=QFxp(4, 4), phasegrad_dtype=QFxp(4, 4))" + ] + }, + { + "cell_type": "markdown", + "id": "ca4ac4bc", + "metadata": { + "cq.autogen": "RzViaPhaseGradient.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92d7b03c", + "metadata": { + "cq.autogen": "RzViaPhaseGradient.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([rz_via_phase_gradient],\n", + " ['`rz_via_phase_gradient`'])" + ] + }, + { + "cell_type": "markdown", + "id": "03999011", + "metadata": { + "cq.autogen": "RzViaPhaseGradient.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0cc79d3", + "metadata": { + "cq.autogen": "RzViaPhaseGradient.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "rz_via_phase_gradient_g, rz_via_phase_gradient_sigma = rz_via_phase_gradient.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(rz_via_phase_gradient_g)\n", + "show_counts_sigma(rz_via_phase_gradient_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/rotations/rz_via_phase_gradient.py b/qualtran/bloqs/rotations/rz_via_phase_gradient.py index 00e71e2ee..d3ec380d2 100644 --- a/qualtran/bloqs/rotations/rz_via_phase_gradient.py +++ b/qualtran/bloqs/rotations/rz_via_phase_gradient.py @@ -17,6 +17,7 @@ Bloq, bloq_example, BloqBuilder, + BloqDocSpec, QBit, QDType, QUInt, @@ -109,3 +110,8 @@ def _rz_via_phase_gradient() -> RzViaPhaseGradient: rz_via_phase_gradient = RzViaPhaseGradient(angle_dtype=QFxp(4, 4), phasegrad_dtype=QFxp(4, 4)) return rz_via_phase_gradient + + +_RZ_VIA_PHASE_GRADIENT_DOC = BloqDocSpec( + bloq_cls=RzViaPhaseGradient, examples=[_rz_via_phase_gradient] +) diff --git a/qualtran/bloqs/rotations/rz_via_phase_gradient_test.py b/qualtran/bloqs/rotations/rz_via_phase_gradient_test.py index 110583b25..4fb5e7987 100644 --- a/qualtran/bloqs/rotations/rz_via_phase_gradient_test.py +++ b/qualtran/bloqs/rotations/rz_via_phase_gradient_test.py @@ -16,6 +16,7 @@ import sympy from attrs import frozen +import qualtran.testing as qlt_testing from qualtran import Bloq, BloqBuilder, QBit, QFxp, QUInt, Signature, Soquet, SoquetT from qualtran.bloqs.basic_gates import IntState, Rz from qualtran.bloqs.rotations.phase_gradient import PhaseGradientState @@ -87,3 +88,8 @@ def test_tensor(bitsize: int): expected = Rz(theta).tensor_contract() np.testing.assert_allclose(actual, expected, atol=1 / 2**bitsize) + + +@pytest.mark.notebook +def test_notebook(): + qlt_testing.execute_notebook('rz_via_phase_gradient') diff --git a/qualtran/bloqs/rotations/zpow_via_phase_gradient.ipynb b/qualtran/bloqs/rotations/zpow_via_phase_gradient.ipynb new file mode 100644 index 000000000..fda19c320 --- /dev/null +++ b/qualtran/bloqs/rotations/zpow_via_phase_gradient.ipynb @@ -0,0 +1,188 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7e96cff5", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# ZPow Rotation via Phase Gradient" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "197bfd0b", + "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": "a1b0782f", + "metadata": { + "cq.autogen": "ZPowConstViaPhaseGradient.bloq_doc.md" + }, + "source": [ + "## `ZPowConstViaPhaseGradient`\n", + "Apply an $Z**t$ on a qubit using a phase gradient state.\n", + "\n", + "This bloq implements a `Z**t` by conditionally loading `t/2` into a quantum\n", + "register, conditioned on the qubit `q` (rotation target), and then adding\n", + "this value to the phase gradient to get a phase kickback, and uncomputes the load.\n", + "This controlled-load trick is taken from Ref. [2], Fig 2a.\n", + "\n", + "See :class:`PhaseGradientState` for details on phase gradients.\n", + "\n", + "It loads an approximation of `t/2` to `phase_grad_bitsize` bits,\n", + "which is loaded using `phase_grad_bitsize` clean ancilla.\n", + "\n", + "The total Tofolli cost is `phase_grad_bitsize - 2`.\n", + "\n", + "\n", + "#### Parameters\n", + " - `exponent`: value of `t` to apply `Z**t`\n", + " - `phase_grad_bitsize`: number of qubits of the phase gradient state. \n", + "\n", + "#### Registers\n", + " - `q`: qubit to apply rotation on.\n", + " - `phase_grad`: phase gradient state of type `QFxp` with `phase_grad_bitsize` fractional bits. \n", + "\n", + "#### References\n", + " - [Improved quantum circuits for elliptic curve discrete logarithms](https://arxiv.org/abs/2001.09580). Haner et al. 2020. Section 3: Components. \"Integer addition\" and Fig 2a.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9111c04", + "metadata": { + "cq.autogen": "ZPowConstViaPhaseGradient.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.rotations import ZPowConstViaPhaseGradient" + ] + }, + { + "cell_type": "markdown", + "id": "b09c61b5", + "metadata": { + "cq.autogen": "ZPowConstViaPhaseGradient.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab048833", + "metadata": { + "cq.autogen": "ZPowConstViaPhaseGradient.zpow_const_via_phase_grad" + }, + "outputs": [], + "source": [ + "zpow_const_via_phase_grad = ZPowConstViaPhaseGradient.from_precision(3 / 8, eps=1e-11)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f61b0a60", + "metadata": { + "cq.autogen": "ZPowConstViaPhaseGradient.zpow_const_via_phase_grad_symb_prec" + }, + "outputs": [], + "source": [ + "eps = sympy.symbols(\"eps\")\n", + "zpow_const_via_phase_grad_symb_prec = ZPowConstViaPhaseGradient.from_precision(3 / 8, eps=eps)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66d60de6", + "metadata": { + "cq.autogen": "ZPowConstViaPhaseGradient.zpow_const_via_phase_grad_symb_angle" + }, + "outputs": [], + "source": [ + "t = sympy.symbols(\"t\")\n", + "zpow_const_via_phase_grad_symb_angle = ZPowConstViaPhaseGradient.from_precision(t, eps=1e-11)" + ] + }, + { + "cell_type": "markdown", + "id": "9b31feb6", + "metadata": { + "cq.autogen": "ZPowConstViaPhaseGradient.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "faa5dba0", + "metadata": { + "cq.autogen": "ZPowConstViaPhaseGradient.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([zpow_const_via_phase_grad, zpow_const_via_phase_grad_symb_prec, zpow_const_via_phase_grad_symb_angle],\n", + " ['`zpow_const_via_phase_grad`', '`zpow_const_via_phase_grad_symb_prec`', '`zpow_const_via_phase_grad_symb_angle`'])" + ] + }, + { + "cell_type": "markdown", + "id": "91234700", + "metadata": { + "cq.autogen": "ZPowConstViaPhaseGradient.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93ec7137", + "metadata": { + "cq.autogen": "ZPowConstViaPhaseGradient.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "zpow_const_via_phase_grad_g, zpow_const_via_phase_grad_sigma = zpow_const_via_phase_grad.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(zpow_const_via_phase_grad_g)\n", + "show_counts_sigma(zpow_const_via_phase_grad_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/rotations/zpow_via_phase_gradient.py b/qualtran/bloqs/rotations/zpow_via_phase_gradient.py index c1b5ff1d2..b7d7d605d 100644 --- a/qualtran/bloqs/rotations/zpow_via_phase_gradient.py +++ b/qualtran/bloqs/rotations/zpow_via_phase_gradient.py @@ -21,6 +21,7 @@ Bloq, bloq_example, BloqBuilder, + BloqDocSpec, DecomposeTypeError, QBit, QFxp, @@ -142,13 +143,23 @@ def _zpow_const_via_phase_grad() -> ZPowConstViaPhaseGradient: @bloq_example(generalizer=ignore_alloc_free) def _zpow_const_via_phase_grad_symb_prec() -> ZPowConstViaPhaseGradient: - eps = sympy.symbols(r"\epsilon") + eps = sympy.symbols("eps") zpow_const_via_phase_grad_symb_prec = ZPowConstViaPhaseGradient.from_precision(3 / 8, eps=eps) return zpow_const_via_phase_grad_symb_prec @bloq_example(generalizer=ignore_alloc_free) def _zpow_const_via_phase_grad_symb_angle() -> ZPowConstViaPhaseGradient: - t = sympy.symbols(r"t") + t = sympy.symbols("t") zpow_const_via_phase_grad_symb_angle = ZPowConstViaPhaseGradient.from_precision(t, eps=1e-11) return zpow_const_via_phase_grad_symb_angle + + +_ZPOW_CONST_VIA_PHASE_GRADIENT_DOC = BloqDocSpec( + bloq_cls=ZPowConstViaPhaseGradient, + examples=[ + _zpow_const_via_phase_grad, + _zpow_const_via_phase_grad_symb_prec, + _zpow_const_via_phase_grad_symb_angle, + ], +) diff --git a/qualtran/bloqs/rotations/zpow_via_phase_gradient_test.py b/qualtran/bloqs/rotations/zpow_via_phase_gradient_test.py index 10d5cec75..17fa1e59b 100644 --- a/qualtran/bloqs/rotations/zpow_via_phase_gradient_test.py +++ b/qualtran/bloqs/rotations/zpow_via_phase_gradient_test.py @@ -14,6 +14,7 @@ import pytest from attrs import frozen +import qualtran.testing as qlt_testing from qualtran import Bloq, BloqBuilder, Signature, Soquet, SoquetT from qualtran.bloqs.basic_gates import ZPowGate from qualtran.bloqs.rotations.phase_gradient import PhaseGradientState @@ -36,9 +37,6 @@ ], ) def test_examples(bloq_autotester, bloq): - if bloq_autotester.check_name == 'serialize': - pytest.skip() - bloq_autotester(bloq) @@ -95,3 +93,8 @@ def test_unitary(exponent: float, phase_grad_bitsize: int): actual = bloq.tensor_contract() expected = ZPowGate(exponent).tensor_contract() assert_unitaries_equivalent_upto_global_phase(actual, expected, atol=2**-phase_grad_bitsize) + + +@pytest.mark.notebook +def test_notebook(): + qlt_testing.execute_notebook('zpow_via_phase_gradient') diff --git a/qualtran/serialization/resolver_dict.py b/qualtran/serialization/resolver_dict.py index cd01026fb..d81f9030a 100644 --- a/qualtran/serialization/resolver_dict.py +++ b/qualtran/serialization/resolver_dict.py @@ -147,6 +147,7 @@ import qualtran.bloqs.rotations.programmable_rotation_gate_array import qualtran.bloqs.rotations.quantum_variable_rotation import qualtran.bloqs.rotations.rz_via_phase_gradient +import qualtran.bloqs.rotations.zpow_via_phase_gradient import qualtran.bloqs.state_preparation.black_box_prepare import qualtran.bloqs.state_preparation.prepare_base import qualtran.bloqs.state_preparation.prepare_uniform_superposition @@ -427,6 +428,7 @@ "qualtran.bloqs.rotations.quantum_variable_rotation.QvrPhaseGradient": qualtran.bloqs.rotations.quantum_variable_rotation.QvrPhaseGradient, "qualtran.bloqs.rotations.quantum_variable_rotation.QvrZPow": qualtran.bloqs.rotations.quantum_variable_rotation.QvrZPow, "qualtran.bloqs.rotations.rz_via_phase_gradient.RzViaPhaseGradient": qualtran.bloqs.rotations.rz_via_phase_gradient.RzViaPhaseGradient, + "qualtran.bloqs.rotations.zpow_via_phase_gradient.ZPowConstViaPhaseGradient": qualtran.bloqs.rotations.zpow_via_phase_gradient.ZPowConstViaPhaseGradient, "qualtran.bloqs.state_preparation.prepare_uniform_superposition.PrepareUniformSuperposition": qualtran.bloqs.state_preparation.prepare_uniform_superposition.PrepareUniformSuperposition, "qualtran.bloqs.state_preparation.black_box_prepare.BlackBoxPrepare": qualtran.bloqs.state_preparation.black_box_prepare.BlackBoxPrepare, "qualtran.bloqs.state_preparation.prepare_base.PrepareOracle": qualtran.bloqs.state_preparation.prepare_base.PrepareOracle, From 47a36c8179488a615704198a71f9f2b2afcf977d Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Thu, 12 Dec 2024 10:00:52 -0800 Subject: [PATCH 06/12] First pass at fixing chemistry costs to use QECGatesCost (#1505) --- .../qualtran_dev_tools/notebook_specs.py | 2 + .../first_quantization.ipynb | 113 ++++++++++++++++++ .../pbc/first_quantization/prepare_t_test.py | 15 +-- .../pbc/first_quantization/prepare_zeta.py | 21 +++- .../first_quantization/prepare_zeta_test.py | 6 +- .../projectile/prepare_t_test.py | 8 +- .../projectile/select_t_test.py | 6 +- .../pbc/first_quantization/select_t_test.py | 6 +- .../bloqs/chemistry/resource_estimation.ipynb | 17 ++- .../trotter/grid_ham/inverse_sqrt_test.py | 1 - .../chemistry/trotter/hubbard/hopping_test.py | 30 ++--- .../chemistry/trotter/hubbard/hubbard.ipynb | 2 +- .../chemistry/trotter/hubbard/interaction.py | 2 +- .../trotter/hubbard/interaction_test.py | 15 ++- .../hubbard/qpe_cost_optimization.ipynb | 72 +++++------ .../trotter/hubbard/trotter_step_test.py | 23 +--- .../bloqs/chemistry/writing_algorithms.ipynb | 37 +++--- 17 files changed, 236 insertions(+), 140 deletions(-) diff --git a/dev_tools/qualtran_dev_tools/notebook_specs.py b/dev_tools/qualtran_dev_tools/notebook_specs.py index f0b156b58..292f56869 100644 --- a/dev_tools/qualtran_dev_tools/notebook_specs.py +++ b/dev_tools/qualtran_dev_tools/notebook_specs.py @@ -66,6 +66,7 @@ import qualtran.bloqs.chemistry.hubbard_model.qubitization import qualtran.bloqs.chemistry.pbc.first_quantization.prepare_t import qualtran.bloqs.chemistry.pbc.first_quantization.prepare_uv +import qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta import qualtran.bloqs.chemistry.pbc.first_quantization.projectile.select_and_prepare import qualtran.bloqs.chemistry.pbc.first_quantization.select_t import qualtran.bloqs.chemistry.pbc.first_quantization.select_uv @@ -311,6 +312,7 @@ qualtran.bloqs.chemistry.pbc.first_quantization.prepare_uv._PREPARE_UV, qualtran.bloqs.chemistry.pbc.first_quantization.select_t._SELECT_T, qualtran.bloqs.chemistry.pbc.first_quantization.select_uv._SELECT_UV, + qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta._PREPARE_ZETA, ], directory=f'{SOURCE_DIR}/bloqs/chemistry/pbc/first_quantization', ), diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/first_quantization.ipynb b/qualtran/bloqs/chemistry/pbc/first_quantization/first_quantization.ipynb index 69a0ea74a..b1de1dbf2 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/first_quantization.ipynb +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/first_quantization.ipynb @@ -822,6 +822,119 @@ "show_call_graph(select_uv_g)\n", "show_counts_sigma(select_uv_sigma)" ] + }, + { + "cell_type": "markdown", + "id": "69c98b9f", + "metadata": { + "cq.autogen": "PrepareZetaState.bloq_doc.md" + }, + "source": [ + "## `PrepareZetaState`\n", + "PREPARE the superpostion over $l$ weighted by $\\zeta_l$.\n", + "\n", + "See https://github.com/quantumlib/Qualtran/issues/473.\n", + "#### Parameters\n", + " - `num_bits_p`: The number of bits to represent each dimension of the momentum register.\n", + " - `eta`: The number of electrons.\n", + " - `m_param`: $\\mathcal{M}$ in the reference.\n", + " - `lambda_zeta`: sum of nuclear charges. \n", + "\n", + "#### Registers\n", + " - `l`: the register indexing the atomic number. \n", + "\n", + "#### References\n", + " - [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767). page 23-24, last 3 paragraphs.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "074bc1b8", + "metadata": { + "cq.autogen": "PrepareZetaState.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta import PrepareZetaState" + ] + }, + { + "cell_type": "markdown", + "id": "fd3071f7", + "metadata": { + "cq.autogen": "PrepareZetaState.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31ee49a4", + "metadata": { + "cq.autogen": "PrepareZetaState.prepare_zeta" + }, + "outputs": [], + "source": [ + "num_atoms = 10\n", + "lambda_zeta = 10\n", + "num_bits_nuc_pos = 8\n", + "\n", + "prepare_zeta = PrepareZetaState(\n", + " num_atoms=num_atoms, lambda_zeta=lambda_zeta, num_bits_nuc_pos=num_bits_nuc_pos\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4fb19c71", + "metadata": { + "cq.autogen": "PrepareZetaState.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6498eee0", + "metadata": { + "cq.autogen": "PrepareZetaState.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([prepare_zeta],\n", + " ['`prepare_zeta`'])" + ] + }, + { + "cell_type": "markdown", + "id": "1e644753", + "metadata": { + "cq.autogen": "PrepareZetaState.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fbef8e6e", + "metadata": { + "cq.autogen": "PrepareZetaState.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "prepare_zeta_g, prepare_zeta_sigma = prepare_zeta.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(prepare_zeta_g)\n", + "show_counts_sigma(prepare_zeta_sigma)" + ] } ], "metadata": { diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_t_test.py b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_t_test.py index f495ff791..afa581df1 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_t_test.py +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_t_test.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from qualtran.bloqs.basic_gates import Toffoli from qualtran.bloqs.chemistry.pbc.first_quantization.prepare import ( UniformSuperpostionIJFirstQuantization, ) @@ -20,6 +19,7 @@ _prepare_t, PrepareTFirstQuantization, ) +from qualtran.resource_counting import get_cost_value, QECGatesCost def test_prepare_t(bloq_autotester): @@ -33,15 +33,12 @@ def test_prepare_kinetic_t_counts(): n_eta = (eta - 1).bit_length() expected_cost = (14 * n_eta + 8 * b_r - 36) + 2 * (2 * num_bits_p + 9) uni = UniformSuperpostionIJFirstQuantization(eta, num_bits_rot_aa=b_r) - _, counts = uni.call_graph() - qual_cost = counts[Toffoli()] + + qual_cost = get_cost_value(uni, QECGatesCost()).total_toffoli_only() uni = UniformSuperpostionIJFirstQuantization(eta, num_bits_rot_aa=b_r).adjoint() - _, counts = uni.call_graph() - qual_cost += counts[Toffoli()] + qual_cost += get_cost_value(uni, QECGatesCost()).total_toffoli_only() prep = PrepareTFirstQuantization(num_bits_p, eta, num_bits_rot_aa=b_r) - _, counts = prep.call_graph() - qual_cost += counts[Toffoli()] + qual_cost += get_cost_value(prep, QECGatesCost()).total_toffoli_only() prep = PrepareTFirstQuantization(num_bits_p, eta, num_bits_rot_aa=b_r).adjoint() - _, counts = prep.call_graph() - qual_cost += counts[Toffoli()] + qual_cost += get_cost_value(prep, QECGatesCost()).total_toffoli_only() assert qual_cost == expected_cost diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta.py b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta.py index de328009e..dc76e1ccf 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta.py +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta.py @@ -19,7 +19,7 @@ import numpy as np from attrs import evolve, frozen -from qualtran import Bloq, QAny, Register, Signature +from qualtran import Bloq, bloq_example, BloqDocSpec, QAny, Register, Signature from qualtran.bloqs.basic_gates import Toffoli if TYPE_CHECKING: @@ -65,3 +65,22 @@ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': else: return {Toffoli(): self.lambda_zeta} + + +@bloq_example +def _prepare_zeta() -> PrepareZetaState: + num_atoms = 10 + lambda_zeta = 10 + num_bits_nuc_pos = 8 + + prepare_zeta = PrepareZetaState( + num_atoms=num_atoms, lambda_zeta=lambda_zeta, num_bits_nuc_pos=num_bits_nuc_pos + ) + return prepare_zeta + + +_PREPARE_ZETA = BloqDocSpec( + bloq_cls=PrepareZetaState, + import_line='from qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta import PrepareZetaState', + examples=(_prepare_zeta,), +) diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta_test.py b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta_test.py index 212f1adba..55cb3afc0 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta_test.py +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta_test.py @@ -11,8 +11,8 @@ # 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 qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta import PrepareZetaState +from qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta import _prepare_zeta -def test_uniform_superposition_ij(): - prep = PrepareZetaState(num_atoms=10, lambda_zeta=20, num_bits_nuc_pos=8) +def test_prepare_zeta(bloq_autotester): + bloq_autotester(_prepare_zeta) diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t_test.py b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t_test.py index ba3c715a7..a250da50e 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t_test.py +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t_test.py @@ -14,13 +14,13 @@ import pytest -from qualtran.bloqs.basic_gates import Toffoli from qualtran.bloqs.chemistry.pbc.first_quantization.projectile.prepare_t import ( _prep_power_two_proj, _prep_t_proj, PreparePowerTwoStateWithProj, PrepareTFirstQuantizationWithProj, ) +from qualtran.resource_counting import get_cost_value, QECGatesCost def test_prep_t_proj(bloq_autotester): @@ -39,13 +39,11 @@ def test_prepare_kinetic_t_proj_counts(): expected_cost = 2 * (2 * num_bits_n + 9) + 2 * (num_bits_n - num_bits_p) + 20 qual_cost = 0 prep = PrepareTFirstQuantizationWithProj(num_bits_p, num_bits_n, eta, num_bits_rot_aa=b_r) - _, counts = prep.call_graph() - qual_cost += counts[Toffoli()] + qual_cost += get_cost_value(prep, QECGatesCost()).total_toffoli_only() prep = PrepareTFirstQuantizationWithProj( num_bits_p, num_bits_n, eta, num_bits_rot_aa=b_r ).adjoint() - _, counts = prep.call_graph() - qual_cost += counts[Toffoli()] + qual_cost += get_cost_value(prep, QECGatesCost()).total_toffoli_only() assert qual_cost == expected_cost diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_t_test.py b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_t_test.py index b654c30ac..f0752fda4 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_t_test.py +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_t_test.py @@ -11,11 +11,11 @@ # 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 qualtran.bloqs.basic_gates import Toffoli from qualtran.bloqs.chemistry.pbc.first_quantization.projectile.select_t import ( _sel_t_proj, SelectTFirstQuantizationWithProj, ) +from qualtran.resource_counting import get_cost_value, QECGatesCost def test_sel_t_proj(bloq_autotester): @@ -25,5 +25,5 @@ def test_sel_t_proj(bloq_autotester): def test_select_kinetic_t_counts(): num_bits_n = 6 sel = SelectTFirstQuantizationWithProj(num_bits_n, 10) - _, counts = sel.call_graph() - assert counts[Toffoli()] == 5 * (num_bits_n - 1) + 2 + 1 + toffolis = get_cost_value(sel, QECGatesCost()).total_toffoli_only() + assert toffolis == 5 * (num_bits_n - 1) + 2 + 1 diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/select_t_test.py b/qualtran/bloqs/chemistry/pbc/first_quantization/select_t_test.py index 46f1cd2a4..58dec6f7e 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/select_t_test.py +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/select_t_test.py @@ -11,11 +11,11 @@ # 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 qualtran.bloqs.basic_gates import Toffoli from qualtran.bloqs.chemistry.pbc.first_quantization.select_t import ( _select_t, SelectTFirstQuantization, ) +from qualtran.resource_counting import get_cost_value, QECGatesCost def test_select_t(bloq_autotester): @@ -25,5 +25,5 @@ def test_select_t(bloq_autotester): def test_select_kinetic_t_counts(): num_bits_p = 6 sel = SelectTFirstQuantization(num_bits_p, 10) - _, counts = sel.call_graph() - assert counts[Toffoli()] == 5 * (num_bits_p - 1) + 2 + toffolis = get_cost_value(sel, QECGatesCost()).total_toffoli_only() + assert toffolis == 5 * (num_bits_p - 1) + 2 diff --git a/qualtran/bloqs/chemistry/resource_estimation.ipynb b/qualtran/bloqs/chemistry/resource_estimation.ipynb index 9218086ee..39585e49e 100644 --- a/qualtran/bloqs/chemistry/resource_estimation.ipynb +++ b/qualtran/bloqs/chemistry/resource_estimation.ipynb @@ -84,7 +84,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "2fb17f7f", "metadata": {}, "outputs": [], @@ -113,7 +113,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "b9991178", "metadata": {}, "outputs": [], @@ -202,7 +202,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "5efd66ae", "metadata": {}, "outputs": [], @@ -305,7 +305,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "614bfb21", "metadata": {}, "outputs": [], @@ -346,9 +346,8 @@ "metadata": {}, "outputs": [], "source": [ - "_, sigma = df_bloq.call_graph()\n", "refl_cost_and_qpe = (df_bloq.num_aux -1).bit_length() + (num_spin_orb // 2 - 1).bit_length() + num_bits_state_prep + 1 + 2\n", - "print(f'qualtran cost = {sigma[TGate()] // 4} vs paper = {21753 - refl_cost_and_qpe}')" + "print(f'qualtran cost = {get_toffoli_counts(df_bloq)} vs paper = {21753 - refl_cost_and_qpe}')" ] }, { @@ -363,7 +362,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "349b70ab", "metadata": {}, "outputs": [], @@ -384,7 +383,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "2ef506ab", "metadata": {}, "outputs": [], @@ -411,7 +410,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "e34dcb1c", "metadata": {}, "outputs": [], diff --git a/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt_test.py b/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt_test.py index a1af126a1..43ecf9c31 100644 --- a/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt_test.py +++ b/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt_test.py @@ -41,7 +41,6 @@ def test_newton_raphson_inverse_sqrt_bloq_counts(): poly_bitsize = 15 target_bitsize = 22 bloq = NewtonRaphsonApproxInverseSquareRoot(int_bitsize, poly_bitsize, target_bitsize) - _, counts = bloq.call_graph() cost_square = poly_bitsize**2 // 2 - 4 cost_scale = poly_bitsize * (2 * int_bitsize - 1) - int_bitsize**2 cost_mult = 2 * (target_bitsize**2 - target_bitsize - 1) diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/hopping_test.py b/qualtran/bloqs/chemistry/trotter/hubbard/hopping_test.py index 652664817..006c1c6c3 100644 --- a/qualtran/bloqs/chemistry/trotter/hubbard/hopping_test.py +++ b/qualtran/bloqs/chemistry/trotter/hubbard/hopping_test.py @@ -11,15 +11,12 @@ # 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 qualtran import Bloq -from qualtran.bloqs.basic_gates import Rz, TGate, ZPowGate -from qualtran.bloqs.bookkeeping import ArbitraryClifford from qualtran.bloqs.chemistry.trotter.hubbard.hopping import ( _hopping_tile, _hopping_tile_hwp, _plaquette, ) -from qualtran.resource_counting.generalizers import PHI +from qualtran.resource_counting import get_cost_value, QECGatesCost def test_hopping_tile(bloq_autotester): @@ -30,27 +27,18 @@ def test_hopping_plaquette(bloq_autotester): bloq_autotester(_plaquette) -def catch_rotations(bloq) -> Bloq: - if isinstance(bloq, (Rz, ZPowGate)): - if isinstance(bloq, ZPowGate): - return Rz(angle=PHI) - elif abs(float(bloq.angle)) < 1e-12: - return ArbitraryClifford(1) - else: - return Rz(angle=PHI) - return bloq - - def test_hopping_tile_t_counts(): bloq = _hopping_tile() - _, counts = bloq.call_graph(generalizer=catch_rotations) - assert counts[TGate()] == 8 * bloq.length**2 // 2 - assert counts[Rz(PHI)] == 2 * bloq.length**2 // 2 + costs = get_cost_value(bloq, QECGatesCost()) + assert costs.t == 8 * bloq.length**2 // 2 + assert costs.rotation == 2 * bloq.length**2 // 2 def test_hopping_tile_hwp_t_counts(): bloq = _hopping_tile_hwp() - _, counts = bloq.call_graph(generalizer=catch_rotations) + costs = get_cost_value(bloq, QECGatesCost()) n_rot_par = bloq.length**2 // 2 - assert counts[Rz(PHI)] == 2 * n_rot_par.bit_length() - assert counts[TGate()] == 8 * bloq.length**2 // 2 + 2 * 4 * (n_rot_par - n_rot_par.bit_count()) + assert costs.rotation == 2 * n_rot_par.bit_length() + assert costs.total_t_count(ts_per_rotation=0) == 8 * bloq.length**2 // 2 + 2 * 4 * ( + n_rot_par - n_rot_par.bit_count() + ) diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb b/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb index 54fab69e6..0d46f45fd 100644 --- a/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb +++ b/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb @@ -616,7 +616,7 @@ "outputs": [], "source": [ "length = 8\n", - "angle = 0.5\n", + "angle = 0.52728\n", "hubb_u = 4.0\n", "interaction_hwp = InteractionHWP(length, angle, hubb_u)" ] diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py b/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py index 9783ee833..5a1d23cd0 100644 --- a/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py +++ b/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py @@ -130,7 +130,7 @@ def _interaction() -> Interaction: @bloq_example def _interaction_hwp() -> InteractionHWP: length = 8 - angle = 0.5 + angle = 0.52728 hubb_u = 4.0 interaction_hwp = InteractionHWP(length, angle, hubb_u) return interaction_hwp diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/interaction_test.py b/qualtran/bloqs/chemistry/trotter/hubbard/interaction_test.py index 43b0479b5..deeccb2a5 100644 --- a/qualtran/bloqs/chemistry/trotter/hubbard/interaction_test.py +++ b/qualtran/bloqs/chemistry/trotter/hubbard/interaction_test.py @@ -11,10 +11,9 @@ # 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 qualtran.bloqs.basic_gates import Rz, TGate -from qualtran.bloqs.chemistry.trotter.hubbard.hopping_test import catch_rotations + from qualtran.bloqs.chemistry.trotter.hubbard.interaction import _interaction, _interaction_hwp -from qualtran.resource_counting.generalizers import PHI +from qualtran.resource_counting import get_cost_value, QECGatesCost def test_hopping_tile(bloq_autotester): @@ -27,14 +26,14 @@ def test_interaction_hwp(bloq_autotester): def test_interaction_hwp_bloq_counts(): bloq = _interaction_hwp() - _, counts = bloq.call_graph(generalizer=catch_rotations) + costs = get_cost_value(bloq, QECGatesCost()) n_rot_par = bloq.length**2 // 2 - assert counts[Rz(PHI)] == 2 * n_rot_par.bit_length() - assert counts[TGate()] == 2 * 4 * (n_rot_par - n_rot_par.bit_count()) + assert costs.rotation == 2 * n_rot_par.bit_length() + assert costs.total_t_count(ts_per_rotation=0) == 2 * 4 * (n_rot_par - n_rot_par.bit_count()) def test_interaction_bloq_counts(): bloq = _interaction() - _, counts = bloq.call_graph(generalizer=catch_rotations) + costs = get_cost_value(bloq, QECGatesCost()) n_rot = bloq.length**2 - assert counts[Rz(PHI)] == n_rot + assert costs.rotation == n_rot diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/qpe_cost_optimization.ipynb b/qualtran/bloqs/chemistry/trotter/hubbard/qpe_cost_optimization.ipynb index 6f4e10cdd..ef5a26f31 100644 --- a/qualtran/bloqs/chemistry/trotter/hubbard/qpe_cost_optimization.ipynb +++ b/qualtran/bloqs/chemistry/trotter/hubbard/qpe_cost_optimization.ipynb @@ -106,36 +106,21 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Dict, Union, Tuple\n", + "from typing import Tuple\n", "\n", "import numpy as np\n", "import sympy\n", + "import attrs\n", "\n", - "from qualtran.resource_counting.classify_bloqs import bloq_is_rotation\n", - "from qualtran.resource_counting.generalizers import PHI\n", "from qualtran.cirq_interop.t_complexity_protocol import TComplexity\n", - "from qualtran import Bloq\n", - "from qualtran.bloqs.basic_gates import TGate, Rz\n", - "from qualtran.bloqs.bookkeeping import ArbitraryClifford\n", - "\n", - "\n", - "def catch_rotations(bloq) -> Bloq:\n", - " \"\"\"Generalizer to catch rotations.\"\"\"\n", - " if isinstance(bloq, Rz):\n", - " if isinstance(bloq.angle, float) and abs(bloq.angle) < 1e-12:\n", - " return ArbitraryClifford(1)\n", - " else:\n", - " return Rz(angle=PHI, eps=bloq.eps)\n", - " return bloq\n", + "from qualtran.resource_counting import get_cost_value, QECGatesCost\n", "\n", "\n", - "def t_and_rot_counts_from_sigma(sigma: Dict['Bloq', Union[int, 'sympy.Expr']]) -> Tuple[int, int]:\n", - " ret = sigma.get(TGate(), 0)\n", - " n_rot = 0\n", - " for bloq, counts in sigma.items():\n", - " if bloq_is_rotation(bloq):\n", - " n_rot += counts\n", - " return ret, n_rot\n", + "def t_and_rot_counts_from_bloq(bloq) -> Tuple[int, int]:\n", + " costs = get_cost_value(bloq, QECGatesCost())\n", + " n_rot = costs.rotation\n", + " n_t = costs.total_t_count(ts_per_rotation=0)\n", + " return n_t, n_rot\n", "\n", "\n", "def timestep_from_params(delta_ts: float, xi: float, prod_ord: int) -> float:\n", @@ -265,7 +250,7 @@ "from qualtran.bloqs.chemistry.trotter.hubbard.trotter_step import build_plaq_unitary_second_order_suzuki\n", "\n", "trotter_step = build_plaq_unitary_second_order_suzuki(length, hubb_u, timestep, eps=1e-10)\n", - "n_t, n_rot = t_and_rot_counts_from_sigma(trotter_step.call_graph(generalizer=catch_rotations)[1])\n", + "n_t, n_rot = t_and_rot_counts_from_bloq(trotter_step)\n", "print(f\"N_T = {n_t} vs {(3*length**2 // 2)*8}\")\n", "print(f\"N_rot = {n_rot} vs {(3 * length**2 + 2*length**2)}\")" ] @@ -283,8 +268,8 @@ "metadata": {}, "outputs": [], "source": [ - "import attrs\n", "from qualtran.drawing import show_call_graph\n", + "from qualtran.resource_counting.generalizers import generalize_rotation_angle \n", "# get appropriate epsilon given our input parameters now we know the number of rotations\n", "eps_single_rot = get_single_rot_eps(n_rot, delta_ht, timestep)\n", "print(f\"Adjusted eps_single_rot: {eps_single_rot}\")\n", @@ -294,7 +279,7 @@ "# But let's show the call graph anyway to check the parameters all all what we expect.\n", "updated_eps_bloqs = tuple(attrs.evolve(b, eps=eps_single_rot) for b in trotter_step.bloqs)\n", "trotter_step = attrs.evolve(trotter_step, bloqs=updated_eps_bloqs)\n", - "trotter_step_g, _ = trotter_step.call_graph(generalizer=catch_rotations)\n", + "trotter_step_g, _ = trotter_step.call_graph(generalizer=generalize_rotation_angle)\n", "show_call_graph(trotter_step_g)" ] }, @@ -418,7 +403,7 @@ "metadata": {}, "outputs": [], "source": [ - "from scipy.optimize import minimize, bisect, newton\n", + "from scipy.optimize import minimize\n", "def objective(delta_ts, delta_ht, n_rot, n_t, xi_bound, prod_ord):\n", " t_counts = qpe_t_count(epsilon - delta_ts - delta_ht, delta_ts, delta_ht, n_rot, n_t, xi_bound, prod_ord)\n", " return t_counts\n", @@ -467,7 +452,7 @@ "source": [ "from qualtran.bloqs.chemistry.trotter.hubbard.trotter_step import build_plaq_hwp_unitary_second_order_suzuki\n", "trotter_step_hwp = build_plaq_hwp_unitary_second_order_suzuki(length, hubb_u, timestep, eps=1e-10)\n", - "n_t_hwp, n_rot_hwp = t_and_rot_counts_from_sigma(trotter_step_hwp.call_graph(generalizer=catch_rotations)[1])\n", + "n_t_hwp, n_rot_hwp = t_and_rot_counts_from_bloq(trotter_step_hwp)\n", "print(f\"N_T(HWP) = {n_t_hwp} vs {(3*length**2 // 2)*8}\")\n", "print(f\"N_rot(HWP) = {n_rot_hwp} vs {(3 * length**2 + 2*length**2)}\")\n", "delta_ht_opt, delta_ts_opt, delta_pe_opt, t_opt = minimize_linesearch(n_rot_hwp, n_t_hwp, xi_bound, prod_ord)\n", @@ -490,7 +475,7 @@ "outputs": [], "source": [ "trotter_step_hwp = build_plaq_hwp_unitary_second_order_suzuki(length, hubb_u, timestep, eps=1e-10, strip_layer=True)\n", - "n_t_hwp, n_rot_hwp = t_and_rot_counts_from_sigma(trotter_step_hwp.call_graph(generalizer=catch_rotations)[1])\n", + "n_t_hwp, n_rot_hwp = t_and_rot_counts_from_bloq(trotter_step_hwp)\n", "print(f\"N_T(HWP) = {n_t_hwp}\")\n", "print(f\"N_rot(HWP) = {n_rot_hwp}\")\n", "delta_ht_opt, delta_ts_opt, delta_pe_opt, t_opt = minimize_linesearch(n_rot_hwp, n_t_hwp, xi_bound, prod_ord)\n", @@ -513,14 +498,18 @@ "metadata": {}, "outputs": [], "source": [ - "s_eps_r, s_length, s_hubb_u, s_timestep, s_tau = sympy.symbols(r'\\epsilon_{R}, L, u, t, \\tau')" + "s_eps_r, s_length, s_hubb_u, s_timestep, s_tau = sympy.symbols(r'\\epsilon_{R}, L, u, t, \\tau')\n", + "s_delta_ht, s_delta_ts, s_delta_pe, s_p, s_xi = sympy.symbols(\n", + " '\\Delta_{HT}, \\Delta_{TS}, \\Delta_{PE}, p, xi'\n", + ")\n", + "s_n_rot, s_n_t, s_n_pe = sympy.symbols('N_R, N_T, N_PE')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "First let's check the Bloq counts look correct for the Trotter step, there are two sources rotations from the interaction and hopping bloq and some direct T gates from the `TwoBitFFFT` gate." + "First let's check the Bloq counts look correct for the Trotter step, there are two sources: rotations from the interaction and hopping bloq, and some direct T gates from the `TwoBitFFFT` gate." ] }, { @@ -534,8 +523,8 @@ "s_trotter_step = build_plaq_unitary_second_order_suzuki(\n", " s_length, s_hubb_u, s_timestep, eps=s_eps_r, hubb_t=s_tau\n", ")\n", - "t_counts = t_counts_from_sigma(s_trotter_step.call_graph(generalizer=catch_rotations)[1])\n", - "t_counts" + "t_counts, n_rot = t_and_rot_counts_from_bloq(s_trotter_step)\n", + "t_counts + rotation_cost(n_rot, s_delta_ht, s_timestep)" ] }, { @@ -545,7 +534,7 @@ "outputs": [], "source": [ "# check the symbolic counts match the expected counts\n", - "t_counts_orig = t_counts_from_sigma(trotter_step.call_graph(generalizer=catch_rotations)[1])\n", + "t_counts_orig, n_rot = t_and_rot_counts_from_bloq(trotter_step)\n", "# for some reason substituting both at once leads to a precision error\n", "t_counts = t_counts.evalf(subs={s_eps_r: eps_single_rot})\n", "t_counts_symb = t_counts.evalf(subs={s_length: length})\n", @@ -565,10 +554,6 @@ "metadata": {}, "outputs": [], "source": [ - "s_delta_ht, s_delta_ts, s_delta_pe, s_p, s_xi = sympy.symbols(\n", - " '\\Delta_{HT}, \\Delta_{TS}, \\Delta_{PE}, p, xi'\n", - ")\n", - "s_n_rot, s_n_t, s_n_pe = sympy.symbols('N_R, N_T, N_PE')\n", "s_timestep = (s_delta_ts / s_xi) ** (1 / s_p)\n", "s_eps_r = (s_delta_ht * s_timestep) / s_n_rot\n", "s_n_pe = 0.76 * sympy.pi / (s_delta_pe * s_timestep)\n", @@ -577,8 +562,9 @@ ")\n", "# just use this cost in lieu of a QPE bloq\n", "# See: https://github.com/quantumlib/Qualtran/issues/932 this should be replaced by a real bloq.\n", - "t_counts = s_n_pe * t_counts_from_sigma(s_trotter_step.call_graph(generalizer=catch_rotations)[1])\n", - "t_counts" + "s_t_counts, s_n_rot = t_and_rot_counts_from_bloq(s_trotter_step)\n", + "qpe_cost_symb = s_n_pe * (s_t_counts + rotation_cost(s_n_rot, s_delta_ht, s_timestep))\n", + "qpe_cost_symb" ] }, { @@ -587,7 +573,7 @@ "metadata": {}, "outputs": [], "source": [ - "symb_t_count = t_counts.evalf(\n", + "symb_t_count = qpe_cost_symb.evalf(\n", " subs={\n", " s_length: length,\n", " s_delta_ht: delta_ht,\n", @@ -597,7 +583,7 @@ " s_n_rot: n_rot,\n", " }\n", ")\n", - "symb_t_count = symb_t_count.evalf(subs={s_p: prod_ord})\n", + "symb_t_count = symb_t_count.evalf(subs={s_p: prod_ord}, maxn=500)\n", "tot_t_count = qpe_t_count(delta_pe, delta_ts, delta_ht, n_rot, n_t, xi_bound, prod_ord)\n", "assert int(symb_t_count) == int(tot_t_count)" ] @@ -619,7 +605,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/trotter_step_test.py b/qualtran/bloqs/chemistry/trotter/hubbard/trotter_step_test.py index 773235f2b..0bcdb6e32 100644 --- a/qualtran/bloqs/chemistry/trotter/hubbard/trotter_step_test.py +++ b/qualtran/bloqs/chemistry/trotter/hubbard/trotter_step_test.py @@ -13,36 +13,23 @@ # limitations under the License. import pytest -from qualtran import Bloq -from qualtran.bloqs.basic_gates import Rz -from qualtran.bloqs.basic_gates.t_gate import TGate -from qualtran.bloqs.bookkeeping import ArbitraryClifford from qualtran.bloqs.chemistry.trotter.hubbard.trotter_step import ( build_plaq_unitary_second_order_suzuki, ) -from qualtran.resource_counting.generalizers import PHI +from qualtran.resource_counting import get_cost_value, QECGatesCost from qualtran.testing import execute_notebook -def catch_rotations(bloq) -> Bloq: - if isinstance(bloq, Rz): - if isinstance(bloq.angle, float) and abs(bloq.angle) < 1e-12: - return ArbitraryClifford(1) - else: - return Rz(angle=PHI) - return bloq - - def test_second_order_suzuki_costs(): length = 8 u = 4 - dt = 0.1 + dt = 0.1234 unitary = build_plaq_unitary_second_order_suzuki(length, u, dt) - _, sigma = unitary.call_graph(generalizer=catch_rotations) + costs = get_cost_value(unitary, QECGatesCost()) # there are 3 hopping unitaries contributing 8 Ts from from the F gate - assert sigma[TGate()] == (3 * length**2 // 2) * 8 + assert costs.total_t_count(ts_per_rotation=0) == (3 * length**2 // 2) * 8 # 3 hopping unitaries and 2 interaction unitaries - assert sigma[Rz(PHI)] == (3 * length**2 + 2 * length**2) + assert costs.rotation == (3 * length**2 + 2 * length**2) @pytest.mark.notebook diff --git a/qualtran/bloqs/chemistry/writing_algorithms.ipynb b/qualtran/bloqs/chemistry/writing_algorithms.ipynb index f020eef15..262f4e1c4 100644 --- a/qualtran/bloqs/chemistry/writing_algorithms.ipynb +++ b/qualtran/bloqs/chemistry/writing_algorithms.ipynb @@ -180,16 +180,26 @@ "outputs": [], "source": [ "import numpy as np\n", - "from qualtran.bloqs.basic_gates import TGate\n", - "from qualtran.bloqs.chemistry.chem_tutorials import plot_linear_log_log\n", "import matplotlib.pyplot as plt\n", "\n", + "from qualtran import Bloq\n", + "from qualtran.bloqs.chemistry.chem_tutorials import plot_linear_log_log\n", + "from qualtran.resource_counting import get_cost_value, QECGatesCost\n", + "from qualtran.resource_counting.generalizers import generalize_cswap_approx\n", + "\n", + "def toffoli_count(bloq: Bloq) -> int:\n", + " # TODO: The rotations here should be in terms of Toffoli via phase gradient rotation.\n", + " cost = get_cost_value(bloq, QECGatesCost(), generalizer=generalize_cswap_approx).total_t_and_ccz_count(ts_per_rotation=0)\n", + " n_t = cost['n_t']\n", + " assert n_t == 0, \"Found raw T gates, should be only Toffolis\"\n", + " return cost['n_ccz']\n", + "\n", "fig, ax = plt.subplots()\n", "basis_vals = np.linspace(10, 100, 10, dtype=int)\n", - "tcounts = [PrepareSecondQuantization(int(n), qroam_block_size=1).call_graph()[1][TGate()] for n in basis_vals]\n", - "plot_linear_log_log(ax, basis_vals, np.array(tcounts))\n", + "toff_counts = [toffoli_count(PrepareSecondQuantization(int(n), qroam_block_size=1)) for n in basis_vals]\n", + "plot_linear_log_log(ax, basis_vals, np.array(toff_counts))\n", "ax.set_xlabel(\"$N$\")\n", - "ax.set_ylabel(\"$T$ count\")" + "ax.set_ylabel(\"Toffoli count\")" ] }, { @@ -208,12 +218,12 @@ "fig, ax = plt.subplots()\n", "basis_vals = np.linspace(50, 200, 10, dtype=int)\n", "for ib, block_size in enumerate([1, 2, 8, 32, None]):\n", - " tcounts = [PrepareSecondQuantization(int(n), qroam_block_size=block_size).call_graph()[1][TGate()] for n in basis_vals]\n", + " toff_counts = [toffoli_count(PrepareSecondQuantization(int(n), qroam_block_size=block_size)) for n in basis_vals]\n", " if block_size is None:\n", " block_size = 'opt'\n", - " plot_linear_log_log(ax, basis_vals, np.array(tcounts), label=f'block size = {block_size}: ', color=f'C{ib}')\n", + " plot_linear_log_log(ax, basis_vals, np.array(toff_counts), label=f'block size = {block_size}: ', color=f'C{ib}')\n", "ax.set_xlabel(\"$N$\")\n", - "ax.set_ylabel(\"$T$ count\")" + "ax.set_ylabel(\"Tofflis count\")" ] }, { @@ -302,9 +312,8 @@ "from typing import Dict\n", "from qualtran import SoquetT, BloqBuilder, Register\n", "\n", - "from qualtran.bloqs.state_preparation import PrepareUniformSuperposition\n", "from qualtran.bloqs.data_loading.select_swap_qrom import SelectSwapQROM, find_optimal_log_block_size\n", - "from qualtran.bloqs.basic_gates import CSwap, Hadamard, OnEach\n", + "from qualtran.bloqs.basic_gates import Hadamard, OnEach\n", "\n", "\n", "@frozen\n", @@ -519,8 +528,8 @@ "metadata": {}, "outputs": [], "source": [ - "prep_basic = PrepareSecondQuantization(num_spin_orb).call_graph()[1].get(TGate())\n", - "prep_decom = prep.decompose_bloq().call_graph()[1].get(TGate())\n", + "prep_basic = toffoli_count(PrepareSecondQuantization(num_spin_orb))\n", + "prep_decom = toffoli_count(prep.decompose_bloq())\n", "print(f\"call graph = {prep_basic}, decomposition = {prep_decom}\")" ] }, @@ -567,7 +576,7 @@ "alt_pqrs, keep = build_alt_keep_vals(tpq, eris, num_spin_orb, num_bits_state_prep)\n", "prep = PrepareSecondQuantizationDetailed(num_spin_orb, tuple(alt_pqrs), tuple(keep), num_bits_state_prep=num_bits_state_prep)\n", "prep_sparse = PrepareSparse.from_hamiltonian_coeffs(num_spin_orb, tpq, eris, num_bits_state_prep).decompose_bloq()\n", - "print(f\"naive = {prep.call_graph()[1].get(TGate())}, sparse = {prep_sparse.call_graph()[1].get(TGate())}\")" + "print(f\"naive = {toffoli_count(prep)}, sparse = {toffoli_count(prep_sparse)}\")" ] } ], @@ -587,7 +596,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.11.9" } }, "nbformat": 4, From d811be08340246e308aa7145a23b31546af51829 Mon Sep 17 00:00:00 2001 From: Anurudh Peduri <7265746+anurudhp@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:49:05 -0800 Subject: [PATCH 07/12] Make `BloqBuilder.join` accept array-like of soquets (#1509) --- qualtran/_infra/composite_bloq.py | 5 +++-- qualtran/_infra/composite_bloq_test.py | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/qualtran/_infra/composite_bloq.py b/qualtran/_infra/composite_bloq.py index 8ad8b50c4..450612c6d 100644 --- a/qualtran/_infra/composite_bloq.py +++ b/qualtran/_infra/composite_bloq.py @@ -1234,12 +1234,13 @@ def split(self, soq: Soquet) -> NDArray[Soquet]: # type: ignore[type-var] return self.add(Split(dtype=soq.reg.dtype), reg=soq) - def join(self, soqs: NDArray[Soquet], dtype: Optional[QDType] = None) -> Soquet: # type: ignore[type-var] + def join(self, soqs: SoquetInT, dtype: Optional[QDType] = None) -> Soquet: from qualtran.bloqs.bookkeeping import Join try: + soqs = np.asarray(soqs) (n,) = soqs.shape - except AttributeError: + except (AttributeError, ValueError): raise ValueError("`join` expects a 1-d array of input soquets to join.") from None if not all(soq.reg.bitsize == 1 for soq in soqs): diff --git a/qualtran/_infra/composite_bloq_test.py b/qualtran/_infra/composite_bloq_test.py index ffe39213c..114b55083 100644 --- a/qualtran/_infra/composite_bloq_test.py +++ b/qualtran/_infra/composite_bloq_test.py @@ -389,6 +389,12 @@ def test_util_convenience_methods(): assert len(cbloq.connections) == 1 + 10 + 1 +def test_join_list(): + bb = BloqBuilder() + qs = [bb.allocate() for _ in range(10)] + _ = bb.join(qs) + + def test_util_convenience_methods_errors(): bb = BloqBuilder() From 6de1e12583db6696e90873ecad39baa2c2f806ee Mon Sep 17 00:00:00 2001 From: Anurudh Peduri <7265746+anurudhp@users.noreply.github.com> Date: Thu, 19 Dec 2024 08:49:27 -0800 Subject: [PATCH 08/12] Allow computing cost from decomposition of cirq gates (#1510) --- qualtran/bloqs/basic_gates/rotation.py | 2 +- qualtran/cirq_interop/_cirq_to_bloq.py | 8 +++++--- qualtran/cirq_interop/_cirq_to_bloq_test.py | 17 ++++++++++++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/qualtran/bloqs/basic_gates/rotation.py b/qualtran/bloqs/basic_gates/rotation.py index 9adb439e1..ae297c974 100644 --- a/qualtran/bloqs/basic_gates/rotation.py +++ b/qualtran/bloqs/basic_gates/rotation.py @@ -108,7 +108,7 @@ def _z_pow() -> ZPowGate: @frozen class CZPowGate(CirqGateAsBloqBase): - exponent: float = 1.0 + exponent: SymbolicFloat = 1.0 global_shift: float = 0.0 eps: SymbolicFloat = 1e-11 diff --git a/qualtran/cirq_interop/_cirq_to_bloq.py b/qualtran/cirq_interop/_cirq_to_bloq.py index f71810df9..b74383534 100644 --- a/qualtran/cirq_interop/_cirq_to_bloq.py +++ b/qualtran/cirq_interop/_cirq_to_bloq.py @@ -150,9 +150,11 @@ def cirq_gate(self) -> cirq.Gate: def my_static_costs(self, cost_key: 'CostKey'): if isinstance(cost_key, QECGatesCost): t_count = _from_directly_countable_cirq(self.cirq_gate) - if t_count is None: - raise ValueError(f"Cirq gate must be directly countable, not {self.cirq_gate}") - return GateCounts(t=t_count.t, rotation=t_count.rotations, clifford=t_count.clifford) + if t_count is not None: + return GateCounts( + t=t_count.t, rotation=t_count.rotations, clifford=t_count.clifford + ) + return NotImplemented def _cirq_wire_symbol_to_qualtran_wire_symbol(symbol: str, side: Side) -> 'WireSymbol': diff --git a/qualtran/cirq_interop/_cirq_to_bloq_test.py b/qualtran/cirq_interop/_cirq_to_bloq_test.py index a016d85a6..314c404a4 100644 --- a/qualtran/cirq_interop/_cirq_to_bloq_test.py +++ b/qualtran/cirq_interop/_cirq_to_bloq_test.py @@ -17,6 +17,7 @@ import cirq import numpy as np import pytest +import sympy from attrs import frozen import qualtran @@ -34,11 +35,12 @@ SoquetT, ) from qualtran._infra.gate_with_registers import get_named_qubits -from qualtran.bloqs.basic_gates import CNOT, GlobalPhase, OneState +from qualtran.bloqs.basic_gates import CNOT, CZPowGate, GlobalPhase, OneState, YPowGate from qualtran.bloqs.bookkeeping import Allocate, Free, Join, Split from qualtran.bloqs.mcmt.and_bloq import And from qualtran.cirq_interop import cirq_optree_to_cbloq, CirqGateAsBloq, CirqQuregT from qualtran.cirq_interop.t_complexity_protocol import TComplexity +from qualtran.resource_counting import GateCounts, get_cost_value, QECGatesCost @frozen @@ -228,3 +230,16 @@ def test_cirq_gate_as_bloq_decompose_raises(): def test_cirq_gate_as_bloq_diagram_info(): assert cirq.circuit_diagram_info(GlobalPhase(exponent=0.5)) is None + + +def test_cirq_gate_cost_via_decomp(): + theta = sympy.Symbol("theta", real=True) + cirq_swappow = cirq.SwapPowGate(exponent=theta) + + swappow_bloq = CirqGateAsBloq(cirq_swappow) + + _, sigma = swappow_bloq.call_graph() + assert sigma == {CNOT(): 2, YPowGate(0.5): 1, YPowGate(-0.5): 1, CZPowGate(theta): 1} + + gc_swappow = get_cost_value(swappow_bloq, QECGatesCost()) + assert gc_swappow == GateCounts(clifford=4, rotation=1) From a0d4d6bacdac9bed0d0bdaa328f39997d0282cdf Mon Sep 17 00:00:00 2001 From: Matthew Harrigan Date: Thu, 19 Dec 2024 12:35:40 -0500 Subject: [PATCH 09/12] More explicit documentation of endianness (#1500) --- qualtran/_infra/data_types.py | 24 +++++++++++++++++------- qualtran/_infra/data_types_test.py | 7 +++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/qualtran/_infra/data_types.py b/qualtran/_infra/data_types.py index ee918d506..82c7bcdde 100644 --- a/qualtran/_infra/data_types.py +++ b/qualtran/_infra/data_types.py @@ -16,7 +16,7 @@ We often wish to write algorithms which operate on quantum data. One can think of quantum data types, similar to classical data types, where a collection of qubits can be used to represent a specific quantum data type (eg: a quantum -integer of width 32 would comprise of 32 qubits, similar to a classical uint32 +integer of width 32 would comprise 32 qubits, similar to a classical uint32 type). More generally, many current primitives and algorithms in qualtran implicitly expect registers which represent signed or unsigned integers, fixed-point (fp) numbers , or “classical registers” which store some classical @@ -40,7 +40,7 @@ type assuming the bitsizes match. QInt(32) == QAny(32), QInt(32) != QFxp(32, 16). QInt(32) != QUInt(32). 5. We assume a big endian convention for addressing QBits in registers -throughout qualtran. Recall that in a big endian convention the most signficant +throughout qualtran. Recall that in a big endian convention the most significant bit is at index 0. If you iterate through the bits in a register they will be yielded from most significant to least significant. 6. Ones' complement integers are used extensively in quantum algorithms. We have @@ -181,7 +181,7 @@ def __str__(self): @attrs.frozen class QAny(QDType): - """Opaque bag-of-qbits type.""" + """Opaque bag-of-qubits type.""" bitsize: SymbolicInt @@ -214,7 +214,10 @@ def assert_valid_classical_val_array(self, val_array, debug_str: str = 'val'): class QInt(QDType): """Signed Integer of a given width bitsize. - A two's complement representation is assumed for negative integers. + A two's complement representation is used for negative integers. + + Here (and throughout Qualtran), we use a big-endian bit convention. The most significant + bit is at index 0. Attributes: bitsize: The number of qubits used to represent the integer. @@ -275,7 +278,11 @@ def __str__(self): class QIntOnesComp(QDType): """Signed Integer of a given width bitsize. - A ones' complement representation is assumed for negative integers. + In contrast to `QInt`, this data type uses the ones' complement representation for negative + integers. + + Here (and throughout Qualtran), we use a big-endian bit convention. The most significant + bit is at index 0. Attributes: bitsize: The number of qubits used to represent the integer. @@ -323,8 +330,11 @@ def assert_valid_classical_val(self, val, debug_str: str = 'val'): class QUInt(QDType): """Unsigned integer of a given width bitsize which wraps around upon overflow. - Similar to unsigned integer types in C. Any intended wrap around effect is - expected to be handled by the developer. + Any intended wrap around effect is expected to be handled by the developer, similar + to an unsigned integer type in C. + + Here (and throughout Qualtran), we use a big-endian bit convention. The most significant + bit is at index 0. Attributes: bitsize: The number of qubits used to represent the integer. diff --git a/qualtran/_infra/data_types_test.py b/qualtran/_infra/data_types_test.py index 65252c5cb..ff08b7df6 100644 --- a/qualtran/_infra/data_types_test.py +++ b/qualtran/_infra/data_types_test.py @@ -295,6 +295,9 @@ def test_qint_to_and_from_bits(): assert qint4.from_bits(qint4.to_bits(x)) == x assert list(qint4.to_bits(-2)) == [1, 1, 1, 0] assert list(QInt(4).to_bits(2)) == [0, 0, 1, 0] + # MSB at lowest index -- big-endian + assert qint4.from_bits([0, 0, 0, 1]) == 1 + assert qint4.from_bits([0, 0, 0, 1]) < qint4.from_bits([0, 1, 0, 0]) assert qint4.from_bits(qint4.to_bits(-2)) == -2 assert qint4.from_bits(qint4.to_bits(2)) == 2 with pytest.raises(ValueError): @@ -325,6 +328,10 @@ def test_quint_to_and_from_bits(): assert [*quint4.get_classical_domain()] == [*range(0, 16)] assert list(quint4.to_bits(10)) == [1, 0, 1, 0] assert quint4.from_bits(quint4.to_bits(10)) == 10 + # MSB at lowest index -- big-endian + assert quint4.from_bits([0, 0, 0, 1]) == 1 + assert quint4.from_bits([0, 0, 0, 1]) < quint4.from_bits([1, 0, 0, 0]) + for x in range(16): assert quint4.from_bits(quint4.to_bits(x)) == x with pytest.raises(ValueError): From b6cc062b2c1851c9639a4a35bbed58860c7ee90b Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Thu, 19 Dec 2024 12:46:39 -0800 Subject: [PATCH 10/12] Be a bit careful about complexity of the sparse Hamiltonian (#1512) Be a bit careful about complexity Co-authored-by: Matthew Harrigan --- qualtran/bloqs/chemistry/resource_estimation.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qualtran/bloqs/chemistry/resource_estimation.ipynb b/qualtran/bloqs/chemistry/resource_estimation.ipynb index 39585e49e..86889c211 100644 --- a/qualtran/bloqs/chemistry/resource_estimation.ipynb +++ b/qualtran/bloqs/chemistry/resource_estimation.ipynb @@ -64,12 +64,12 @@ "\n", "| Name | $$V_{pqrs} $$ | Space complexity |\n", "| :---- | :---- | :---- | \n", - "| [Sparse](sparse/sparse.ipynb) | $$\\tilde{V}_{pqrs}$$ | $$O(N^3)$$ |\n", + "| [Sparse](sparse/sparse.ipynb) | $$\\tilde{V}_{pqrs}$$ | $$O(N^4)$$ |\n", "| [Single Factorization](sf/single_factorization.ipynb) | $$\\sum_X^{L} L_{pq}^X L_{rs}^X $$ | $$O(N^3)$$ |\n", "| [Double Factorization](df/double_factorization.ipynb) | $$\\sum_X^{} \\left(\\sum_k^{\\Xi} U^{X}_{pk}f_k^X U_{qk}^{X*}\\right)^2$$ | $$O(N^2\\Xi)$$ |\n", "| [Tensor Hypercontraction](thc/thc.ipynb) | $$\\sum_{\\mu\\nu}^{M} \\chi_p^{\\mu}\\chi_q^{\\mu}\\zeta_{\\mu\\nu}\\chi_r^\\nu\\chi_s^\\nu$$ | $$O(N^2)$$ |\n", "\n", - "where the space complexity column represents the amount of classical data ($\\Gamma$, say) required to specify the Hamiltonian. The Toffoli complexity for state preparation for all of these approaches goes roughly like $\\sqrt{\\Gamma}$. " + "the space complexity column represents the amount of classical data ($\\Gamma$, say) required to specify the Hamiltonian. The Toffoli complexity for state preparation for all of these approaches goes roughly like $\\sqrt{\\Gamma}$. Note under certain conditions sparsity may lead to $O(N^3)$ non-zero elements. " ] }, { From af9d13bf448e3c5708bb2f37c9009be8481b4d40 Mon Sep 17 00:00:00 2001 From: Matthew Harrigan Date: Thu, 19 Dec 2024 16:40:11 -0500 Subject: [PATCH 11/12] 2024-12 Bump Dependencies (#1506) * 2024-12 Bump Dependencies * mypy --- dev_tools/requirements/envs/dev.env.txt | 149 +++++++++--------- dev_tools/requirements/envs/docs.env.txt | 102 ++++++------ dev_tools/requirements/envs/format.env.txt | 93 ++++++----- dev_tools/requirements/envs/pip-tools.env.txt | 8 +- dev_tools/requirements/envs/pylint.env.txt | 105 ++++++------ dev_tools/requirements/envs/pytest.env.txt | 103 ++++++------ dev_tools/requirements/envs/runtime.env.txt | 93 ++++++----- qualtran/_infra/controlled.py | 2 +- qualtran/bloqs/multiplexers/apply_lth_bloq.py | 2 +- .../multiplexers/selected_majorana_fermion.py | 4 +- qualtran/cirq_interop/_bloq_to_cirq.py | 2 +- qualtran/cirq_interop/_cirq_to_bloq.py | 6 +- 12 files changed, 325 insertions(+), 344 deletions(-) diff --git a/dev_tools/requirements/envs/dev.env.txt b/dev_tools/requirements/envs/dev.env.txt index e536aa3d7..b88a9a8bf 100644 --- a/dev_tools/requirements/envs/dev.env.txt +++ b/dev_tools/requirements/envs/dev.env.txt @@ -12,7 +12,7 @@ alabaster==1.0.0 # via sphinx annotated-types==0.7.0 # via pydantic -anyio==4.6.0 +anyio==4.7.0 # via # httpx # jupyter-server @@ -30,9 +30,9 @@ astor==0.8.1 # via # flynt # tensorflow-docs -astroid==3.3.5 +astroid==3.3.6 # via pylint -asttokens==2.4.1 +asttokens==3.0.0 # via stack-data async-lru==2.0.4 # via jupyterlab @@ -43,7 +43,7 @@ attrs==24.2.0 # jsonschema # jupyter-cache # referencing -autoray==0.6.12 +autoray==0.7.0 # via # cotengra # quimb @@ -62,11 +62,11 @@ beautifulsoup4==4.12.3 # pydata-sphinx-theme black==24.8.0 # via -r deps/format.txt -bleach==6.1.0 +bleach==6.2.0 # via nbconvert -blinker==1.8.2 +blinker==1.9.0 # via flask -build==1.2.2 +build==1.2.2.post1 # via pip-tools cachetools==5.5.0 # via -r deps/runtime.txt @@ -79,7 +79,7 @@ cffi==1.17.1 # via # argon2-cffi-bindings # cryptography -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via requests cirq-core==1.4.0 # via @@ -95,19 +95,19 @@ comm==0.2.2 # via # ipykernel # ipywidgets -contourpy==1.3.0 +contourpy==1.3.1 # via matplotlib cotengra==0.6.2 # via quimb -coverage[toml]==7.6.1 +coverage[toml]==7.6.9 # via pytest-cov -cryptography==43.0.1 +cryptography==44.0.0 # via secretstorage cycler==0.12.1 # via matplotlib cytoolz==1.0.0 # via quimb -dash==2.18.1 +dash==2.18.2 # via -r deps/runtime.txt dash-core-components==2.0.0 # via dash @@ -115,7 +115,7 @@ dash-html-components==2.0.0 # via dash dash-table==5.0.0 # via dash -debugpy==1.8.6 +debugpy==1.8.9 # via ipykernel decorator==5.1.1 # via ipython @@ -125,7 +125,7 @@ deprecation==2.1.0 # via openfermion dill==0.3.9 # via pylint -distlib==0.3.8 +distlib==0.3.9 # via virtualenv docutils==0.21.2 # via @@ -144,7 +144,7 @@ execnet==2.1.1 # via pytest-xdist executing==2.1.0 # via stack-data -fastjsonschema==2.20.0 +fastjsonschema==2.21.1 # via nbformat filelock==3.16.1 # via @@ -155,21 +155,21 @@ flask==3.0.3 # via dash flynt==0.78 # via -r deps/format.txt -fonttools==4.54.1 +fonttools==4.55.2 # via matplotlib fqdn==1.5.1 # via jsonschema fxpmath==0.4.9 # via -r deps/runtime.txt -galois==0.4.2 +galois==0.4.3 # via -r deps/runtime.txt graphviz==0.20.3 # via qref greenlet==3.1.1 # via sqlalchemy -grpcio==1.66.2 +grpcio==1.68.1 # via grpcio-tools -grpcio-tools==1.66.2 +grpcio-tools==1.68.1 # via -r deps/packaging.txt h11==0.14.0 # via httpcore @@ -177,9 +177,9 @@ h5py==3.12.1 # via # openfermion # pyscf -httpcore==1.0.6 +httpcore==1.0.7 # via httpx -httpx==0.27.2 +httpx==0.28.1 # via jupyterlab idna==3.10 # via @@ -195,7 +195,6 @@ importlib-metadata==8.5.0 # jupyter-cache # keyring # myst-nb - # twine iniconfig==2.0.0 # via pytest ipykernel==6.29.5 @@ -203,7 +202,7 @@ ipykernel==6.29.5 # -r deps/pytest.txt # jupyterlab # myst-nb -ipython==8.28.0 +ipython==8.30.0 # via # -r deps/runtime.txt # ipykernel @@ -228,13 +227,13 @@ jaraco-context==6.0.1 # via keyring jaraco-functools==4.1.0 # via keyring -jax==0.4.34 +jax==0.4.36 # via openfermion -jaxlib==0.4.34 +jaxlib==0.4.36 # via # jax # openfermion -jedi==0.19.1 +jedi==0.19.2 # via ipython jeepney==0.8.0 # via @@ -250,7 +249,7 @@ jinja2==3.1.4 # nbconvert # sphinx # tensorflow-docs -json5==0.9.25 +json5==0.10.0 # via jupyterlab-server jsonpointer==3.0.0 # via jsonschema @@ -259,9 +258,9 @@ jsonschema[format-nongpl]==4.23.0 # jupyter-events # jupyterlab-server # nbformat -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via jsonschema -jupyter-cache==1.0.0 +jupyter-cache==1.0.1 # via myst-nb jupyter-client==8.6.3 # via @@ -290,7 +289,7 @@ jupyter-server==2.14.2 # notebook-shim jupyter-server-terminals==0.5.3 # via jupyter-server -jupyterlab==4.2.5 +jupyterlab==4.3.2 # via notebook jupyterlab-pygments==0.3.0 # via nbconvert @@ -300,7 +299,7 @@ jupyterlab-server==2.27.3 # notebook jupyterlab-widgets==3.0.13 # via ipywidgets -keyring==25.4.1 +keyring==25.5.0 # via twine kiwisolver==1.4.7 # via matplotlib @@ -311,12 +310,12 @@ markdown-it-py==3.0.0 # mdit-py-plugins # myst-parser # rich -markupsafe==2.1.5 +markupsafe==3.0.2 # via # jinja2 # nbconvert # werkzeug -matplotlib==3.9.2 +matplotlib==3.9.3 # via # -r deps/runtime.txt # ase @@ -343,7 +342,7 @@ more-itertools==10.5.0 # jaraco-functools mpmath==1.3.0 # via sympy -mypy==1.11.2 +mypy==1.13.0 # via -r deps/mypy.txt mypy-extensions==1.0.0 # via @@ -355,7 +354,7 @@ myst-nb==1.1.2 # via -r deps/docs.txt myst-parser==4.0.0 # via myst-nb -nbclient==0.10.0 +nbclient==0.10.1 # via # jupyter-cache # myst-nb @@ -379,14 +378,14 @@ nest-asyncio==1.6.0 # via # dash # ipykernel -networkx==3.3 +networkx==3.4.2 # via # -r deps/runtime.txt # cirq-core # openfermion -nh3==0.2.18 +nh3==0.2.19 # via readme-renderer -notebook==7.2.2 +notebook==7.3.1 # via # -r deps/dev-tools.txt # -r deps/runtime.txt @@ -425,7 +424,7 @@ opt-einsum==3.4.0 # via jax overrides==7.7.0 # via jupyter-server -packaging==24.1 +packaging==24.2 # via # black # build @@ -437,9 +436,9 @@ packaging==24.1 # matplotlib # nbconvert # plotly - # pydata-sphinx-theme # pytest # sphinx + # twine pandas==2.2.3 # via cirq-core pandocfilters==1.5.1 @@ -450,11 +449,11 @@ pathspec==0.12.1 # via black pexpect==4.9.0 # via ipython -pillow==10.4.0 +pillow==11.0.0 # via matplotlib pip-tools==7.4.1 # via -r deps/pip-tools.txt -pkginfo==1.10.0 +pkginfo==1.12.0 # via twine platformdirs==4.3.6 # via @@ -468,17 +467,17 @@ plotly==5.24.1 # dash pluggy==1.5.0 # via pytest -prometheus-client==0.21.0 +prometheus-client==0.21.1 # via jupyter-server prompt-toolkit==3.0.48 # via ipython -protobuf==5.28.2 +protobuf==5.29.1 # via # -r deps/runtime.txt # grpcio-tools # mypy-protobuf # tensorflow-docs -psutil==6.0.0 +psutil==6.1.0 # via # ipykernel # quimb @@ -494,15 +493,15 @@ pure-eval==0.2.3 # via stack-data pycparser==2.22 # via cffi -pydantic==2.9.2 +pydantic==2.10.3 # via # bartiq # qref -pydantic-core==2.23.4 +pydantic-core==2.27.1 # via pydantic -pydata-sphinx-theme==0.15.4 +pydata-sphinx-theme==0.16.0 # via -r deps/docs.txt -pydot==3.0.2 +pydot==3.0.3 # via -r deps/runtime.txt pygments==2.18.0 # via @@ -513,7 +512,7 @@ pygments==2.18.0 # readme-renderer # rich # sphinx -pylint==3.3.1 +pylint==3.3.2 # via -r deps/pylint.txt pyparsing==3.1.4 # via @@ -526,7 +525,7 @@ pyproject-hooks==1.2.0 # pip-tools pyscf==2.7.0 # via openfermion -pytest==8.3.3 +pytest==8.3.4 # via # -r deps/pylint.txt # -r deps/pytest.txt @@ -535,7 +534,7 @@ pytest==8.3.3 # pytest-xdist pytest-asyncio==0.24.0 # via -r deps/pytest.txt -pytest-cov==5.0.0 +pytest-cov==6.0.0 # via -r deps/pytest.txt pytest-xdist==3.6.1 # via -r deps/pytest.txt @@ -565,11 +564,11 @@ qref==0.7.0 # via # -r deps/runtime.txt # bartiq -qsharp==1.9.0 +qsharp==1.11.1 # via -r deps/runtime.txt -qsharp-widgets==1.9.0 +qsharp-widgets==1.11.1 # via -r deps/runtime.txt -quimb==1.8.4 +quimb==1.9.0 # via -r deps/runtime.txt readme-renderer==44.0 # via twine @@ -600,9 +599,9 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events -rich==13.9.2 +rich==13.9.4 # via twine -rpds-py==0.20.0 +rpds-py==0.22.3 # via # jsonschema # referencing @@ -619,24 +618,20 @@ secretstorage==3.3.3 # via keyring send2trash==1.8.3 # via jupyter-server -six==1.16.0 +six==1.17.0 # via - # asttokens - # bleach # python-dateutil # retrying # rfc3339-validator sniffio==1.3.1 - # via - # anyio - # httpx + # via anyio snowballstemmer==2.2.0 # via sphinx sortedcontainers==2.4.0 # via cirq-core soupsieve==2.6 # via beautifulsoup4 -sphinx==8.0.2 +sphinx==8.1.3 # via # -r deps/docs.txt # -r deps/pylint.txt @@ -655,7 +650,7 @@ sphinxcontrib-qthelp==2.0.0 # via sphinx sphinxcontrib-serializinghtml==2.0.0 # via sphinx -sqlalchemy==2.0.35 +sqlalchemy==2.0.36 # via jupyter-cache stack-data==0.6.3 # via ipython @@ -678,9 +673,9 @@ terminado==0.18.1 # via # jupyter-server # jupyter-server-terminals -tinycss2==1.3.0 +tinycss2==1.4.0 # via nbconvert -tomli==2.0.2 +tomli==2.2.1 # via # black # build @@ -696,7 +691,7 @@ tomlkit==0.13.2 # via pylint toolz==1.0.0 # via cytoolz -tornado==6.4.1 +tornado==6.4.2 # via # ipykernel # jupyter-client @@ -704,7 +699,7 @@ tornado==6.4.1 # jupyterlab # notebook # terminado -tqdm==4.66.5 +tqdm==4.67.1 # via # cirq-core # quimb @@ -723,11 +718,11 @@ traitlets==5.14.3 # nbclient # nbconvert # nbformat -twine==5.1.1 +twine==6.0.1 # via -r deps/packaging.txt -types-protobuf==5.28.0.20240924 +types-protobuf==5.29.1.20241207 # via mypy-protobuf -types-python-dateutil==2.9.0.20241003 +types-python-dateutil==2.9.0.20241206 # via arrow typing-extensions==4.12.2 # via @@ -756,11 +751,11 @@ urllib3==2.2.3 # via # requests # twine -virtualenv==20.26.6 +virtualenv==20.28.0 # via -r deps/packaging.txt wcwidth==0.2.13 # via prompt-toolkit -webcolors==24.8.0 +webcolors==24.11.1 # via jsonschema webencodings==0.5.1 # via @@ -768,17 +763,17 @@ webencodings==0.5.1 # tinycss2 websocket-client==1.8.0 # via jupyter-server -werkzeug==3.0.4 +werkzeug==3.0.6 # via # dash # flask -wheel==0.44.0 +wheel==0.45.1 # via # -r deps/packaging.txt # pip-tools widgetsnbextension==4.0.13 # via ipywidgets -zipp==3.20.2 +zipp==3.21.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/dev_tools/requirements/envs/docs.env.txt b/dev_tools/requirements/envs/docs.env.txt index d712bd9b1..2dd447053 100644 --- a/dev_tools/requirements/envs/docs.env.txt +++ b/dev_tools/requirements/envs/docs.env.txt @@ -20,7 +20,7 @@ annotated-types==0.7.0 # via # -c envs/dev.env.txt # pydantic -anyio==4.6.0 +anyio==4.7.0 # via # -c envs/dev.env.txt # httpx @@ -45,7 +45,7 @@ astor==0.8.1 # via # -c envs/dev.env.txt # tensorflow-docs -asttokens==2.4.1 +asttokens==3.0.0 # via # -c envs/dev.env.txt # stack-data @@ -61,7 +61,7 @@ attrs==24.2.0 # jsonschema # jupyter-cache # referencing -autoray==0.6.12 +autoray==0.7.0 # via # -c envs/dev.env.txt # cotengra @@ -81,11 +81,11 @@ beautifulsoup4==4.12.3 # -c envs/dev.env.txt # nbconvert # pydata-sphinx-theme -bleach==6.1.0 +bleach==6.2.0 # via # -c envs/dev.env.txt # nbconvert -blinker==1.8.2 +blinker==1.9.0 # via # -c envs/dev.env.txt # flask @@ -103,7 +103,7 @@ cffi==1.17.1 # via # -c envs/dev.env.txt # argon2-cffi-bindings -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -c envs/dev.env.txt # requests @@ -121,7 +121,7 @@ comm==0.2.2 # -c envs/dev.env.txt # ipykernel # ipywidgets -contourpy==1.3.0 +contourpy==1.3.1 # via # -c envs/dev.env.txt # matplotlib @@ -137,7 +137,7 @@ cytoolz==1.0.0 # via # -c envs/dev.env.txt # quimb -dash==2.18.1 +dash==2.18.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -153,7 +153,7 @@ dash-table==5.0.0 # via # -c envs/dev.env.txt # dash -debugpy==1.8.6 +debugpy==1.8.9 # via # -c envs/dev.env.txt # ipykernel @@ -184,7 +184,7 @@ executing==2.1.0 # via # -c envs/dev.env.txt # stack-data -fastjsonschema==2.20.0 +fastjsonschema==2.21.1 # via # -c envs/dev.env.txt # nbformat @@ -192,7 +192,7 @@ flask==3.0.3 # via # -c envs/dev.env.txt # dash -fonttools==4.54.1 +fonttools==4.55.2 # via # -c envs/dev.env.txt # matplotlib @@ -204,7 +204,7 @@ fxpmath==0.4.9 # via # -c envs/dev.env.txt # -r deps/runtime.txt -galois==0.4.2 +galois==0.4.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -220,11 +220,11 @@ h11==0.14.0 # via # -c envs/dev.env.txt # httpcore -httpcore==1.0.6 +httpcore==1.0.7 # via # -c envs/dev.env.txt # httpx -httpx==0.27.2 +httpx==0.28.1 # via # -c envs/dev.env.txt # jupyterlab @@ -250,7 +250,7 @@ ipykernel==6.29.5 # -c envs/dev.env.txt # jupyterlab # myst-nb -ipython==8.28.0 +ipython==8.30.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -271,7 +271,7 @@ itsdangerous==2.2.0 # via # -c envs/dev.env.txt # flask -jedi==0.19.1 +jedi==0.19.2 # via # -c envs/dev.env.txt # ipython @@ -286,7 +286,7 @@ jinja2==3.1.4 # nbconvert # sphinx # tensorflow-docs -json5==0.9.25 +json5==0.10.0 # via # -c envs/dev.env.txt # jupyterlab-server @@ -300,11 +300,11 @@ jsonschema[format-nongpl]==4.23.0 # jupyter-events # jupyterlab-server # nbformat -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -c envs/dev.env.txt # jsonschema -jupyter-cache==1.0.0 +jupyter-cache==1.0.1 # via # -c envs/dev.env.txt # myst-nb @@ -344,7 +344,7 @@ jupyter-server-terminals==0.5.3 # via # -c envs/dev.env.txt # jupyter-server -jupyterlab==4.2.5 +jupyterlab==4.3.2 # via # -c envs/dev.env.txt # notebook @@ -374,13 +374,13 @@ markdown-it-py==3.0.0 # -c envs/dev.env.txt # mdit-py-plugins # myst-parser -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -c envs/dev.env.txt # jinja2 # nbconvert # werkzeug -matplotlib==3.9.2 +matplotlib==3.9.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -414,7 +414,7 @@ myst-parser==4.0.0 # via # -c envs/dev.env.txt # myst-nb -nbclient==0.10.0 +nbclient==0.10.1 # via # -c envs/dev.env.txt # jupyter-cache @@ -442,12 +442,12 @@ nest-asyncio==1.6.0 # -c envs/dev.env.txt # dash # ipykernel -networkx==3.3 +networkx==3.4.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt # cirq-core -notebook==7.2.2 +notebook==7.3.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -478,7 +478,7 @@ overrides==7.7.0 # via # -c envs/dev.env.txt # jupyter-server -packaging==24.1 +packaging==24.2 # via # -c envs/dev.env.txt # ipykernel @@ -488,7 +488,6 @@ packaging==24.1 # matplotlib # nbconvert # plotly - # pydata-sphinx-theme # sphinx pandas==2.2.3 # via @@ -506,7 +505,7 @@ pexpect==4.9.0 # via # -c envs/dev.env.txt # ipython -pillow==10.4.0 +pillow==11.0.0 # via # -c envs/dev.env.txt # matplotlib @@ -519,7 +518,7 @@ plotly==5.24.1 # -c envs/dev.env.txt # -r deps/runtime.txt # dash -prometheus-client==0.21.0 +prometheus-client==0.21.1 # via # -c envs/dev.env.txt # jupyter-server @@ -527,12 +526,12 @@ prompt-toolkit==3.0.48 # via # -c envs/dev.env.txt # ipython -protobuf==5.28.2 +protobuf==5.29.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt # tensorflow-docs -psutil==6.0.0 +psutil==6.1.0 # via # -c envs/dev.env.txt # ipykernel @@ -554,20 +553,20 @@ pycparser==2.22 # via # -c envs/dev.env.txt # cffi -pydantic==2.9.2 +pydantic==2.10.3 # via # -c envs/dev.env.txt # bartiq # qref -pydantic-core==2.23.4 +pydantic-core==2.27.1 # via # -c envs/dev.env.txt # pydantic -pydata-sphinx-theme==0.15.4 +pydata-sphinx-theme==0.16.0 # via # -c envs/dev.env.txt # -r deps/docs.txt -pydot==3.0.2 +pydot==3.0.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -619,15 +618,15 @@ qref==0.7.0 # -c envs/dev.env.txt # -r deps/runtime.txt # bartiq -qsharp==1.9.0 +qsharp==1.11.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -qsharp-widgets==1.9.0 +qsharp-widgets==1.11.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -quimb==1.8.4 +quimb==1.9.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -657,7 +656,7 @@ rfc3986-validator==0.1.1 # -c envs/dev.env.txt # jsonschema # jupyter-events -rpds-py==0.20.0 +rpds-py==0.22.3 # via # -c envs/dev.env.txt # jsonschema @@ -671,11 +670,9 @@ send2trash==1.8.3 # via # -c envs/dev.env.txt # jupyter-server -six==1.16.0 +six==1.17.0 # via # -c envs/dev.env.txt - # asttokens - # bleach # python-dateutil # retrying # rfc3339-validator @@ -683,7 +680,6 @@ sniffio==1.3.1 # via # -c envs/dev.env.txt # anyio - # httpx snowballstemmer==2.2.0 # via # -c envs/dev.env.txt @@ -696,7 +692,7 @@ soupsieve==2.6 # via # -c envs/dev.env.txt # beautifulsoup4 -sphinx==8.0.2 +sphinx==8.1.3 # via # -c envs/dev.env.txt # -r deps/docs.txt @@ -727,7 +723,7 @@ sphinxcontrib-serializinghtml==2.0.0 # via # -c envs/dev.env.txt # sphinx -sqlalchemy==2.0.35 +sqlalchemy==2.0.36 # via # -c envs/dev.env.txt # jupyter-cache @@ -758,11 +754,11 @@ terminado==0.18.1 # -c envs/dev.env.txt # jupyter-server # jupyter-server-terminals -tinycss2==1.3.0 +tinycss2==1.4.0 # via # -c envs/dev.env.txt # nbconvert -tomli==2.0.2 +tomli==2.2.1 # via # -c envs/dev.env.txt # jupyterlab @@ -771,7 +767,7 @@ toolz==1.0.0 # via # -c envs/dev.env.txt # cytoolz -tornado==6.4.1 +tornado==6.4.2 # via # -c envs/dev.env.txt # ipykernel @@ -780,7 +776,7 @@ tornado==6.4.1 # jupyterlab # notebook # terminado -tqdm==4.66.5 +tqdm==4.67.1 # via # -c envs/dev.env.txt # cirq-core @@ -801,7 +797,7 @@ traitlets==5.14.3 # nbclient # nbconvert # nbformat -types-python-dateutil==2.9.0.20241003 +types-python-dateutil==2.9.0.20241206 # via # -c envs/dev.env.txt # arrow @@ -837,7 +833,7 @@ wcwidth==0.2.13 # via # -c envs/dev.env.txt # prompt-toolkit -webcolors==24.8.0 +webcolors==24.11.1 # via # -c envs/dev.env.txt # jsonschema @@ -850,7 +846,7 @@ websocket-client==1.8.0 # via # -c envs/dev.env.txt # jupyter-server -werkzeug==3.0.4 +werkzeug==3.0.6 # via # -c envs/dev.env.txt # dash @@ -859,7 +855,7 @@ widgetsnbextension==4.0.13 # via # -c envs/dev.env.txt # ipywidgets -zipp==3.20.2 +zipp==3.21.0 # via # -c envs/dev.env.txt # importlib-metadata diff --git a/dev_tools/requirements/envs/format.env.txt b/dev_tools/requirements/envs/format.env.txt index 22872de48..9b960b1be 100644 --- a/dev_tools/requirements/envs/format.env.txt +++ b/dev_tools/requirements/envs/format.env.txt @@ -8,7 +8,7 @@ annotated-types==0.7.0 # via # -c envs/dev.env.txt # pydantic -anyio==4.6.0 +anyio==4.7.0 # via # -c envs/dev.env.txt # httpx @@ -33,7 +33,7 @@ astor==0.8.1 # via # -c envs/dev.env.txt # flynt -asttokens==2.4.1 +asttokens==3.0.0 # via # -c envs/dev.env.txt # stack-data @@ -48,7 +48,7 @@ attrs==24.2.0 # cirq-core # jsonschema # referencing -autoray==0.6.12 +autoray==0.7.0 # via # -c envs/dev.env.txt # cotengra @@ -69,11 +69,11 @@ black==24.8.0 # via # -c envs/dev.env.txt # -r deps/format.txt -bleach==6.1.0 +bleach==6.2.0 # via # -c envs/dev.env.txt # nbconvert -blinker==1.8.2 +blinker==1.9.0 # via # -c envs/dev.env.txt # flask @@ -91,7 +91,7 @@ cffi==1.17.1 # via # -c envs/dev.env.txt # argon2-cffi-bindings -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -c envs/dev.env.txt # requests @@ -109,7 +109,7 @@ comm==0.2.2 # -c envs/dev.env.txt # ipykernel # ipywidgets -contourpy==1.3.0 +contourpy==1.3.1 # via # -c envs/dev.env.txt # matplotlib @@ -125,7 +125,7 @@ cytoolz==1.0.0 # via # -c envs/dev.env.txt # quimb -dash==2.18.1 +dash==2.18.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -141,7 +141,7 @@ dash-table==5.0.0 # via # -c envs/dev.env.txt # dash -debugpy==1.8.6 +debugpy==1.8.9 # via # -c envs/dev.env.txt # ipykernel @@ -166,7 +166,7 @@ executing==2.1.0 # via # -c envs/dev.env.txt # stack-data -fastjsonschema==2.20.0 +fastjsonschema==2.21.1 # via # -c envs/dev.env.txt # nbformat @@ -178,7 +178,7 @@ flynt==0.78 # via # -c envs/dev.env.txt # -r deps/format.txt -fonttools==4.54.1 +fonttools==4.55.2 # via # -c envs/dev.env.txt # matplotlib @@ -190,7 +190,7 @@ fxpmath==0.4.9 # via # -c envs/dev.env.txt # -r deps/runtime.txt -galois==0.4.2 +galois==0.4.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -202,11 +202,11 @@ h11==0.14.0 # via # -c envs/dev.env.txt # httpcore -httpcore==1.0.6 +httpcore==1.0.7 # via # -c envs/dev.env.txt # httpx -httpx==0.27.2 +httpx==0.28.1 # via # -c envs/dev.env.txt # jupyterlab @@ -225,7 +225,7 @@ ipykernel==6.29.5 # via # -c envs/dev.env.txt # jupyterlab -ipython==8.28.0 +ipython==8.30.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -248,7 +248,7 @@ itsdangerous==2.2.0 # via # -c envs/dev.env.txt # flask -jedi==0.19.1 +jedi==0.19.2 # via # -c envs/dev.env.txt # ipython @@ -260,7 +260,7 @@ jinja2==3.1.4 # jupyterlab # jupyterlab-server # nbconvert -json5==0.9.25 +json5==0.10.0 # via # -c envs/dev.env.txt # jupyterlab-server @@ -274,7 +274,7 @@ jsonschema[format-nongpl]==4.23.0 # jupyter-events # jupyterlab-server # nbformat -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -c envs/dev.env.txt # jsonschema @@ -314,7 +314,7 @@ jupyter-server-terminals==0.5.3 # via # -c envs/dev.env.txt # jupyter-server -jupyterlab==4.2.5 +jupyterlab==4.3.2 # via # -c envs/dev.env.txt # notebook @@ -339,13 +339,13 @@ llvmlite==0.43.0 # via # -c envs/dev.env.txt # numba -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -c envs/dev.env.txt # jinja2 # nbconvert # werkzeug -matplotlib==3.9.2 +matplotlib==3.9.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -367,7 +367,7 @@ mypy-extensions==1.0.0 # via # -c envs/dev.env.txt # black -nbclient==0.10.0 +nbclient==0.10.1 # via # -c envs/dev.env.txt # nbconvert @@ -388,12 +388,12 @@ nest-asyncio==1.6.0 # -c envs/dev.env.txt # dash # ipykernel -networkx==3.3 +networkx==3.4.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt # cirq-core -notebook==7.2.2 +notebook==7.3.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -424,7 +424,7 @@ overrides==7.7.0 # via # -c envs/dev.env.txt # jupyter-server -packaging==24.1 +packaging==24.2 # via # -c envs/dev.env.txt # black @@ -455,7 +455,7 @@ pexpect==4.9.0 # via # -c envs/dev.env.txt # ipython -pillow==10.4.0 +pillow==11.0.0 # via # -c envs/dev.env.txt # matplotlib @@ -469,7 +469,7 @@ plotly==5.24.1 # -c envs/dev.env.txt # -r deps/runtime.txt # dash -prometheus-client==0.21.0 +prometheus-client==0.21.1 # via # -c envs/dev.env.txt # jupyter-server @@ -477,11 +477,11 @@ prompt-toolkit==3.0.48 # via # -c envs/dev.env.txt # ipython -protobuf==5.28.2 +protobuf==5.29.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -psutil==6.0.0 +psutil==6.1.0 # via # -c envs/dev.env.txt # ipykernel @@ -503,16 +503,16 @@ pycparser==2.22 # via # -c envs/dev.env.txt # cffi -pydantic==2.9.2 +pydantic==2.10.3 # via # -c envs/dev.env.txt # bartiq # qref -pydantic-core==2.23.4 +pydantic-core==2.27.1 # via # -c envs/dev.env.txt # pydantic -pydot==3.0.2 +pydot==3.0.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -557,15 +557,15 @@ qref==0.7.0 # -c envs/dev.env.txt # -r deps/runtime.txt # bartiq -qsharp==1.9.0 +qsharp==1.11.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -qsharp-widgets==1.9.0 +qsharp-widgets==1.11.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -quimb==1.8.4 +quimb==1.9.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -594,7 +594,7 @@ rfc3986-validator==0.1.1 # -c envs/dev.env.txt # jsonschema # jupyter-events -rpds-py==0.20.0 +rpds-py==0.22.3 # via # -c envs/dev.env.txt # jsonschema @@ -608,11 +608,9 @@ send2trash==1.8.3 # via # -c envs/dev.env.txt # jupyter-server -six==1.16.0 +six==1.17.0 # via # -c envs/dev.env.txt - # asttokens - # bleach # python-dateutil # retrying # rfc3339-validator @@ -620,7 +618,6 @@ sniffio==1.3.1 # via # -c envs/dev.env.txt # anyio - # httpx sortedcontainers==2.4.0 # via # -c envs/dev.env.txt @@ -648,11 +645,11 @@ terminado==0.18.1 # -c envs/dev.env.txt # jupyter-server # jupyter-server-terminals -tinycss2==1.3.0 +tinycss2==1.4.0 # via # -c envs/dev.env.txt # nbconvert -tomli==2.0.2 +tomli==2.2.1 # via # -c envs/dev.env.txt # black @@ -662,7 +659,7 @@ toolz==1.0.0 # via # -c envs/dev.env.txt # cytoolz -tornado==6.4.1 +tornado==6.4.2 # via # -c envs/dev.env.txt # ipykernel @@ -671,7 +668,7 @@ tornado==6.4.1 # jupyterlab # notebook # terminado -tqdm==4.66.5 +tqdm==4.67.1 # via # -c envs/dev.env.txt # cirq-core @@ -692,7 +689,7 @@ traitlets==5.14.3 # nbclient # nbconvert # nbformat -types-python-dateutil==2.9.0.20241003 +types-python-dateutil==2.9.0.20241206 # via # -c envs/dev.env.txt # arrow @@ -726,7 +723,7 @@ wcwidth==0.2.13 # via # -c envs/dev.env.txt # prompt-toolkit -webcolors==24.8.0 +webcolors==24.11.1 # via # -c envs/dev.env.txt # jsonschema @@ -739,7 +736,7 @@ websocket-client==1.8.0 # via # -c envs/dev.env.txt # jupyter-server -werkzeug==3.0.4 +werkzeug==3.0.6 # via # -c envs/dev.env.txt # dash @@ -748,7 +745,7 @@ widgetsnbextension==4.0.13 # via # -c envs/dev.env.txt # ipywidgets -zipp==3.20.2 +zipp==3.21.0 # via # -c envs/dev.env.txt # importlib-metadata diff --git a/dev_tools/requirements/envs/pip-tools.env.txt b/dev_tools/requirements/envs/pip-tools.env.txt index 1503cbbc1..128c16b94 100644 --- a/dev_tools/requirements/envs/pip-tools.env.txt +++ b/dev_tools/requirements/envs/pip-tools.env.txt @@ -4,7 +4,7 @@ # # pip-compile --constraint=envs/dev.env.txt --output-file=envs/pip-tools.env.txt deps/pip-tools.txt # -build==1.2.2 +build==1.2.2.post1 # via # -c envs/dev.env.txt # pip-tools @@ -12,7 +12,7 @@ click==8.1.7 # via # -c envs/dev.env.txt # pip-tools -packaging==24.1 +packaging==24.2 # via # -c envs/dev.env.txt # build @@ -25,12 +25,12 @@ pyproject-hooks==1.2.0 # -c envs/dev.env.txt # build # pip-tools -tomli==2.0.2 +tomli==2.2.1 # via # -c envs/dev.env.txt # build # pip-tools -wheel==0.44.0 +wheel==0.45.1 # via # -c envs/dev.env.txt # pip-tools diff --git a/dev_tools/requirements/envs/pylint.env.txt b/dev_tools/requirements/envs/pylint.env.txt index 0e3dddb6e..6953013bd 100644 --- a/dev_tools/requirements/envs/pylint.env.txt +++ b/dev_tools/requirements/envs/pylint.env.txt @@ -16,7 +16,7 @@ annotated-types==0.7.0 # via # -c envs/dev.env.txt # pydantic -anyio==4.6.0 +anyio==4.7.0 # via # -c envs/dev.env.txt # httpx @@ -45,11 +45,11 @@ astor==0.8.1 # via # -c envs/dev.env.txt # tensorflow-docs -astroid==3.3.5 +astroid==3.3.6 # via # -c envs/dev.env.txt # pylint -asttokens==2.4.1 +asttokens==3.0.0 # via # -c envs/dev.env.txt # stack-data @@ -64,7 +64,7 @@ attrs==24.2.0 # cirq-core # jsonschema # referencing -autoray==0.6.12 +autoray==0.7.0 # via # -c envs/dev.env.txt # cotengra @@ -82,11 +82,11 @@ beautifulsoup4==4.12.3 # via # -c envs/dev.env.txt # nbconvert -bleach==6.1.0 +bleach==6.2.0 # via # -c envs/dev.env.txt # nbconvert -blinker==1.8.2 +blinker==1.9.0 # via # -c envs/dev.env.txt # flask @@ -104,7 +104,7 @@ cffi==1.17.1 # via # -c envs/dev.env.txt # argon2-cffi-bindings -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -c envs/dev.env.txt # requests @@ -122,7 +122,7 @@ comm==0.2.2 # -c envs/dev.env.txt # ipykernel # ipywidgets -contourpy==1.3.0 +contourpy==1.3.1 # via # -c envs/dev.env.txt # matplotlib @@ -138,7 +138,7 @@ cytoolz==1.0.0 # via # -c envs/dev.env.txt # quimb -dash==2.18.1 +dash==2.18.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -154,7 +154,7 @@ dash-table==5.0.0 # via # -c envs/dev.env.txt # dash -debugpy==1.8.6 +debugpy==1.8.9 # via # -c envs/dev.env.txt # ipykernel @@ -192,7 +192,7 @@ executing==2.1.0 # via # -c envs/dev.env.txt # stack-data -fastjsonschema==2.20.0 +fastjsonschema==2.21.1 # via # -c envs/dev.env.txt # nbformat @@ -204,7 +204,7 @@ flask==3.0.3 # via # -c envs/dev.env.txt # dash -fonttools==4.54.1 +fonttools==4.55.2 # via # -c envs/dev.env.txt # matplotlib @@ -216,7 +216,7 @@ fxpmath==0.4.9 # via # -c envs/dev.env.txt # -r deps/runtime.txt -galois==0.4.2 +galois==0.4.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -233,11 +233,11 @@ h5py==3.12.1 # -c envs/dev.env.txt # openfermion # pyscf -httpcore==1.0.6 +httpcore==1.0.7 # via # -c envs/dev.env.txt # httpx -httpx==0.27.2 +httpx==0.28.1 # via # -c envs/dev.env.txt # jupyterlab @@ -264,7 +264,7 @@ ipykernel==6.29.5 # via # -c envs/dev.env.txt # jupyterlab -ipython==8.28.0 +ipython==8.30.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -287,16 +287,16 @@ itsdangerous==2.2.0 # via # -c envs/dev.env.txt # flask -jax==0.4.34 +jax==0.4.36 # via # -c envs/dev.env.txt # openfermion -jaxlib==0.4.34 +jaxlib==0.4.36 # via # -c envs/dev.env.txt # jax # openfermion -jedi==0.19.1 +jedi==0.19.2 # via # -c envs/dev.env.txt # ipython @@ -310,7 +310,7 @@ jinja2==3.1.4 # nbconvert # sphinx # tensorflow-docs -json5==0.9.25 +json5==0.10.0 # via # -c envs/dev.env.txt # jupyterlab-server @@ -324,7 +324,7 @@ jsonschema[format-nongpl]==4.23.0 # jupyter-events # jupyterlab-server # nbformat -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -c envs/dev.env.txt # jsonschema @@ -364,7 +364,7 @@ jupyter-server-terminals==0.5.3 # via # -c envs/dev.env.txt # jupyter-server -jupyterlab==4.2.5 +jupyterlab==4.3.2 # via # -c envs/dev.env.txt # notebook @@ -389,13 +389,13 @@ llvmlite==0.43.0 # via # -c envs/dev.env.txt # numba -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -c envs/dev.env.txt # jinja2 # nbconvert # werkzeug -matplotlib==3.9.2 +matplotlib==3.9.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -423,7 +423,7 @@ mpmath==1.3.0 # via # -c envs/dev.env.txt # sympy -nbclient==0.10.0 +nbclient==0.10.1 # via # -c envs/dev.env.txt # nbconvert @@ -445,13 +445,13 @@ nest-asyncio==1.6.0 # -c envs/dev.env.txt # dash # ipykernel -networkx==3.3 +networkx==3.4.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt # cirq-core # openfermion -notebook==7.2.2 +notebook==7.3.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -497,7 +497,7 @@ overrides==7.7.0 # via # -c envs/dev.env.txt # jupyter-server -packaging==24.1 +packaging==24.2 # via # -c envs/dev.env.txt # deprecation @@ -526,7 +526,7 @@ pexpect==4.9.0 # via # -c envs/dev.env.txt # ipython -pillow==10.4.0 +pillow==11.0.0 # via # -c envs/dev.env.txt # matplotlib @@ -544,7 +544,7 @@ pluggy==1.5.0 # via # -c envs/dev.env.txt # pytest -prometheus-client==0.21.0 +prometheus-client==0.21.1 # via # -c envs/dev.env.txt # jupyter-server @@ -552,12 +552,12 @@ prompt-toolkit==3.0.48 # via # -c envs/dev.env.txt # ipython -protobuf==5.28.2 +protobuf==5.29.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt # tensorflow-docs -psutil==6.0.0 +psutil==6.1.0 # via # -c envs/dev.env.txt # ipykernel @@ -583,16 +583,16 @@ pycparser==2.22 # via # -c envs/dev.env.txt # cffi -pydantic==2.9.2 +pydantic==2.10.3 # via # -c envs/dev.env.txt # bartiq # qref -pydantic-core==2.23.4 +pydantic-core==2.27.1 # via # -c envs/dev.env.txt # pydantic -pydot==3.0.2 +pydot==3.0.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -602,7 +602,7 @@ pygments==2.18.0 # ipython # nbconvert # sphinx -pylint==3.3.1 +pylint==3.3.2 # via # -c envs/dev.env.txt # -r deps/pylint.txt @@ -616,7 +616,7 @@ pyscf==2.7.0 # via # -c envs/dev.env.txt # openfermion -pytest==8.3.3 +pytest==8.3.4 # via # -c envs/dev.env.txt # -r deps/pylint.txt @@ -651,15 +651,15 @@ qref==0.7.0 # -c envs/dev.env.txt # -r deps/runtime.txt # bartiq -qsharp==1.9.0 +qsharp==1.11.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -qsharp-widgets==1.9.0 +qsharp-widgets==1.11.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -quimb==1.8.4 +quimb==1.9.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -690,7 +690,7 @@ rfc3986-validator==0.1.1 # -c envs/dev.env.txt # jsonschema # jupyter-events -rpds-py==0.20.0 +rpds-py==0.22.3 # via # -c envs/dev.env.txt # jsonschema @@ -709,11 +709,9 @@ send2trash==1.8.3 # via # -c envs/dev.env.txt # jupyter-server -six==1.16.0 +six==1.17.0 # via # -c envs/dev.env.txt - # asttokens - # bleach # python-dateutil # retrying # rfc3339-validator @@ -721,7 +719,6 @@ sniffio==1.3.1 # via # -c envs/dev.env.txt # anyio - # httpx snowballstemmer==2.2.0 # via # -c envs/dev.env.txt @@ -734,7 +731,7 @@ soupsieve==2.6 # via # -c envs/dev.env.txt # beautifulsoup4 -sphinx==8.0.2 +sphinx==8.1.3 # via # -c envs/dev.env.txt # -r deps/pylint.txt @@ -786,11 +783,11 @@ terminado==0.18.1 # -c envs/dev.env.txt # jupyter-server # jupyter-server-terminals -tinycss2==1.3.0 +tinycss2==1.4.0 # via # -c envs/dev.env.txt # nbconvert -tomli==2.0.2 +tomli==2.2.1 # via # -c envs/dev.env.txt # jupyterlab @@ -805,7 +802,7 @@ toolz==1.0.0 # via # -c envs/dev.env.txt # cytoolz -tornado==6.4.1 +tornado==6.4.2 # via # -c envs/dev.env.txt # ipykernel @@ -814,7 +811,7 @@ tornado==6.4.1 # jupyterlab # notebook # terminado -tqdm==4.66.5 +tqdm==4.67.1 # via # -c envs/dev.env.txt # cirq-core @@ -835,7 +832,7 @@ traitlets==5.14.3 # nbclient # nbconvert # nbformat -types-python-dateutil==2.9.0.20241003 +types-python-dateutil==2.9.0.20241206 # via # -c envs/dev.env.txt # arrow @@ -869,7 +866,7 @@ wcwidth==0.2.13 # via # -c envs/dev.env.txt # prompt-toolkit -webcolors==24.8.0 +webcolors==24.11.1 # via # -c envs/dev.env.txt # jsonschema @@ -882,7 +879,7 @@ websocket-client==1.8.0 # via # -c envs/dev.env.txt # jupyter-server -werkzeug==3.0.4 +werkzeug==3.0.6 # via # -c envs/dev.env.txt # dash @@ -891,7 +888,7 @@ widgetsnbextension==4.0.13 # via # -c envs/dev.env.txt # ipywidgets -zipp==3.20.2 +zipp==3.21.0 # via # -c envs/dev.env.txt # importlib-metadata diff --git a/dev_tools/requirements/envs/pytest.env.txt b/dev_tools/requirements/envs/pytest.env.txt index 3ecafa454..a69181dd8 100644 --- a/dev_tools/requirements/envs/pytest.env.txt +++ b/dev_tools/requirements/envs/pytest.env.txt @@ -8,7 +8,7 @@ annotated-types==0.7.0 # via # -c envs/dev.env.txt # pydantic -anyio==4.6.0 +anyio==4.7.0 # via # -c envs/dev.env.txt # httpx @@ -33,7 +33,7 @@ ase==3.23.0 # via # -c envs/dev.env.txt # openfermion -asttokens==2.4.1 +asttokens==3.0.0 # via # -c envs/dev.env.txt # stack-data @@ -48,7 +48,7 @@ attrs==24.2.0 # cirq-core # jsonschema # referencing -autoray==0.6.12 +autoray==0.7.0 # via # -c envs/dev.env.txt # cotengra @@ -65,11 +65,11 @@ beautifulsoup4==4.12.3 # via # -c envs/dev.env.txt # nbconvert -bleach==6.1.0 +bleach==6.2.0 # via # -c envs/dev.env.txt # nbconvert -blinker==1.8.2 +blinker==1.9.0 # via # -c envs/dev.env.txt # flask @@ -87,7 +87,7 @@ cffi==1.17.1 # via # -c envs/dev.env.txt # argon2-cffi-bindings -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -c envs/dev.env.txt # requests @@ -105,7 +105,7 @@ comm==0.2.2 # -c envs/dev.env.txt # ipykernel # ipywidgets -contourpy==1.3.0 +contourpy==1.3.1 # via # -c envs/dev.env.txt # matplotlib @@ -113,7 +113,7 @@ cotengra==0.6.2 # via # -c envs/dev.env.txt # quimb -coverage[toml]==7.6.1 +coverage[toml]==7.6.9 # via # -c envs/dev.env.txt # pytest-cov @@ -125,7 +125,7 @@ cytoolz==1.0.0 # via # -c envs/dev.env.txt # quimb -dash==2.18.1 +dash==2.18.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -141,7 +141,7 @@ dash-table==5.0.0 # via # -c envs/dev.env.txt # dash -debugpy==1.8.6 +debugpy==1.8.9 # via # -c envs/dev.env.txt # ipykernel @@ -175,7 +175,7 @@ executing==2.1.0 # via # -c envs/dev.env.txt # stack-data -fastjsonschema==2.20.0 +fastjsonschema==2.21.1 # via # -c envs/dev.env.txt # nbformat @@ -187,7 +187,7 @@ flask==3.0.3 # via # -c envs/dev.env.txt # dash -fonttools==4.54.1 +fonttools==4.55.2 # via # -c envs/dev.env.txt # matplotlib @@ -199,7 +199,7 @@ fxpmath==0.4.9 # via # -c envs/dev.env.txt # -r deps/runtime.txt -galois==0.4.2 +galois==0.4.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -216,11 +216,11 @@ h5py==3.12.1 # -c envs/dev.env.txt # openfermion # pyscf -httpcore==1.0.6 +httpcore==1.0.7 # via # -c envs/dev.env.txt # httpx -httpx==0.27.2 +httpx==0.28.1 # via # -c envs/dev.env.txt # jupyterlab @@ -244,7 +244,7 @@ ipykernel==6.29.5 # -c envs/dev.env.txt # -r deps/pytest.txt # jupyterlab -ipython==8.28.0 +ipython==8.30.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -263,16 +263,16 @@ itsdangerous==2.2.0 # via # -c envs/dev.env.txt # flask -jax==0.4.34 +jax==0.4.36 # via # -c envs/dev.env.txt # openfermion -jaxlib==0.4.34 +jaxlib==0.4.36 # via # -c envs/dev.env.txt # jax # openfermion -jedi==0.19.1 +jedi==0.19.2 # via # -c envs/dev.env.txt # ipython @@ -284,7 +284,7 @@ jinja2==3.1.4 # jupyterlab # jupyterlab-server # nbconvert -json5==0.9.25 +json5==0.10.0 # via # -c envs/dev.env.txt # jupyterlab-server @@ -298,7 +298,7 @@ jsonschema[format-nongpl]==4.23.0 # jupyter-events # jupyterlab-server # nbformat -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -c envs/dev.env.txt # jsonschema @@ -338,7 +338,7 @@ jupyter-server-terminals==0.5.3 # via # -c envs/dev.env.txt # jupyter-server -jupyterlab==4.2.5 +jupyterlab==4.3.2 # via # -c envs/dev.env.txt # notebook @@ -363,13 +363,13 @@ llvmlite==0.43.0 # via # -c envs/dev.env.txt # numba -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -c envs/dev.env.txt # jinja2 # nbconvert # werkzeug -matplotlib==3.9.2 +matplotlib==3.9.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -393,7 +393,7 @@ mpmath==1.3.0 # via # -c envs/dev.env.txt # sympy -nbclient==0.10.0 +nbclient==0.10.1 # via # -c envs/dev.env.txt # nbconvert @@ -414,13 +414,13 @@ nest-asyncio==1.6.0 # -c envs/dev.env.txt # dash # ipykernel -networkx==3.3 +networkx==3.4.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt # cirq-core # openfermion -notebook==7.2.2 +notebook==7.3.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -466,7 +466,7 @@ overrides==7.7.0 # via # -c envs/dev.env.txt # jupyter-server -packaging==24.1 +packaging==24.2 # via # -c envs/dev.env.txt # deprecation @@ -494,7 +494,7 @@ pexpect==4.9.0 # via # -c envs/dev.env.txt # ipython -pillow==10.4.0 +pillow==11.0.0 # via # -c envs/dev.env.txt # matplotlib @@ -511,7 +511,7 @@ pluggy==1.5.0 # via # -c envs/dev.env.txt # pytest -prometheus-client==0.21.0 +prometheus-client==0.21.1 # via # -c envs/dev.env.txt # jupyter-server @@ -519,11 +519,11 @@ prompt-toolkit==3.0.48 # via # -c envs/dev.env.txt # ipython -protobuf==5.28.2 +protobuf==5.29.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -psutil==6.0.0 +psutil==6.1.0 # via # -c envs/dev.env.txt # ipykernel @@ -549,16 +549,16 @@ pycparser==2.22 # via # -c envs/dev.env.txt # cffi -pydantic==2.9.2 +pydantic==2.10.3 # via # -c envs/dev.env.txt # bartiq # qref -pydantic-core==2.23.4 +pydantic-core==2.27.1 # via # -c envs/dev.env.txt # pydantic -pydot==3.0.2 +pydot==3.0.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -577,7 +577,7 @@ pyscf==2.7.0 # via # -c envs/dev.env.txt # openfermion -pytest==8.3.3 +pytest==8.3.4 # via # -c envs/dev.env.txt # -r deps/pytest.txt @@ -588,7 +588,7 @@ pytest-asyncio==0.24.0 # via # -c envs/dev.env.txt # -r deps/pytest.txt -pytest-cov==5.0.0 +pytest-cov==6.0.0 # via # -c envs/dev.env.txt # -r deps/pytest.txt @@ -626,15 +626,15 @@ qref==0.7.0 # -c envs/dev.env.txt # -r deps/runtime.txt # bartiq -qsharp==1.9.0 +qsharp==1.11.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -qsharp-widgets==1.9.0 +qsharp-widgets==1.11.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -quimb==1.8.4 +quimb==1.9.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -664,7 +664,7 @@ rfc3986-validator==0.1.1 # -c envs/dev.env.txt # jsonschema # jupyter-events -rpds-py==0.20.0 +rpds-py==0.22.3 # via # -c envs/dev.env.txt # jsonschema @@ -683,11 +683,9 @@ send2trash==1.8.3 # via # -c envs/dev.env.txt # jupyter-server -six==1.16.0 +six==1.17.0 # via # -c envs/dev.env.txt - # asttokens - # bleach # python-dateutil # retrying # rfc3339-validator @@ -695,7 +693,6 @@ sniffio==1.3.1 # via # -c envs/dev.env.txt # anyio - # httpx sortedcontainers==2.4.0 # via # -c envs/dev.env.txt @@ -724,11 +721,11 @@ terminado==0.18.1 # -c envs/dev.env.txt # jupyter-server # jupyter-server-terminals -tinycss2==1.3.0 +tinycss2==1.4.0 # via # -c envs/dev.env.txt # nbconvert -tomli==2.0.2 +tomli==2.2.1 # via # -c envs/dev.env.txt # coverage @@ -738,7 +735,7 @@ toolz==1.0.0 # via # -c envs/dev.env.txt # cytoolz -tornado==6.4.1 +tornado==6.4.2 # via # -c envs/dev.env.txt # ipykernel @@ -747,7 +744,7 @@ tornado==6.4.1 # jupyterlab # notebook # terminado -tqdm==4.66.5 +tqdm==4.67.1 # via # -c envs/dev.env.txt # cirq-core @@ -768,7 +765,7 @@ traitlets==5.14.3 # nbclient # nbconvert # nbformat -types-python-dateutil==2.9.0.20241003 +types-python-dateutil==2.9.0.20241206 # via # -c envs/dev.env.txt # arrow @@ -801,7 +798,7 @@ wcwidth==0.2.13 # via # -c envs/dev.env.txt # prompt-toolkit -webcolors==24.8.0 +webcolors==24.11.1 # via # -c envs/dev.env.txt # jsonschema @@ -814,7 +811,7 @@ websocket-client==1.8.0 # via # -c envs/dev.env.txt # jupyter-server -werkzeug==3.0.4 +werkzeug==3.0.6 # via # -c envs/dev.env.txt # dash @@ -823,7 +820,7 @@ widgetsnbextension==4.0.13 # via # -c envs/dev.env.txt # ipywidgets -zipp==3.20.2 +zipp==3.21.0 # via # -c envs/dev.env.txt # importlib-metadata diff --git a/dev_tools/requirements/envs/runtime.env.txt b/dev_tools/requirements/envs/runtime.env.txt index 18d4d020b..7da724d88 100644 --- a/dev_tools/requirements/envs/runtime.env.txt +++ b/dev_tools/requirements/envs/runtime.env.txt @@ -8,7 +8,7 @@ annotated-types==0.7.0 # via # -c envs/dev.env.txt # pydantic -anyio==4.6.0 +anyio==4.7.0 # via # -c envs/dev.env.txt # httpx @@ -29,7 +29,7 @@ arrow==1.3.0 # via # -c envs/dev.env.txt # isoduration -asttokens==2.4.1 +asttokens==3.0.0 # via # -c envs/dev.env.txt # stack-data @@ -44,7 +44,7 @@ attrs==24.2.0 # cirq-core # jsonschema # referencing -autoray==0.6.12 +autoray==0.7.0 # via # -c envs/dev.env.txt # cotengra @@ -61,11 +61,11 @@ beautifulsoup4==4.12.3 # via # -c envs/dev.env.txt # nbconvert -bleach==6.1.0 +bleach==6.2.0 # via # -c envs/dev.env.txt # nbconvert -blinker==1.8.2 +blinker==1.9.0 # via # -c envs/dev.env.txt # flask @@ -83,7 +83,7 @@ cffi==1.17.1 # via # -c envs/dev.env.txt # argon2-cffi-bindings -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -c envs/dev.env.txt # requests @@ -100,7 +100,7 @@ comm==0.2.2 # -c envs/dev.env.txt # ipykernel # ipywidgets -contourpy==1.3.0 +contourpy==1.3.1 # via # -c envs/dev.env.txt # matplotlib @@ -116,7 +116,7 @@ cytoolz==1.0.0 # via # -c envs/dev.env.txt # quimb -dash==2.18.1 +dash==2.18.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -132,7 +132,7 @@ dash-table==5.0.0 # via # -c envs/dev.env.txt # dash -debugpy==1.8.6 +debugpy==1.8.9 # via # -c envs/dev.env.txt # ipykernel @@ -157,7 +157,7 @@ executing==2.1.0 # via # -c envs/dev.env.txt # stack-data -fastjsonschema==2.20.0 +fastjsonschema==2.21.1 # via # -c envs/dev.env.txt # nbformat @@ -165,7 +165,7 @@ flask==3.0.3 # via # -c envs/dev.env.txt # dash -fonttools==4.54.1 +fonttools==4.55.2 # via # -c envs/dev.env.txt # matplotlib @@ -177,7 +177,7 @@ fxpmath==0.4.9 # via # -c envs/dev.env.txt # -r deps/runtime.txt -galois==0.4.2 +galois==0.4.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -189,11 +189,11 @@ h11==0.14.0 # via # -c envs/dev.env.txt # httpcore -httpcore==1.0.6 +httpcore==1.0.7 # via # -c envs/dev.env.txt # httpx -httpx==0.27.2 +httpx==0.28.1 # via # -c envs/dev.env.txt # jupyterlab @@ -212,7 +212,7 @@ ipykernel==6.29.5 # via # -c envs/dev.env.txt # jupyterlab -ipython==8.28.0 +ipython==8.30.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -231,7 +231,7 @@ itsdangerous==2.2.0 # via # -c envs/dev.env.txt # flask -jedi==0.19.1 +jedi==0.19.2 # via # -c envs/dev.env.txt # ipython @@ -243,7 +243,7 @@ jinja2==3.1.4 # jupyterlab # jupyterlab-server # nbconvert -json5==0.9.25 +json5==0.10.0 # via # -c envs/dev.env.txt # jupyterlab-server @@ -257,7 +257,7 @@ jsonschema[format-nongpl]==4.23.0 # jupyter-events # jupyterlab-server # nbformat -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -c envs/dev.env.txt # jsonschema @@ -297,7 +297,7 @@ jupyter-server-terminals==0.5.3 # via # -c envs/dev.env.txt # jupyter-server -jupyterlab==4.2.5 +jupyterlab==4.3.2 # via # -c envs/dev.env.txt # notebook @@ -322,13 +322,13 @@ llvmlite==0.43.0 # via # -c envs/dev.env.txt # numba -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -c envs/dev.env.txt # jinja2 # nbconvert # werkzeug -matplotlib==3.9.2 +matplotlib==3.9.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -346,7 +346,7 @@ mpmath==1.3.0 # via # -c envs/dev.env.txt # sympy -nbclient==0.10.0 +nbclient==0.10.1 # via # -c envs/dev.env.txt # nbconvert @@ -367,12 +367,12 @@ nest-asyncio==1.6.0 # -c envs/dev.env.txt # dash # ipykernel -networkx==3.3 +networkx==3.4.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt # cirq-core -notebook==7.2.2 +notebook==7.3.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -403,7 +403,7 @@ overrides==7.7.0 # via # -c envs/dev.env.txt # jupyter-server -packaging==24.1 +packaging==24.2 # via # -c envs/dev.env.txt # ipykernel @@ -429,7 +429,7 @@ pexpect==4.9.0 # via # -c envs/dev.env.txt # ipython -pillow==10.4.0 +pillow==11.0.0 # via # -c envs/dev.env.txt # matplotlib @@ -442,7 +442,7 @@ plotly==5.24.1 # -c envs/dev.env.txt # -r deps/runtime.txt # dash -prometheus-client==0.21.0 +prometheus-client==0.21.1 # via # -c envs/dev.env.txt # jupyter-server @@ -450,11 +450,11 @@ prompt-toolkit==3.0.48 # via # -c envs/dev.env.txt # ipython -protobuf==5.28.2 +protobuf==5.29.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -psutil==6.0.0 +psutil==6.1.0 # via # -c envs/dev.env.txt # ipykernel @@ -476,16 +476,16 @@ pycparser==2.22 # via # -c envs/dev.env.txt # cffi -pydantic==2.9.2 +pydantic==2.10.3 # via # -c envs/dev.env.txt # bartiq # qref -pydantic-core==2.23.4 +pydantic-core==2.27.1 # via # -c envs/dev.env.txt # pydantic -pydot==3.0.2 +pydot==3.0.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -530,15 +530,15 @@ qref==0.7.0 # -c envs/dev.env.txt # -r deps/runtime.txt # bartiq -qsharp==1.9.0 +qsharp==1.11.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -qsharp-widgets==1.9.0 +qsharp-widgets==1.11.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt -quimb==1.8.4 +quimb==1.9.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -567,7 +567,7 @@ rfc3986-validator==0.1.1 # -c envs/dev.env.txt # jsonschema # jupyter-events -rpds-py==0.20.0 +rpds-py==0.22.3 # via # -c envs/dev.env.txt # jsonschema @@ -581,11 +581,9 @@ send2trash==1.8.3 # via # -c envs/dev.env.txt # jupyter-server -six==1.16.0 +six==1.17.0 # via # -c envs/dev.env.txt - # asttokens - # bleach # python-dateutil # retrying # rfc3339-validator @@ -593,7 +591,6 @@ sniffio==1.3.1 # via # -c envs/dev.env.txt # anyio - # httpx sortedcontainers==2.4.0 # via # -c envs/dev.env.txt @@ -621,11 +618,11 @@ terminado==0.18.1 # -c envs/dev.env.txt # jupyter-server # jupyter-server-terminals -tinycss2==1.3.0 +tinycss2==1.4.0 # via # -c envs/dev.env.txt # nbconvert -tomli==2.0.2 +tomli==2.2.1 # via # -c envs/dev.env.txt # jupyterlab @@ -633,7 +630,7 @@ toolz==1.0.0 # via # -c envs/dev.env.txt # cytoolz -tornado==6.4.1 +tornado==6.4.2 # via # -c envs/dev.env.txt # ipykernel @@ -642,7 +639,7 @@ tornado==6.4.1 # jupyterlab # notebook # terminado -tqdm==4.66.5 +tqdm==4.67.1 # via # -c envs/dev.env.txt # cirq-core @@ -663,7 +660,7 @@ traitlets==5.14.3 # nbclient # nbconvert # nbformat -types-python-dateutil==2.9.0.20241003 +types-python-dateutil==2.9.0.20241206 # via # -c envs/dev.env.txt # arrow @@ -696,7 +693,7 @@ wcwidth==0.2.13 # via # -c envs/dev.env.txt # prompt-toolkit -webcolors==24.8.0 +webcolors==24.11.1 # via # -c envs/dev.env.txt # jsonschema @@ -709,7 +706,7 @@ websocket-client==1.8.0 # via # -c envs/dev.env.txt # jupyter-server -werkzeug==3.0.4 +werkzeug==3.0.6 # via # -c envs/dev.env.txt # dash @@ -718,7 +715,7 @@ widgetsnbextension==4.0.13 # via # -c envs/dev.env.txt # ipywidgets -zipp==3.20.2 +zipp==3.21.0 # via # -c envs/dev.env.txt # importlib-metadata diff --git a/qualtran/_infra/controlled.py b/qualtran/_infra/controlled.py index bcfc1ee6f..beeeb5933 100644 --- a/qualtran/_infra/controlled.py +++ b/qualtran/_infra/controlled.py @@ -284,7 +284,7 @@ def from_cirq_cv( for qdtype, shape in zip(qdtypes, shapes): full_shape = shape + (qdtype.num_qubits,) curr_cvs_bits = np.array(cv[idx : idx + int(np.prod(full_shape))]).reshape(full_shape) - curr_cvs = np.apply_along_axis(qdtype.from_bits, -1, curr_cvs_bits) # type: ignore[arg-type] + curr_cvs = np.apply_along_axis(qdtype.from_bits, -1, curr_cvs_bits) # type: ignore bloq_cvs.append(curr_cvs) return CtrlSpec(tuple(qdtypes), tuple(bloq_cvs)) diff --git a/qualtran/bloqs/multiplexers/apply_lth_bloq.py b/qualtran/bloqs/multiplexers/apply_lth_bloq.py index 5ff1e6c74..57ea79336 100644 --- a/qualtran/bloqs/multiplexers/apply_lth_bloq.py +++ b/qualtran/bloqs/multiplexers/apply_lth_bloq.py @@ -98,7 +98,7 @@ def selection_registers(self) -> Tuple[Register, ...]: @cached_property def target_registers(self) -> Tuple[Register, ...]: - return tuple(self.ops.flat[0].signature) + return tuple(self.ops.flat[0].signature) # type: ignore def nth_operation_callgraph(self, **kwargs: int) -> Set[BloqCountT]: return {(self.ops[tuple(kwargs.values())].controlled(), 1)} diff --git a/qualtran/bloqs/multiplexers/selected_majorana_fermion.py b/qualtran/bloqs/multiplexers/selected_majorana_fermion.py index 8be4daaea..fd5a94952 100644 --- a/qualtran/bloqs/multiplexers/selected_majorana_fermion.py +++ b/qualtran/bloqs/multiplexers/selected_majorana_fermion.py @@ -100,7 +100,9 @@ def decompose_from_registers( self, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid] ) -> Iterator[cirq.OP_TREE]: quregs['accumulator'] = np.array(context.qubit_manager.qalloc(1)) - control = quregs[self.control_regs[0].name] if total_bits(self.control_registers) else [] + control: Sequence['cirq.Qid'] = ( + quregs[self.control_regs[0].name].tolist() if total_bits(self.control_registers) else [] + ) yield cirq.X(*quregs['accumulator']).controlled_by(*control) yield super(SelectedMajoranaFermion, self).decompose_from_registers( context=context, **quregs diff --git a/qualtran/cirq_interop/_bloq_to_cirq.py b/qualtran/cirq_interop/_bloq_to_cirq.py index 70f6d122c..53cfbadc9 100644 --- a/qualtran/cirq_interop/_bloq_to_cirq.py +++ b/qualtran/cirq_interop/_bloq_to_cirq.py @@ -255,7 +255,7 @@ def _cbloq_to_cirq_circuit( cirq_quregs: The output mapping from right register names to Cirq qubit arrays. """ cirq_quregs: Dict[str, 'CirqQuregInT'] = { - k: np.apply_along_axis(_QReg, -1, *(v, signature.get_left(k).dtype)) # type: ignore[arg-type] + k: np.apply_along_axis(_QReg, -1, *(v, signature.get_left(k).dtype)) # type: ignore for k, v in cirq_quregs.items() } qvar_to_qreg: Dict[Soquet, _QReg] = { diff --git a/qualtran/cirq_interop/_cirq_to_bloq.py b/qualtran/cirq_interop/_cirq_to_bloq.py index b74383534..2c55177ae 100644 --- a/qualtran/cirq_interop/_cirq_to_bloq.py +++ b/qualtran/cirq_interop/_cirq_to_bloq.py @@ -482,11 +482,11 @@ def cirq_optree_to_cbloq( raise ValueError("`signature` requires specifying both `in_quregs` and `out_quregs`.") in_quregs: Dict[str, NDArray] = { - k: np.apply_along_axis(_QReg, -1, *(v, signature.get_left(k).dtype)) # type: ignore[arg-type] + k: np.apply_along_axis(_QReg, -1, *(v, signature.get_left(k).dtype)) # type: ignore for k, v in in_quregs.items() } out_quregs: Dict[str, NDArray] = { - k: np.apply_along_axis(_QReg, -1, *(v, signature.get_right(k).dtype)) # type: ignore[arg-type] + k: np.apply_along_axis(_QReg, -1, *(v, signature.get_right(k).dtype)) # type: ignore for k, v in out_quregs.items() } @@ -517,7 +517,7 @@ def cirq_optree_to_cbloq( reg_dtypes = [r.dtype for r in bloq.signature] # 3.1 Find input / output registers. all_op_quregs: Dict[str, NDArray[_QReg]] = { - k: np.apply_along_axis(_QReg, -1, *(v, reg_dtypes[i])) # type: ignore[arg-type] + k: np.apply_along_axis(_QReg, -1, *(v, reg_dtypes[i])) # type: ignore for i, (k, v) in enumerate(split_qubits(bloq.signature, op.qubits).items()) } From 1bd4f70e35321bf06e6c785b31be7422711c8ef9 Mon Sep 17 00:00:00 2001 From: Anurudh Peduri <7265746+anurudhp@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:07:57 -0800 Subject: [PATCH 12/12] Make `And` a leaf bloq (#1513) * make `And` a leaf bloq * cleanup `And` * fix `DoubleFactorizationOneBody` call graph * fix `t_counts_from_sigma` (deprecated but used in flame graph) --- qualtran/bloqs/arithmetic/permutation_test.py | 10 ++-- .../chemistry/df/double_factorization.py | 7 ++- .../chemistry/df/double_factorization_test.py | 9 ++-- .../chemistry/sf/single_factorization_test.py | 9 ++-- .../hamiltonian_simulation_by_gqsp_test.py | 9 ++-- qualtran/bloqs/mcmt/and_bloq.py | 49 ++----------------- qualtran/bloqs/mcmt/specialized_ctrl_test.py | 20 ++------ .../bloqs/multiplexers/apply_lth_bloq_test.py | 10 ++-- qualtran/drawing/bloq_counts_graph_test.py | 6 +-- .../resource_counting/t_counts_from_sigma.py | 4 ++ 10 files changed, 38 insertions(+), 95 deletions(-) diff --git a/qualtran/bloqs/arithmetic/permutation_test.py b/qualtran/bloqs/arithmetic/permutation_test.py index 07e207300..b5d84a16e 100644 --- a/qualtran/bloqs/arithmetic/permutation_test.py +++ b/qualtran/bloqs/arithmetic/permutation_test.py @@ -90,9 +90,7 @@ def test_permutation_cycle_unitary_and_call_graph(): ) cv = sympy.Symbol('cv') - _, sigma = bloq.call_graph( - generalizer=[ignore_split_join, generalize_cvs], keep=lambda b: isinstance(b, And) - ) + _, sigma = bloq.call_graph(generalizer=[ignore_split_join, generalize_cvs]) assert sigma == { CNOT(): 8, And(cv1=cv, cv2=cv): 4, @@ -106,7 +104,7 @@ def test_permutation_cycle_symbolic_call_graph(): bloq = _permutation_cycle_symb() logN, L = ceil(log2(bloq.N)), slen(bloq.cycle) - _, sigma = bloq.call_graph(keep=lambda b: isinstance(b, And)) + _, sigma = bloq.call_graph() assert sigma == { And(): (L + 1) * (logN - 1), And().adjoint(): (L + 1) * (logN - 1), @@ -133,7 +131,7 @@ def test_permutation_unitary_and_call_graph(): ), ) - _, sigma = bloq.call_graph(generalizer=ignore_split_join, keep=lambda b: isinstance(b, And)) + _, sigma = bloq.call_graph(generalizer=ignore_split_join) assert sigma == { CNOT(): 17, And(): 56 // 4, @@ -160,7 +158,7 @@ def test_permutation_symbolic_call_graph(): logN = ceil(log2(N)) bloq = _permutation_symb() - _, sigma = bloq.call_graph(keep=lambda b: isinstance(b, And)) + _, sigma = bloq.call_graph() assert sigma == { And().adjoint(): (N + 1) * (logN - 1), And(): (N + 1) * (logN - 1), diff --git a/qualtran/bloqs/chemistry/df/double_factorization.py b/qualtran/bloqs/chemistry/df/double_factorization.py index 4cc9b47b7..e83b38524 100644 --- a/qualtran/bloqs/chemistry/df/double_factorization.py +++ b/qualtran/bloqs/chemistry/df/double_factorization.py @@ -48,9 +48,8 @@ Soquet, SoquetT, ) -from qualtran.bloqs.basic_gates import CSwap, Hadamard, Toffoli +from qualtran.bloqs.basic_gates import CSwap, Hadamard from qualtran.bloqs.block_encoding import BlockEncoding -from qualtran.bloqs.bookkeeping import ArbitraryClifford from qualtran.bloqs.chemistry.black_boxes import ApplyControlledZs from qualtran.bloqs.chemistry.df.prepare import ( InnerPrepareDoubleFactorization, @@ -280,10 +279,10 @@ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': in_prep_dag: 1, # in_prep_l^dag rot: 1, # rotate into system basis listing 4 pg 54 # apply CCZ first then CCCZ, the cost is 1 + 2 Toffolis (step 4e, and 7) - Toffoli(): 1, + ApplyControlledZs(cvs=(1, 1), bitsize=5): 1, rot_dag: 1, # Undo rotations CSwap(self.num_spin_orb // 2): 2, # Swaps for spins - ArbitraryClifford(n=1): 1, # 2 Hadamards for spin superposition + Hadamard(): 2, # 2 Hadamards for spin superposition } def __str__(self) -> str: diff --git a/qualtran/bloqs/chemistry/df/double_factorization_test.py b/qualtran/bloqs/chemistry/df/double_factorization_test.py index 5dd65346f..41ed8e065 100644 --- a/qualtran/bloqs/chemistry/df/double_factorization_test.py +++ b/qualtran/bloqs/chemistry/df/double_factorization_test.py @@ -15,7 +15,7 @@ from openfermion.resource_estimates.df.compute_cost_df import compute_cost from openfermion.resource_estimates.utils import power_two -from qualtran.bloqs.basic_gates import TGate +import qualtran.testing as qlt_testing from qualtran.bloqs.chemistry.df.double_factorization import ( _df_block_encoding, _df_one_body, @@ -26,7 +26,6 @@ PrepareUniformSuperposition, ) from qualtran.resource_counting import get_cost_value, QECGatesCost -from qualtran.testing import execute_notebook def test_df_block_encoding(bloq_autotester): @@ -51,9 +50,7 @@ def test_compare_cost_one_body_decomp(): num_bits_rot_aa=7, num_bits_rot=num_bits_rot, ) - costs = bloq.call_graph()[1] - cbloq_costs = bloq.decompose_bloq().call_graph()[1] - assert costs[TGate()] == cbloq_costs[TGate()] + qlt_testing.assert_equivalent_bloq_counts(bloq) def test_compare_cost_to_openfermion(): @@ -106,4 +103,4 @@ def test_compare_cost_to_openfermion(): @pytest.mark.notebook def test_notebook(): - execute_notebook("double_factorization") + qlt_testing.execute_notebook("double_factorization") diff --git a/qualtran/bloqs/chemistry/sf/single_factorization_test.py b/qualtran/bloqs/chemistry/sf/single_factorization_test.py index e3e351ee8..1c35e9d97 100644 --- a/qualtran/bloqs/chemistry/sf/single_factorization_test.py +++ b/qualtran/bloqs/chemistry/sf/single_factorization_test.py @@ -15,7 +15,7 @@ from openfermion.resource_estimates.sf.compute_cost_sf import compute_cost from openfermion.resource_estimates.utils import power_two, QI, QI2, QR2 -from qualtran.bloqs.basic_gates import TGate +import qualtran.testing as qlt_testing from qualtran.bloqs.chemistry.sf.single_factorization import ( _sf_block_encoding, _sf_one_body, @@ -26,7 +26,6 @@ PrepareUniformSuperposition, ) from qualtran.resource_counting import get_cost_value, QECGatesCost -from qualtran.testing import execute_notebook def test_sf_block_encoding(bloq_autotester): @@ -48,9 +47,7 @@ def test_compare_cost_one_body_decomp(): num_bits_state_prep=num_bits_state_prep, num_bits_rot_aa=num_bits_rot_aa, ) - costs = bloq.call_graph()[1] - cbloq_costs = bloq.decompose_bloq().call_graph()[1] - assert costs[TGate()] == cbloq_costs[TGate()] + qlt_testing.assert_equivalent_bloq_counts(bloq) def test_compare_cost_to_openfermion(): @@ -126,4 +123,4 @@ def test_compare_cost_to_openfermion(): @pytest.mark.notebook def test_notebook(): - execute_notebook("single_factorization") + qlt_testing.execute_notebook("single_factorization") diff --git a/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp_test.py b/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp_test.py index 5d0e68be1..f587c8726 100644 --- a/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp_test.py +++ b/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp_test.py @@ -19,7 +19,6 @@ import sympy from numpy.typing import NDArray -from qualtran.bloqs.basic_gates import TGate, TwoBitCSwap from qualtran.bloqs.for_testing.matrix_gate import MatrixGate from qualtran.bloqs.for_testing.random_select_and_prepare import random_qubitization_walk_operator from qualtran.bloqs.hamiltonian_simulation.hamiltonian_simulation_by_gqsp import ( @@ -34,7 +33,7 @@ ) from qualtran.bloqs.qubitization.qubitization_walk_operator import QubitizationWalkOperator from qualtran.cirq_interop import BloqAsCirqGate -from qualtran.resource_counting import big_O, BloqCount, get_cost_value, QECGatesCost, QubitCount +from qualtran.resource_counting import big_O, get_cost_value, QECGatesCost, QubitCount from qualtran.symbolics import Shaped @@ -109,8 +108,10 @@ def test_hamiltonian_simulation_by_gqsp_t_complexity(): hubbard_time_evolution_by_gqsp = _hubbard_time_evolution_by_gqsp.make() t_comp = hubbard_time_evolution_by_gqsp.t_complexity() - counts = get_cost_value(hubbard_time_evolution_by_gqsp, BloqCount.for_gateset('t+tof+cswap')) - assert t_comp.t == counts[TwoBitCSwap()] * 7 + counts[TGate()] + counts = get_cost_value(hubbard_time_evolution_by_gqsp, QECGatesCost()) + t_comp_from_qec = counts.to_legacy_t_complexity() + assert t_comp.t == t_comp_from_qec.t + assert t_comp.rotations == t_comp_from_qec.rotations def test_symbolic_t_cost(): diff --git a/qualtran/bloqs/mcmt/and_bloq.py b/qualtran/bloqs/mcmt/and_bloq.py index e1da7fff5..486bb3e49 100644 --- a/qualtran/bloqs/mcmt/and_bloq.py +++ b/qualtran/bloqs/mcmt/and_bloq.py @@ -45,32 +45,17 @@ Side, Signature, ) -from qualtran.bloqs.basic_gates import TGate, XGate -from qualtran.bloqs.bookkeeping import ArbitraryClifford +from qualtran.bloqs.basic_gates import XGate from qualtran.cirq_interop import decompose_from_cirq_style_method from qualtran.drawing import Circle, directional_text_box, Text, WireSymbol -from qualtran.resource_counting import ( - big_O, - BloqCountDictT, - MutableBloqCountDictT, - SympySymbolAllocator, -) -from qualtran.resource_counting.generalizers import ( - cirq_to_bloqs, - generalize_cvs, - generalize_rotation_angle, - ignore_alloc_free, - ignore_cliffords, -) +from qualtran.resource_counting import BloqCountDictT, MutableBloqCountDictT, SympySymbolAllocator +from qualtran.resource_counting.generalizers import generalize_cvs, ignore_cliffords from qualtran.simulation.classical_sim import ClassicalValT from qualtran.symbolics import HasLength, is_symbolic, SymbolicInt if TYPE_CHECKING: import quimb.tensor as qtn -# TODO: https://github.com/quantumlib/Qualtran/issues/1346 -FLAG_AND_AS_LEAF = False - @frozen class And(GateWithRegisters): @@ -108,22 +93,7 @@ def adjoint(self) -> 'And': return attrs.evolve(self, uncompute=not self.uncompute) def decompose_bloq(self) -> 'CompositeBloq': - if FLAG_AND_AS_LEAF: - raise DecomposeTypeError(f"{self} is atomic.") - return decompose_from_cirq_style_method(self) - - def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': - if FLAG_AND_AS_LEAF: - raise DecomposeTypeError(f"{self} is atomic.") - - if isinstance(self.cv1, sympy.Expr) or isinstance(self.cv2, sympy.Expr): - pre_post_cliffords: Union[sympy.Order, int] = big_O(1) - else: - pre_post_cliffords = 2 - self.cv1 - self.cv2 - if self.uncompute: - return {ArbitraryClifford(n=2): 4 + 2 * pre_post_cliffords} - - return {ArbitraryClifford(n=2): 9 + 2 * pre_post_cliffords, TGate(): 4} + raise DecomposeTypeError(f"{self} is atomic.") def on_classical_vals( self, *, ctrl: NDArray[np.uint8], target: Optional[int] = None @@ -243,13 +213,6 @@ def to_clifford_t_circuit(self) -> 'cirq.FrozenCircuit': circuit += pre_post_ops return circuit.freeze() - def __pow__(self, power: int) -> 'And': - if power == 1: - return self - if power == -1: - return self.adjoint() - return NotImplemented # pragma: no cover - def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> cirq.CircuitDiagramInfo: controls = ["(0)", "@"] target = "And†" if self.uncompute else "And" @@ -263,9 +226,7 @@ def _has_unitary_(self) -> bool: return not self.uncompute -@bloq_example( - generalizer=[cirq_to_bloqs, ignore_cliffords, ignore_alloc_free, generalize_rotation_angle] -) +@bloq_example() def _and_bloq() -> And: and_bloq = And() return and_bloq diff --git a/qualtran/bloqs/mcmt/specialized_ctrl_test.py b/qualtran/bloqs/mcmt/specialized_ctrl_test.py index c42e95da7..3c1ae249e 100644 --- a/qualtran/bloqs/mcmt/specialized_ctrl_test.py +++ b/qualtran/bloqs/mcmt/specialized_ctrl_test.py @@ -40,11 +40,6 @@ from qualtran.resource_counting import CostKey, GateCounts, get_cost_value, QECGatesCost -def _keep_and(b): - # TODO remove this after https://github.com/quantumlib/Qualtran/issues/1346 is resolved. - return isinstance(b, And) - - @attrs.frozen class AtomWithSpecializedControl(Bloq): cv: Optional[int] = None @@ -169,30 +164,25 @@ def test_bloq_with_controlled_bloq(): assert TestAtom('g').controlled() == CTestAtom('g') ctrl_bloq = CTestAtom('g').controlled() - _, sigma = ctrl_bloq.call_graph(keep=_keep_and) + _, sigma = ctrl_bloq.call_graph() assert sigma == {And(): 1, CTestAtom('g'): 1, And().adjoint(): 1} ctrl_bloq = CTestAtom('n').controlled(CtrlSpec(cvs=0)) - _, sigma = ctrl_bloq.call_graph(keep=_keep_and) + _, sigma = ctrl_bloq.call_graph() assert sigma == {And(0, 1): 1, CTestAtom('n'): 1, And(0, 1).adjoint(): 1} ctrl_bloq = TestAtom('nn').controlled(CtrlSpec(cvs=[0, 0])) - _, sigma = ctrl_bloq.call_graph(keep=_keep_and) + _, sigma = ctrl_bloq.call_graph() assert sigma == {And(0, 0): 1, CTestAtom('nn'): 1, And(0, 0).adjoint(): 1} def test_ctrl_adjoint(): assert TestAtom('a').adjoint().controlled() == CTestAtom('a').adjoint() - _, sigma = ( - TestAtom('g') - .adjoint() - .controlled(ctrl_spec=CtrlSpec(cvs=[1, 1])) - .call_graph(keep=_keep_and) - ) + _, sigma = TestAtom('g').adjoint().controlled(ctrl_spec=CtrlSpec(cvs=[1, 1])).call_graph() assert sigma == {And(): 1, And().adjoint(): 1, CTestAtom('g').adjoint(): 1} - _, sigma = CTestAtom('c').adjoint().controlled().call_graph(keep=_keep_and) + _, sigma = CTestAtom('c').adjoint().controlled().call_graph() assert sigma == {And(): 1, And().adjoint(): 1, CTestAtom('c').adjoint(): 1} for cv in [0, 1]: diff --git a/qualtran/bloqs/multiplexers/apply_lth_bloq_test.py b/qualtran/bloqs/multiplexers/apply_lth_bloq_test.py index 3f4733218..b98e5bcdf 100644 --- a/qualtran/bloqs/multiplexers/apply_lth_bloq_test.py +++ b/qualtran/bloqs/multiplexers/apply_lth_bloq_test.py @@ -17,7 +17,7 @@ import numpy as np import pytest -from qualtran import BloqBuilder, BQUInt, Controlled, CtrlSpec, QBit, Register, Signature, Soquet +from qualtran import BloqBuilder, BQUInt, QBit, Register, Signature, Soquet from qualtran.bloqs.basic_gates import ( CHadamard, CNOT, @@ -34,7 +34,7 @@ ZeroState, ZGate, ) -from qualtran.bloqs.bookkeeping.arbitrary_clifford import ArbitraryClifford +from qualtran.bloqs.mcmt import And from qualtran.bloqs.multiplexers.apply_lth_bloq import _apply_lth_bloq, ApplyLthBloq from qualtran.resource_counting.generalizers import ignore_split_join from qualtran.testing import assert_valid_bloq_decomposition @@ -64,11 +64,11 @@ def test_call_graph(): _, sigma = _apply_lth_bloq().call_graph(generalizer=ignore_split_join) assert sigma == { CHadamard(): 1, - Controlled(TGate(), CtrlSpec()): 1, + TGate().controlled(): 1, CZ(): 1, CNOT(): 4, - TGate(): 12, - ArbitraryClifford(2): 45, + And(1, 0): 3, + And().adjoint(): 3, } diff --git a/qualtran/drawing/bloq_counts_graph_test.py b/qualtran/drawing/bloq_counts_graph_test.py index 29f9ad98c..8f9d8a24d 100644 --- a/qualtran/drawing/bloq_counts_graph_test.py +++ b/qualtran/drawing/bloq_counts_graph_test.py @@ -30,8 +30,7 @@ def test_format_counts_sigma(): ret == """\ #### Counts totals: - - `ArbitraryClifford(n=2)`: 45 - - `T`: 20""" + - `And`: 5""" ) @@ -43,9 +42,6 @@ def test_format_counts_graph_markdown(): == """\ - `MultiAnd(n=6)` - `And`: $\\displaystyle 5$ - - `And` - - `ArbitraryClifford(n=2)`: $\\displaystyle 9$ - - `T`: $\\displaystyle 4$ """ ) diff --git a/qualtran/resource_counting/t_counts_from_sigma.py b/qualtran/resource_counting/t_counts_from_sigma.py index 02c6673c6..eb4578e36 100644 --- a/qualtran/resource_counting/t_counts_from_sigma.py +++ b/qualtran/resource_counting/t_counts_from_sigma.py @@ -25,6 +25,7 @@ def t_counts_from_sigma(sigma: Mapping['Bloq', SymbolicInt]) -> SymbolicInt: import cirq from qualtran.bloqs.basic_gates import TGate, Toffoli, TwoBitCSwap + from qualtran.bloqs.mcmt import And from qualtran.cirq_interop.t_complexity_protocol import TComplexity from qualtran.resource_counting.classify_bloqs import bloq_is_rotation @@ -32,6 +33,9 @@ def t_counts_from_sigma(sigma: Mapping['Bloq', SymbolicInt]) -> SymbolicInt: ret += sigma.get(Toffoli(), 0) * 4 ret += sigma.get(TwoBitCSwap(), 0) * 7 for bloq, counts in sigma.items(): + if isinstance(bloq, And) and not bloq.uncompute: + ret += counts * 4 + if bloq_is_rotation(bloq) and not cirq.has_stabilizer_effect(bloq): if isinstance(bloq, Controlled): # TODO native controlled rotation bloqs missing (CRz, CRy etc.)