@@ -71,8 +71,47 @@ def __attrs_post_init__(self):
7171
7272 @cached_property
7373 def signature (self ) -> 'Signature' :
74- n , _ = self .matrix .shape
75- return Signature ([Register ('q' , QBit (), shape = (n ,))])
74+ return Signature ([Register ('q' , QBit (), shape = (self .n ,))])
75+
76+ @cached_property
77+ def n (self ) -> SymbolicInt :
78+ return self .matrix .shape [0 ]
79+
80+ @cached_property
81+ def lup (self ) -> tuple [np .ndarray , np .ndarray , np .ndarray ]:
82+ """Returns the LUP decomposition of the matrix representing the operation.
83+
84+ If m_x is irreducible, then the operation y := (y*f_x)%m_x can be represented
85+ by a full rank matrix that can be decomposed into PLU where L and U are lower
86+ and upper traingular matricies and P is a permutation matrix.
87+ """
88+ assert isinstance (self .matrix , np .ndarray )
89+ P , L , U = GF (2 )(self .matrix ).plu_decompose ()
90+ return np .asarray (L , dtype = int ), np .asarray (U , dtype = int ), np .asarray (P , dtype = int )
91+
92+ def build_composite_bloq (self , bb : 'BloqBuilder' , * , q : 'SoquetT' ) -> Dict [str , 'SoquetT' ]:
93+ assert isinstance (q , np .ndarray ) # make mypy happy
94+ L , U , P = self .lup
95+ if is_symbolic (self .n ):
96+ raise DecomposeTypeError (f"Symbolic decomposition isn't supported for { self } " )
97+
98+ for i in range (self .n ):
99+ for j in range (i + 1 , self .n ):
100+ if U [i , j ]:
101+ q [j ], q [i ] = bb .add (CNOT (), ctrl = q [j ], target = q [i ])
102+
103+ for i in reversed (range (self .n )):
104+ for j in reversed (range (i )):
105+ if L [i , j ]:
106+ q [j ], q [i ] = bb .add (CNOT (), ctrl = q [j ], target = q [i ])
107+
108+ column = [* range (self .n )]
109+ for i in range (self .n ):
110+ for j in range (i + 1 , self .n ):
111+ if P [i , column [j ]]:
112+ q [i ], q [j ] = q [j ], q [i ]
113+ column [i ], column [j ] = column [j ], column [i ]
114+ return {'q' : q }
76115
77116 def on_classical_vals (self , * , q : 'ClassicalValT' ) -> Dict [str , 'ClassicalValT' ]:
78117 if is_symbolic (self .matrix ):
@@ -91,7 +130,14 @@ def build_call_graph(
91130 self , ssa : 'SympySymbolAllocator'
92131 ) -> Union ['BloqCountDictT' , Set ['BloqCountT' ]]:
93132 n = self .matrix .shape [0 ]
94- return {CNOT (): ceil (n ** 2 / log2 (n ))}
133+ if is_symbolic (n ):
134+ return {CNOT (): ceil (n ** 2 )}
135+ L , U , _ = self .lup
136+ # The number of cnots is the number of non zero off-diagnoal entries in L and U.
137+ cnots = np .sum (L ) + np .sum (U ) - 2 * self .n
138+ if cnots :
139+ return {CNOT (): cnots }
140+ return {}
95141
96142 def adjoint (self ) -> 'SynthesizeLRCircuit' :
97143 return attrs .evolve (self , is_adjoint = not self .is_adjoint )
@@ -402,21 +448,15 @@ def from_polynomials(
402448 return GF2MulK (dtype = qgf , const = sum (2 ** int (i ) for i in f_x .nonzero_degrees ))
403449
404450 @cached_property
405- def lup (self ) -> tuple [np .ndarray , np .ndarray , np .ndarray ]:
406- """Returns the LUP decomposition of the matrix representing the operation.
407-
408- If m_x is irreducible, then the operation y := (y*f_x)%m_x can be represented
409- by a full rank matrix that can be decomposed into PLU where L and U are lower
410- and upper traingular matricies and P is a permutation matrix.
411- """
451+ def reduction_matrix_q (self ) -> np .ndarray :
452+ """Returns the matrix representing the operation."""
412453 n = int (self .n )
413454 matrix = np .zeros ((n , n ), dtype = int )
414455 for i in range (n ):
415456 p = self ._const * self .galois_field (2 ** i )
416457 for j , v in enumerate (reversed (self .qgf .to_bits (p ))):
417458 matrix [j , i ] = v
418- P , L , U = GF (2 )(matrix ).plu_decompose ()
419- return np .asarray (L , dtype = int ), np .asarray (U , dtype = int ), np .asarray (P , dtype = int )
459+ return matrix
420460
421461 @cached_property
422462 def signature (self ) -> 'Signature' :
@@ -430,41 +470,17 @@ def on_classical_vals(self, g) -> Dict[str, 'ClassicalValT']:
430470 return {'g' : g * self ._const }
431471
432472 def build_composite_bloq (self , bb : 'BloqBuilder' , g : 'Soquet' ) -> Dict [str , 'SoquetT' ]:
433- L , U , P = self .lup
434- if is_symbolic (self .n ):
435- raise DecomposeTypeError (f"Symbolic decomposition isn't supported for { self } " )
436-
437473 g_arr = bb .split (g )
438474 g_arr = g_arr [::- 1 ]
439- for i in range (self .n ):
440- for j in range (i + 1 , self .n ):
441- if U [i , j ]:
442- g_arr [j ], g_arr [i ] = bb .add (CNOT (), ctrl = g_arr [j ], target = g_arr [i ])
443-
444- for i in reversed (range (self .n )):
445- for j in reversed (range (i )):
446- if L [i , j ]:
447- g_arr [j ], g_arr [i ] = bb .add (CNOT (), ctrl = g_arr [j ], target = g_arr [i ])
448-
449- column = [* range (self .n )]
450- for i in range (self .n ):
451- for j in range (i + 1 , self .n ):
452- if P [i , column [j ]]:
453- g_arr [i ], g_arr [j ] = g_arr [j ], g_arr [i ]
454- column [i ], column [j ] = column [j ], column [i ]
475+ g_arr = bb .add (SynthesizeLRCircuit (self .reduction_matrix_q ), q = g_arr )
455476 g_arr = g_arr [::- 1 ]
456477 g = bb .join (g_arr , dtype = self .qgf )
457478 return {'g' : g }
458479
459480 def build_call_graph (
460481 self , ssa : 'SympySymbolAllocator'
461482 ) -> Union ['BloqCountDictT' , Set ['BloqCountT' ]]:
462- L , U , _ = self .lup
463- # The number of cnots is the number of non zero off-diagnoal entries in L and U.
464- cnots = np .sum (L ) + np .sum (U ) - 2 * self .n
465- if cnots :
466- return {CNOT (): cnots }
467- return {}
483+ return {SynthesizeLRCircuit (self .reduction_matrix_q ): 1 }
468484
469485
470486@bloq_example
0 commit comments