A package for creation and manipulation with multidimensional data arrays in the form of a `Tensor` class.

The package is somehow a Dart implementation of Tensor API from TensorFlow (or/and NumPy library). And was initially created to power the loredart_nn package, which is also inspired by the TF (and currently is under rebuilding).

## The `Tensor`s

Each `Tensor` is described by two main properties:

• shape: `TensorShape` instance
• single data type: `DType` instance

The `DType` of the `Tensor` determines the type of the values and how `loredart` stores them. For now, the only supported `DType`s are the numerical ones (`float32`, `int64` etc.).

There are many ways to create a Tensor instance, the basic ones - named factories of class:

``````final x = Tensor.constant([2,0,2,3], shape: [2,2]);
print(x);
// <Tensor(shape: [2, 2], values:
// [[2, 0]
// [2, 3]], dType: int32)>

final y = Tensor.fill([3,3], 0.3, dType: DType.float64);
print(y);
// <Tensor(shape: [3, 3], values:
//  [[0.3, 0.3, 0.3]
//  [0.3, 0.3, 0.3]
//  [0.3, 0.3, 0.3]], dType: float64)>
``````

There is no explicit limitation on the Tensors rank (length of the shape):

``````final x = Tensor.ones([1,1,2,3,5,8,13,21,34], dType: DType.float64);

print(x.shape); // [1,1,2,3,5,8,13,21,34]

print(x.shape[-3]); // 13

// Number of indices to get a single element
print(x.rank); // 9

// Number of elements in the tensor
print(x.shape.size); // 2227680
``````

Also, there are some particular ways to create tensor, like random generation:

``````final x = uniform([10, 10], min: -3, max: 0);
print(x.dType); // DType.float32
print(x.shape); // [10, 10]
``````

And many others, e.g., `oneHotTensor`, `Tensor.fill`, `Tensor.zeros`, etc.

Like TensorFlow tensors, `loredart` ones are immutable (with final fields), and any operation on `Tensor`(s) will produce a new instance.

## Operations on `Tensor`s

There are many implemented and documented operations on Tensors, including math and linear algebra funcs, shape transformations, casting, reducing operations, etc.

The operation requires Tensors of the same DType, and most of them - require equal shapes; however, there are some exceptions.

### Arithmetic and comparison ops

Arithmetic and comparison operations support broadcasting, which means they can operate on the `Tensor`s with broadcastable shapes or even with Dart `num`s.

``````final x = Tensor.ones([3,4,5]); // with DType.float32

final y = Tensor.ones([3,1,5]);
print((x+y).shape); // [3, 4, 5]

final v = Tensor.ones([4,5]);
print((x*y).shape); // [3, 4, 5]

final k = Tensor.ones([1,1,1,1,1]);
print((x/k).shape); // [1, 1, 3, 4, 5]

print(less(x, 3.5).shape); // [3, 4, 5]
``````

### Math and statistics

`loredart` supports most of the math and statistical functions:

``````final x = Tensor.constant(<double>[0,1,2,3,4,5], shape: [2, 3]);
var t = exp(x); // element-wise exp
print(t.shape); // same as x.shape

var m = mean(x); // the mean of the whole tensor
print(m.shape); // [1]

final y = Tensor.diag([3, 4], numCols: 3); // shape is [2, 3]
pow(x, y); // element-wise pow
``````

, but more interesting are the reducing versions of them:

``````final x = Tensor.fill([3,4,5,6], 0.5);

var s = reduceMax(x, axis: [0, -1]);
print(s.shape); // [4, 5]

var m = reduceMean(x, axis: [1,2], keepDims: true);
print(m.shape); // [3, 1, 1, 6]
``````

### Linear algebra

The set of LA operations is not that big, `loredart` supports batched `matmul` and `matrixTransposition`.

``````final a = Tensor.fill([2, 5, 3, 4], 0.1);
final b = Tensor.eye(4, numCols: 7, batchShape: [2, 5]);
print(b.shape); // [2, 5, 4, 7]

final c = matmul(a, b);
print(c.shape); // [2, 5, 3, 7]

final cT = matrixTranspose(c);
print(cT.shape); // [2, 5, 7, 3]
``````

### Other ops

Reshaping operations includes `reshape`, `expandDims`, and `squeeze`:

``````var x = normal([10, 10]); // shape: [10, 10]
x = expandDims(x, -1);
print(x.shape); // new shape: [10, 10, 1]
``````

Also casting with `cast`, concatenation with `concat`, and slicing with `slice`. Slicing is the only available way to extract sub-tensors:

``````var x = Tensor.zeros([5, 8, 13]);

var v = slice(x, [3, 0, 0], [4, 8, 13]);
print(v.shape); // [1, 8, 13]

var y = slice(x, [0, 4, 0], [5, 6, 0]);
print(y.shape); // [5, 2]
``````

## `Tensor`s serialization

For some use cases, there are two ways to de- and serialize the `Tensor`: with either json or bytes (binary file). See `tensorToJson`, `tensorFromJson` and, `tensorToBytes`, `tensorFromBytes`.

Broadcasting of shapes for the arithmetic and comparison ops is really convenient (for instance, for adding bias in neural network layers). `loredart` consider shapes as broadcastable if at least one of the following criteria is met:

• shapes are equal;
• shapes are compatible (if they are of the same rank, and corresponding dims are either equal or one of them is 1);
• shapes have equal last \$k\$ dims

See `TensorShape` methods for examples.

## Notes on limitations and future work

There are some limitations, but the most obvious is element extraction (because there is still no need for it), which might appear in the future, even though it won't be as easy to use as in TF.

Also, the next steps are to add support for other `DType`s, and extend the set of LA methods (permutations, matvec, etc.)

If you would like to support UKRAINE with a donation, visit UNITED24 for more info. Thanks.

## Libraries

loredart_tensor
A Dart-pure package for manipulation with tensors (multidimensional arrays of data) inspired by the TensorFlow API.