Skip to main content

Basic functions

This page uses Wasm code blocks so you can run the examples directly in the browser.

There is a large number of functions stored in SA Engine's in-memory object-oriented database. For example, the following function call returns the version of SA Engine you are running.

system_version()
Exercise

Run the function by clicking the play button play_circle

Documentation of functions and other SA Engine artifacts are stored in the database as well. You can query the database about the documentation of a function named myfunction by evaluating doc("myfunction"). The doc() function returns the documentation of the function as a string.

Example:

doc("sin")

You can edit the examples in the tutorial. Try changing sqrt to sin!

Note

You can enter several OSQL statements by separating them with a ;.

Examples:

doc("heartbeat");

doc("euclid")

If you don't know exactly the name of a function you can also pass a string pattern to doc() to return all functions matching the string pattern in their names. In a string pattern a * matches any sequence of characters while a ? matches a single character.

Example:

doc("atan*");

The function sourcecode("fn") where fn can be a function name or a string pattern returns the definitions of the matching functions.

Example:

sourcecode("tan");
sourcecode("atan*")

The signature of a function is a string denoting its arguments and results. You can get the signature of function or string pattern fn by calling the system function signature(fn), for example:

signature('tan')

Functions may have different definitions depending on the types of their arguments. Such functions are said to be overloaded on their types. Overloaded function have several signatures, for example:

signature('range')

Some functions have corresponding infix operators. For example, the function plus has the infix operator +, so both these calls return the same result:

1+2;
plus(1,2)

You can call signature(op) to get the functions corresponding to the infix operator op, for example:

signature('/');
signature('.*')

Visit Documentation functions for a list of functions used for documenting the system.

Math functions​

There is a large library of basic mathematical functions built into the system and the usual arithmetic operators are available.

Example:

sin(3.14/2)*cos(3.14)

To learn more about basic math functions go to Math in the OSQL reference documentation.

Session variables​

During sessions with SA Engine it is often practical to temporarily save values of expressions in session variables prefixed with : by using the set statement, e.g.:

set :pi = asin(1)*2;

sin(:pi/2)*cos(:pi)

The session variable :pi is first assigned the value of asin(1)*2.

Note

Session variables disappear when you exit SA Engine. To permanently save values in the built-in database you must use stored functions, as explained next.

Stored functions​

Analysis models are defined in terms of a number of OSQL functions. OSQL functions are created and stored in the database by evaluating a create function statement. There are functions of different kinds, depending on how they are used.

Stored functions are functions that hold tabulated function values permanently in the built-in database. They are created by a create function ... as stored statement.

Example:

create function const(Charstring c) -> Real
as stored

The above statement creates a tabulated function called const taking a string c as argument and returning a real number as result.

You are recommended to document your functions by a master comment preceding the as.

Example:

create function const(Charstring c) -> Real
/* The value of the math constant named `c` */
as stored;

doc("const")

Stored functions can be populated using the set statement.

Example:

set const("pi") = 3.14;

set const("e") = 2.71;

const("pi");

ln(const("e"));

sin(const("pi")/2)*cos(const("pi"));

You can also update stored functions using set, for example:

set const("pi") = asin(1)*2;

set const("e") = exp(1);

ln(const("e"));

sin(const("pi")/2)*cos(const("pi"))

Functions may have no arguments at all.

Example:

create function mypi() -> Real
/* The constant PI */
as stored;

set mypi() = asin(1)*2;

mypi()

Derived functions​

A derived function is a function defined by an OSQL expression.

Example: The following function converts Celsius degrees to Fahrenheit:

create function ctof(Real c) -> Real
/* Convert a Celsius degree `c` to Fahrenheit */
as c * 9 / 5 + 32;

ctof(10)

The head of the function, e.g ctof(Real c) -> Real, specifies the function's signature.

Temporal functions​

The function local_time() returns the current wall time as an ISO time stamp.

While local_time returns the current wall time as a string, the function now() returns the wall time as an object of type Timeval, which is the internal representation of time points.

Examples:

local_time();

now()

The function ts(Object o)->Timeval returns a time stamped object, which represents a timestamped value o.

Examples:

ts(1);

value(ts(1));

local_time(ts(1))

For more about functions over time visit Time in the reference documentation.

Vectors​

So far the function calls were made over single values and sets of single values. Building mathematical models, e.g. to identify interesting properties of streams, also require vectors with the usual [...] notation.

Example:

[1,4,3,2]+[1,5,6,7]
Exercise

Change visualization of the vector to Bar plot by adding //plot: Bar plot before the query.

The mathematical operators +,-,*,/,^ can be used for whole vectors or mixes of vectors and scalar values.

Example:

[1,4,3,2]-[0,2,3,4]+1

How would it look as text?

A . in front of an operator indicates element-wise application of it on each element of the vectors. For example, for multiplication the * operator over vectors returns the scalar product while .* returns element-wise multiplication.

Examples:

[1, 2, 3, 4] * [5, 6, 7, 8];

[1, 2, 3, 4] .* [5, 6, 7, 8]

More examples:

"Addition";
[1, 2, 3, 4] + [5, 6, 7, 8];

"Scalar times vector";
5 * [1, 2, 3, 4];

"Element-wise power of two";
[1, 2, 3, 4] .^ 2

There are many built-in vector functions, e.g.:

"sum";
sum([1, 2, 3, 4]);

"mean";
mean([1, 2, 3, 4]);

"standard deviation";
stdev([1, 2, 3, 4]);

"median";
median([1, 4, 2, 3]);

"max";
max([1, 4, 2, 3]);

"dimension";
dim([1, 3, 2])

Often vectors are used in derived function definitions. For example, the following function computes the Euclidean distance between vector v and w:

