Skip to content

Commit 7c6715b

Browse files
anurudhpmpharrigan
andauthored
Improve interface for bloqs with specialized single-qubit-controlled versions (#1451)
* get_ctrl_system for bloqs with custom single-qubit-controlled implementations * replace uses of old interface * replace uses of old interface * specialize when `ctrl_spec.num_qubits == 1` * add test example of bloq with a separate controlled bloq * refactor: pass parameters instead of using protocol * `CtrlSpec.get_single_control_bit` to get the correct control bit in the single qubit case. * cleanup * use new single control bit method * test ctrl bit * `control` -> `ctrl` * don't pass `bloq` * use callable * update examples * mypy * add helper method which accepts bloqs instead of a callable * upgrade more usecases * typo * fix bug in adder * rename file to `specialized_ctrl` * rename function to `get_ctrl_system_1bit_cv` * mypy * cleanup design - use a helper bloq that accepts CU to build CCU - do not pass `bloq_without_ctrl` * add exposed helpers with clearer types --------- Co-authored-by: Matthew Harrigan <[email protected]>
1 parent e3aeee0 commit 7c6715b

File tree

12 files changed

+650
-23
lines changed

12 files changed

+650
-23
lines changed

qualtran/_infra/controlled.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
Sequence,
2424
Tuple,
2525
TYPE_CHECKING,
26+
TypeAlias,
2627
Union,
2728
)
2829

@@ -45,6 +46,9 @@
4546
from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator
4647
from qualtran.simulation.classical_sim import ClassicalValT
4748

49+
ControlBit: TypeAlias = int
50+
"""A control bit, either 0 or 1."""
51+
4852

