Skip to content

Commit 1bd4f70

Browse files
authored
Make And a leaf bloq (#1513)
* make `And` a leaf bloq * cleanup `And` * fix `DoubleFactorizationOneBody` call graph * fix `t_counts_from_sigma` (deprecated but used in flame graph)
1 parent af9d13b commit 1bd4f70

10 files changed

Lines changed: 38 additions & 95 deletions

File tree

qualtran/bloqs/arithmetic/permutation_test.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,7 @@ def test_permutation_cycle_unitary_and_call_graph():
9090
)
9191

9292
cv = sympy.Symbol('cv')
93-
_, sigma = bloq.call_graph(
94-
generalizer=[ignore_split_join, generalize_cvs], keep=lambda b: isinstance(b, And)
95-
)
93+
_, sigma = bloq.call_graph(generalizer=[ignore_split_join, generalize_cvs])
9694
assert sigma == {
9795
CNOT(): 8,
9896
And(cv1=cv, cv2=cv): 4,
@@ -106,7 +104,7 @@ def test_permutation_cycle_symbolic_call_graph():
106104
bloq = _permutation_cycle_symb()
107105
logN, L = ceil(log2(bloq.N)), slen(bloq.cycle)
108106

109-
_, sigma = bloq.call_graph(keep=lambda b: isinstance(b, And))
107+
_, sigma = bloq.call_graph()
110108
assert sigma == {
111109
And(): (L + 1) * (logN - 1),
112110
And().adjoint(): (L + 1) * (logN - 1),
@@ -133,7 +131,7 @@ def test_permutation_unitary_and_call_graph():
133131
),
134132
)
135133

136-
_, sigma = bloq.call_graph(generalizer=ignore_split_join, keep=lambda b: isinstance(b, And))
134+
_, sigma = bloq.call_graph(generalizer=ignore_split_join)
137135
assert sigma == {
138136
CNOT(): 17,
139137
And(): 56 // 4,
@@ -160,7 +158,7 @@ def test_permutation_symbolic_call_graph():
160158
logN = ceil(log2(N))
161159
bloq = _permutation_symb()
162160

163-
_, sigma = bloq.call_graph(keep=lambda b: isinstance(b, And))
161+
_, sigma = bloq.call_graph()
164162
assert sigma == {
165163
And().adjoint(): (N + 1) * (logN - 1),
166164
And(): (N + 1) * (logN - 1),

qualtran/bloqs/chemistry/df/double_factorization.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,8 @@
4848
Soquet,
4949
SoquetT,
5050
)
51-
from qualtran.bloqs.basic_gates import CSwap, Hadamard, Toffoli
51+
from qualtran.bloqs.basic_gates import CSwap, Hadamard
5252
from qualtran.bloqs.block_encoding import BlockEncoding
53-
from qualtran.bloqs.bookkeeping import ArbitraryClifford
5453
from qualtran.bloqs.chemistry.black_boxes import ApplyControlledZs
5554
from qualtran.bloqs.chemistry.df.prepare import (
5655
InnerPrepareDoubleFactorization,
@@ -280,10 +279,10 @@ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT':
280279
in_prep_dag: 1, # in_prep_l^dag
281280
rot: 1, # rotate into system basis listing 4 pg 54
282281
# apply CCZ first then CCCZ, the cost is 1 + 2 Toffolis (step 4e, and 7)
283-
Toffoli(): 1,
282+
ApplyControlledZs(cvs=(1, 1), bitsize=5): 1,
284283
rot_dag: 1, # Undo rotations
285284
CSwap(self.num_spin_orb // 2): 2, # Swaps for spins
286-
ArbitraryClifford(n=1): 1, # 2 Hadamards for spin superposition
285+
Hadamard(): 2, # 2 Hadamards for spin superposition
287286
}
288287

289288
def __str__(self) -> str:

qualtran/bloqs/chemistry/df/double_factorization_test.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from openfermion.resource_estimates.df.compute_cost_df import compute_cost
1616
from openfermion.resource_estimates.utils import power_two
1717

18-
from qualtran.bloqs.basic_gates import TGate
18+
import qualtran.testing as qlt_testing
1919
from qualtran.bloqs.chemistry.df.double_factorization import (
2020
_df_block_encoding,
2121
_df_one_body,
@@ -26,7 +26,6 @@
2626
PrepareUniformSuperposition,
2727
)
2828
from qualtran.resource_counting import get_cost_value, QECGatesCost
29-
from qualtran.testing import execute_notebook
3029

3130

3231
def test_df_block_encoding(bloq_autotester):
@@ -51,9 +50,7 @@ def test_compare_cost_one_body_decomp():
5150
num_bits_rot_aa=7,
5251
num_bits_rot=num_bits_rot,
5352
)
54-
costs = bloq.call_graph()[1]
55-
cbloq_costs = bloq.decompose_bloq().call_graph()[1]
56-
assert costs[TGate()] == cbloq_costs[TGate()]
53+
qlt_testing.assert_equivalent_bloq_counts(bloq)
5754

5855

5956
def test_compare_cost_to_openfermion():
@@ -106,4 +103,4 @@ def test_compare_cost_to_openfermion():
106103

107104
@pytest.mark.notebook
108105
def test_notebook():
109-
execute_notebook("double_factorization")
106+
qlt_testing.execute_notebook("double_factorization")

qualtran/bloqs/chemistry/sf/single_factorization_test.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from openfermion.resource_estimates.sf.compute_cost_sf import compute_cost
1616
from openfermion.resource_estimates.utils import power_two, QI, QI2, QR2
1717

18-
from qualtran.bloqs.basic_gates import TGate
18+
import qualtran.testing as qlt_testing
1919
from qualtran.bloqs.chemistry.sf.single_factorization import (
2020
_sf_block_encoding,
2121
_sf_one_body,
@@ -26,7 +26,6 @@
2626
PrepareUniformSuperposition,
2727
)
2828
from qualtran.resource_counting import get_cost_value, QECGatesCost
29-
from qualtran.testing import execute_notebook
3029

3130

3231
def test_sf_block_encoding(bloq_autotester):
@@ -48,9 +47,7 @@ def test_compare_cost_one_body_decomp():
4847
num_bits_state_prep=num_bits_state_prep,
4948
num_bits_rot_aa=num_bits_rot_aa,
5049
)
51-
costs = bloq.call_graph()[1]
52-
cbloq_costs = bloq.decompose_bloq().call_graph()[1]
53-
assert costs[TGate()] == cbloq_costs[TGate()]
50+
qlt_testing.assert_equivalent_bloq_counts(bloq)
5451

