From 5d47e0d133655c458cf27c9f0b14ae9f661a4262 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Thu, 25 Apr 2024 13:56:14 -0700 Subject: [PATCH 1/6] Mypy issues part 3 More miscellaenous mypy fixes. --- .../qualtran_dev_tools/jupyter_autogen.py | 8 ++++--- .../projectile/select_and_prepare.py | 20 +++++++++------- .../first_quantization/select_and_prepare.py | 23 +++++++++++------- .../chemistry/sf/single_factorization.py | 8 ++++--- qualtran/bloqs/chemistry/sparse/prepare.py | 14 ++++++----- qualtran/bloqs/factoring/mod_add.py | 4 ++-- .../hamiltonian_simulation_by_gqsp.py | 10 ++++---- qualtran/bloqs/mcmt/and_bloq.py | 10 ++++---- .../multi_control_multi_target_pauli_test.py | 5 ++-- .../apply_gate_to_lth_target_test.py | 3 ++- .../multiplexers/select_pauli_lcu_test.py | 24 ++++++++++--------- .../multiplexers/unary_iteration_bloq_test.py | 10 ++++---- qualtran/bloqs/qsp/generalized_qsp_test.py | 18 +++++++------- .../bloqs/rotations/phase_gradient_test.py | 9 ++++--- .../programmable_rotation_gate_array_test.py | 1 + .../quantum_variable_rotation_test.py | 2 +- .../state_preparation_alias_sampling.py | 4 ++-- .../bloqs/swap_network/cswap_approx_test.py | 7 ++++-- qualtran/bloqs/util_bloqs_test.py | 3 +++ qualtran/cirq_interop/_cirq_to_bloq_test.py | 3 +++ .../t_complexity_protocol_test.py | 7 +++--- qualtran/resource_counting/classify_bloqs.py | 2 +- .../resource_counting/t_counts_from_sigma.py | 4 ++-- qualtran/simulation/classical_sim.py | 6 ++--- qualtran/simulation/classical_sim_test.py | 20 ++++++++++------ qualtran/testing.py | 6 +++-- qualtran/testing_test.py | 2 +- 27 files changed, 137 insertions(+), 96 deletions(-) diff --git a/dev_tools/qualtran_dev_tools/jupyter_autogen.py b/dev_tools/qualtran_dev_tools/jupyter_autogen.py index 7b2d4d7a9..d46252fef 100644 --- a/dev_tools/qualtran_dev_tools/jupyter_autogen.py +++ b/dev_tools/qualtran_dev_tools/jupyter_autogen.py @@ -72,7 +72,9 @@ class NotebookSpecV2: @directory.default def _default_directory(self) -> str: - return str(Path(self.module.__file__).parent) + path = self.module.__file__ + assert path is not None + return str(Path(path).parent) @property def path_stem(self): @@ -156,7 +158,7 @@ class _PyCell(_Cell): cell_id: str -def get_bloq_doc_cells(bloqdoc: BloqDocSpec, cid_prefix: str) -> List[_MarkdownCell]: +def get_bloq_doc_cells(bloqdoc: BloqDocSpec, cid_prefix: str) -> List[_Cell]: """Cells introducing the `bloq_cls`""" md_doc: str = '\n'.join(get_markdown_docstring_lines(bloqdoc.bloq_cls)) @@ -230,7 +232,7 @@ def get_call_graph_cells(bloqdoc: BloqDocSpec, cid_prefix: str) -> List[_Cell]: def get_cells(bloqdoc: BloqDocSpec) -> List[_Cell]: - cells = [] + cells: List[_Cell] = [] cid_prefix = f'{bloqdoc.bloq_cls.__name__}' cells += get_bloq_doc_cells(bloqdoc, cid_prefix) cells += get_example_instances_cells(bloqdoc, cid_prefix) diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_and_prepare.py b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_and_prepare.py index 1d9923021..836531f67 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_and_prepare.py +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_and_prepare.py @@ -17,6 +17,7 @@ import numpy as np from attrs import evolve, field, frozen +from numpy.typing import NDArray from qualtran import ( Bloq, @@ -107,7 +108,7 @@ def short_name(self) -> str: def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']: if self.is_adjoint: # inverting inequality tests at zero Toffoli. - return {} + return set() else: return {(Toffoli(), 6 * self.num_bits_t + 2)} @@ -147,6 +148,7 @@ def wire_symbol(self, soq: 'Soquet') -> 'WireSymbol': (c_idx,) = soq.idx filled = bool(self.cvs[c_idx]) return Circle(filled) + raise ValueError(f'Unknown name: {soq.reg.name}') def build_composite_bloq( self, bb: BloqBuilder, ctrl: SoquetT, sel: SoquetT, targets: SoquetT, junk: SoquetT @@ -294,9 +296,9 @@ def build_composite_bloq( r: SoquetT, s: SoquetT, mu: SoquetT, - nu_x: SoquetT, - nu_y: SoquetT, - nu_z: SoquetT, + nu_x: Soquet, + nu_y: Soquet, + nu_z: Soquet, m: SoquetT, succ_nu: SoquetT, l: SoquetT, @@ -462,7 +464,7 @@ def short_name(self) -> str: def build_composite_bloq( self, bb: BloqBuilder, - ham_ctrl: SoquetT, + ham_ctrl: NDArray[Soquet], # type: ignore[type-var] i_ne_j: SoquetT, plus_t: SoquetT, i: SoquetT, @@ -478,7 +480,7 @@ def build_composite_bloq( m: SoquetT, l: SoquetT, sys: SoquetT, - proj: SoquetT, + proj: NDArray[Soquet], # type: ignore[type-var] ) -> Dict[str, 'SoquetT']: # ancilla for swaps from electronic and projectile system registers. # we assume these are left in a clean state after SELECT operations @@ -543,8 +545,10 @@ def build_composite_bloq( j, sys, q = bb.add( MultiplexedCSwap3D(self.num_bits_p, self.eta), sel=j, targets=sys, junk=q ) - _ = [bb.free(pi) for pi in p] - _ = [bb.free(qi) for qi in q] + for pi in p: + bb.free(pi) + for qi in q: + bb.free(qi) ham_ctrl[:] = flag_t, flag_t_mean, flag_uv, flag_proj bb.free(rl) return { diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/select_and_prepare.py b/qualtran/bloqs/chemistry/pbc/first_quantization/select_and_prepare.py index 88566d216..b83ab176d 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/select_and_prepare.py +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/select_and_prepare.py @@ -17,6 +17,7 @@ import numpy as np from attrs import frozen +from numpy.typing import NDArray from qualtran import ( Bloq, @@ -28,6 +29,7 @@ QBit, Register, Signature, + Soquet, SoquetT, ) from qualtran.bloqs.basic_gates import Toffoli @@ -131,7 +133,7 @@ def signature(self) -> Signature: @staticmethod def _reshape_reg( bb: BloqBuilder, in_reg: SoquetT, out_shape: Tuple[int, ...], bitsize: int - ) -> SoquetT: + ) -> NDArray[Soquet]: # type: ignore[type-var] """Reshape registers allocated as a big register. Example: @@ -162,6 +164,7 @@ def wire_symbol(self, soq: 'Soquet') -> 'WireSymbol': return TextBox('×(x)') elif soq.reg.name == 'junk': return TextBox('×(y)') + raise ValueError(f'Unknown name: {soq.reg.name}') def short_name(self) -> str: return 'MultiSwap' @@ -279,9 +282,9 @@ def build_composite_bloq( r: SoquetT, s: SoquetT, mu: SoquetT, - nu_x: SoquetT, - nu_y: SoquetT, - nu_z: SoquetT, + nu_x: Soquet, + nu_y: Soquet, + nu_z: Soquet, m: SoquetT, succ_nu: SoquetT, l: SoquetT, @@ -445,9 +448,9 @@ def build_composite_bloq( r: SoquetT, s: SoquetT, mu: SoquetT, - nu_x: SoquetT, - nu_y: SoquetT, - nu_z: SoquetT, + nu_x: Soquet, + nu_y: Soquet, + nu_z: Soquet, m: SoquetT, l: SoquetT, sys: SoquetT, @@ -490,8 +493,10 @@ def build_composite_bloq( j, sys, q = bb.add( MultiplexedCSwap3D(self.num_bits_p, self.eta), sel=j, targets=sys, junk=q ) - _ = [bb.free(pi) for pi in p] - _ = [bb.free(qi) for qi in q] + for pi in p: + bb.free(pi) + for qi in q: + bb.free(qi) bb.free(rl) return { 'tuv': tuv, diff --git a/qualtran/bloqs/chemistry/sf/single_factorization.py b/qualtran/bloqs/chemistry/sf/single_factorization.py index 4fa4e9243..46f11753a 100644 --- a/qualtran/bloqs/chemistry/sf/single_factorization.py +++ b/qualtran/bloqs/chemistry/sf/single_factorization.py @@ -27,6 +27,7 @@ import numpy as np from attrs import evolve, frozen +from numpy.typing import NDArray from qualtran import ( Bloq, @@ -37,6 +38,7 @@ QBit, Register, Signature, + Soquet, SoquetT, ) from qualtran._infra.data_types import BoundedQUInt @@ -329,10 +331,10 @@ def build_composite_bloq( self, bb: 'BloqBuilder', *, - ctrl: SoquetT, + ctrl: NDArray[Soquet], # type: ignore[type-var] l: SoquetT, - pq: SoquetT, - rot_aa: SoquetT, + pq: Soquet, + rot_aa: NDArray[Soquet], # type: ignore[type-var] swap_pq: SoquetT, spin: SoquetT, sys: SoquetT, diff --git a/qualtran/bloqs/chemistry/sparse/prepare.py b/qualtran/bloqs/chemistry/sparse/prepare.py index 1d445a831..fb9fea8eb 100644 --- a/qualtran/bloqs/chemistry/sparse/prepare.py +++ b/qualtran/bloqs/chemistry/sparse/prepare.py @@ -29,6 +29,7 @@ QAny, QBit, Register, + Soquet, SoquetT, ) from qualtran.bloqs.arithmetic.comparison import LessThanEqual @@ -43,6 +44,7 @@ from qualtran.linalg.lcu_util import preprocess_lcu_coefficients_for_reversible_sampling if TYPE_CHECKING: + from qualtran import Bloq from qualtran.resource_counting import BloqCountT, SympySymbolAllocator @@ -102,12 +104,12 @@ def _add(p_indx: int, q_indx: int, r_indx: int, s_indx: int): _add(q, q, q, p) for p in range(num_spat): _add(p, p, p, p) - eris_eight = np.array(eris_eight) + eris_eight_np = np.array(eris_eight) pqrs_indx_np = np.array(pqrs_indx) - keep_indx = np.where(np.abs(eris_eight) > drop_element_thresh) - eris_eight = eris_eight[keep_indx] + keep_indx = np.where(np.abs(eris_eight_np) > drop_element_thresh) + eris_eight_np = eris_eight_np[keep_indx] pqrs_indx_np = pqrs_indx_np[keep_indx[0]] - return np.concatenate((tpq_indx, pqrs_indx_np)), np.concatenate((tpq_sparse, eris_eight)) + return np.concatenate((tpq_indx, pqrs_indx_np)), np.concatenate((tpq_sparse, eris_eight_np)) @frozen @@ -319,8 +321,8 @@ def build_composite_bloq( swap_rs: 'SoquetT', swap_pqrs: 'SoquetT', flag_1b: 'SoquetT', - alt_pqrs: 'SoquetT', - theta: 'SoquetT', + alt_pqrs: NDArray[Soquet], # type: ignore[type-var] + theta: NDArray[Soquet], # type: ignore[type-var] keep: 'SoquetT', less_than: 'SoquetT', alt_flag_1b: 'SoquetT', diff --git a/qualtran/bloqs/factoring/mod_add.py b/qualtran/bloqs/factoring/mod_add.py index a750de332..e45704e8a 100644 --- a/qualtran/bloqs/factoring/mod_add.py +++ b/qualtran/bloqs/factoring/mod_add.py @@ -243,8 +243,8 @@ def build_composite_bloq( sign = bb.add(XGate(), q=sign) # Free the ancilla qubits. - junk_bit = bb.free(junk_bit) - sign = bb.free(sign) + bb.free(junk_bit) + bb.free(sign) # Return the output registers. return {'x': x, 'y': y} diff --git a/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py b/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py index 4a3ec4d6e..f7e5f5ff1 100644 --- a/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py +++ b/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from functools import cached_property -from typing import Dict, Set, Tuple, TYPE_CHECKING +from typing import cast, Dict, Set, Tuple, TYPE_CHECKING import numpy as np from attrs import field, frozen @@ -119,16 +119,16 @@ def approx_cos(self) -> NDArray[np.complex_]: r"""polynomial approximation for $$e^{i\theta} \mapsto e^{it\cos(\theta)}$$""" if self._parameterized_(): raise ValueError(f"cannot compute `cos` approximation for parameterized Bloq {self}") - poly = approx_exp_cos_by_jacobi_anger(-self.t * self.alpha, degree=self.degree) + poly = approx_exp_cos_by_jacobi_anger(-self.t * self.alpha, degree=cast(int, self.degree)) # TODO(#860) current scaling method does not compute true maximum, so we scale down a bit more by (1 - 2\eps) - poly = scale_down_to_qsp_polynomial(poly) * (1 - 2 * self.precision) + poly = scale_down_to_qsp_polynomial(list(poly)) * (1 - 2 * self.precision) return poly @cached_property def gqsp(self) -> GeneralizedQSP: return GeneralizedQSP.from_qsp_polynomial( self.walk_operator, - self.approx_cos, + list(self.approx_cos), negative_power=int(self.degree), verify=True, verify_precision=1e-4, @@ -176,7 +176,7 @@ def build_composite_bloq(self, bb: 'BloqBuilder', **soqs: 'SoquetT') -> Dict[str bb.free(soq) else: for soq_element in soq: - bb.free(soq) + bb.free(cast(Soquet, soq)) return soqs diff --git a/qualtran/bloqs/mcmt/and_bloq.py b/qualtran/bloqs/mcmt/and_bloq.py index 6c7bbadca..4827d8ee0 100644 --- a/qualtran/bloqs/mcmt/and_bloq.py +++ b/qualtran/bloqs/mcmt/and_bloq.py @@ -24,7 +24,7 @@ import itertools from functools import cached_property -from typing import Any, Dict, Iterable, Optional, Sequence, Set, Tuple +from typing import Any, Dict, Iterable, Optional, Sequence, Set, Tuple, Union import attrs import cirq @@ -81,8 +81,8 @@ class And(GateWithRegisters): [Verifying Measurement Based Uncomputation](https://algassert.com/post/1903). Gidney, C. 2019. """ - cv1: int = 1 - cv2: int = 1 + cv1: Union[int, sympy.Expr] = 1 + cv2: Union[int, sympy.Expr] = 1 uncompute: bool = False @cached_property @@ -229,7 +229,7 @@ def _and_bloq() -> And: ) -def _to_tuple(x: Iterable[int]) -> Sequence[int]: +def _to_tuple(x: Iterable[Union[int, sympy.Expr]]) -> Sequence[Union[int, sympy.Expr]]: return tuple(x) @@ -247,7 +247,7 @@ class MultiAnd(Bloq): target [right]: The output bit. """ - cvs: Tuple[int, ...] = field(converter=_to_tuple) + cvs: Tuple[Union[int, sympy.Expr], ...] = field(converter=_to_tuple) @cvs.validator def _validate_cvs(self, field, val): diff --git a/qualtran/bloqs/mcmt/multi_control_multi_target_pauli_test.py b/qualtran/bloqs/mcmt/multi_control_multi_target_pauli_test.py index 426c5da7b..3ce4c64fd 100644 --- a/qualtran/bloqs/mcmt/multi_control_multi_target_pauli_test.py +++ b/qualtran/bloqs/mcmt/multi_control_multi_target_pauli_test.py @@ -28,13 +28,14 @@ def test_multi_target_cnot(num_targets): qubits = cirq.LineQubit.range(num_targets + 1) naive_circuit = cirq.Circuit(cirq.CNOT(qubits[0], q) for q in qubits[1:]) - op = MultiTargetCNOT(num_targets).on(*qubits) + bloq = MultiTargetCNOT(num_targets) + op = bloq.on(*qubits) cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( cirq.Circuit(op), naive_circuit, atol=1e-6 ) optimal_circuit = cirq.Circuit(cirq.decompose_once(op)) assert len(optimal_circuit) == 2 * np.ceil(np.log2(num_targets)) + 1 - qlt_testing.assert_valid_bloq_decomposition(op.gate) + qlt_testing.assert_valid_bloq_decomposition(bloq) @pytest.mark.parametrize("num_controls", [0, 1, 2, *range(7, 17)]) diff --git a/qualtran/bloqs/multiplexers/apply_gate_to_lth_target_test.py b/qualtran/bloqs/multiplexers/apply_gate_to_lth_target_test.py index 4168898fa..92a5f6262 100644 --- a/qualtran/bloqs/multiplexers/apply_gate_to_lth_target_test.py +++ b/qualtran/bloqs/multiplexers/apply_gate_to_lth_target_test.py @@ -108,7 +108,8 @@ def test_apply_gate_to_lth_qubit_make_on(): ) op = gate.on_registers(**get_named_qubits(gate.signature)) op2 = ApplyGateToLthQubit.make_on( - nth_gate=lambda n: cirq.Z if n & 1 else cirq.I, **get_named_qubits(gate.signature) + nth_gate=lambda n: cirq.Z if n & 1 else cirq.I, + **get_named_qubits(gate.signature), # type: ignore[arg-type] ) # Note: ApplyGateToLthQubit doesn't support value equality. assert op.qubits == op2.qubits diff --git a/qualtran/bloqs/multiplexers/select_pauli_lcu_test.py b/qualtran/bloqs/multiplexers/select_pauli_lcu_test.py index a8ba1fadf..b3bc8d25f 100644 --- a/qualtran/bloqs/multiplexers/select_pauli_lcu_test.py +++ b/qualtran/bloqs/multiplexers/select_pauli_lcu_test.py @@ -49,14 +49,15 @@ def test_ising_zero_bitflip_select(control_val): ham = get_1d_ising_hamiltonian(target, 1, 1) dense_pauli_string_hamiltonian = [tt.dense(target) for tt in ham] # built select with unary iteration gate - op = SelectPauliLCU( + bloq = SelectPauliLCU( selection_bitsize=selection_bitsize, target_bitsize=target_bitsize, select_unitaries=dense_pauli_string_hamiltonian, control_val=control_val, - ).on(control, *selection, *target) + ) + op = bloq.on(control, *selection, *target) - assert_valid_bloq_decomposition(op.gate) + assert_valid_bloq_decomposition(bloq) circuit = cirq.Circuit(cirq.decompose(op)) all_qubits = circuit.all_qubits() @@ -77,7 +78,7 @@ def test_ising_zero_bitflip_select(control_val): qubit_vals[target[i]] = 1 final_state = [qubit_vals[x] for x in all_qubits] - assert_circuit_inp_out_cirqsim(circuit, all_qubits, initial_state, final_state) + assert_circuit_inp_out_cirqsim(circuit, list(all_qubits), initial_state, final_state) def test_ising_one_bitflip_select(): @@ -99,14 +100,14 @@ def test_ising_one_bitflip_select(): ham = get_1d_ising_hamiltonian(target, 1, 1) dense_pauli_string_hamiltonian = [tt.dense(target) for tt in ham] # built select with unary iteration gate - op = SelectPauliLCU( + bloq = SelectPauliLCU( selection_bitsize=selection_bitsize, target_bitsize=target_bitsize, select_unitaries=dense_pauli_string_hamiltonian, control_val=1, - ).on(control, *selection, *target) - - assert_valid_bloq_decomposition(op.gate) + ) + op = bloq.on(control, *selection, *target) + assert_valid_bloq_decomposition(bloq) circuit = cirq.Circuit(cirq.decompose(op)) all_qubits = sorted(circuit.all_qubits()) @@ -172,14 +173,15 @@ def test_select_application_to_eigenstates(): ham = get_1d_ising_hamiltonian(target, 1, 1) dense_pauli_string_hamiltonian = [tt.dense(target) for tt in ham] # built select with unary iteration gate - op = SelectPauliLCU( + bloq = SelectPauliLCU( selection_bitsize=selection_bitsize, target_bitsize=target_bitsize, select_unitaries=dense_pauli_string_hamiltonian, control_val=1, - ).on(control, *selection, *target) + ) + op = bloq.on(control, *selection, *target) - assert_valid_bloq_decomposition(op.gate) + assert_valid_bloq_decomposition(bloq) select_circuit = cirq.Circuit(cirq.decompose(op)) all_qubits = select_circuit.all_qubits() diff --git a/qualtran/bloqs/multiplexers/unary_iteration_bloq_test.py b/qualtran/bloqs/multiplexers/unary_iteration_bloq_test.py index 3c01bbd41..77ffb04cb 100644 --- a/qualtran/bloqs/multiplexers/unary_iteration_bloq_test.py +++ b/qualtran/bloqs/multiplexers/unary_iteration_bloq_test.py @@ -14,7 +14,7 @@ import itertools from functools import cached_property -from typing import Sequence, Set, Tuple, TYPE_CHECKING +from typing import List, Sequence, Set, Tuple, TYPE_CHECKING import cirq import pytest @@ -166,15 +166,15 @@ def test_unary_iteration_loop(): target = {(n, m): cirq.q(f't({n}, {m})') for n in range(*n_range) for m in range(*m_range)} qm = cirq.GreedyQubitManager("ancilla", maximize_reuse=True) circuit = cirq.Circuit() - i_ops = [] + i_ops: List[cirq.Operation] = [] # Build the unary iteration circuit for i_optree, i_ctrl, i in unary_iteration( - n_range[0], n_range[1], i_ops, [], selection['n'], qm + n_range[0], n_range[1], i_ops, [], list(selection['n']), qm ): circuit.append(i_optree) - j_ops = [] + j_ops: List[cirq.Operation] = [] for j_optree, j_ctrl, j in unary_iteration( - m_range[0], m_range[1], j_ops, [i_ctrl], selection['m'], qm + m_range[0], m_range[1], j_ops, [i_ctrl], list(selection['m']), qm ): circuit.append(j_optree) # Conditionally perform operations on target register using `j_ctrl`, `i` & `j`. diff --git a/qualtran/bloqs/qsp/generalized_qsp_test.py b/qualtran/bloqs/qsp/generalized_qsp_test.py index f6542c39f..096ca6566 100644 --- a/qualtran/bloqs/qsp/generalized_qsp_test.py +++ b/qualtran/bloqs/qsp/generalized_qsp_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from functools import cached_property -from typing import Optional, Sequence, Union +from typing import Dict, Optional, Sequence, Union import cirq import numpy as np @@ -74,7 +74,7 @@ def random_qsp_polynomial( poly = random_state.random(size=degree) / degree if not only_real_coeffs: poly = poly * np.exp(random_state.random(size=degree) * np.pi * 2j) - return poly + return list(poly) @pytest.mark.parametrize("degree", [4, 5]) @@ -208,7 +208,7 @@ def catch_rotations(bloq: Bloq) -> Bloq: g, sigma = gsqp_U.call_graph(max_depth=1, generalizer=catch_rotations) - expected_counts = {arbitrary_rotation: 3} + expected_counts: Dict[Bloq, int] = {arbitrary_rotation: 3} if negative_power < 2: expected_counts[Controlled(U, CtrlSpec(cvs=0))] = 2 - negative_power if negative_power > 0: @@ -301,14 +301,14 @@ def test_complementary_polynomials_for_jacobi_anger_approximations(t: float, pre random_state = np.random.RandomState(42 + int(t)) d = degree_jacobi_anger_approximation(t, precision=precision) - + assert isinstance(d, int) P = approx_exp_cos_by_jacobi_anger(t, degree=d) # TODO(#860) current scaling method does not compute true maximum, so we scale down a bit more by (1 - 2\eps) - P = scale_down_to_qsp_polynomial(P) * (1 - 2 * precision) - assert_is_qsp_polynomial(P) + P = scale_down_to_qsp_polynomial(list(P)) * (1 - 2 * precision) + assert_is_qsp_polynomial(list(P)) - Q = qsp_complementary_polynomial(P, verify=True, verify_precision=1e-5) + Q = qsp_complementary_polynomial(list(P), verify=True, verify_precision=1e-5) check_polynomial_pair_on_random_points_on_unit_circle( - P, Q, random_state=random_state, rtol=precision + list(P), Q, random_state=random_state, rtol=precision ) - verify_generalized_qsp(RandomGate.create(1, random_state=random_state), P, Q) + verify_generalized_qsp(RandomGate.create(1, random_state=random_state), list(P), Q) diff --git a/qualtran/bloqs/rotations/phase_gradient_test.py b/qualtran/bloqs/rotations/phase_gradient_test.py index c4245d103..7f396d149 100644 --- a/qualtran/bloqs/rotations/phase_gradient_test.py +++ b/qualtran/bloqs/rotations/phase_gradient_test.py @@ -11,6 +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 typing import Dict + import cirq import numpy as np import pytest @@ -68,7 +70,7 @@ def test_phase_gradient_gate(n: int, exponent, controlled): bloq = PhaseGradientUnitary(n, exponent, controlled, eps=eps) assert_valid_bloq_decomposition(bloq) assert_valid_bloq_decomposition(bloq**-1) - cirq_gate = cirq.PhaseGradientGate(num_qubits=n, exponent=exponent) + cirq_gate: cirq.Gate = cirq.PhaseGradientGate(num_qubits=n, exponent=exponent) if controlled: cirq_gate = cirq_gate.controlled() assert np.allclose(cirq.unitary(bloq), cirq.unitary(cirq_gate), atol=eps) @@ -79,7 +81,7 @@ def test_add_into_phase_grad(): x_bit, phase_bit = 4, 7 bloq = AddIntoPhaseGrad(x_bit, phase_bit) - basis_map = {} + basis_map: Dict[int, int] = {} for x in range(2**x_bit): for phase_grad in range(2**phase_bit): phase_fxp = _fxp(phase_grad / 2**phase_bit, phase_bit) @@ -104,7 +106,7 @@ def test_add_into_phase_grad_controlled(controlled: int): x_bit, phase_bit = 4, 7 bloq = AddIntoPhaseGrad(x_bit, phase_bit, controlled=controlled) - basis_map = {} + basis_map: Dict[int, int] = {} num_bits = 1 + x_bit + phase_bit expected_unitary = np.zeros((2**num_bits, 2**num_bits)) for control in range(2): @@ -129,6 +131,7 @@ def test_add_into_phase_grad_controlled(controlled: int): output_state = int( f'{control}' + f'{x:0{x_bit}b}' + f'{phase_grad_out:0{phase_bit}b}', 2 ) + basis_map[input_state] = output_state expected_unitary[output_state, input_state] = 1 # Test cirq style simulation. assert len(basis_map) == len(set(basis_map.values())) diff --git a/qualtran/bloqs/rotations/programmable_rotation_gate_array_test.py b/qualtran/bloqs/rotations/programmable_rotation_gate_array_test.py index bfd2eec4a..ff48d3547 100644 --- a/qualtran/bloqs/rotations/programmable_rotation_gate_array_test.py +++ b/qualtran/bloqs/rotations/programmable_rotation_gate_array_test.py @@ -145,6 +145,7 @@ def rotation_ops(theta: int) -> cirq.OP_TREE: qid_shape=(2,) * len(ancilla_indices), dtype=np.complex128, ).state_vector() + assert expected_ancilla_state_vector is not None cirq.testing.assert_allclose_up_to_global_phase( ancilla_state_vector, expected_ancilla_state_vector, atol=1e-8 ) diff --git a/qualtran/bloqs/rotations/quantum_variable_rotation_test.py b/qualtran/bloqs/rotations/quantum_variable_rotation_test.py index a1aa72378..6630877f6 100644 --- a/qualtran/bloqs/rotations/quantum_variable_rotation_test.py +++ b/qualtran/bloqs/rotations/quantum_variable_rotation_test.py @@ -50,7 +50,7 @@ def qvr(self) -> QvrPhaseGradient: return QvrPhaseGradient(self.cost_reg, self.gamma, self.eps) def decompose_from_registers( - self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid] + self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid] # type: ignore[type-var] ) -> cirq.OP_TREE: x = quregs[self.cost_reg.name] phase_grad = context.qubit_manager.qalloc(self.qvr.b_grad) diff --git a/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.py b/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.py index 3f64b0d1d..c9903ab8a 100644 --- a/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.py +++ b/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.py @@ -21,7 +21,7 @@ """ from functools import cached_property -from typing import List, Tuple +from typing import Sequence, Tuple import attrs import cirq @@ -99,7 +99,7 @@ class StatePreparationAliasSampling(PrepareOracle): @classmethod def from_lcu_probs( - cls, lcu_probabilities: List[float], *, probability_epsilon: float = 1.0e-5 + cls, lcu_probabilities: Sequence[float], *, probability_epsilon: float = 1.0e-5 ) -> 'StatePreparationAliasSampling': """Factory to construct the state preparation gate for a given set of LCU coefficients. diff --git a/qualtran/bloqs/swap_network/cswap_approx_test.py b/qualtran/bloqs/swap_network/cswap_approx_test.py index 70e28f583..8ab87a474 100644 --- a/qualtran/bloqs/swap_network/cswap_approx_test.py +++ b/qualtran/bloqs/swap_network/cswap_approx_test.py @@ -13,9 +13,10 @@ # limitations under the License. import random -from typing import Dict, Tuple +from typing import Dict, Tuple, Union import pytest +import sympy import qualtran.cirq_interop.testing as cq_testing from qualtran import Bloq @@ -45,7 +46,9 @@ def test_approx_cswap_t_count(n): assert cswap.t_complexity() == cswap_d.t_complexity() -def get_t_count_and_clifford(bc: Dict[Bloq, int]) -> Tuple[int, int]: +def get_t_count_and_clifford( + bc: Dict[Bloq, Union[int, sympy.Expr]] +) -> Tuple[Union[int, sympy.Expr], Union[int, sympy.Expr]]: """Get the t count and clifford cost from bloq count.""" cliff_cost = sum([v for k, v in bc.items() if isinstance(k, ArbitraryClifford)]) t_cost = sum([v for k, v in bc.items() if isinstance(k, TGate)]) diff --git a/qualtran/bloqs/util_bloqs_test.py b/qualtran/bloqs/util_bloqs_test.py index 711057811..8261be701 100644 --- a/qualtran/bloqs/util_bloqs_test.py +++ b/qualtran/bloqs/util_bloqs_test.py @@ -179,6 +179,7 @@ def test_classical_sim(): def test_classical_sim_dtypes(): s = Split(QAny(8)) (xx,) = s.call_classically(reg=255) + assert isinstance(xx, np.ndarray) assert xx.tolist() == [1, 1, 1, 1, 1, 1, 1, 1] # TODO: Re-enable when Split/Join have real data types @@ -188,10 +189,12 @@ def test_classical_sim_dtypes(): # with numpy types (xx,) = s.call_classically(reg=np.uint8(255)) + assert isinstance(xx, np.ndarray) assert xx.tolist() == [1, 1, 1, 1, 1, 1, 1, 1] # Warning: numpy will wrap too-large values (xx,) = s.call_classically(reg=np.uint8(256)) + assert isinstance(xx, np.ndarray) assert xx.tolist() == [0, 0, 0, 0, 0, 0, 0, 0] # TODO: Re-enable when Split/Join have real data types diff --git a/qualtran/cirq_interop/_cirq_to_bloq_test.py b/qualtran/cirq_interop/_cirq_to_bloq_test.py index ac720a426..cc029300c 100644 --- a/qualtran/cirq_interop/_cirq_to_bloq_test.py +++ b/qualtran/cirq_interop/_cirq_to_bloq_test.py @@ -30,6 +30,7 @@ Register, Side, Signature, + Soquet, SoquetT, ) from qualtran._infra.gate_with_registers import get_named_qubits @@ -48,6 +49,8 @@ def signature(self) -> Signature: def build_composite_bloq(self, bb: 'BloqBuilder', **soqs: 'SoquetT') -> Dict[str, 'SoquetT']: ctrl, target = soqs['control'], soqs['target'] + assert isinstance(ctrl, Soquet) + assert isinstance(target, Soquet) ctrl, target = bb.add(CirqGateAsBloq(cirq.CNOT), q=[ctrl, target]) return {'control': ctrl, 'target': target} diff --git a/qualtran/cirq_interop/t_complexity_protocol_test.py b/qualtran/cirq_interop/t_complexity_protocol_test.py index 4322f52e1..bf5c80b61 100644 --- a/qualtran/cirq_interop/t_complexity_protocol_test.py +++ b/qualtran/cirq_interop/t_complexity_protocol_test.py @@ -95,7 +95,7 @@ def test_t_complexity(): assert t_complexity(SupportTComplexityGate().on(cirq.q('t'))) == TComplexity(t=1) g = GateHelper(SupportsTComplexityGateWithRegisters()) - assert g.gate._decompose_with_context_(g.operation.qubits) is NotImplemented + assert g.gate._decompose_with_context_(g.operation.qubits) is NotImplemented # type: ignore[attr-defined] assert t_complexity(g.gate) == TComplexity(t=1, clifford=2) assert t_complexity(g.operation) == TComplexity(t=1, clifford=2) @@ -229,11 +229,12 @@ def __hash__(self): # Using a global cache will result in a failure of this test since `cirq.X` has # `T-complexity(clifford=1)` but we explicitly return `TComplexity()` for IsCachable # operation; for which the hash would be equivalent to the hash of its subgate i.e. `cirq.X`. - t_complexity.cache_clear() + # TODO: t_complexity protocol will be refactored (#735) + t_complexity.cache_clear() # type: ignore[attr-defined] op = Cachable2() assert t_complexity([op, op]) == TComplexity() assert op.num_calls == 1 - t_complexity.cache_clear() + t_complexity.cache_clear() # type: ignore[attr-defined] @pytest.mark.notebook diff --git a/qualtran/resource_counting/classify_bloqs.py b/qualtran/resource_counting/classify_bloqs.py index 6f437ac54..69a8543d7 100644 --- a/qualtran/resource_counting/classify_bloqs.py +++ b/qualtran/resource_counting/classify_bloqs.py @@ -95,7 +95,7 @@ def classify_t_count_by_bloq_type( else: basic_generalizer.append(generalizer) _, sigma = bloq.call_graph(generalizer=basic_generalizer, keep=keeper) - classified_bloqs = defaultdict(int) + classified_bloqs: Dict[str, Union[int, sympy.Expr]] = defaultdict(int) for k, v in sigma.items(): classification = classify_bloq(k, bloq_classification) classified_bloqs[classification] += v * t_counts_from_sigma(k.call_graph()[1]) diff --git a/qualtran/resource_counting/t_counts_from_sigma.py b/qualtran/resource_counting/t_counts_from_sigma.py index 1c111cd4d..7337c0be3 100644 --- a/qualtran/resource_counting/t_counts_from_sigma.py +++ b/qualtran/resource_counting/t_counts_from_sigma.py @@ -13,7 +13,7 @@ # limitations under the License. import inspect import sys -from typing import Dict, Optional, Tuple, TYPE_CHECKING, Union +from typing import Mapping, Optional, Tuple, TYPE_CHECKING, Union import cirq @@ -41,7 +41,7 @@ def _get_all_rotation_types() -> Tuple['_HasEps', ...]: def t_counts_from_sigma( - sigma: Dict['Bloq', Union[int, 'sympy.Expr']], + sigma: Mapping['Bloq', Union[int, 'sympy.Expr']], rotation_types: Optional[Tuple['_HasEps', ...]] = None, ) -> SymbolicInt: """Aggregates T-counts from a sigma dictionary by summing T-costs for all rotation bloqs.""" diff --git a/qualtran/simulation/classical_sim.py b/qualtran/simulation/classical_sim.py index eb4b39f99..8586113ce 100644 --- a/qualtran/simulation/classical_sim.py +++ b/qualtran/simulation/classical_sim.py @@ -14,7 +14,7 @@ """Functionality for the `Bloq.call_classically(...)` protocol.""" import itertools -from typing import Any, Dict, Iterable, List, Sequence, Tuple, Union +from typing import Any, Dict, Iterable, List, Mapping, Sequence, Tuple, Union import networkx as nx import numpy as np @@ -34,7 +34,7 @@ ) from qualtran._infra.composite_bloq import _binst_to_cxns -ClassicalValT = Union[int, NDArray[np.integer]] +ClassicalValT = Union[int, np.integer, NDArray[np.integer]] def bits_to_ints(bitstrings: Union[Sequence[int], NDArray[np.uint]]) -> NDArray[np.uint]: @@ -172,7 +172,7 @@ def _in_vals(reg: Register): def call_cbloq_classically( - signature: Signature, vals: Dict[str, ClassicalValT], binst_graph: nx.DiGraph + signature: Signature, vals: Mapping[str, ClassicalValT], binst_graph: nx.DiGraph ) -> Tuple[Dict[str, ClassicalValT], Dict[Soquet, ClassicalValT]]: """Propagate `on_classical_vals` calls through a composite bloq's contents. diff --git a/qualtran/simulation/classical_sim_test.py b/qualtran/simulation/classical_sim_test.py index b3239aabf..8a018335c 100644 --- a/qualtran/simulation/classical_sim_test.py +++ b/qualtran/simulation/classical_sim_test.py @@ -20,12 +20,13 @@ from attrs import frozen from numpy.typing import NDArray -from qualtran import Bloq, BloqBuilder, QAny, QBit, Register, Side, Signature +from qualtran import Bloq, BloqBuilder, QAny, QBit, Register, Side, Signature, Soquet from qualtran.bloqs.basic_gates import CNOT from qualtran.simulation.classical_sim import ( _update_assign_from_vals, bits_to_ints, call_cbloq_classically, + ClassicalValT, ints_to_bits, ) from qualtran.testing import execute_notebook @@ -68,7 +69,7 @@ def test_int_to_bits(): def test_dtype_validation(): # set up mocks for `_update_assign_from_vals` - soq_assign = {} # gets assigned to; we discard in this test. + soq_assign: Dict[Soquet, ClassicalValT] = {} # gets assigned to; we discard in this test. binst = 'MyBinst' # binst is only used for error messages, so we can mock with a string # set up different register dtypes @@ -86,21 +87,21 @@ def test_dtype_validation(): 'bit_arr': np.array([1, 0, 1, 0, 1], dtype=np.uint8), 'int_arr': np.arange(5), } - _update_assign_from_vals(regs, binst, vals, soq_assign) + _update_assign_from_vals(regs, binst, vals, soq_assign) # type: ignore[arg-type] # bad integer vals2 = {**vals, 'one_bit_int': 2} with pytest.raises(ValueError, match=r'Bad QBit().*one_bit_int'): - _update_assign_from_vals(regs, binst, vals2, soq_assign) + _update_assign_from_vals(regs, binst, vals2, soq_assign) # type: ignore[arg-type] # int is a numpy int vals3 = {**vals, 'int': np.arange(5, dtype=np.uint8)[4]} - _update_assign_from_vals(regs, binst, vals3, soq_assign) + _update_assign_from_vals(regs, binst, vals3, soq_assign) # type: ignore[arg-type] # wrong shape vals4 = {**vals, 'int_arr': np.arange(6)} with pytest.raises(ValueError, match=r'Incorrect shape.*Want \(5,\)\.'): - _update_assign_from_vals(regs, binst, vals4, soq_assign) + _update_assign_from_vals(regs, binst, vals4, soq_assign) # type: ignore[arg-type] @frozen @@ -121,11 +122,15 @@ def test_apply_classical(): bloq = ApplyClassicalTest() x, z = bloq.call_classically(x=np.zeros(5, dtype=np.uint8)) np.testing.assert_array_equal(x, np.zeros(5)) + assert not isinstance(x, int) + assert not isinstance(z, int) assert x.dtype == np.uint8 assert z.dtype == np.uint8 np.testing.assert_array_equal(z, [1, 0, 1, 0, 1]) x2, z2 = bloq.call_classically(x=np.ones(5, dtype=np.uint8)) + assert not isinstance(x2, int) + assert not isinstance(z2, int) assert x2.dtype == np.uint8 assert z2.dtype == np.uint8 np.testing.assert_array_equal(x2, np.ones(5)) @@ -147,11 +152,12 @@ def test_cnot_assign_dict(): def test_apply_classical_cbloq(): bb = BloqBuilder() x = bb.add_register(Register('x', QBit(), shape=(5,))) + assert x is not None x, y = bb.add(ApplyClassicalTest(), x=x) y, z = bb.add(ApplyClassicalTest(), x=y) cbloq = bb.finalize(x=x, y=y, z=z) - xarr = np.zeros(5) + xarr = np.zeros(5, dtype=np.intc) x, y, z = cbloq.call_classically(x=xarr) np.testing.assert_array_equal(x, xarr) np.testing.assert_array_equal(y, [1, 0, 1, 0, 1]) diff --git a/qualtran/testing.py b/qualtran/testing.py index 5bf41d035..b4e92b4ed 100644 --- a/qualtran/testing.py +++ b/qualtran/testing.py @@ -16,7 +16,7 @@ import traceback from enum import Enum from pathlib import Path -from typing import Dict, List, Optional, Tuple, Union +from typing import Dict, List, Optional, Sequence, Tuple, Union import sympy @@ -483,7 +483,9 @@ def assert_equivalent_bloq_example_counts(bloq_ex: BloqExample) -> None: raise BloqCheckException.unverified(f'{bloq_ex.name} only has counts from decomposition.') -def assert_equivalent_bloq_counts(bloq: Bloq, generalizer: GeneralizerT = lambda x: x) -> None: +def assert_equivalent_bloq_counts( + bloq: Bloq, generalizer: Union[GeneralizerT, Sequence[GeneralizerT]] = lambda x: x +) -> None: """Assert that the BloqExample has consistent bloq counts. See the documentation for `assert_equivalent_bloq_example_counts` for details on this function. diff --git a/qualtran/testing_test.py b/qualtran/testing_test.py index c021cbab0..6d604c2c3 100644 --- a/qualtran/testing_test.py +++ b/qualtran/testing_test.py @@ -142,7 +142,7 @@ def test_assert_soquets_used_exactly_once(): def test_check_bloq_example_make(): @bloq_example - def _my_cnot() -> CNOT: + def _my_cnot() -> Bloq: return 'CNOT 0 1' res, msg = check_bloq_example_make(_my_cnot) From 2ded1c228d70db38d46549631c47f05fb5973581 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Fri, 26 Apr 2024 06:51:03 -0700 Subject: [PATCH 2/6] Fix some more issues --- qualtran/_infra/bloq_example.py | 2 +- qualtran/bloqs/factoring/mod_mul.py | 13 +++++++++---- qualtran/resource_counting/classify_bloqs.py | 17 +++++++++++------ qualtran/simulation/xcheck_classical_quimb.py | 19 ++++++++++++------- qualtran/surface_code/ccz2t_cost_model.py | 13 ++++++++----- 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/qualtran/_infra/bloq_example.py b/qualtran/_infra/bloq_example.py index a28befab3..29a0b2351 100644 --- a/qualtran/_infra/bloq_example.py +++ b/qualtran/_infra/bloq_example.py @@ -84,7 +84,7 @@ def bloq_example(_func: Callable[[], _BloqType], **kwargs: Any) -> BloqExample[_ @typing.overload def bloq_example( - _func: None, *, generalizer: _GeneralizerType = lambda x: x + _func: None = None, *, generalizer: _GeneralizerType = lambda x: x ) -> Callable[[Callable[[], _BloqType]], BloqExample[_BloqType]]: ... diff --git a/qualtran/bloqs/factoring/mod_mul.py b/qualtran/bloqs/factoring/mod_mul.py index a945a665e..96ccfd144 100644 --- a/qualtran/bloqs/factoring/mod_mul.py +++ b/qualtran/bloqs/factoring/mod_mul.py @@ -78,10 +78,15 @@ def build_composite_bloq( self, bb: 'BloqBuilder', ctrl: 'SoquetT', x: 'SoquetT' ) -> Dict[str, 'SoquetT']: k = self.k - neg_k_inv = -pow(k, -1, mod=self.mod) + if isinstance(self.mod, sympy.Expr) or isinstance(k, sympy.Expr): + neg_k_inv = sympy.Mod(sympy.Pow(k, -1), self.mod) + else: + neg_k_inv = -pow(k, -1, mod=self.mod) # We store the result of the CtrlScaleModAdd into this new register # and then clear the original `x` register by multiplying in the inverse. + if isinstance(self.bitsize, sympy.Expr): + raise ValueError(f'bitsize: {self.bitsize} must be an int not an expression') y = bb.allocate(self.bitsize) # y += x*k @@ -148,7 +153,7 @@ def signature(self) -> 'Signature': def on_classical_vals(self, x: 'ClassicalValT') -> Dict[str, 'ClassicalValT']: return {'x': (2 * x) % self.p} - def build_composite_bloq(self, bb: 'BloqBuilder', x: SoquetT) -> Dict[str, 'SoquetT']: + def build_composite_bloq(self, bb: 'BloqBuilder', x: Soquet) -> Dict[str, 'SoquetT']: # Allocate ancilla bits for sign and double. lower_bit = bb.allocate(n=1) @@ -215,13 +220,13 @@ def _generalize_k(b: Bloq) -> Optional[Bloq]: return b -@bloq_example(generalizer=(ignore_split_join, ignore_alloc_free, _generalize_k)) +@bloq_example(None, generalizer=(ignore_split_join, ignore_alloc_free, _generalize_k)) def _modmul() -> CtrlModMul: modmul = CtrlModMul(k=123, mod=13 * 17, bitsize=8) return modmul -@bloq_example(generalizer=(ignore_split_join, ignore_alloc_free, _generalize_k)) +@bloq_example(None, generalizer=(ignore_split_join, ignore_alloc_free, _generalize_k)) def _modmul_symb() -> CtrlModMul: import sympy diff --git a/qualtran/resource_counting/classify_bloqs.py b/qualtran/resource_counting/classify_bloqs.py index 69a8543d7..c88c818f2 100644 --- a/qualtran/resource_counting/classify_bloqs.py +++ b/qualtran/resource_counting/classify_bloqs.py @@ -11,12 +11,13 @@ # 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 collections.abc as abc from collections import defaultdict -from typing import Dict, Optional, Sequence, TYPE_CHECKING, Union +from typing import cast, Dict, List, Optional, Sequence, TYPE_CHECKING, Union import sympy -from qualtran import Bloq +from qualtran import Adjoint, Bloq from qualtran.resource_counting.generalizers import ( ignore_alloc_free, ignore_cliffords, @@ -58,7 +59,7 @@ def classify_bloq(bloq: Bloq, bloq_classification: Dict[str, str]) -> str: classification: The matching key in bloq_classification. Returns other if not classified. """ if 'adjoint' in bloq.__module__: - mod_name = bloq.subbloq.__module__ + mod_name = cast(Adjoint, bloq).subbloq.__module__ else: mod_name = bloq.__module__ for k, v in bloq_classification.items(): @@ -88,10 +89,14 @@ def classify_t_count_by_bloq_type( if bloq_classification is None: bloq_classification = _get_basic_bloq_classification() keeper = lambda bloq: classify_bloq(bloq, bloq_classification) != 'other' - basic_generalizer = [ignore_split_join, ignore_alloc_free, ignore_cliffords] + basic_generalizer: List['GeneralizerT'] = [ + ignore_split_join, + ignore_alloc_free, + ignore_cliffords, + ] if generalizer is not None: - if isinstance(generalizer, (list, tuple)): - basic_generalizer = basic_generalizer + list(generalizer) + if isinstance(generalizer, abc.Sequence): + basic_generalizer.extend(generalizer) else: basic_generalizer.append(generalizer) _, sigma = bloq.call_graph(generalizer=basic_generalizer, keep=keeper) diff --git a/qualtran/simulation/xcheck_classical_quimb.py b/qualtran/simulation/xcheck_classical_quimb.py index d32ab5b27..9b644d67b 100644 --- a/qualtran/simulation/xcheck_classical_quimb.py +++ b/qualtran/simulation/xcheck_classical_quimb.py @@ -11,15 +11,15 @@ # 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 typing import Dict, Iterable, Optional, TYPE_CHECKING +from typing import cast, Dict, Iterable, Optional, TYPE_CHECKING import numpy as np -from qualtran import Bloq, BloqBuilder, CompositeBloq, Register, SoquetT +from qualtran import Bloq, BloqBuilder, CompositeBloq, Register, Soquet, SoquetT from qualtran.bloqs.basic_gates import IntEffect, IntState if TYPE_CHECKING: - from qualtran.simulation import ClassicalValT + from qualtran.simulation.classical_sim import ClassicalValT def _add_classical_kets( @@ -34,7 +34,7 @@ def _add_classical_kets( for idx in reg.all_idxs(): soq[idx] = bb.add(IntState(val=reg_vals[idx], bitsize=reg.bitsize)) else: - soq = bb.add(IntState(val=vals[reg.name], bitsize=reg.bitsize)) + soq = bb.add(IntState(val=cast(int, vals[reg.name]), bitsize=reg.bitsize)) soqs[reg.name] = soq return soqs @@ -49,11 +49,16 @@ def _add_classical_bras( """Use `bb` to add `IntEffect` on `soqs` for all the `vals`.""" for reg in registers: if reg.shape: - reg_vals = vals[reg.name] + reg_vals = np.asarray(vals[reg.name]) + reg_name = soqs[reg.name] + if isinstance(reg_name, Soquet): + raise ValueError(f'soqs {reg.name} must be a numpy array: {soqs[reg.name]}') for idx in reg.all_idxs(): - bb.add(IntEffect(val=reg_vals[idx], bitsize=reg.bitsize), val=soqs[reg.name][idx]) + bb.add(IntEffect(val=reg_vals[idx], bitsize=reg.bitsize), val=reg_name[idx]) else: - bb.add(IntEffect(val=vals[reg.name], bitsize=reg.bitsize), val=soqs[reg.name]) + bb.add( + IntEffect(val=cast(int, vals[reg.name]), bitsize=reg.bitsize), val=soqs[reg.name] + ) def flank_with_classical_vectors( diff --git a/qualtran/surface_code/ccz2t_cost_model.py b/qualtran/surface_code/ccz2t_cost_model.py index 7cdc6fe44..5b37b2ee8 100644 --- a/qualtran/surface_code/ccz2t_cost_model.py +++ b/qualtran/surface_code/ccz2t_cost_model.py @@ -183,13 +183,13 @@ def get_ccz2t_costs( data_block: data block configuration. Used to evaluate data error and footprint. """ distillation_error = factory.distillation_error(n_magic=n_magic, phys_err=phys_err) - n_generation_cycles = factory.n_cycles(n_magic=n_magic) + n_generation_cycles = factory.n_cycles(n_magic=n_magic, phys_err=phys_err) n_consumption_cycles = ( n_magic.n_t / 4 + n_magic.n_ccz ) * data_block.n_cycles_to_consume_a_magic_state() n_cycles = max(n_generation_cycles, n_consumption_cycles) data_error = data_block.data_error( - n_algo_qubits=n_algo_qubits, n_cycles=n_cycles, phys_err=phys_err + n_algo_qubits=n_algo_qubits, n_cycles=int(n_cycles), phys_err=phys_err ) failure_prob = distillation_error + data_error footprint = factory.footprint() + data_block.footprint(n_algo_qubits=n_algo_qubits) @@ -240,7 +240,7 @@ def get_ccz2t_costs_from_error_budget( factory = CCZ2TFactory() distillation_error = factory.distillation_error(n_magic=n_magic, phys_err=phys_err) - n_cycles = factory.n_cycles(n_magic=n_magic) + n_cycles = factory.n_cycles(n_magic=n_magic, phys_err=phys_err) if data_block is None: # Use "left over" budget for data qubits. @@ -287,7 +287,7 @@ def iter_ccz2t_factories( factory = CCZ2TFactory elif n_factories > 1: - def factory(distillation_l1_d, distillation_l2_d): + def factory(distillation_l1_d, distillation_l2_d): # type: ignore[misc] base_factory = CCZ2TFactory( distillation_l1_d=l1_distance, distillation_l2_d=l2_distance ) @@ -340,7 +340,7 @@ def get_ccz2t_costs_from_grid_search( version of the spreadsheet from https://arxiv.org/abs/1812.01238 """ best_cost: Optional[PhysicalCost] = None - best_params: Optional[Tuple[CCZ2TFactory, SimpleDataBlock]] = None + best_params: Optional[Tuple[MagicStateFactory, DataBlock]] = None for factory in factory_iter: for data_block in data_block_iter: cost = get_ccz2t_costs( @@ -358,6 +358,9 @@ def get_ccz2t_costs_from_grid_search( best_cost = cost best_params = (factory, data_block) + if best_params is None: + raise ValueError("No valid factories found!") + best_factory, best_data_block = best_params return best_cost, best_factory, best_data_block From a9b0e6c110cd6221fe224be5da9df0f63e7a2891 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Fri, 26 Apr 2024 07:09:35 -0700 Subject: [PATCH 3/6] Fix tests. --- qualtran/bloqs/chemistry/df/double_factorization.py | 9 +++++---- qualtran/bloqs/chemistry/sf/single_factorization.py | 2 +- qualtran/bloqs/factoring/mod_mul.py | 2 -- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/qualtran/bloqs/chemistry/df/double_factorization.py b/qualtran/bloqs/chemistry/df/double_factorization.py index cba028eff..11fa51637 100644 --- a/qualtran/bloqs/chemistry/df/double_factorization.py +++ b/qualtran/bloqs/chemistry/df/double_factorization.py @@ -46,6 +46,7 @@ QBit, Register, Signature, + Soquet, SoquetT, ) from qualtran.bloqs.basic_gates import CSwap, Hadamard, Toffoli @@ -156,9 +157,9 @@ def short_name(self) -> str: def build_composite_bloq( self, bb: 'BloqBuilder', - succ_l: SoquetT, + succ_l: Soquet, l_ne_zero: SoquetT, - succ_p: SoquetT, + succ_p: Soquet, p: SoquetT, rot_aa: SoquetT, spin: SoquetT, @@ -166,7 +167,7 @@ def build_composite_bloq( offset: SoquetT, rot: SoquetT, rotations: SoquetT, - sys: SoquetT, + sys: NDArray[Soquet], # type: ignore[type-var] ) -> Dict[str, 'SoquetT']: # 1st half in_prep = InnerPrepareDoubleFactorization( @@ -374,7 +375,7 @@ def signature(self) -> Signature: def build_composite_bloq( self, bb: 'BloqBuilder', - ctrl: SoquetT, + ctrl: NDArray[Soquet], # type: ignore[type-var] l: SoquetT, p: SoquetT, spin: SoquetT, diff --git a/qualtran/bloqs/chemistry/sf/single_factorization.py b/qualtran/bloqs/chemistry/sf/single_factorization.py index 46f11753a..fc6b0d6df 100644 --- a/qualtran/bloqs/chemistry/sf/single_factorization.py +++ b/qualtran/bloqs/chemistry/sf/single_factorization.py @@ -333,7 +333,7 @@ def build_composite_bloq( *, ctrl: NDArray[Soquet], # type: ignore[type-var] l: SoquetT, - pq: Soquet, + pq: NDArray[Soquet], # type: ignore[type-var] rot_aa: NDArray[Soquet], # type: ignore[type-var] swap_pq: SoquetT, spin: SoquetT, diff --git a/qualtran/bloqs/factoring/mod_mul.py b/qualtran/bloqs/factoring/mod_mul.py index 96ccfd144..234559cac 100644 --- a/qualtran/bloqs/factoring/mod_mul.py +++ b/qualtran/bloqs/factoring/mod_mul.py @@ -85,8 +85,6 @@ def build_composite_bloq( # We store the result of the CtrlScaleModAdd into this new register # and then clear the original `x` register by multiplying in the inverse. - if isinstance(self.bitsize, sympy.Expr): - raise ValueError(f'bitsize: {self.bitsize} must be an int not an expression') y = bb.allocate(self.bitsize) # y += x*k From 0f1abdcc74d74a59d6dc719d89fdc09453a8cd2d Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Fri, 26 Apr 2024 14:22:45 -0700 Subject: [PATCH 4/6] Update mod_mul.py --- qualtran/bloqs/factoring/mod_mul.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qualtran/bloqs/factoring/mod_mul.py b/qualtran/bloqs/factoring/mod_mul.py index 234559cac..db9f4aa52 100644 --- a/qualtran/bloqs/factoring/mod_mul.py +++ b/qualtran/bloqs/factoring/mod_mul.py @@ -218,13 +218,13 @@ def _generalize_k(b: Bloq) -> Optional[Bloq]: return b -@bloq_example(None, generalizer=(ignore_split_join, ignore_alloc_free, _generalize_k)) +@bloq_example(generalizer=(ignore_split_join, ignore_alloc_free, _generalize_k)) def _modmul() -> CtrlModMul: modmul = CtrlModMul(k=123, mod=13 * 17, bitsize=8) return modmul -@bloq_example(None, generalizer=(ignore_split_join, ignore_alloc_free, _generalize_k)) +@bloq_example(generalizer=(ignore_split_join, ignore_alloc_free, _generalize_k)) def _modmul_symb() -> CtrlModMul: import sympy From df1057ba35401b1d973a1321ac7605e7292448e9 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Sun, 28 Apr 2024 17:10:05 -0700 Subject: [PATCH 5/6] Fix imports and format --- .../hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py | 2 +- qualtran/bloqs/qsp/generalized_qsp_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py b/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py index 4c52f9376..10833903a 100644 --- a/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py +++ b/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from functools import cached_property -from typing import cast, Dict, Set, Tuple, TYPE_CHECKING, Union +from typing import cast, Dict, Tuple, TYPE_CHECKING, Union import numpy as np from attrs import field, frozen diff --git a/qualtran/bloqs/qsp/generalized_qsp_test.py b/qualtran/bloqs/qsp/generalized_qsp_test.py index ba6a8858d..68a1fb884 100644 --- a/qualtran/bloqs/qsp/generalized_qsp_test.py +++ b/qualtran/bloqs/qsp/generalized_qsp_test.py @@ -349,4 +349,4 @@ def test_complementary_polynomials_for_jacobi_anger_approximations(t: float, pre check_polynomial_pair_on_random_points_on_unit_circle( list(P), Q, random_state=random_state, rtol=precision ) - verify_generalized_qsp(MatrixGate.random(1, random_state=random_state), list(P), Q) \ No newline at end of file + verify_generalized_qsp(MatrixGate.random(1, random_state=random_state), list(P), Q) From d42a5b5d6ea6ee5cfde9145f7c8d071739da0712 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Mon, 29 Apr 2024 08:34:59 -0700 Subject: [PATCH 6/6] Fix type conversions. --- .../hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py b/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py index 10833903a..1cae72ef2 100644 --- a/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py +++ b/qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py @@ -121,8 +121,8 @@ def approx_cos(self) -> Union[NDArray[np.complex_], Shaped]: def gqsp(self) -> GeneralizedQSP: return GeneralizedQSP.from_qsp_polynomial( self.walk_operator, - list(self.approx_cos), - negative_power=int(self.degree), + self.approx_cos, + negative_power=self.degree, verify=True, verify_precision=1e-4, )