quantumlib / Cirq

A Python framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
Apache License 2.0
4.24k stars 1.01k forks source link

Fraction exponents #1099

Closed viathor closed 5 years ago

viathor commented 5 years ago

A common operation to obtain a unitary from another unitary is exponentiation, e.g. T gate is Z^(1/4). In practice, the exponents are always rational numbers (AFAIK). The collection of rational powers k/n with k=0..n-1 of a gate is generally a cyclic group of finite order which gives rise to exploitable identities, e.g. Z^(7/8) is the inverse of Z^(1/8). It'd be easy to take advantage of these group properties if exponents where fractions instead of floats.

I'm filing this issue to start a discussion about such a change. I imagine we'd want to continue to allow users to pass float values as exponents. However, we'd convert them to fractions internally, i.e. the public interface of our gates would be the conversion boundary.

The representation based on fractions imposes more appropriate and more useful constraints on the possible values of the exponent which leads to a more robust internal representation of a gate. For example, cirq.X(exponent=3) would be trivially equal to cirq.X because 3 = 1 mod 2.

Fraction exponents would also enable us to improve and simplify the code that computes the period of EigenGates by removing the need for approximation and arbitrary constants.

Finally, the change would protect us against the pitfalls of dealing with floating point numbers in future.

Related: #1098

Strilanc commented 5 years ago

I don't think we should do this.

I do think that checking that a fractions.Fraction can make it into a gate exponent and get printed out into a diagram is a good idea. This could act as a test case for working with unexpected kinds of numeric values in general. I also think that noticing an exponent is close to a fraction, and printing eg 7/8 instead of 0.875 or 0.875000001 in diagrams could be helpful to users.

But as far explicitly having semantics that depend strongly on the behavior of rationals... I expect there are unfortunate complications such as:

1) We use floating point numbers in the code. Floats tend to "infect" other values. We will need to actively "fight" this infection from spreading in order to protect the rational numbers. That is not easy.

2) Floats are already pretty good at approximating the behavior you want. Z**(7/8) == Z**(1/8) evaluates to True, for example. On the other hand, 1/3 vs 7/3 doesn't work.

3) Adding two fractions is slower than adding two floats, because it involves multiplications and a gcd (actually I don't know if python normalizes fractions after every operation by default, but if we don't make that happen now and then the values might grow out of control from typical usage). Given that we recently realized we have a performance problem, we need to profile before doing something like this.

4) I don't think it quite has the right "style" for cirq. NISQ machines live in arbitrary angle land, where chaos reigns and who knows if anything is a multiple of anything else? Rationals feel more error correction focused.

kevinsung commented 5 years ago

I agree with @Strilanc . I'll add that in Hamiltonian simulation, the t in exp(-iHt) is not usually a rational number, and neither are the coefficients of terms in H for actual molecular Hamiltonians. Also, variational algorithm optimizations typically work over floats, not fractions.

viathor commented 5 years ago

Thank you both for helping to evaluate this! This kept bugging me and now I feel better about abandoning the idea.