Industrial Data Science
in C# and .NET:
Simple. Fast. Reliable.
 
 

ILNumerics - Technical Computing

Modern High Performance Tools for Technical

Computing and Visualization in Industry and Science

tgt

ILNumerics® Function Rules

Three simple function rules establish an efficient memory management and great performance for your algorithms: 

At the end of this article, an example of a computational function in ILNumerics is presented, with some best practices for writing ILNumerics functions.

1. Array Types for Function Declarations

ILNumerics memory management is based on strongly typed array object classes. Each array object represents the element type, the purpose, the lifetime and mutability of the data. At runtime implicit type conversions manage the array's lifetime.

Three fundamental array objects serve as the starting point in ILNumerics algorithms. Within your algorithms you will use them as your array variable types: 

  • Array<T> is a general purpose, n-dimensional array, mostly used for numeric element types. Example: Array<T> A
  • Logical is a n-dimensional, Boolean array, which is often used in the context of relational, binary operators. For example: Logical L = A < B
  • Cell is a flexible, n-dimensional container array for holding array objects or cells. Example: Cell = cellv(A, B, A < B).

Within a function body all array variables must be of one of these types: ILNumerics.Array<T>, ILNumerics.Cell and ILNumerics.Logical.

Make sure to explicitly specify the type of all array variables! Don't use 'var' with array variables in C#!

Each fundamental array type comes in three flavors. However, they are only used in a function signature:

  • "Input arrays" are immutable and used as input parameter of a function: InArray<T>, InLogical and InCell.
  • "Output arrays" serve as output parameter of a function. They require a local array in the calling scope. Output arrays allow to modify the local array instance in the caller's scope: OutArray<T>, OutLogical and OutCell.
  • "Return arrays" are always and only used as return type of a function: RetArray<T>, RetLogical and RetCell.

All array types are easily distinguishable by name. The general naming scheme is as follows:

[|In|Out|Ret][Array<T>|Cell|Logical]

Let's assume, a function receives two matrices (i.e.: a 2-dimensional array) as input parameters and returns an array of double precision floating point elements. The function signature looks as follows:

The following table lists all array types existing in ILNumerics and describes their lifetimes:

 
Content Local Array Input Parameter Output Parameter Return Value
Dense Array Array<T> InArray<T> OutArray<T> RetArray<T>
Cell Array Cell InCell OutCell RetCell
Logical Array Logical InLogical OutLogical RetLogical
Lifetimes
Used in function .. body signature signature signature
Disposed automatically ... when current scope is left when function scope is left when target array is freed after the first use
Functionality
Im-/mutability mutable immutable mutable immutable
Assignments to variables of that type: A = ... inA = ...  outA.a = ... --

All array types share the same set of functionality. They provide the same set of member functions for read access and can be used interchangeably. ILNumerics transparently converts between compatible types automatically.

Input arrays and return arrays are immutable. This means, they lack of any member function or setter property which could be used to alter the content of the array. Also, obviously, these types cannot be used in situations where a mutable array is expected. The compiler will produce an error at compile time when trying to use an array in a wrong context.

It may look confusing at first to have 12 array classes to choose from. However, within your algorithms you will only create variables of one of the local array types: Array<T> , Cell and Logical. It is only in the function signature, where the other types come into play. A simple way to deal with them is to start writing your function using all local array types. In a second step you can go ahead and refine the array types used for input arrays (adding the "In..." prefix), for output arrays (adding the "Out...") prefix and for the return array (adding the "Ret" prefix). See also the function example at the end of this article.

2. using() blocks as artificial scopes

Function bodies of algorithm functions must be enclosed with an artificial scope, assembled out of a using( ) { ... block:

In Visual Basic, a corresponding Using ... End Using block is used.

The ILNumerics.Scope.Enter() function creates an artificial scope and provides the following guarantees:

  • All arrays created inside the scope are released immediately when leaving the block: memory for A and B is released immediately and pooled for subsequent reuse.
  • If an array style is established with entering a new scope (optional) the old array style is robustly restored.

Scope.Enter(...,style) accepts an additional, optional parameter determining the array style which is to be used within the scope block. The scoping mechanism ensures the array style value for the code inside the using () { ... } block. When the block is exited the former array style is recovered. Note, that the array style setting affects the current thread only.

Note: in an earlier release of ILNumerics it was obligatory to provide all input parameters to the Scope.Enter() function. With version 7.0 this requirement is removed. If legacy code still provides such parameters they are now ignored by the Scope.Enter() function.

3. Output parameters

Output parameters are used whenever a function must return more than one array. In ILNumerics, output parameters act as a reference or proxy to another (local) array in the calling scope. If a function receives a valid object reference for an output parameter (i.e. an empty array or anything but null), this signals the function to actually perform all computations required to provide the parameter. Otherwise, when an output parameter is null, the function can choose another more efficient way of computation without calculating the corresponding results.

Modifications and assignments to output parameters immediately alter the underlying source array in the callers scope. In order to make this work, output parameters can be modified in one of the following ways only. Consider an output variable named 'frequencies' from the example below:

  • frequencies[0,full] = ... - use the setter properties to alter parts of the array. All subarray access rules are permitted. Alternatively, the SetRange() method may be used if the language used does not support such properties.
  • frequencies.a = ... - reassign a new array to the output parameter. It is important to use the .a setter property. (Again: the compiler is your friend.) For languages not supporting such properties (f.e. Visual Basic .NET) the Assign() method is used instead.

By using output parameters in ILNumerics, neither the out nor the ref keyword (C#) are allowed. By convention, providing null as value for an output parameter instructs the function to skip computations for this parameter, if possible. Hence, it is not necessary to declare dummy variables if a certain output parameter is not required.

Note, that ILNumerics functions may declare any number of output parameters (OutArray<T>, OutLogical or OutCell) in their function signature. They all belong to the parameter list. In difference to that, the return type must always be declared as one of the special return array types: RetArray<T>, RetLogical or RetCell.

Example of a computational function in ILNumerics®

Note that only local array types are declared in the body - all other types are placed inside the function signature (only there, but nowhere else!) Also note that the parameter list may just as well contain any other parameter useful for the function, next to ILNumerics input and/or output parameters. For example, we could extend the list by, let's say: another double a1 parameter and use this inside the function normally. Also, ILNumerics does not impose any order of parameters nor need the ILNumerics parameters be grouped together. The following function signature is perfectly fine:

 

Best Practices for Writing ILNumerics® Functions

In contrast to the obligatory rules, the following hints are optional. The user may decide to use them in order to create more readable and convenient code.

  • All parameters - where applicable - should be defined as optional parameters (see: C# language reference) at the end of the parameter list. Default value for all array parameters should be null. This allows the user to pick only those output parameters, which are really required in long parameter lists (using named arguments).

 

  • Input parameters are immutable - their content cannot change. If your algorithm requires an input parameter to be changed, copy it into a local variable and change this instead: