Basic Instructions
At the core of our framework lies the Algorithm
class, which is a torch.nn.Module
.
To implement an algorithm, follow the following structure:
algo = Algorithm(
Input('x'),
# ... more input arguments ...
Var('y', torch.tensor(0.)),
# ... more variables ...
Let('y', 'x'), # corresponds to y = x
# ... more instructions ...
Output('y'),
# ... more return values ...
# optionally define hyperparameters like beta
)
and to execute simply run algo(x)
where x
is a respective PyTorch tensor of shape [B, ]
where B
is the batch size.
In the following, we go into detail for each of the module types.
Input
and Output
Input
and Output
specify which variables are the inputs / outputs of the algorithm.
All returned variables (as specified via Output
), have to be either input variables or declared via Variable
.
Var
and Variable
Variable
declares and initialized a variable. Var
is an alias for Variable
.
Note that variables that are inputs do not have to be / cannot additionally be initialized by Var
.
Also, note that if it is initialized with a tensor, a batch dimension will be added and the tensor will be repeated during execution of the algorithm.
For example, if the shape was [4, ]
, it will be changed to [B, 4, ]
where B
is the batch size.
λ Lambda Expressions
Key to defining an algorithm are lambda
expressions (see here for a reference).
They allow defining anonymous functions and therefore allow expressing computations in-place.
In most cases in algovision
, it is possible to write a value in terms of a lambda expressions.
The name of the used variable will be inferred from the signature of the expression.
For example, lambda x: x**2
will take the variable named x
and return the square of it at the location where the expression is written.
Let('z', lambda x, y: x**2 + y)
corresponds to the regular line of code z = x**2 + y
.
This also allows inserting complex external functions including neural networks as part of the lambda expression.
Assuming net
is a neural networks, one can write Let('y', lambda x: net(x))
(corresponding to y = net(x)
).
Let
Let
is one of the core instructions as it sets the variable given as the left argument to the value of the right expression.
Let
is a very flexible instruction.
In its most simple form Let
obtains two arguments, a string naming the variable where the result is written, and the value that may be expressed via a lambda
expression.
If the lambda expression returns multiple values, e.g., because a complex function is called and has two return values, the left argument can be a list of strings.
That is, Let(['a', 'b'], lamba x, y: (x+y, x-y))
corresponds to a, b = x+y, x-y
.
Let
also supports indexing. This is denoted by an additional list argument after the left and/or the right argument.
For example, Let('a', 'array', ['i'])
corresponds to a = array[i]
, while Let('array', ['i'], 'b')
corresponds to array[i] = b
.
Let('array', ['i'], 'array', ['j'])
corresponding to array[i] = array[j]
is also supported.
Note that indexing can also be expressed through lambda
expressions.
For example, Let('a', 'array', ['i'])
is equivalent to Let('a', lambda array, i: array[:, i])
. Note how in this case the batch dimension has to be explicitly taken into account ([:, ]
).
Relaxed indexing on the right-hand side is only supported through lambda
expressions due to its complexity.
Relaxed indexing on the left-hand side is supported if exactly one probability weight tensor is in the list (e.g., Let('array', [lambda x: get_weights(x)], 'a')
).
AlgoVision |
Python |
Description |
---|---|---|
|
|
Variable |
|
|
As soon as we compute anything on the right hand side of the equation, we need to write it as a |
|
|
Indexing on the right hand requires an additional list parameter after the second argument. |
|
|
Equivalent to the row above: indexing can also be manually done inside of a |
|
|
Multiple indices and lambda expressions are also supported. |
|
|
|
|
|
Indexing can also be done on the left hand side of the equation. |
|
|
…or on both sides. |
|
|
Multiple return values are supported. |