Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HammingWeightCompute: replace ArbitraryClifford with CNOT, support symbolic bitsizes #1355

Merged
merged 4 commits into from
Aug 28, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 25 additions & 16 deletions qualtran/bloqs/arithmetic/hamming_weight.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
from numpy.typing import NDArray

from qualtran import GateWithRegisters, QAny, QUInt, Register, Side, Signature
from qualtran.bloqs.bookkeeping import ArbitraryClifford
from qualtran.bloqs.basic_gates import CNOT
from qualtran.bloqs.mcmt.and_bloq import And
from qualtran.symbolics import bit_length, is_symbolic, SymbolicInt

if TYPE_CHECKING:
from qualtran.resource_counting import BloqCountT, SympySymbolAllocator
Expand Down Expand Up @@ -49,20 +50,35 @@ class HammingWeightCompute(GateWithRegisters):
[Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648), Page-4
"""

bitsize: int
bitsize: SymbolicInt

@cached_property
def signature(self):
jnk_size = self.bitsize - self.bitsize.bit_count()
out_size = self.bitsize.bit_length()
return Signature(
[
Register('x', QUInt(self.bitsize)),
Register('junk', QAny(jnk_size), side=Side.RIGHT),
Register('out', QUInt(out_size), side=Side.RIGHT),
Register('junk', QAny(self.junk_bitsize), side=Side.RIGHT),
Register('out', QUInt(self.out_bitsize), side=Side.RIGHT),
]
)

@cached_property
def junk_bitsize(self) -> SymbolicInt:
return self.bitsize - self.bit_count_of_bitsize

@cached_property
def out_bitsize(self) -> SymbolicInt:
return bit_length(self.bitsize)

@cached_property
def bit_count_of_bitsize(self) -> SymbolicInt:
"""lower bound on number of 1s in bitsize"""
# TODO https://github.com/quantumlib/Qualtran/issues/1357
# add explicit support for symbolic functions without relying on pre-computed bounds.
if is_symbolic(self.bitsize):
return 1 # worst case
Comment on lines +78 to +79
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you open an issue, and link it here, with a request that we should add support for custom sympy functions so we don't need to assume a worst case bound here? Ideally, we would define a bit_count(x) function in symbolics/ and report the costs in terms of bit_count(N). When users have values of N, one could substitute it in the symbolic cost expression and bit_count(N) would evaluate to a number as expected.

return self.bitsize.bit_count()

def pretty_name(self) -> str:
return "out = x.bit_count()"

Expand Down Expand Up @@ -102,13 +118,6 @@ def decompose_from_registers(
yield self._decompose_using_three_to_two_adders(x, junk, out)

def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']:
num_and = self.bitsize - self.bitsize.bit_count()
num_clifford = num_and * 5 + self.bitsize.bit_count()
return {(And(), num_and), (ArbitraryClifford(n=2), num_clifford)}

def __pow__(self, power: int):
if power == 1:
return self
if power == -1:
return self.adjoint()
raise NotImplementedError("HammingWeightCompute.__pow__ defined only for +1/-1.")
num_and = self.junk_bitsize
num_cnot = num_and * 5 + self.bit_count_of_bitsize
return {(And(), num_and), (CNOT(), num_cnot)}
Loading