Skip to content

Commit 7344830

Browse files
Create linear half comparison bloqs (#1408)
This PR creates all versions of half a comparison operation in $n$ toffoli complexity. The uncomputation of these bloqs has zero cost. The half comparason operation is needed in the modular inversion bloq. I also modify the OutOfPlaceAdder to allow it to not compute the extra And when it's not needed --- This PR also shows how we can implement all versions of comparison by implementing just one. for example creating a half greater than bloq that use logarithmic depth is enough to implement all the others.
1 parent c227032 commit 7344830

File tree

8 files changed

+1063
-8
lines changed

8 files changed

+1063
-8
lines changed

dev_tools/qualtran_dev_tools/notebook_specs.py

+4
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,10 @@
435435
qualtran.bloqs.arithmetic.comparison._SQ_CMP_DOC,
436436
qualtran.bloqs.arithmetic.comparison._LEQ_DOC,
437437
qualtran.bloqs.arithmetic.comparison._CLinearDepthGreaterThan_DOC,
438+
qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_GREATERTHAN_DOC,
439+
qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_GREATERTHANEQUAL_DOC,
440+
qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_LESSTHAN_DOC,
441+
qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_LESSTHANEQUAL_DOC,
438442
],
439443
),
440444
NotebookSpecV2(

qualtran/bloqs/arithmetic/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
GreaterThanConstant,
2424
LessThanConstant,
2525
LessThanEqual,
26+
LinearDepthHalfGreaterThan,
27+
LinearDepthHalfGreaterThanEqual,
28+
LinearDepthHalfLessThan,
29+
LinearDepthHalfLessThanEqual,
2630
SingleQubitCompare,
2731
)
2832
from qualtran.bloqs.arithmetic.controlled_addition import CAdd

qualtran/bloqs/arithmetic/addition.ipynb

+4-2
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,14 @@
186186
"using $4n - 4 T$ gates. Uncomputation requires 0 T-gates.\n",
187187
"\n",
188188
"#### Parameters\n",
189-
" - `bitsize`: Number of bits used to represent each input integer. The allocated output register is of size `bitsize+1` so it has enough space to hold the sum of `a+b`. \n",
189+
" - `bitsize`: Number of bits used to represent each input integer. The allocated output register is of size `bitsize+1` so it has enough space to hold the sum of `a+b`.\n",
190+
" - `is_adjoint`: Whether this is compute or uncompute version.\n",
191+
" - `include_most_significant_bit`: Whether to add an extra most significant (i.e. carry) bit. \n",
190192
"\n",
191193
"#### Registers\n",
192194
" - `a`: A bitsize-sized input register (register a above).\n",
193195
" - `b`: A bitsize-sized input register (register b above).\n",
194-
" - `c`: A bitize+1-sized LEFT/RIGHT register depending on whether the gate adjoint or not. \n",
196+
" - `c`: The LEFT/RIGHT register depending on whether the gate adjoint or not. This register size is either bitsize or bitsize+1 depending on the value of `include_most_significant_bit`. \n",
195197
"\n",
196198
"#### References\n",
197199
" - [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). \n"

qualtran/bloqs/arithmetic/addition.py

+22-6
Original file line numberDiff line numberDiff line change
@@ -260,18 +260,27 @@ class OutOfPlaceAdder(GateWithRegisters, cirq.ArithmeticGate): # type: ignore[m
260260
Args:
261261
bitsize: Number of bits used to represent each input integer. The allocated output register
262262
is of size `bitsize+1` so it has enough space to hold the sum of `a+b`.
263+
is_adjoint: Whether this is compute or uncompute version.
264+
include_most_significant_bit: Whether to add an extra most significant (i.e. carry) bit.
263265
264266
Registers:
265267
a: A bitsize-sized input register (register a above).
266268
b: A bitsize-sized input register (register b above).
267-
c: A bitize+1-sized LEFT/RIGHT register depending on whether the gate adjoint or not.
269+
c: The LEFT/RIGHT register depending on whether the gate adjoint or not.
270+
This register size is either bitsize or bitsize+1 depending on
271+
the value of `include_most_significant_bit`.
268272
269273
References:
270274
[Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648)
271275
"""
272276

273277
bitsize: 'SymbolicInt'
274278
is_adjoint: bool = False
279+
include_most_significant_bit: bool = True
280+
281+
@property
282+
def out_bitsize(self):
283+
return self.bitsize + (1 if self.include_most_significant_bit else 0)
275284

276285
@property
277286
def signature(self):
@@ -280,14 +289,14 @@ def signature(self):
280289
[
281290
Register('a', QUInt(self.bitsize)),
282291
Register('b', QUInt(self.bitsize)),
283-
Register('c', QUInt(self.bitsize + 1), side=side),
292+
Register('c', QUInt(self.out_bitsize), side=side),
284293
]
285294
)
286295

287296
def registers(self) -> Sequence[Union[int, Sequence[int]]]:
288297
if not isinstance(self.bitsize, int):
289298
raise ValueError(f'Symbolic bitsize {self.bitsize} not supported')
290-
return [2] * self.bitsize, [2] * self.bitsize, [2] * (self.bitsize + 1)
299+
return [2] * self.bitsize, [2] * self.bitsize, [2] * self.out_bitsize
291300

292301
def apply(self, a: int, b: int, c: int) -> Tuple[int, int, int]:
293302
return a, b, c + a + b
@@ -307,7 +316,7 @@ def on_classical_vals(
307316
return {
308317
'a': a,
309318
'b': b,
310-
'c': add_ints(int(a), int(b), num_bits=self.bitsize + 1, is_signed=False),
319+
'c': add_ints(int(a), int(b), num_bits=self.out_bitsize, is_signed=False),
311320
}
312321

313322
def with_registers(self, *new_registers: Union[int, Sequence[int]]):
@@ -328,12 +337,19 @@ def decompose_from_registers(
328337
cirq.CX(a[i], c[i + 1]),
329338
cirq.CX(b[i], c[i]),
330339
]
331-
for i in range(self.bitsize)
340+
for i in range(self.out_bitsize - 1)
332341
]
342+
if not self.include_most_significant_bit:
343+
# Update c[-1] as c[-1] ^= a[-1]^b[-1]
344+
i = self.bitsize - 1
345+
optree.append([cirq.CX(a[i], c[i]), cirq.CX(b[i], c[i])])
333346
return cirq.inverse(optree) if self.is_adjoint else optree
334347

335348
def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT':
336-
return {And(uncompute=self.is_adjoint): self.bitsize, CNOT(): 5 * self.bitsize}
349+
return {
350+
And(uncompute=self.is_adjoint): self.out_bitsize - 1,
351+
CNOT(): 5 * (self.bitsize - 1) + 2 + (3 if self.include_most_significant_bit else 0),
352+
}
337353

338354
def __pow__(self, power: int):
339355
if power == 1:

0 commit comments

Comments
 (0)