5552

5653
def test_compare_cost_to_openfermion():
@@ -126,4 +123,4 @@ def test_compare_cost_to_openfermion():
126123

127124
@pytest.mark.notebook
128125
def test_notebook():
129-
execute_notebook("single_factorization")
126+
qlt_testing.execute_notebook("single_factorization")

qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp_test.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import sympy
2020
from numpy.typing import NDArray
2121

22-
from qualtran.bloqs.basic_gates import TGate, TwoBitCSwap
2322
from qualtran.bloqs.for_testing.matrix_gate import MatrixGate
2423
from qualtran.bloqs.for_testing.random_select_and_prepare import random_qubitization_walk_operator
2524
from qualtran.bloqs.hamiltonian_simulation.hamiltonian_simulation_by_gqsp import (
@@ -34,7 +33,7 @@
3433
)
3534
from qualtran.bloqs.qubitization.qubitization_walk_operator import QubitizationWalkOperator
3635
from qualtran.cirq_interop import BloqAsCirqGate
37-
from qualtran.resource_counting import big_O, BloqCount, get_cost_value, QECGatesCost, QubitCount
36+
from qualtran.resource_counting import big_O, get_cost_value, QECGatesCost, QubitCount
3837
from qualtran.symbolics import Shaped
3938

4039

@@ -109,8 +108,10 @@ def test_hamiltonian_simulation_by_gqsp_t_complexity():
109108
hubbard_time_evolution_by_gqsp = _hubbard_time_evolution_by_gqsp.make()
110109
t_comp = hubbard_time_evolution_by_gqsp.t_complexity()
111110

112-
counts = get_cost_value(hubbard_time_evolution_by_gqsp, BloqCount.for_gateset('t+tof+cswap'))
113-
assert t_comp.t == counts[TwoBitCSwap()] * 7 + counts[TGate()]
111+
counts = get_cost_value(hubbard_time_evolution_by_gqsp, QECGatesCost())
112+
t_comp_from_qec = counts.to_legacy_t_complexity()
113+
assert t_comp.t == t_comp_from_qec.t
114+
assert t_comp.rotations == t_comp_from_qec.rotations
114115

115116

116117
def test_symbolic_t_cost():

