13
13
# limitations under the License.
14
14
import logging
15
15
from collections import Counter , defaultdict
16
- from typing import Callable , Dict , Mapping , Sequence , Tuple , TYPE_CHECKING
16
+ from typing import Callable , Dict , Iterator , Mapping , Sequence , Tuple , TYPE_CHECKING
17
17
18
18
import attrs
19
19
import networkx as nx
@@ -112,7 +112,7 @@ def __str__(self):
112
112
return f'{ self .gateset_name } counts'
113
113
114
114
115
- def _mapping_to_counter (mapping : Mapping [float , int ]) -> Counter [float ]:
115
+ def _mapping_to_counter (mapping : Mapping [int , int ]) -> Counter [int ]:
116
116
if isinstance (mapping , Counter ):
117
117
return mapping
118
118
return Counter (mapping )
@@ -132,29 +132,74 @@ class GateCounts:
132
132
and_bloq : int = 0
133
133
clifford : int = 0
134
134
measurement : int = 0
135
- rotation_epsilons : Counter [float ] = field (factory = Counter , converter = _mapping_to_counter )
135
+ binned_rotation_epsilons : Counter [int ] = field (factory = Counter , converter = _mapping_to_counter )
136
+ eps_bin_prec : int = 10
137
+
138
+ @classmethod
139
+ def from_rotation_with_eps (cls , eps : float , * , eps_bin_prec : int = 10 , n_rotations : int = 1 ):
140
+ """Construct a GateCount with a rotation of precision `eps`.
141
+
142
+ Args:
143
+ eps: precision to synthesize the rotation(s).
144
+ eps_bin_prec: number of bits to approximate `eps` to, defaults to 10.
145
+ n_rotations: number of rotations, defaults to 1.
146
+ """
147
+ eps_bin = int (eps * 2 ** eps_bin_prec )
148
+ return cls (binned_rotation_epsilons = Counter ({eps_bin : n_rotations }))
149
+
150
+ def with_rotation_eps_bin_prec (self , new_eps_bin_prec : int ) -> 'GateCounts' :
151
+ """Returns `GateCounts` with a new bin precision for rotation epsilons."""
152
+ if new_eps_bin_prec == self .eps_bin_prec :
153
+ return self
154
+
155
+ def _get_new_eps_bin (eps_bin ):
156
+ return int (eps_bin * 2 ** (new_eps_bin_prec - self .eps_bin_prec ))
157
+
158
+ new_binned_rotation_epsilons = Counter (
159
+ {
160
+ _get_new_eps_bin (eps_bin ): n_rot
161
+ for eps_bin , n_rot in self .binned_rotation_epsilons .items ()
162
+ }
163
+ )
164
+
165
+ return attrs .evolve (
166
+ self ,
167
+ binned_rotation_epsilons = new_binned_rotation_epsilons ,
168
+ eps_bin_prec = new_eps_bin_prec ,
169
+ )
136
170
137
171
@property
138
172
def rotation (self ):
173
+ # TODO return correct value and document precisely.
139
174
from qualtran .cirq_interop .t_complexity_protocol import TComplexity
140
175
141
176
return sum (
142
- n_rotations * int (TComplexity .rotation_cost (eps ))
143
- for eps , n_rotations in self .rotation_epsilons .items ()
177
+ n_rotations * int (TComplexity .rotation_cost (eps_bin / 2 ** self . eps_bin_prec ))
178
+ for eps_bin , n_rotations in self .binned_rotation_epsilons .items ()
144
179
)
145
180
181
+ def iter_rotations_with_epsilon (self ) -> Iterator [tuple [float , int ]]:
182
+ """Iterate through the rotation precisions (epsilon) and their frequency."""
183
+ for eps_bin , n_rot in self .binned_rotation_epsilons .items ():
184
+ yield eps_bin / 2 ** self .eps_bin_prec , n_rot
185
+
146
186
def __add__ (self , other ):
147
187
if not isinstance (other , GateCounts ):
148
188
raise TypeError (f"Can only add other `GateCounts` objects, not { self } " )
149
189
190
+ eps_bin_prec = max (self .eps_bin_prec , other .eps_bin_prec )
191
+ this = self .with_rotation_eps_bin_prec (eps_bin_prec )
192
+ other = other .with_rotation_eps_bin_prec (other .eps_bin_prec )
193
+
150
194
return GateCounts (
151
- t = self .t + other .t ,
152
- toffoli = self .toffoli + other .toffoli ,
153
- cswap = self .cswap + other .cswap ,
154
- and_bloq = self .and_bloq + other .and_bloq ,
155
- clifford = self .clifford + other .clifford ,
156
- measurement = self .measurement + other .measurement ,
157
- rotation_epsilons = self .rotation_epsilons + other .rotation_epsilons ,
195
+ t = this .t + other .t ,
196
+ toffoli = this .toffoli + other .toffoli ,
197
+ cswap = this .cswap + other .cswap ,
198
+ and_bloq = this .and_bloq + other .and_bloq ,
199
+ clifford = this .clifford + other .clifford ,
200
+ measurement = this .measurement + other .measurement ,
201
+ binned_rotation_epsilons = this .binned_rotation_epsilons + other .binned_rotation_epsilons ,
202
+ eps_bin_prec = eps_bin_prec ,
158
203
)
159
204
160
205
def __mul__ (self , other ):
@@ -165,7 +210,10 @@ def __mul__(self, other):
165
210
and_bloq = other * self .and_bloq ,
166
211
clifford = other * self .clifford ,
167
212
measurement = other * self .measurement ,
168
- rotation_epsilons = Counter ({k : other * v for k , v in self .rotation_epsilons .items ()}),
213
+ binned_rotation_epsilons = Counter (
214
+ {eps_bin : other * n_rot for eps_bin , n_rot in self .binned_rotation_epsilons .items ()}
215
+ ),
216
+ eps_bin_prec = self .eps_bin_prec ,
169
217
)
170
218
171
219
def __rmul__ (self , other ):
@@ -280,7 +328,7 @@ def compute(self, bloq: 'Bloq', get_callee_cost: Callable[['Bloq'], GateCounts])
280
328
281
329
if bloq_is_rotation (bloq ):
282
330
assert isinstance (bloq , _HasEps )
283
- return GateCounts ( rotation_epsilons = { bloq .eps : 1 } )
331
+ return GateCounts . from_rotation_with_eps ( bloq .eps )
284
332
285
333
# Recursive case
286
334
totals = GateCounts ()
0 commit comments