GPUTensor<T> constructor

GPUTensor<T>(
  1. T initialValue, {
  2. GPUNode? creator,
  3. String? id,
  4. bool? requiresGrad,
})

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

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

Additionally for faster allocation, the following methods can be used:

GPUTensor<T>a=GPUTensor.empty([1]);
GPUTensor<T>a=GPUTensor.randomUniform([1,2,3],1.0);

Regardless of creation method, a GPUNode creator can be given. This parameter will be used for calculation of connected input GPUTensors gradient values. If no creator is given, this GPUTensor can only be used as an input inside of the computational structure.

Connecting GPUTensors is one of the main focus points of this engine to enable automatic gradient calculation and can be done like the following example:

GPUTensor<T> addGPU<T>(GPUTensor<T> a, GPUTensor<T> b, CommandBuffer tape) {
  GPUTensor<T> out = GPUTensor<T>.empty(a.shape);

  tape.putInt(OP_ADD);
  tape.putString(a.id);
  tape.putString(b.id);
  tape.putString(out.id);

  out.creator = GPUNode(
    <GPUTensor>[a, b],
        (CommandBuffer bTape) {
      bTape.putInt(OP_ADD_INTO);
      bTape.putString('${out.id}_grad');
      bTape.putString('${a.id}_grad');

      bTape.putInt(OP_ADD_INTO);
      bTape.putString('${out.id}_grad');
      bTape.putString('${b.id}_grad');
    },
    opName: 'addGPU',
  );
  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. Both require toCpu to be called directly before that if the value is to be pulled from VRAM.

Implementation

GPUTensor(T initialValue, {this.creator, String? id, bool? requiresGrad}) {
  localGrad = requiresGrad ?? globalGrad;
  this.id = id ?? _generateId();
  // 1. Determine Shape
  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 ArgumentError("Invalid initialization for GPUTensor<$T>. "
        "Expected Scalar, Vector, Matrix, or Tensor3D, got: ${initialValue.runtimeType}");
  }

  // 2. Allocate VRAM
  _allocateEmptyInVram();

  // 3. Push Initial Data if provided
  _pushInitialValue(initialValue);
}