qualtran/bloqs/mcmt/and_bloq.py

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -45,32 +45,17 @@
4545
Side,
4646
Signature,
4747
)
48-
from qualtran.bloqs.basic_gates import TGate, XGate
49-
from qualtran.bloqs.bookkeeping import ArbitraryClifford
48+
from qualtran.bloqs.basic_gates import XGate
5049
from qualtran.cirq_interop import decompose_from_cirq_style_method
5150
from qualtran.drawing import Circle, directional_text_box, Text, WireSymbol
52-
from qualtran.resource_counting import (
53-
big_O,
54-
BloqCountDictT,
55-
MutableBloqCountDictT,
56-
SympySymbolAllocator,
57-
)
58-
from qualtran.resource_counting.generalizers import (
59-
cirq_to_bloqs,
60-
generalize_cvs,
61-
generalize_rotation_angle,
62-
ignore_alloc_free,
63-
ignore_cliffords,
64-
)
51+
from qualtran.resource_counting import BloqCountDictT, MutableBloqCountDictT, SympySymbolAllocator
52+
from qualtran.resource_counting.generalizers import generalize_cvs, ignore_cliffords
6553
from qualtran.simulation.classical_sim import ClassicalValT
6654
from qualtran.symbolics import HasLength, is_symbolic, SymbolicInt
6755

6856
if TYPE_CHECKING:
6957
import quimb.tensor as qtn
7058

71-
# TODO: https://github.com/quantumlib/Qualtran/issues/1346
72-
FLAG_AND_AS_LEAF = False
73-
7459

7560
@frozen
7661
class And(GateWithRegisters):
@@ -108,22 +93,7 @@ def adjoint(self) -> 'And':
10893
return attrs.evolve(self, uncompute=not self.uncompute)
10994

11095
def decompose_bloq(self) -> 'CompositeBloq':
111-
if FLAG_AND_AS_LEAF:
112-
raise DecomposeTypeError(f"{self} is atomic.")
113-
return decompose_from_cirq_style_method(self)
114-
115-
def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT':
116-
if FLAG_AND_AS_LEAF:
117-
raise DecomposeTypeError(f"{self} is atomic.")
118-
119-
if isinstance(self.cv1, sympy.Expr) or isinstance(self.cv2, sympy.Expr):
120-
pre_post_cliffords: Union[sympy.Order, int] = big_O(1)
121-
else:
122-
pre_post_cliffords = 2 - self.cv1 - self.cv2
123-
if self.uncompute:
124-
return {ArbitraryClifford(n=2): 4 + 2 * pre_post_cliffords}
125-
126-
return {ArbitraryClifford(n=2): 9 + 2 * pre_post_cliffords, TGate(): 4}
96+
raise DecomposeTypeError(f"{self} is atomic.")
12797

