Skip to content

Commit 0b7a9e3

Browse files
committed
bin epsilons
1 parent 8feb870 commit 0b7a9e3

File tree

6 files changed

+84
-40
lines changed

6 files changed

+84
-40
lines changed

qualtran/resource_counting/_bloq_counts.py

+62-14
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414
import logging
1515
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
1717

1818
import attrs
1919
import networkx as nx
@@ -112,7 +112,7 @@ def __str__(self):
112112
return f'{self.gateset_name} counts'
113113

114114

115-
def _mapping_to_counter(mapping: Mapping[float, int]) -> Counter[float]:
115+
def _mapping_to_counter(mapping: Mapping[int, int]) -> Counter[int]:
116116
if isinstance(mapping, Counter):
117117
return mapping
118118
return Counter(mapping)
@@ -132,29 +132,74 @@ class GateCounts:
132132
and_bloq: int = 0
133133
clifford: int = 0
134134
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+
)
136170

137171
@property
138172
def rotation(self):
173+
# TODO return correct value and document precisely.
139174
from qualtran.cirq_interop.t_complexity_protocol import TComplexity
140175

141176
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()
144179
)
145180

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+
146186
def __add__(self, other):
147187
if not isinstance(other, GateCounts):
148188
raise TypeError(f"Can only add other `GateCounts` objects, not {self}")
149189

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+
150194
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,
158203
)
159204

160205
def __mul__(self, other):
@@ -165,7 +210,10 @@ def __mul__(self, other):
165210
and_bloq=other * self.and_bloq,
166211
clifford=other * self.clifford,
167212
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,
169217
)
170218

171219
def __rmul__(self, other):
@@ -280,7 +328,7 @@ def compute(self, bloq: 'Bloq', get_callee_cost: Callable[['Bloq'], GateCounts])
280328

281329
if bloq_is_rotation(bloq):
282330
assert isinstance(bloq, _HasEps)
283-
return GateCounts(rotation_epsilons={bloq.eps: 1})
331+
return GateCounts.from_rotation_with_eps(bloq.eps)
284332

285333
# Recursive case
286334
totals = GateCounts()

