qrisp.qcla#

qcla(a, b, radix_base=2, radix_exponent=1, t_depth_reduction=True, ctrl=None)[source]#

Implementation of the higher radix quantum carry lookahead adder (QCLA) as described here. This adder stands out for having logarithmic T-depth like Drapers QCLA. Compared to Drapers QCLA, the higher radix QCLA allows a more dynamic structure and the use of customizable “sub-adders” which enables this adder to beat Drapers QCLA in terms of speed (ie. T-depth).

In Python syntax, this function performs the inplace addition:

b += a

Apart from the two quantum arguments, this function supports the specification of the adder-radix R. The adder-radix can be specified in the form of an exponential of integers:

\[R = r^k\]

Where \(r\) is the radix base and \(k\) is the radix exponent.

Calling qcla with radix base \(r\) and radix exponent \(k\), will precalculate the carry values using the Brent-Kung tree with carry-radix \(r\) and cancel the recursion \(k\) layers before conclusion.

An additional compilation option is given with the t_depth_reduction keyword. This compilation option modifies the way the carry values are uncomputed. If t_depth_reduction is set to True the carry values will be uncomputed using the intermediate result of the sub-adder - if set to False they will be uncomputed using the automatic uncomputation algorithm.

The advantage of the automated version is that, both T-depth and CNOT-depth are scaling with the the logarithm of the input size. For t_depth_reduction = True the T-depth is significantly reduced (and still logarithmic) however the CNOT depth becomes linear.

Parameters:
aQuantumFloat or List[Qubit] or int

The value that is added.

bQuantumFloat or List[Qubit]

The value that is operated on.

radix_baseinteger, optional

The radix of the Brent-Kung tree. The default is 2.

radix_exponentinteger, optional

The cancellation treshold for the Brent-Kung recursion. The adder-Radix is then \(R = r^k\). The default is 1.

t_depth_reductionbool, optional

A compilation option that reduces T-depth but in turn weakens CNOT depth to linear scaling. The default is True.

Raises:
Exception

Tried to add QuantumFloat of higher precision onto QuantumFloat of lower precision.

Examples

We try out several constellations of parameters:

>>> from qrisp import QuantumFloat, qcla
>>> a = QuantumFloat(8)
>>> b = QuantumFloat(8)
>>> a[:] = 4
>>> b[:] = 15
>>> qcla(a, b)
>>> print(b)
{19: 1.0}

We now measure the T-depth. To get the optimal result, we need to tell the compiler that we only care about T-gates. This can be achieved with the gate_speed keyword of the compile method. This keyword allows you to specify a function of Operation objects, which returns the speed of that Operation. For more information check out the compile documentation page.

For T-depth, there is already a pre-coded function: T-depth.

>>> from qrisp import t_depth_indicator
>>> gate_speed = lambda x : t_depth_indicator(x, epsilon = 2**-10)
>>> qc = b.qs.compile(gate_speed = gate_speed, compile_mcm = True)
>>> qc.t_depth()
17

This function contains many allocations/deallocations that can be leveraged into parallelism, implying it can profit a lot from additional workspace:

>>> qc = b.qs.compile(workspace = 10, gate_speed = gate_speed, compile_mcm = True)
>>> qc.t_depth()        
7

We can verify the logarithmic behavior by comparing to the Gidney-adder:

>>> from qrisp import gidney_adder
>>> a = QuantumFloat(40)
>>> b = QuantumFloat(40)
>>> gidney_adder(a, b)
>>> qc = b.qs.compile(gate_speed = gate_speed, compile_mcm = True)
>>> qc.t_depth()
40
>>> a = QuantumFloat(40)
>>> b = QuantumFloat(40)
>>> qcla(a, b)
>>> qc = b.qs.compile(workspace = 50, gate_speed = gate_speed, compile_mcm = True)
>>> qc.t_depth()    
19

The function can also be used to perform semi-classical in-place addition

>>> b = QuantumFloat(10)
>>> b[:] = 20
>>> qcla(22, b)
>>> print(b)
{42: 1.0}