Skip to content

Commit

Permalink
Completed implementation of HammingWeightPhasingWithConfigurableAncil…
Browse files Browse the repository at this point in the history
…la and tests.
  • Loading branch information
dobbse42 committed Oct 17, 2024
1 parent 4d84d9d commit e484cc0
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 27 deletions.
51 changes: 30 additions & 21 deletions qualtran/bloqs/rotations/hamming_weight_phasing.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ class HammingWeightPhasingWithConfigurableAncilla(GateWithRegisters):
"""

bitsize: int
ancillasize: int
ancillasize: int # TODO: verify that ancillasize is always < bitsize-1
exponent: float = 1
eps: SymbolicFloat = 1e-10

Expand All @@ -243,34 +243,22 @@ def signature(self) -> 'Signature':
HammingWeightPhasing bloqs on subsets of the input.
'''
def build_composite_bloq(self, bb: 'BloqBuilder', *, x: 'SoquetT') -> Dict[str, 'SoquetT']:
self.ancillasize = min(self.ancillasize, self.bitsize-1) # TODO: this is surely the wrong way to do this, but this at least allows tests to be run for now.
num_iters = self.bitsize // (self.ancillasize + 1)
remainder = self.bitsize - (self.ancillasize + 1) * num_iters
remainder = self.bitsize % (self.ancillasize+1)
x = bb.split(x)
x_parts = []

for i in range(num_iters):
x_part = bb.join(x[i*(self.ancillasize+1):(i+1)*(self.ancillasize+1)], dtype=QUInt(self.ancillasize+1)) #maybe off-by-1
x_part = bb.join(x[i*(self.ancillasize+1):(i+1)*(self.ancillasize+1)], dtype=QUInt(self.ancillasize+1))
x_part = bb.add(HammingWeightPhasing(bitsize=self.ancillasize+1, exponent=self.exponent, eps=self.eps), x=x_part)
x_part = bb.add(HammingWeightPhasing(bitsize=self.ancillasize+1, exponent=self.exponent, eps=self.eps).adjoint(), x=x_part)
x_parts.extend(bb.split(x_part))

if remainder > 0:
if remainder > 1:
x_part = bb.join(x[(-1*remainder):], dtype=QUInt(remainder))
x_part = bb.add(HammingWeightPhasing(bitsize=remainder, exponent=self.exponent, eps=self.eps), x=x_part)
x_part = bb.add(HammingWeightPhasing(bitsize=remainder, exponent=self.exponent, eps=self.eps).adjoint(), x=x_part)
x_parts.extend(bb.split(x_part))
#print("shape prior to flatten: ", np.shape(x_parts))
#x_parts.flatten()
''' x_parts = [
a
for x_part in x_parts
for a in x_part
]
'''
#print("shape after flatten: ", np.shape(x_parts))
for part in x:
print("next elem: ", part)
x_parts.extend(bb.split(x_part))
if remainder == 1:
x_part = x[-1]
x_part = bb.add(ZPowGate(exponent=self.exponent, eps=self.eps), q=x_part)
x_parts.append(x_part)
x = bb.join(np.array(x_parts), dtype=QUInt(self.bitsize))
return {'x': x}

Expand All @@ -281,6 +269,27 @@ def wire_symbol(self, reg: Optional[Register], idx: Tuple[int, ...] = tuple()) -
return super().wire_symbol(reg, idx)


def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT':
num_iters = self.bitsize // (self.ancillasize + 1)
remainder = self.bitsize - (self.ancillasize + 1) * num_iters
# TODO: Surely there is a better way of doing this
if remainder > 1:

return {
HammingWeightPhasing(self.ancillasize+1, self.exponent, self.eps): num_iters,
HammingWeightPhasing(remainder, self.exponent, self.eps): bool(remainder),
}
elif remainder:
return {
HammingWeightPhasing(self.ancillasize+1, self.exponent, self.eps): num_iters,
ZPowGate(exponent=self.exponent, eps=self.eps): 1
}
else:
return {
HammingWeightPhasing(self.ancillasize+1, self.exponent, self.eps): num_iters,
}


@bloq_example
def _hamming_weight_phasing_with_configurable_ancilla() -> HammingWeightPhasingWithConfigurableAncilla:
hamming_weight_phasing_with_configurable_ancilla = HammingWeightPhasingWithConfigurableAncilla(4, 2, np.pi / 2.0)
Expand Down
13 changes: 7 additions & 6 deletions qualtran/bloqs/rotations/hamming_weight_phasing_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,7 @@ def test_hamming_weight_phasing_via_phase_gradient_t_complexity(n: int, theta: f

assert total_t < naive_total_t


@pytest.mark.parametrize('ancillasize', [1, 2, 3, 4, 5, 6, 7])
@pytest.mark.parametrize('n', [2, 3, 4, 5, 6, 7, 8])
@pytest.mark.parametrize('n, ancillasize', [(n, ancillasize) for n in range(3, 9) for ancillasize in range(1, n-1)])
@pytest.mark.parametrize('theta', [1 / 10, 1 / 5, 1 / 7, np.pi / 2])
def test_hamming_weight_phasing_with_configurable_ancilla(n: int, ancillasize: int, theta: float):
gate = HammingWeightPhasingWithConfigurableAncilla(n, ancillasize, theta)
Expand All @@ -140,9 +138,12 @@ def test_hamming_weight_phasing_with_configurable_ancilla(n: int, ancillasize: i
gate, [ignore_split_join, cirq_to_bloqs, generalize_rotation_angle]
)

assert gate.t_complexity().rotations == ceil(n / ancillasize+1) * ancillasize.bit_length() # possibly wrong
assert gate.t_complexity().t == 4 * ancillasize * ceil(n / (ancillasize+1))
# TODO: add an ancilla size assertion
remainder = n % (ancillasize+1)

# assert gate.t_complexity().rotations == (-(-n // (ancillasize+1))-1) * (ancillasize+1).bit_length() + remainder.bit_length() # exact, fails for remainder = 0.
assert gate.t_complexity().rotations <= (-(-n // (ancillasize+1))) * (ancillasize+1).bit_length() + remainder.bit_length() # upper bound
assert gate.t_complexity().t <= 4 * (ancillasize) * -(-n // (ancillasize+1))
# TODO: add an assertion that number of ancilla allocated is never > ancillasize.

gh = GateHelper(gate)
sim = cirq.Simulator(dtype=np.complex128)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from hamming_weight_phasing import HammingWeightPhasing, HammingWeightPhasingWithConfigurableAncilla


import numpy as np
from qualtran import Bloq, CompositeBloq, BloqBuilder, Signature, Register
from qualtran import QBit, QInt, QUInt, QAny
Expand Down

0 comments on commit e484cc0

Please sign in to comment.