Qualtran
Qualtran copied to clipboard
Propagate rotation epsilon in gate cost
The t_complexity protocol ignores the eps parameter when counting rotations.
The solution is to enhance the QECGatesCost cost and associated dataclass GateCounts to count rotation/eps pairs.
Sample code snippet:
import sympy
from qualtran.bloqs.basic_gates import ZPowGate
t, eps = sympy.symbols(r"t \epsilon")
bloq = ZPowGate(exponent=t, eps=eps)
print(bloq.t_complexity())
Prints TComplexity(rotations=1).
One could do bloq.t_complexity().t_incl_rotations(eps), but this eps is not the same as the bloq's eps (which in general could be different for each subbloq).
Some history:
eps for rotations was introduced in the early days of Qualtran and I think is a useful parameter when constructing rotation bloqs. I agree it's probably not propagated correctly everywhere (though I remember implementing and correctly propagating eps in qvr, qft and hamming weight phasing bloq) -- we should file issues and try to get that fixed.
Originally, the rotation bloqs had a _t_complexity_ method implemented and would return the number of T gates scaling with log(1/eps); so the T-counts for call graphs with rotation bloqs would take the eps in account. (eg: https://github.com/quantumlib/Qualtran/pull/469)
As part of unifying / deprecating the T-complexity protocol; a temporary fix was to return rotations=1 for all rotation bloqs and add a method on the TComplexity class to convert rotations into T-gates. Eventually, the idea was to use the new costing framework to keep track of bloq counts and then use the eps to convert rotation bloqs to t-gates like we used to do originally. Here are some relevant discussions - https://github.com/quantumlib/Qualtran/issues/662#issuecomment-1949215534 https://github.com/quantumlib/Qualtran/pull/678#discussion_r1492944645
Using a single / hardcoded eps to convert all rotation bloqs to t gates was never meant to be a permanent solution. We've just not gotten around completing the migration yet I guess.
@mpharrigan As per our discussion offline, it'll be nice to have this as part of the v1.0 milestone.
I would also please like to have this feature!
Results such as the ones produced by this code snipet:
from qualtran.bloqs.basic_gates import Ry
from qualtran.cirq_interop.t_complexity_protocol import t_complexity
for eps in [1e-11, 1e-3]:
print(f"t_cost at eps={eps}:", t_complexity(Rx(angle, eps=eps)).t_incl_rotations())
are pretty confusing at first. I know this can be "fixed" by passing eps=eps to .t_incl_rotations() but this is not elegant.
Moreover, imagine one wants to get T gate counts for Bloqs containing rotations with different level of precision. To my understanding this is currently not possible:
from qualtran import Bloq, BloqBuilder
from qualtran.drawing import show_bloq
from qualtran.cirq_interop.t_complexity_protocol import t_complexity
class TwoRotations(Bloq):
@property
def signature(self):
return Signature([
Register('x', QBit()),
])
def build_composite_bloq(self, bb: BloqBuilder, *, x):
x = bb.add(Rx(angle=0.1, eps=1e-11), q=x)
x = bb.add(Ry(angle=0.2, eps=1e-3), q=x)
return {'x': x}
two_rotations = TwoRotations()
show_bloq(two_rotations)
show_bloq(two_rotations.decompose_bloq())
t_complexity(two_rotations).t_incl_rotations()
This will always output 2 * 52 = 104 even though, due to the lower precision of the Ry rotation the T count should be lower.
Or is there a way to get correct resource estimates in such cases?