`FunctionOperators.FunctionOperators_global_settings`

— ConstantObject that holds global settings for `FunctionOperators`

library

Fields:

`verbose::Bool`

If set to true, then allocation information and calculated plan function will be displayed upon creation (i.e., when a composite operator is first used). Default:`false`

`macro_verbose::Bool`

If set to true, then recycling macros (@♻ and @recycle) will print the transformed code. Default:`false`

`auto_reshape::Bool`

If set to true, then input and output is reshaped according to the inDims and outDims values of the FunctionOperator before and after any multiplication. Default:`false`

`FunctionOperators.FunOp`

— TypeSupertype for FunctionOperator and FunctionOperatorComposite

`FunctionOperators.FunctionOperator`

— TypeFunctionOperator is an operator that maps from a multidimensional space to another multidimensional space. The mapping is defined by a function (`forw`

), and optionally the reverse mapping can also be defined (`backw`

). The input the mapping must be subtype of AbstractArray.

The following constructors are available:

- Positional constructor #1:
`FunctionOperator{eltype}(forw, inDims, outDims)`

- Positional constructor #2:
`FunctionOperator{eltype}(forw, backw, inDims, outDims)`

- Positional constructor #3:
`FunctionOperator{eltype}(name, forw, inDims, outDims)`

- Positional constructor #4:
`FunctionOperator{eltype}(name, forw, backw, inDims, outDims)`

- Keyword constructor:
`FunctionOperator{eltype}(;kwargs...)`

where `eltype`

is the type enforced on elements of input array.

Arguments

`name::String`

(Optional but strongly recommended) The operator is referenced later in error messages by this string.**Warning!**It is also used to check equality of (composite) FunctionOperators. Default value:`OpX`

where X is a number incremented in each constructor-call.`forw::Function`

Function defining the mapping. Must accept one or two arguments. In case of two arguments, the first argument is a preallocated buffer to write the result into (to speed up code by avoiding repeated allocations). In case of both one and two arguments, the return value must be the result of the mapping.`backw::Function`

(Optional) Same as backw, but defines the backward mapping`inDims::Tuple{Vararg{Int}}`

Size of input array`outDims::Tuple{Vararg{Int}}`

Size of output array

`FunctionOperators.setPlan`

— FunctionManually set the plan of a FunctionOperator Arguments:

`FO`

composite FunctionOperator to be changed`f`

manually defined plan (function with two arguments, first is the output buffer, second is the input array)`f_str`

(Optional) string representation of the plan

`FunctionOperators.@recycle`

— MacroSpeed up iteratively executed code fragments with many matrix operations by transforming code in such a way that preserves arrays allocated for intermediate results, and re-use them for subsequent iterations.

First variant:

`@recycle <code to be optimized>`

Second variant:

`@recycle(arrays = [<list of array variables>], funops = [<list of funop variables], numbers = [<list of number variables>], <code to be optimized>)`