4953
def _cvs_convert(
5054
cvs: Union[
@@ -250,6 +254,18 @@ def from_cirq_cv(
250254
bloq_cvs.append(curr_cvs)
251255
return CtrlSpec(tuple(qdtypes), tuple(bloq_cvs))
252256

257+
def get_single_ctrl_bit(self) -> ControlBit:
258+
"""If controlled by a single qubit, return the control bit, otherwise raise"""
259+
if self.num_qubits != 1:
260+
raise ValueError(f"expected a single qubit control, got {self.num_qubits}")
261+
262+
(qdtype,) = self.qdtypes
263+
(cv,) = self.cvs
264+
(idx,) = Register('', qdtype, cv.shape).all_idxs()
265+
(control_bit,) = qdtype.to_bits(cv[idx])
266+
267+
return int(control_bit)
268+
253269

254270
class AddControlledT(Protocol):
255271
"""The signature for the `add_controlled` callback part of `ctrl_system`.

qualtran/_infra/controlled_test.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,26 @@ def test_ctrl_spec_to_cirq_cv_roundtrip():
100100
assert CtrlSpec.from_cirq_cv(cirq_cv, qdtypes=ctrl_spec.qdtypes, shapes=ctrl_spec.shapes)
101101

102102

103+
@pytest.mark.parametrize(
104+
"ctrl_spec", [CtrlSpec(), CtrlSpec(cvs=[1]), CtrlSpec(cvs=np.atleast_2d([1]))]
105+
)
106+
def test_ctrl_spec_single_bit_one(ctrl_spec: CtrlSpec):
107+
assert ctrl_spec.get_single_ctrl_bit() == 1
108+
109+
110+
@pytest.mark.parametrize(
111+
"ctrl_spec", [CtrlSpec(cvs=0), CtrlSpec(cvs=[0]), CtrlSpec(cvs=np.atleast_2d([0]))]
112+
)
113+
def test_ctrl_spec_single_bit_zero(ctrl_spec: CtrlSpec):
114+
assert ctrl_spec.get_single_ctrl_bit() == 0
115+
116+
117+
@pytest.mark.parametrize("ctrl_spec", [CtrlSpec(cvs=[1, 1]), CtrlSpec(qdtypes=QUInt(2), cvs=0)])
118+
def test_ctrl_spec_single_bit_raises(ctrl_spec: CtrlSpec):
119+
with pytest.raises(ValueError):
120+
ctrl_spec.get_single_ctrl_bit()
121+
122+
103123
def _test_cirq_equivalence(bloq: Bloq, gate: 'cirq.Gate'):
104124
import cirq
105125

qualtran/bloqs/chemistry/hubbard_model/qubitization/select_hubbard.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,27 @@
2020
import numpy as np
2121
from numpy.typing import NDArray
2222

23-
from qualtran import bloq_example, BloqDocSpec, BQUInt, QAny, QBit, Register, Signature
23+
from qualtran import (
24+
AddControlledT,
25+
Bloq,
26+
bloq_example,
27+
BloqDocSpec,
28+
BQUInt,
29+
CtrlSpec,
30+
QAny,
31+
QBit,
32+
Register,
33+
Signature,
34+
)
2435
from qualtran._infra.gate_with_registers import total_bits
25-
from qualtran._infra.single_qubit_controlled import SpecializedSingleQubitControlledExtension
2636
from qualtran.bloqs.basic_gates import CSwap
2737
from qualtran.bloqs.multiplexers.apply_gate_to_lth_target import ApplyGateToLthQubit
2838
from qualtran.bloqs.multiplexers.select_base import SelectOracle
2939
from qualtran.bloqs.multiplexers.selected_majorana_fermion import SelectedMajoranaFermion
3040

3141

3242
@attrs.frozen
33-
class SelectHubbard(SelectOracle, SpecializedSingleQubitControlledExtension): # type: ignore[misc]
43+
class SelectHubbard(SelectOracle):
3444
r"""The SELECT operation optimized for the 2D Hubbard model.
3545
3646
In contrast to SELECT for an arbitrary chemistry Hamiltonian, we:
@@ -180,6 +190,19 @@ def __str__(self):
180190
return f'C{s}'
181191
return s
182192

193+
def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> Tuple['Bloq', 'AddControlledT']:
194+
from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv
195+
196+
return get_ctrl_system_1bit_cv(
197+
self,
198+
ctrl_spec=ctrl_spec,
199+
current_ctrl_bit=self.control_val,
200+
get_ctrl_bloq_and_ctrl_reg_name=lambda cv: (
201+
attrs.evolve(self, control_val=cv),
202+
'control',
203+
),
204+
)
205+
183206

184207
@bloq_example
185208
def _sel_hubb() -> SelectHubbard:

qualtran/bloqs/chemistry/sparse/select_bloq.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,23 @@
1616
from functools import cached_property
1717
from typing import Dict, Optional, Tuple, TYPE_CHECKING
1818

19+
import attrs
1920
import cirq
2021
from attrs import frozen
2122

22-
from qualtran import bloq_example, BloqBuilder, BloqDocSpec, BQUInt, QAny, QBit, Register, SoquetT
23-
from qualtran._infra.single_qubit_controlled import SpecializedSingleQubitControlledExtension
23+
from qualtran import (
24+
AddControlledT,
25+
Bloq,
26+
bloq_example,
27+
BloqBuilder,
28+
BloqDocSpec,
29+
BQUInt,
30+
CtrlSpec,
31+
QAny,
32+
QBit,
33+
Register,
34+
SoquetT,
35+
)
2436
from qualtran.bloqs.basic_gates import SGate
2537
from qualtran.bloqs.multiplexers.select_base import SelectOracle
2638
from qualtran.bloqs.multiplexers.selected_majorana_fermion import SelectedMajoranaFermion
@@ -30,7 +42,7 @@
3042

3143

3244
@frozen
33-
class SelectSparse(SpecializedSingleQubitControlledExtension, SelectOracle): # type: ignore[misc]
45+
class SelectSparse(SelectOracle):
3446
r"""SELECT oracle for the sparse Hamiltonian.
3547
3648
Implements the two applications of Fig. 13.
@@ -157,6 +169,19 @@ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT':
157169
c_maj_y = SelectedMajoranaFermion(sel_pa, target_gate=cirq.Y)
158170
return {SGate(): 1, maj_x: 1, c_maj_x: 1, maj_y: 1, c_maj_y: 1}
159171

172+
def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> Tuple['Bloq', 'AddControlledT']:
173+
from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv
174+
175+
return get_ctrl_system_1bit_cv(
176+
self,
177+
ctrl_spec=ctrl_spec,
178+
current_ctrl_bit=self.control_val,
179+
get_ctrl_bloq_and_ctrl_reg_name=lambda cv: (
180+
attrs.evolve(self, control_val=cv),
181+
'control',
182+
),
183+
)
184+
160185

161186
@bloq_example
162187
def _sel_sparse() -> SelectSparse:

qualtran/bloqs/chemistry/thc/select_bloq.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,19 @@
2020
from attrs import evolve, frozen
2121

2222
from qualtran import (
23+
AddControlledT,
2324
Bloq,
2425
bloq_example,
2526
BloqBuilder,
2627
BloqDocSpec,
2728
BQUInt,
29+
CtrlSpec,
2830
QAny,
2931
QBit,
3032
Register,
3133
Signature,
3234
SoquetT,
3335
)
34-
from qualtran._infra.single_qubit_controlled import SpecializedSingleQubitControlledExtension
3536
from qualtran.bloqs.basic_gates import CSwap, Toffoli, XGate
3637
from qualtran.bloqs.chemistry.black_boxes import ApplyControlledZs
3738
from qualtran.bloqs.multiplexers.select_base import SelectOracle
@@ -120,7 +121,7 @@ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT':
120121

121122

122123
@frozen
123-
class SelectTHC(SpecializedSingleQubitControlledExtension, SelectOracle): # type: ignore[misc]
124+
class SelectTHC(SelectOracle):
124125
r"""SELECT for THC Hamiltonian.
125126
126127
Args:
@@ -313,6 +314,16 @@ def build_composite_bloq(self, bb: 'BloqBuilder', **soqs: 'SoquetT') -> Dict[str
313314

314315
return out_soqs
315316

317+
def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> Tuple['Bloq', 'AddControlledT']:
318+
from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv
319+
320+
return get_ctrl_system_1bit_cv(
321+
self,
322+
ctrl_spec=ctrl_spec,
323+
current_ctrl_bit=self.control_val,
324+
get_ctrl_bloq_and_ctrl_reg_name=lambda cv: (evolve(self, control_val=cv), 'control'),
325+
)
326+
316327

317328
@bloq_example
318329
def _thc_sel() -> SelectTHC:

qualtran/bloqs/for_testing/random_select_and_prepare.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
from functools import cached_property
1515
from typing import Iterator, Optional, Tuple
1616

17+
import attrs
1718
import cirq
1819
import numpy as np
1920
from attrs import frozen
2021
from numpy.typing import NDArray
2122

22-
from qualtran import BloqBuilder, BQUInt, QBit, Register, SoquetT
23-
from qualtran._infra.single_qubit_controlled import SpecializedSingleQubitControlledExtension
23+
from qualtran import AddControlledT, Bloq, BloqBuilder, BQUInt, CtrlSpec, QBit, Register, SoquetT
2424
from qualtran.bloqs.block_encoding.lcu_block_encoding import SelectBlockEncoding
2525
from qualtran.bloqs.for_testing.matrix_gate import MatrixGate
2626
from qualtran.bloqs.multiplexers.select_base import SelectOracle
@@ -84,7 +84,7 @@ def alphas(self):
8484

8585

8686
@frozen
87-
class TestPauliSelectOracle(SpecializedSingleQubitControlledExtension, SelectOracle): # type: ignore[misc]
87+
class TestPauliSelectOracle(SelectOracle): # type: ignore[misc]
8888
r"""Paulis acting on $m$ qubits, controlled by an $n$-qubit register.
8989
9090
Given $2^n$ multi-qubit-Paulis (acting on $m$ qubits) $U_j$,
@@ -149,6 +149,19 @@ def decompose_from_registers(
149149
op = op.controlled_by(*quregs['control'], control_values=[self.control_val])
150150
yield op
151151

152+
def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> Tuple['Bloq', 'AddControlledT']:
153+
from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv
154+
155+
return get_ctrl_system_1bit_cv(
156+
self,
157+
ctrl_spec=ctrl_spec,
158+
current_ctrl_bit=self.control_val,
159+
get_ctrl_bloq_and_ctrl_reg_name=lambda cv: (
160+
attrs.evolve(self, control_val=cv),
161+
'control',
162+
),
163+
)
164+
152165

153166
def random_qubitization_walk_operator(
154167
select_bitsize: int, target_bitsize: int, *, random_state: np.random.RandomState

qualtran/bloqs/mcmt/controlled_via_and.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ def _is_single_bit_control(self) -> bool:
5050

5151
@cached_property
5252
def _single_control_value(self) -> int:
53-
assert self._is_single_bit_control()
54-
return self.ctrl_spec._cvs_tuple[0]
53+
return self.ctrl_spec.get_single_ctrl_bit()
5554

5655
def adjoint(self) -> 'ControlledViaAnd':
5756
return ControlledViaAnd(self.subbloq.adjoint(), self.ctrl_spec)

0 commit comments

Comments
 (0)