create function dist(Vector v, Vector w) -> Real
/* Compute the Euclidean distance between `v` and `w` */
as sqrt(sum((v - w) .^ 2));

dist([1,2],[3,4])

The built-in function euclid(v,w) does the same.

The general Minkowski distance computation function minkowski(v,w,r) is also built-in. This example shows Manhattan distance (L1L_1), Euclidean (L2L_2) and Chebyshev (LinftyL_{infty}) distance expressed in terms of minkowski(v,w,r).

Examples:

minkowski([1,2],[3,4], 1);

minkowski([1,2],[3,4], 2);

minkowski([1,2],[3,4], +inf);

Arrays​

An array or a tensor is a multi-dimensional cube of numbers. It is represented by type Array. Arrays can be created with the function array(Vector data)->Array, where the (nested) vector data contains its elements.

For example:

array([1,2,3]);
array([[1,2],[3,4]])

As for vectors, you can apply a number of arithmtic oparators and functions on arrays, for examle:

array([[1,2],[3,4]]) + array([[2,3],[6,7]]);

array([[1,2],[3,4]]) .* array([[2,3],[6,7]])

The elements of an array have a format, which specifies their binary representations in memory. For example, elements of type Integer are represented as 64-bits integers having the format I64 and type Real has the format F64. The first argument of the function array(Charstring fmt,Vector data)->Array specifies the elements' format.

For example:

array("f64",[1,2,3,4])

When called with one argument, array(Vector data)->Array will return an array having the format of the most general type in data, where real numbers are considered more general than integers,

For example:

array([1,2,3,4]);
array([1.0,2,3,4])

An array also has a shape being the number of elements in each of its dimensions, computed by the function shape(Array a)->Vector of Integer.

For example:

shape(array([[1,2,3],[4,5,6]]))

The function dot(Array u, Array v)->Number computes the scalar product of the elements in the arrays u and v.

For example:

dot(array([1,2,3]),array([4,5,6]));

The function matmul(Array a, Array b)->Array computes the array multiplication of a and b.

For example:

matmul(array([[1,2,3]]),array([[1,2],[3,4],[5,6]]));

Visit Arrays and tensors for a detailed description of arrays.

The Fast Fourier Transform​

The function rfft(a) computes the real number Fast Fourier Transform (FFT) over a one-dimensional array of real numbers.

Example:

rfft(array([1.0, 2, 3, 4]))

You can also apply rfft() on vectors of numbers, even though this is much slower. For example:

rfft([1.0, 2, 3, 4])

The inverse real FFT is computed with irfft(a).

Example:

irfft(rfft(array([1.0, 2, 3, 4])));

The later tutorial section Querying the microphone shows how user-defined functions over vectors and FFT can be used for filtering audio streams in real-time.

Vectors of objects​

So far the vectors contained only numbers, as required by math functions and operators. Unlike arrays, vectors can contain any kind of objects including strings and numbers. For example, the following vector contains both a number and a string:

Example:

["Tore", 1234]

In general vectors of objects are used for representing ordered finite sequences of objects. By contrast, a stream can be seen as a possibly infinite growing sequence of objects. Vectors can be used to group elements in streams, as will be explained later.

Go to Vectors in the reference documentation for documentation of vectors.

Sets and bags​

Functions can also produce sets of objects where the order is not guaranteed as with vectors. For example, the function range(n) returns the set of numbers from 1 up to n.

Example:

range(10)

Here range(10) produces the set of all integers from 1 to 10.

Example:

sin(range(200)*0.2)

The function call range(200)returns a bag of numbers. Here the sine function is applied one each element in the set of all integers from 1 to 200.

Note

Applying other functions, such as sin(x) on a set means that the function is applied on each element of the set.

tip

Try visualizing the result as a line plot by inserting the row //plot: Line plot before the query!

In general sets in OSQL may contain duplicates.

Example:

floor(range(200)/2)

The term bag is used rather than set to indicate sets with possible duplicates.

Exercise

Apply sin(x) on the result of floor!

Explicit bags can be create as using the bag(e1,e2,...,en)bag(e_1,e_2,...,e_n) notation, for example:

set :mybag = bag(1,2,2,3);
select i from Integer i where i in :mybag

As for vectors, aggregate functions over bags are applied on entire bags, rather than on each element of the bag as, for example range(n) returns a bag of number and sum(Bag of Number)->Number sums the elements of a bag of number.

Example:

sum(range(200)/2)

See Aggregate functions in the reference documentation for more on aggregate functions.

Boolean and null values​

The constant null is used to represent unknown or missing values. For example, the last element of the vector [1,2,null] is unknown.

A Boolean value null value is regarded as false. Thus, the query 2<1 returns null, while 2>1 return true. The only object of type Boolean is true. The constant false is null casted as a Boolean.

null values in bags are regarded as missing values that are ignored by aggregate functions.

Passing null as argument in a function call will return null without calling the function. For example:

sin(sqrt(-1));

An OSQL query variable bound to the value null is regarded as unbound and therefore null cannot be regraded as a regular value. Instead, to test that an expression x is null, use the notany(x) function that returns true if x is null.

Often it is practical to use a default value instead of null. For this the coalsece(x1,x2,...,dlft)coalsece(x_1,x_2,...,dlft) expression can be used that returns the first xix_i not being null and dlft if all

For example:

coalesce(sqrt(-1),ln(-1),1)+1

Tests in case statements succeed unless they return null. For example, the following query returns one:

case when sqrt(-1) then 1 else 0 end

To replace all nulls in a vector use whennull(Vector v, Object dflt)->Vector.

For example:

whennull([1,2,null,3],0)+2

The next part of the tutorial will show how to make general queries that selects collections of objects from the database.