qualtran/resource_counting/_bloq_counts_test.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,13 @@ def test_qec_gates_cost():
7676
# Rotations
7777
[
7878
basic_gates.ZPowGate(exponent=0.1, global_shift=0.0, eps=1e-11),
79-
GateCounts(rotation_epsilons={1e-11: 1}),
79+
GateCounts.from_rotation_with_eps(1e-11),
8080
],
8181
[
8282
rotations.phase_gradient.PhaseGradientUnitary(
8383
bitsize=10, exponent=1, is_controlled=False, eps=1e-10
8484
),
85-
GateCounts(rotation_epsilons={1e-10: 10}),
85+
GateCounts.from_rotation_with_eps(1e-10),
8686
],
8787
# Recursive
8888
[

qualtran/surface_code/algorithm_summary_test.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@
3939
[
4040
basic_gates.ZPowGate(exponent=0.1, global_shift=0.0, eps=1e-11),
4141
AlgorithmSummary(
42-
n_algo_qubits=1, n_logical_gates=GateCounts(rotation_epsilons={1e-11: 1})
42+
n_algo_qubits=1, n_logical_gates=GateCounts.from_rotation_with_eps(1e-11)
4343
),
4444
],
4545
[
4646
rotations.phase_gradient.PhaseGradientUnitary(
4747
bitsize=10, exponent=1, is_controlled=False, eps=1e-10
4848
),
4949
AlgorithmSummary(
50-
n_algo_qubits=10, n_logical_gates=GateCounts(rotation_epsilons={1e-10: 10})
50+
n_algo_qubits=10, n_logical_gates=GateCounts.from_rotation_with_eps(1e-10)
5151
),
5252
],
5353
[

qualtran/surface_code/beverland_et_al_model.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,12 @@ def n_discrete_logical_gates(
116116
rotation_model: Cost model used to compute the number of T gates
117117
needed to approximate rotations.
118118
"""
119-
rotation_epsilons: dict[float, int] = alg.n_logical_gates.rotation_epsilons
120-
ret = attrs.evolve(alg.n_logical_gates, rotation_epsilons={})
121-
if rotation_epsilons:
122-
rotation_model.preparation_overhead(min(eps for eps in rotation_epsilons.values()))
123-
for eps, n_rotations in rotation_epsilons.items():
119+
n_logical_gates = alg.n_logical_gates
120+
ret = attrs.evolve(alg.n_logical_gates, binned_rotation_epsilons={})
121+
if n_logical_gates.binned_rotation_epsilons:
122+
min_eps_rot = min(eps for eps, _ in n_logical_gates.iter_rotations_with_epsilon())
123+
rotation_model.preparation_overhead(min(min_eps_rot, eps_syn)) # TODO is this correct?
124+
for eps, n_rotations in n_logical_gates.iter_rotations_with_epsilon():
124125
ret += n_rotations * rotation_model.rotation_cost(eps)
125126
return ret
126127

qualtran/surface_code/beverland_et_al_model_test.py

+9-12
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ class Test:
3838
Test(
3939
alg=AlgorithmSummary(
4040
n_algo_qubits=100,
41-
n_logical_gates=GateCounts(
42-
rotation_epsilons={1e-3 / 30_000: 30_000}, measurement=int(1.4e6)
41+
n_logical_gates=(
42+
GateCounts.from_rotation_with_eps(1e-3 / 30_000, n_rotations=30_000)
43+
+ GateCounts(measurement=int(1.4e6))
4344
),
4445
n_rotation_layers=501,
4546
),
@@ -52,11 +53,9 @@ class Test:
5253
Test(
5354
alg=AlgorithmSummary(
5455
n_algo_qubits=1318,
55-
n_logical_gates=GateCounts(
56-
t=int(5.53e7),
57-
rotation_epsilons={1e-2 / 2.06e8: int(2.06e8)},
58-
toffoli=int(1.35e11),
59-
measurement=int(1.37e9),
56+
n_logical_gates=(
57+
GateCounts(t=int(5.53e7), toffoli=int(1.35e11), measurement=int(1.37e9))
58+
+ GateCounts.from_rotation_with_eps(1e-2 / 2.06e8, n_rotations=int(2.06e8))
6059
),
6160
n_rotation_layers=int(2.05e8),
6261
),
@@ -69,11 +68,9 @@ class Test:
6968
Test(
7069
alg=AlgorithmSummary(
7170
n_algo_qubits=12581,
72-
n_logical_gates=GateCounts(
73-
t=12,
74-
rotation_epsilons={1 / 3 / 12: 12},
75-
toffoli=int(3.73e9),
76-
measurement=int(1.08e9),
71+
n_logical_gates=(
72+
GateCounts(t=12, toffoli=int(3.73e9), measurement=int(1.08e9))
73+
+ GateCounts.from_rotation_with_eps(1 / 3 / 12, n_rotations=12)
7774
),
7875
n_rotation_layers=12,
7976
),

qualtran/surface_code/ui.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -535,11 +535,9 @@ def update(
535535

536536
algorithm = AlgorithmSummary(
537537
n_algo_qubits=qubits,
538-
n_logical_gates=GateCounts(
539-
measurement=measurements,
540-
t=ts,
541-
toffoli=toffolis,
542-
rotation_epsilons={rotation_eps: rotations},
538+
n_logical_gates=(
539+
GateCounts(measurement=measurements, t=ts, toffoli=toffolis)
540+
+ GateCounts.from_rotation_with_eps(rotation_eps, n_rotations=rotations)
543541
),
544542
n_rotation_layers=n_rotation_layers,
545543
)

0 commit comments

Comments
 (0)