Tensor<T> constructor

Tensor<T>(
  1. dynamic initialValue, {
  2. Node? creator,
})

Creates a Tensor object storing and managing the value initialValue given. Supported types for Tensor initialisation are: Scalar,Vector,Matrix and Tensor3D. Short versions of those types are Sc,Vec,Mat and T3D. Tensors can be created the following ways:

Tensor<Scalar>scalar=Tensor<Scalar>(3.0);
Tensor<Vector>vector=Tensor<Vector>([1.0,2.0,3.0]);
Tensor<Matrix>matrix=Tensor<Matrix>([[1.0,2.0],[3.0,4.0],);

Additionally a Node creator can be given. This parameter will be used for calculation of connected input Tensors gradient values. If no creator is given this Tensor can only be used as an input inside of a computational structure.

Connecting Tensors is one of the main focus points of this engine and can be done like the following example:

Tensor<Scalar> add(Tensor<Scalar> a, Tensor<Scalar> b) {
  Tensor<Scalar> out = Tensor<Scalar>(a.data[0] + b.data[0]);

  out.creator = Node(
    [a, b],
        () {
      a.grad[0] = a.grad[0] + out.grad[0];
      b.grad[0] = b.grad[0] + out.grad[0];
    },
    opName: 'add',
    cost: 1,
  );
  return out;
}

The raw data can be accessed via .data. To return a structured value .value converts it via shape into T. The raw grad can be accessed via .grad. To return a structured value .gradValue converts it via shape into T.

A Tensor carries multiple variables:

id : A String id to allow easy identification. Can be set manually for variable tracing in development.

dataPtr : A direct System pointer on the native C memory where the data is located.

gradPtr : A direct System pointer on the native C memory where the grad is located.

shape : List<int>of the dimensions of the stored arrays. Shapes of grad and data are always the same.

creator : An optional Node parameter to tie the tensor as a result of a specific operation.

Implementation

Tensor(dynamic initialValue, {this.creator}) {
  id = 't_$_idCounter';
  _idCounter = _idCounter + 1;

  if (initialValue is double) {
    shape = [];
  } else if (initialValue is List<double>) {
    shape = [initialValue.length];
  } else if (initialValue is List<List<double>>) {
    int rows = initialValue.length;
    int cols = rows > 0 ? initialValue[0].length : 0;
    shape = [rows, cols];
  } else if (initialValue is List<List<List<double>>>) {
    int depth = initialValue.length;
    int height = depth > 0 ? initialValue[0].length : 0;
    int width = height > 0 ? initialValue[0][0].length : 0;
    shape = [depth, height, width];
  } else {
    throw Exception("Unsupported tensor initialization type.");
  }

  int numElements = _elementCount(shape);

  dataPtr = calloc<Float>(numElements);
  data = dataPtr.asTypedList(numElements);
  gradPtr = calloc<Float>(numElements);
  grad = gradPtr.asTypedList(numElements);

  _finalizer.attach(this, (dataPtr, gradPtr), detach: this);

  if (initialValue is double) {
    data[0] = initialValue;
  } else if (initialValue is List<double>) {
    for (int i = 0; i < initialValue.length; i = i + 1) {
      data[i] = initialValue[i];
    }
  } else if (initialValue is List<List<double>>) {
    int cols = shape[1];
    for (int i = 0; i < initialValue.length; i = i + 1) {
      for (int j = 0; j < initialValue[i].length; j = j + 1) {
        data[(i * cols) + j] = initialValue[i][j];
      }
    }
  } else if (initialValue is List<List<List<double>>>) {
    int height = shape[1];
    int width = shape[2];
    for (int d = 0; d < initialValue.length; d = d + 1) {
      for (int h = 0; h < initialValue[d].length; h = h + 1) {
        for (int w = 0; w < initialValue[d][h].length; w = w + 1) {
          data[(d * height * width) + (h * width) + w] =
              initialValue[d][h][w];
        }
      }
    }
  }
}