The **first variant** is the more convenient one that tries to guess the type of variables (the other variant requires its user to declare explicitly the list of variables which are type of Array, FunOp, and Number. As a tradeoff, this variant fails when the optimized code contains either a closure or non-const global variable.

The **second variant** is the more flexible (and also more verbose) one one that requires its user to declare explicitly the list of variables which are type of Array, FunOp, and Number. The other variant tries to guess the type of variables, thus it is more convenient, but as a tradeoff, that variant fails when the optimized code contains either a closure or non-const global variable. On the other hand, this (more verbose) variant is free from these limitations. *Note: All of the "keyword arguments" are optional, and also their order is arbitrary.*

An example to first variant: This function

```
function foo()
A = rand(100,100)
B = rand(100,100)
@recycle for i = 1:5
A += A / 2 + B
C = A * B + 5
end
end
```

is turned into the following:

```
function foo()
A = rand(100,100)
B = rand(100,100)
(C, 🔃₂) = fill(nothing, 2)
(is_first_run₁,) = fill(true, 1)
for i = 1:5
A .+= A ./ 2 .+ B
if is_first_run₁
is_first_run₁ = false
🔃₂ = A * B
C = 🔃₂ .+ 5
else
mul!(🔃₂, A, B)
C .= 🔃₂ .+ 5
end
end
end
```

Another example showing what second variant can do (and the first can't):

```
bar = @recycle(arrays=[A,B], (A, B) -> begin
A += A / 2 + B
B = A * B .+ 5
end)
function baz()
A = rand(100,100)
B = rand(100,100)
for i = 1:5
bar(A,B)
end
end
```

is turned into the following:

```
bar = begin
(🔃₁,) = fill(nothing, 1)
(is_first_run₁,) = fill(true, 1)
(A, B)->begin
A .+= A ./ 2 .+ B
begin
if is_first_run₁
is_first_run₁ = false
🔃₁ = A * B
else
mul!(🔃₁, A, B)
end
B .= 🔃₁ .+ 5
end
end
end
function baz()
A = rand(100,100)
B = rand(100,100)
for i = 1:5
bar(A,B)
end
end
```

`FunctionOperators.@♻`

— Macro**Recycling macro**: Reduce the number of allocations inside a for loop by preallocation of arrays for the outputs of marked operations. Markers: `@♻`

(`\:recycle:`

), `🔝`

(`\:top:`

), `🔃`

(`\:arrows_clockwise:`

), and `@🔃`

Macro @♻ should be placed right before a for loop, and then it executes the following substitutions:

**Expressions marked by**`🔝`

:

They are going to be calculated before the loop, the result is stored in a variable, and the expression will be replaced by that variable. It also can be useful when a constant expression is used in the loop, but the idea behind creating that substitution is to allow caching of composite FunctionMatrices. Eg:

```
@♻ for i=1:5
result = 🔝((FuncOp₁ + 2I) * FuncOp₂) * data
end
```

will be transformed to

```
🔝_1 = (FuncOp₁ + 2I) * FuncOp₂
for i = 1:5
result = 🔝_1 * data
end
```

so that way plan is calculated only once, and also buffers for intermediate results of the composite operator are allocated once.

**Expressions marked by**`🔃`

:

They are going to be calculated before the loop (to allocate an array to store the result), but the expression is also evaluated in each loop iteration. The difference after the substitution is that the result of the expression is always saved to the preallocated array. Eg:

```
@♻ for i=1:5
result = FuncOp₁ * 🔃(A + B)
end
```

will be transformed to

```
🔃_1 = A + B
for i = 1:5
result = FuncOp₁ * @.(🔃_1 = A + B)
end
```

This transformation first allocates an array named `🔃_1`

, and then in every iteration it is recalculated, saved to `🔃_1`

, and the this value is used for the rest of the operation (i.e.: `FuncOp₁ * 🔃_1`

. Note that `@.`

macro is inserted before the inline assignment. This is needed otherwise `A + B`

would allocate a new array before it is stored in `🔃_1`

. **Warning!** It can break your code, e.g. `@.(🔃_1 = A * B) ≠ (🔃_1 = A .* B)`

{matrix multiplication vs. elementwise multiplication}! On the other hand, when the marked expression consists only a multiplication, then it is transformed into a call of `mul!`

. Eg:

```
@♻ for i=1:5
result = FuncOp₁ * 🔃(A * B)
end
```

will be transformed to

```
🔃_1 = A * B
for i = 1:5
result = FuncOp₁ * mul!(🔃_1, A, B)
end
```

**Lastly, assignments marked by**`@🔃`

:

They will be transformed into a call of `mul!`

. Of course, it works only if `@🔃`

is directly followed by an assignment that has a single multiplication on the right side. Eg:

```
@♻ for i=1:5
@🔃 result = FuncOp₁ * A
end
```

will be transformed to

```
result = FuncOp₁ * A
for i = 1:5
mul!(result, FuncOp₁, A)
end
```

Final note: `🔝`

can be arbitrarily nested, and it can be embedded in expressions marked by `🔃`

. `🔃`

can also be nested, and it can be used in assigments marked by `@🔃`

(along with `🔝`

, of course).

`Base.:==`

— MethodEquality check based on name

**HEADS UP!** This works only when operators with the same name has also the same functionality!

It performs basic arithmetic transformations on the expressions, so it recognizes even some less obvious equalities. The rules it uses:

- Associativity: $op1 * op2 * op3 = (op1 * op2) * op3 = op1 * (op2 * op3)$, $op1 + op2 + op3 = (op1 + op2) + op3 = op1 + (op2 + op3)$, $op1 + (op2 - op3) == (op1 + op2) - op3$, $op1 - (op2 + op3) == (op1 - op2) - op3$
- Commutativity: $op1 - op2 - op3 = op1 - op3 - op2$, $op1 + op2 = op2 + op1$
- Distributivity: $(op1 + op2) * op3 = op1 * op3 + op2 * op3$ (Note that $op1 * (op2 + op3) ≠ op1 * op2 + op1 * op3$)

`Base.eltype`

— MethodDetermine type of elements of array accepted by this operator