12898
def on_classical_vals(
12999
self, *, ctrl: NDArray[np.uint8], target: Optional[int] = None
@@ -243,13 +213,6 @@ def to_clifford_t_circuit(self) -> 'cirq.FrozenCircuit':
243213
circuit += pre_post_ops
244214
return circuit.freeze()
245215

246-
def __pow__(self, power: int) -> 'And':
247-
if power == 1:
248-
return self
249-
if power == -1:
250-
return self.adjoint()
251-
return NotImplemented # pragma: no cover
252-
253216
def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> cirq.CircuitDiagramInfo:
254217
controls = ["(0)", "@"]
255218
target = "And†" if self.uncompute else "And"
@@ -263,9 +226,7 @@ def _has_unitary_(self) -> bool:
263226
return not self.uncompute
264227

265228

266-
@bloq_example(
267-
generalizer=[cirq_to_bloqs, ignore_cliffords, ignore_alloc_free, generalize_rotation_angle]
268-
)
229+
@bloq_example()
269230
def _and_bloq() -> And:
270231
and_bloq = And()
271232
return and_bloq

qualtran/bloqs/mcmt/specialized_ctrl_test.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@
4040
from qualtran.resource_counting import CostKey, GateCounts, get_cost_value, QECGatesCost
4141

4242

43-
def _keep_and(b):
44-
# TODO remove this after https://github.com/quantumlib/Qualtran/issues/1346 is resolved.
45-
return isinstance(b, And)
46-
47-
4843
@attrs.frozen
4944
class AtomWithSpecializedControl(Bloq):
5045
cv: Optional[int] = None
@@ -169,30 +164,25 @@ def test_bloq_with_controlled_bloq():
169164
assert TestAtom('g').controlled() == CTestAtom('g')
170165

171166
ctrl_bloq = CTestAtom('g').controlled()
172-
_, sigma = ctrl_bloq.call_graph(keep=_keep_and)
167+
_, sigma = ctrl_bloq.call_graph()
173168
assert sigma == {And(): 1, CTestAtom('g'): 1, And().adjoint(): 1}
174169

175170
ctrl_bloq = CTestAtom('n').controlled(CtrlSpec(cvs=0))
176-
_, sigma = ctrl_bloq.call_graph(keep=_keep_and)
171+
_, sigma = ctrl_bloq.call_graph()
177172
assert sigma == {And(0, 1): 1, CTestAtom('n'): 1, And(0, 1).adjoint(): 1}
178173

179174
ctrl_bloq = TestAtom('nn').controlled(CtrlSpec(cvs=[0, 0]))
180-
_, sigma = ctrl_bloq.call_graph(keep=_keep_and)
175+
_, sigma = ctrl_bloq.call_graph()
181176
assert sigma == {And(0, 0): 1, CTestAtom('nn'): 1, And(0, 0).adjoint(): 1}
182177

183178

184179
def test_ctrl_adjoint():
185180
assert TestAtom('a').adjoint().controlled() == CTestAtom('a').adjoint()
186181

187-
_, sigma = (
188-
TestAtom('g')
189-
.adjoint()
190-
.controlled(ctrl_spec=CtrlSpec(cvs=[1, 1]))
191-
.call_graph(keep=_keep_and)
192-
)
182+
_, sigma = TestAtom('g').adjoint().controlled(ctrl_spec=CtrlSpec(cvs=[1, 1])).call_graph()
193183
assert sigma == {And(): 1, And().adjoint(): 1, CTestAtom('g').adjoint(): 1}
194184

195-
_, sigma = CTestAtom('c').adjoint().controlled().call_graph(keep=_keep_and)
185+
_, sigma = CTestAtom('c').adjoint().controlled().call_graph()
196186
assert sigma == {And(): 1, And().adjoint(): 1, CTestAtom('c').adjoint(): 1}
197187

198188
for cv in [0, 1]:

qualtran/bloqs/multiplexers/apply_lth_bloq_test.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import numpy as np
1818
import pytest
1919

20-
from qualtran import BloqBuilder, BQUInt, Controlled, CtrlSpec, QBit, Register, Signature, Soquet
20+
from qualtran import BloqBuilder, BQUInt, QBit, Register, Signature, Soquet
2121
from qualtran.bloqs.basic_gates import (
2222
CHadamard,
2323
CNOT,
@@ -34,7 +34,7 @@
3434
ZeroState,
3535
ZGate,
3636
)
37-
from qualtran.bloqs.bookkeeping.arbitrary_clifford import ArbitraryClifford
37+
from qualtran.bloqs.mcmt import And
3838
from qualtran.bloqs.multiplexers.apply_lth_bloq import _apply_lth_bloq, ApplyLthBloq
3939
from qualtran.resource_counting.generalizers import ignore_split_join
4040
from qualtran.testing import assert_valid_bloq_decomposition
@@ -64,11 +64,11 @@ def test_call_graph():
6464
_, sigma = _apply_lth_bloq().call_graph(generalizer=ignore_split_join)
6565
assert sigma == {
6666
CHadamard(): 1,
67-
Controlled(TGate(), CtrlSpec()): 1,
67+
TGate().controlled(): 1,
6868
CZ(): 1,
6969
CNOT(): 4,
70-
TGate(): 12,
71-
ArbitraryClifford(2): 45,
70+
And(1, 0): 3,
71+
And().adjoint(): 3,
7272
}
7373

7474

qualtran/drawing/bloq_counts_graph_test.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ def test_format_counts_sigma():
3030
ret
3131
== """\
3232
#### Counts totals:
33-
- `ArbitraryClifford(n=2)`: 45
34-
- `T`: 20"""
33+
- `And`: 5"""
3534
)
3635

3736

@@ -43,9 +42,6 @@ def test_format_counts_graph_markdown():
4342
== """\
4443
- `MultiAnd(n=6)`
4544
- `And`: $\\displaystyle 5$
46-
- `And`
47-
- `ArbitraryClifford(n=2)`: $\\displaystyle 9$
48-
- `T`: $\\displaystyle 4$
4945
"""
5046
)
5147

qualtran/resource_counting/t_counts_from_sigma.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,17 @@ def t_counts_from_sigma(sigma: Mapping['Bloq', SymbolicInt]) -> SymbolicInt:
2525
import cirq
2626

2727
from qualtran.bloqs.basic_gates import TGate, Toffoli, TwoBitCSwap
28+
from qualtran.bloqs.mcmt import And
2829
from qualtran.cirq_interop.t_complexity_protocol import TComplexity
2930
from qualtran.resource_counting.classify_bloqs import bloq_is_rotation
3031

3132
ret = sigma.get(TGate(), 0) + sigma.get(TGate().adjoint(), 0)
3233
ret += sigma.get(Toffoli(), 0) * 4
3334
ret += sigma.get(TwoBitCSwap(), 0) * 7
3435
for bloq, counts in sigma.items():
36+
if isinstance(bloq, And) and not bloq.uncompute:
37+
ret += counts * 4
38+
3539
if bloq_is_rotation(bloq) and not cirq.has_stabilizer_effect(bloq):
3640
if isinstance(bloq, Controlled):
3741
# TODO native controlled rotation bloqs missing (CRz, CRy etc.)

0 commit comments

Comments
 (0)