qat.lang.qrout

qat.lang.qrout(*fun, unroll=None, **kwargs)

A decorator that turns a function into a QRoutine.

Parameters

unroll (optional, bool) – If set to True, the decorator will unroll statically the function, generating a QRoutine object. If set to False, the function will be called everytime the wrapped function is called. If set to None (or not set), the decorator will attempt to unroll and still wrap the function if the unrolling fails.

Inside the decorated function, qbits can be referred to via their indices (hence integers). The intended usage is the following:

from qat.lang import qrout, H, CNOT

@qrout
def bell_state():
    # Inside the function, qbits can be referred to directly by their indices
    H(0)
    CNOT(0, 1)
    # the return value will be ignore, no need to have one

# The resulting object can be called as a function:
bell_state().display(batchmode=True)

for sample in bell_state().run():
    print(sample.state, sample.probability)
 ┌─┐   
─┤H├─●─
 └─┘ │ 
     │ 
    ┌┴┐
────┤X├
    └─┘
       

|00> 0.4999999999999999
|11> 0.4999999999999999

Passing simple arguments:

The decorated function can take parameters. The decorator assumes that any parameter is a floating point number. It will replace it by some abstract variable with a similar name:

import numpy as np
from qat.lang import qrout, H, RZ

@qrout
def h_rz(theta):
    H(0)
    RZ(theta)(1)

# The resulting object already contains the unrolled circuit
h_rz.display(batchmode=True)
# calling the decorated function instantiates the angles to their values
h_rz(np.pi/2).display(batchmode=True)
 ┌─┐        
─┤H├────────
 └─┘        
            
 ┌─────────┐
─┤RZ[theta]├
 └─────────┘
            

 ┌─┐      
─┤H├──────
 └─┘      
          
 ┌───────┐
─┤RZ[π/2]├
 └───────┘
          

Passing arrays of angles:

It is also possible to decorate functions that take arrays of floating point values as input. For the decorator to be able to replace those arrays by variables, it needs to know the dimension of the arrays. These dimensions are passed as keyword arguments to the decorator.

Warning

When such a shape is specified the argument will be replaced by a numpy array of variables with a matching shape. It should thus be manipulated as a numpy array (see code below).

import numpy as np
from qat.lang import qrout, RX, RZ

@qrout(angles=2)
def rx_rz(angles):
    RX(angles[0])(0)
    RZ(angles[1])(1)

rx_rz.display(batchmode=True)
rx_rz([np.pi/2, np.pi/4]).display(batchmode=True)

@qrout(angles=(2, 3, 4))
def custom(angles):
    # angles is a numpy array of shape (2, 3, 4) containing Variable objects
    n, m, p = angles.shape
    for i in range(n):
        for j in range(m):
            for k in range(p):
                RX(angles[i, j, k])(i)
 ┌────────────┐
─┤RX[angles_0]├
 └────────────┘
               
 ┌────────────┐
─┤RZ[angles_1]├
 └────────────┘
               

 ┌───────┐
─┤RX[π/2]├
 └───────┘
          
 ┌───────┐
─┤RZ[π/4]├
 └───────┘
          

Blocking the unroll:

In some cases, one might want to avoir the static unrolling of the function/circuit. This unrolling can be blocked using the unroll keyword argument:

import numpy as np
from qat.lang import qrout, RX, RZ

@qrout(unroll=False)
def rx_rz(angles):
    RX(angles[0])(0)
    RZ(angles[1])(1)

# This line would fail, rx_rz does not contain any circuit yet
# rx_rz.display(batchmode=True)
rx_rz([np.pi/2, np.pi/4]).display(batchmode=True)
 ┌───────┐
─┤RX[π/2]├
 └───────┘
          
 ┌───────┐
─┤RZ[π/4]├
 └───────┘
          

By default, the decorator will attempt to unroll the function. If the unrolling fails, it will still decorate without unrolling.