Classical Computations in QUA¶
This section describes standard syntax rules for classical computations in QUA.
Arithmetic expressions¶
multiplication, addition and subtraction of fixed point varibales is supported.
Note
division incurs a computational overhead of approximately 400nsec per operation.
Operator |
symbol |
example |
---|---|---|
multiplication |
* |
a*b |
division |
/ |
a/b |
addition |
+ |
a+b |
subtraction |
- |
a-b |
left bitshift |
<< |
a<<2 |
right bitshift |
>> |
a>>2 |
with program() as prog:
a = declare(fixed)
b = declare(int)
c = declare(fixed, value=0.3)
d = declare(fixed, value=-0.02)
e = declare(int, value=3)
f = declare(int, value=5)
assign(a, c*d-d+c*0.25)
assign(b, e+f*123*e-e)
assign(c, b/a)
save(a, "a")
save(b, "b")
save(c, "c")
The evaluation of each operator takes only a few clock cycles. For example, the addition (+) and substraction (-) operations each take 1 clock cycle to evaluate, i.e. 4ns. However, the compiler parallelizes operations, resulting in a much reduced effective calculation time which will often be zero.
Boolean Operations¶
Operator |
symbol |
example |
---|---|---|
AND |
& |
a&b |
OR |
| |
a|b |
XOR |
^ |
a^b |
NOT |
~ |
~a |
compounded boolean expressions are supported.
Note
it is necessary to wrap the atomic boolean expressions in paranthesis as seen in the example below, due to python operator precedence rules.
Example:
with if_( (~( a > b )) | ( c > d) ):
Arrays¶
QUA arrays are defined and accessed as follows.
Declare a new array:
syntax:
declare(fixed/int/bool, size=N)
declare(fixed/int/bool, value=[…])
Examples:
v1 = declare(int, size=3)
v2 = declare(int, value=[1,2,3])
Access cell in array
# syntax and examples
assign(v1[0], 5)
assign(b, v1[i]+6)
assign(v1[i+5], v[i+4])
save(v1[2], "v1_2")
Get array length
# syntax and examples
v1.length()
assign(length, v1.length())
Use example:
with program() as arrays_use:
v1 = declare(int, value = [1, 2, 4, 8, 16])
v2 = declare(int, size = 5) # will be initialized with zeros
v3 = declare(fixed, size = 30) # will be initialized with zeros
i = declare(int)
assign(v3[0], 16)
with for_(i, 0, i < v2.length(), i + 1):
assign(v2[i], i*2)
with for_(i, 0, i < v1.length(), i + 1):
assign(v2[i], v2[i] + v3[i])
with for_(i, 0, i < v1.length(), i + 1):
save(v1[i], "v1")
save(v2[i], "v2")
with for_(i, 0, i < v3.length(), i + 1):
save(v3[i], "v3")
Note
array length is fixed and cannot be changed after declaration.
Note
No validation is performed for reading/writing out of bounds. This will be added in a future version.
Computational library functions¶
QUA allows the user the real time evaluation of several mathematical operators and functions. Besides the standard mathematical operators (+, -, *, /) the user can access various libraries including
Math - trigonometric functions, array reduction functions etc.
Random - pseudo-random number generation.
Cast - allows casting between QUA variable types.
Util - Miscellaneous operators, including a hardware optimized conditional expression.
Math¶
Trigonometric functions¶
Math.cos(x)
: Take the cosine of a fixed in radiansMath.sin(x)
: Take the sine of a fixed in radiansMath.cos2pi(x)
: Take the cosine of a fixed in 2pi radiansMath.sin2pi(x)
: Take the cosine of a fixed in 2pi radians
cos2pi(x) and sin2pi(x) are equivalent to cos(2*pi*x) and sin(2*pi*x) but save a few clock cycles as the extra multiplication stage required to calculate 2*pi are removed by simply having 2*pi stored in memory. So whenever working with degrees instead of radians we suggest using the latter. The usage is straightforward, for example:
amplitude = declare(fixed)
time = declare(int)frequency = declare(int)
assign(amplitude, 1)assign(frequency, 1e6)
with for_(time, 0, time<100, time+1):
play('pulse_1' * amp(amplitude*Math.cos2pi(frequency*time)), 'element_1')
This program will play pulse_1 for 100 iterations, where for each iteration the amplitude will be modulated by the envelope function cos(2pi*frequency*time). For evaluation of non-real time mathematical expressions one can always use standard python libraries such as numpy.
Array reduction¶
QUA provides several function to reduce arrays. These functions run much more efficiently (with less latency) when compared to a manual implementing in QUA as they use hardware optimizations.
Math.sum(x)
: sums an array. result must of the same type as arrayMath.max(x)
,Math.min(x)
: max,min an array. result must of the same type as arrayMath.argmin(x)
/Math.argmax(x)
: returns the index of the max/minMath.dot(x,y)
: returns the dot product of two QUA arrays of the same size
Others¶
Math.abs(x)
: absolute value of a QUA variable
Random()¶
This class generates a pseudo-random number using a the LCG algorithm with the following parameters: a = 137939405, c = 12345, m = 2**28.
The class constructor optionally takes a seed number and can generate int or fixed values.
with program() as prog:
r = Random()
a = declare(int)
b = declare(fixed)
assign(a, r.rand_int(100)) # a will be a number between 0 and 99
assign(b, r.rand_fixed()) # b will be a number between 0.0 and 1.0
# you can set the seed:
r.set_seed(123213)
Cast¶
Cast.mul_fixed_by_int(x,y)
: Multiplies a fixed x by an int y, returning a fixedCast.mul_int_by_fixed(x,y)
: Multiplies an int x by a fixed y, returning an intCast.to_int(x)
: Casts a variable to int. Supports int, fixed or boolCast.to_fixed(x)
: Casts a variable to fixed. Supports int, fixed or boolCast.to_bool(x)
: Casts a variable to bool. Supports int, fixed or bool
Util¶
Util.cond(a,b,c)
: Quick conditional operation.
This is equivalent to a ternary operator available in some languages: i.e. a ? b : c, meaning ‘b’ if ‘a’ is true, or ‘c’ if ‘a’ is false. There is less computation overhead (less latency) when running this operation relative to the if_ conditional
assign(b, Util.cond(a > b, c, d))