qml.math.binary_decimals

binary_decimals(phi, precision, unit=1.0)[source]

Compute the binary decimals \(X_{-1} X_{-2} \cdots X_{-p}\) of the binary representation of \(\phi = (\cdots X_1 X_0.X_{-1} X_{-2} \cdots X_{-p} X_{-p-1} \cdots)_2\) up to precision \(p\).

For example, for \(\phi = 0.375\) we obtain [0, 1, 1] because its binary representation is \(\phi = (0.011)_2\) (\(0 \cdot 2^{-1} + 1 \cdot 2^{-2} + 1 \cdot 2^{-3} = \tfrac02 + \tfrac14 + \tfrac18 = 0.375\)).

It is often handy to calculate the binary decimals in some unit of \(2\pi\) or \(4\pi\), depending on the convention for how angles are treated. For example, we often require the binary representation of the decimals of \(\phi\) in phase factors such as \(\exp(-i \phi 2 \pi)\). In this case, we set the unit to \(2\pi\). In case we additionally divide the angle by 2, as is the case in rotation gates such as RZ, we use a unit of \(4\pi\).

Parameters:
  • phi (float) – The number to be represented in binary.

  • precision (int) – The number of digits to keep.

  • unit (float) – The angle is to be in the unit of unit, meaning we consider (phi % unit)/unit. The default is \(1\).

Returns:

Binary representation of the decimals

Return type:

array

Example

We round the binary representation of \((0.11011)_2\), which simply yields \((0.11)_2\) from rounding down.

>>> precision = 2
>>> phi = (1 / 2 + 1 / 4 + 0 / 8 + 1 / 16 + 1 / 32) # = 0.84375
>>> qml.math.binary_decimals(phi, precision)
array([1, 1])

When we pass the midpoint of the cut off decimals, we round up. In particular, for \((0.1011)_2\), we round to \((0.11)_2\):

>>> phi = (1 / 2 + 0 / 4 + 1 / 8 + 1 / 16) # = 0.6875
>>> qml.math.binary_decimals(phi, precision)
array([1, 1])

If we want to represent the angle for a rotation like RZ, where the convention the angle is divided by \(2\), we want to specify the unit. For example, looking at \((0.1011)_2 4\pi\) we obtain the following:

>>> phi = (1 / 2 + 0 / 4 + 1 / 8 + 1 / 16) * 4 * np.pi # = 0.6875 * 4pi
>>> qml.math.binary_decimals(phi, precision, unit = 4 * np.pi)
array([1, 1])

Note that we always ignore the integer part. E.g., because \((0.1111)_2\) rounds to \((1.0000)_2\), we obtain [0, 0]:

>>> phi = (1 / 2 + 1 / 4 + 1 / 8 + 1 / 16) # = 0.9375
>>> qml.math.binary_decimals(phi, precision)
array([0, 0])

The non-trivial case is when we are exactly at the midpoint, i.e. the truncated bits are \(100\). In this case, the so-called tie to even rule kicks in. This is automatically handled by numpy under the hood. For example, take \((0.10100)_2 = 0.625\). We can either round down to \((0.10)_2 = 0.5\), or round up to \((0.11)_2 = 0.75\), but it is a tie because both numbers are equally close to \(0.625\). The tie to even rule is handled under the hood by numpy.round as we convert the decimals to an integer by multiplying by 2**precision. In this case, we obtain \(0.625 * 2^2 = 2.5\), which is then rounded to the closest even integer, \(2\). Turning it back to a fraction we obtain \(\tfrac{2}{2^2} = 0.5 = (0.10)_2\).

>>> phi = 1 / 2 + 0 / 4 + 1 / 8 + 0 / 16 + 0 / 32
>>> qml.math.binary_decimals(phi, precision)
array([1, 0])

Note it is easier and cheaper to to simply translate the integer \(2=(10)_2\) to its binary representation, which is equivalent.