Loops#

jrange(*args)[source]#

Performs a loop with a dynamic bound. Similar to the Python native range, this iterator can receive multiple arguments. If it receives just one, this value is interpreted as the stop value and the start value is assumed to be 0. Two arguments represent start and stop value, whereas three represent start, stop and step.

Warning

Similar to the ClControlEnvironment, this feature must not have external carry values, implying values computed within the loop can’t be used outside of the loop. It is however possible to carry on values from the previous iteration.

Warning

Each loop iteration must perform exactly the same instructions - the only thing that changes is the loop index

Parameters:
startint

The loop index to start at.

stopint

The loop index to stop at.

stepint

The value to increase the loop index by after each iteration.

Examples

We construct a function that encodes an integer into an arbitrarily sized QuantumVariable:

from qrisp import QuantumFloat, control, x
from qrisp.jasp import jrange, make_jaspr

@qache
def int_encoder(qv, encoding_int):

    for i in jrange(qv.size):
        with control(encoding_int & (1<<i)):
            x(qv[i])

def test_f(a, b):

    qv = QuantumFloat(a)

    int_encoder(qv, b+1)

    return measure(qv)

jaspr = make_jaspr(test_f)(1,1)

Test the result:

>>> jaspr(5, 8)
9
>>> jaspr(5, 9)
10

We now give examples that violate the above rules (ie. no carries and changing iteration behavior).

To create a loop with carry behavior we simply return the final loop index

@qache
def int_encoder(qv, encoding_int):

    for i in jrange(qv.size):
        with control(encoding_int & (1<<i)):
            x(qv[i])
    return i

def test_f(a, b):

    qv = QuantumFloat(a)

    int_encoder(qv, b+1)

    return measure(qv)

jaspr = make_jaspr(test_f)(1,1)
>>> jaspr(5, 8)
Exception: Found jrange with external carry value

To demonstrate the second kind of illegal behavior, we construct a loop that behaves differently on the first iteration:

@qache
def int_encoder(qv, encoding_int):

    flag = True
    for i in jrange(qv.size):
        if flag:
            with control(encoding_int & (1<<i)):
                x(qv[i])
        else:
            x(qv[0])
        flag = False

def test_f(a, b):

    qv = QuantumFloat(a)

    int_encoder(qv, b+1)

    return measure(qv)

jaspr = make_jaspr(test_f)(1,1)

In this script, int_encoder defines a boolean flag that changes the semantics of the iteration behavior. After the first iteration the flag is set to False such that the alternate behavior is activated.

>>> jaspr(5, 8)
Exception: Jax semantics changed during jrange iteration