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 Tensors

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 DTypes 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 Tensors

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 Tensors with broadcastable shapes or even with Dart nums.

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]

Tensors 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.

Notes on shape broadcasting

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 DTypes, 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.