1717from functools import cached_property
1818from typing import Dict , Optional , Set , Tuple , TYPE_CHECKING
1919
20+ import attrs
2021import numpy as np
21- from attrs import evolve , field , frozen
2222from numpy .typing import NDArray
2323
2424from qualtran import (
2525 bloq_example ,
2626 BloqBuilder ,
2727 BloqDocSpec ,
2828 BoundedQUInt ,
29+ CtrlSpec ,
2930 QAny ,
3031 QBit ,
3132 Register ,
3233 Soquet ,
3334 SoquetT ,
3435)
3536from qualtran .bloqs .arithmetic .comparison import LessThanEqual
36- from qualtran .bloqs .basic_gates import CSwap , Hadamard , Toffoli
37+ from qualtran .bloqs .basic_gates import CSwap , Hadamard
3738from qualtran .bloqs .basic_gates .on_each import OnEach
3839from qualtran .bloqs .basic_gates .z_basis import ZGate
3940from qualtran .bloqs .data_loading .select_swap_qrom import find_optimal_log_block_size , SelectSwapQROM
@@ -112,7 +113,7 @@ def _add(p_indx: int, q_indx: int, r_indx: int, s_indx: int):
112113 return np .concatenate ((tpq_indx , pqrs_indx_np )), np .concatenate ((tpq_sparse , eris_eight_np ))
113114
114115
115- @frozen
116+ @attrs . frozen
116117class PrepareSparse (PrepareOracle ):
117118 r"""Prepare oracle for the sparse chemistry Hamiltonian
118119
@@ -163,16 +164,17 @@ class PrepareSparse(PrepareOracle):
163164 [Even More Efficient Quantum Computations of Chemistry Through Tensor
164165 hypercontraction](https://arxiv.org/abs/2011.03494) Eq. A11.
165166 """
167+
166168 num_spin_orb : int
167169 num_non_zero : int
168170 num_bits_state_prep : int
169- alt_pqrs : Tuple [Tuple [int , ...], ...] = field (repr = False )
170- alt_theta : Tuple [int , ...] = field (repr = False )
171- alt_one_body : Tuple [int , ...] = field (repr = False )
172- ind_pqrs : Tuple [Tuple [int , ...], ...] = field (repr = False )
173- theta : Tuple [int , ...] = field (repr = False )
174- one_body : Tuple [int , ...] = field (repr = False )
175- keep : Tuple [int , ...] = field (repr = False )
171+ alt_pqrs : Tuple [Tuple [int , ...], ...] = attrs . field (repr = False )
172+ alt_theta : Tuple [int , ...] = attrs . field (repr = False )
173+ alt_one_body : Tuple [int , ...] = attrs . field (repr = False )
174+ ind_pqrs : Tuple [Tuple [int , ...], ...] = attrs . field (repr = False )
175+ theta : Tuple [int , ...] = attrs . field (repr = False )
176+ one_body : Tuple [int , ...] = attrs . field (repr = False )
177+ keep : Tuple [int , ...] = attrs . field (repr = False )
176178 num_bits_rot_aa : int = 8
177179 is_adjoint : bool = False
178180 qroam_block_size : Optional [int ] = None
@@ -226,9 +228,6 @@ def selection_registers(self) -> Tuple[Register, ...]:
226228 Register ("flag_1b" , BoundedQUInt (1 )),
227229 )
228230
229- def adjoint (self ) -> 'Bloq' :
230- return evolve (self , is_adjoint = not self .is_adjoint )
231-
232231 @cached_property
233232 def junk_registers (self ) -> Tuple [Register , ...]:
234233 return (
@@ -241,6 +240,9 @@ def junk_registers(self) -> Tuple[Register, ...]:
241240 Register ("alt_flag_1b" , QBit ()),
242241 )
243242
243+ def adjoint (self ) -> 'Bloq' :
244+ return attrs .evolve (self , is_adjoint = not self .is_adjoint )
245+
244246 @classmethod
245247 def from_hamiltonian_coeffs (
246248 cls ,
@@ -281,7 +283,7 @@ def from_hamiltonian_coeffs(
281283 tpq_prime , eris , drop_element_thresh = drop_element_thresh
282284 )
283285 num_non_zero = len (integrals )
284- alt , keep , mu = preprocess_lcu_coefficients_for_reversible_sampling (
286+ alt , keep , _ = preprocess_lcu_coefficients_for_reversible_sampling (
285287 np .abs (integrals ), epsilon = 2 ** - num_bits_state_prep / num_non_zero
286288 )
287289 theta = (1 - np .sign (integrals )) // 2
@@ -305,31 +307,7 @@ def from_hamiltonian_coeffs(
305307 qroam_block_size = qroam_block_size ,
306308 )
307309
308- def build_composite_bloq (
309- self ,
310- bb : 'BloqBuilder' ,
311- d : 'SoquetT' ,
312- p : 'SoquetT' ,
313- q : 'SoquetT' ,
314- r : 'SoquetT' ,
315- s : 'SoquetT' ,
316- sigma : 'SoquetT' ,
317- alpha : 'SoquetT' ,
318- beta : 'SoquetT' ,
319- rot_aa : 'SoquetT' ,
320- swap_pq : 'SoquetT' ,
321- swap_rs : 'SoquetT' ,
322- swap_pqrs : 'SoquetT' ,
323- flag_1b : 'SoquetT' ,
324- alt_pqrs : NDArray [Soquet ], # type: ignore[type-var]
325- theta : NDArray [Soquet ], # type: ignore[type-var]
326- keep : 'SoquetT' ,
327- less_than : 'SoquetT' ,
328- alt_flag_1b : 'SoquetT' ,
329- ) -> Dict [str , 'SoquetT' ]:
330- # 1. Prepare \sum_d |d\rangle
331- d = bb .add (PrepareUniformSuperposition (self .num_non_zero ), target = d )
332- # 2. QROM the ind_d alt_d values
310+ def build_qrom_bloq (self ) -> 'Bloq' :
333311 n_n = (self .num_spin_orb // 2 - 1 ).bit_length ()
334312 target_bitsizes = (
335313 (n_n ,) * 4 + (1 ,) * 2 + (n_n ,) * 4 + (1 ,) * 2 + (self .num_bits_state_prep ,)
@@ -355,6 +333,34 @@ def build_composite_bloq(
355333 target_bitsizes = target_bitsizes ,
356334 block_size = block_size ,
357335 )
336+ return qrom
337+
338+ def build_composite_bloq (
339+ self ,
340+ bb : 'BloqBuilder' ,
341+ d : 'SoquetT' ,
342+ p : 'SoquetT' ,
343+ q : 'SoquetT' ,
344+ r : 'SoquetT' ,
345+ s : 'SoquetT' ,
346+ sigma : 'SoquetT' ,
347+ alpha : 'SoquetT' ,
348+ beta : 'SoquetT' ,
349+ rot_aa : 'SoquetT' ,
350+ swap_pq : 'SoquetT' ,
351+ swap_rs : 'SoquetT' ,
352+ swap_pqrs : 'SoquetT' ,
353+ flag_1b : 'SoquetT' ,
354+ alt_pqrs : NDArray [Soquet ], # type: ignore[type-var]
355+ theta : NDArray [Soquet ], # type: ignore[type-var]
356+ keep : 'SoquetT' ,
357+ less_than : 'SoquetT' ,
358+ alt_flag_1b : 'SoquetT' ,
359+ ) -> Dict [str , 'SoquetT' ]:
360+ # 1. Prepare \sum_d |d\rangle
361+ d = bb .add (PrepareUniformSuperposition (self .num_non_zero ), target = d )
362+ # 2. QROM the ind_d alt_d values
363+ qrom = self .build_qrom_bloq ()
358364 (
359365 d ,
360366 p ,
@@ -392,8 +398,8 @@ def build_composite_bloq(
392398 sigma = bb .add (OnEach (self .num_bits_state_prep , Hadamard ()), q = sigma )
393399 keep , sigma , less_than = bb .add (lte_bloq , x = keep , y = sigma , target = less_than )
394400 less_than , theta [1 ] = bb .add (ZGate ().controlled (), ctrl = less_than , q = theta [1 ])
395- # TODO: This should be off control
396- less_than , theta [0 ] = bb .add (ZGate ().controlled (), ctrl = less_than , q = theta [0 ])
401+ ctrl_spec = CtrlSpec ( QBit (), 0b0 )
402+ less_than , theta [0 ] = bb .add (ZGate ().controlled (ctrl_spec ), ctrl = less_than , q = theta [0 ])
397403 # swap the ind and alt_pqrs values
398404 # TODO: These swaps are inverted at zero Toffoli cost in the reference.
399405 # The method is to copy all values being swapped before they are swapped. Then
@@ -402,14 +408,13 @@ def build_composite_bloq(
402408 # controlled-phase operations, where the control is the control qubit
403409 # for the controlled swaps, and the targets are the copies of the
404410 # registers.
411+ n_n = (self .num_spin_orb // 2 - 1 ).bit_length ()
405412 less_than , alt_pqrs [0 ], p = bb .add (CSwap (n_n ), ctrl = less_than , x = alt_pqrs [0 ], y = p )
406413 less_than , alt_pqrs [1 ], q = bb .add (CSwap (n_n ), ctrl = less_than , x = alt_pqrs [1 ], y = q )
407414 less_than , alt_pqrs [2 ], r = bb .add (CSwap (n_n ), ctrl = less_than , x = alt_pqrs [2 ], y = r )
408415 less_than , alt_pqrs [3 ], s = bb .add (CSwap (n_n ), ctrl = less_than , x = alt_pqrs [3 ], y = s )
409416 # swap the 1b/2b alt values
410- # less_than, flag_1b, alt_flag_1b = bb.add(CSwap(1), ctrl=less_than, x=flag_1b, y=alt_flag_1b)
411- # invert the comparator
412- keep , sigma , less_than = bb .add (lte_bloq , x = keep , y = sigma , target = less_than )
417+ less_than , flag_1b , alt_flag_1b = bb .add (CSwap (1 ), ctrl = less_than , x = flag_1b , y = alt_flag_1b )
413418 # prepare |+> states for symmetry swaps
414419 swap_pq = bb .add (Hadamard (), q = swap_pq )
415420 swap_rs = bb .add (Hadamard (), q = swap_rs )
@@ -441,48 +446,23 @@ def build_composite_bloq(
441446 }
442447
443448 def build_call_graph (self , ssa : 'SympySymbolAllocator' ) -> Set ['BloqCountT' ]:
444- num_bits_spat = (self .num_spin_orb // 2 - 1 ).bit_length ()
445- if self .qroam_block_size is None :
446- target_bitsizes = (
447- (num_bits_spat ,) * 4
448- + (1 ,) * 2
449- + (num_bits_spat ,) * 4
450- + (1 ,) * 2
451- + (self .num_bits_state_prep ,)
452- )
453- block_size = 2 ** find_optimal_log_block_size (self .num_non_zero , sum (target_bitsizes ))
454- else :
455- block_size = self .qroam_block_size
456- if self .is_adjoint :
457- num_toff_qrom = int (np .ceil (self .num_non_zero / block_size )) + block_size # A15
458- else :
459- output_size = self .num_bits_state_prep + 8 * num_bits_spat + 4
460- num_toff_qrom = int (np .ceil (self .num_non_zero / block_size )) + output_size * (
461- block_size - 1
462- ) # A14
463- qrom_cost = (Toffoli (), num_toff_qrom )
464- if self .is_adjoint :
465- return {(PrepareUniformSuperposition (self .num_non_zero ), 1 ), qrom_cost }
466- swap_cost_state_prep = (CSwap (num_bits_spat ), 4 + 4 ) # 2. pg 39
467- ineq_cost_state_prep = (Toffoli (), (self .num_bits_state_prep + 1 )) # 2. pg 39
468-
469449 return {
470450 (PrepareUniformSuperposition (self .num_non_zero ), 1 ),
471- qrom_cost ,
472- swap_cost_state_prep ,
473- ineq_cost_state_prep ,
451+ (self .build_qrom_bloq (), 1 ),
452+ (OnEach (self .num_bits_state_prep , Hadamard ()), 1 ),
453+ (Hadamard (), 3 ),
454+ (CSwap (1 ), 1 ),
455+ (CSwap ((self .num_spin_orb // 2 - 1 ).bit_length ()), 4 + 4 ),
456+ (LessThanEqual (self .num_bits_state_prep , self .num_bits_state_prep ), 1 ),
474457 }
475458
476459
477460@bloq_example
478461def _prep_sparse () -> PrepareSparse :
462+ from qualtran .bloqs .chemistry .sparse .prepare_test import build_random_test_integrals
463+
479464 num_spin_orb = 4
480- tpq = np .random .random ((num_spin_orb // 2 , num_spin_orb // 2 ))
481- tpq = 0.5 * (tpq + tpq .T )
482- eris = np .random .random ((num_spin_orb // 2 ,) * 4 )
483- eris += np .transpose (eris , (0 , 1 , 3 , 2 ))
484- eris += np .transpose (eris , (1 , 0 , 2 , 3 ))
485- eris += np .transpose (eris , (2 , 3 , 0 , 1 ))
465+ tpq , eris = build_random_test_integrals (num_spin_orb // 2 )
486466 prep_sparse = PrepareSparse .from_hamiltonian_coeffs (num_spin_orb , tpq , eris )
487467 return prep_sparse
488468
0 commit comments