Skip to main content

flutter_ml 3.0.4 copy "flutter_ml: ^3.0.4" to clipboard
flutter_ml: ^3.0.4 copied to clipboard

Bring native GPU-accelerated machine learning to Flutter. Train models, build transformers, and run local inference without Python or cloud APIs.

example/lib/main.dart

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_ml/full_library.dart';
/*
void main() {
  runApp(MaterialApp(home: SimpleGPUApp()));
}



class SimpleGPUApp extends StatelessWidget {
  Target target=Target.cuda;
  SimpleGPUApp({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('WebGPU Test')),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            try {
              // useCuda: false zwingt die Engine, das WebGPU-Backend zu nutzen
              //print("✅ GPUEngine erfolgreich initialisiert!");
              //await testMath();
              //print("_____________________________________________________________________________________________");
              //await testLinear();
              //print("_____________________________________________________________________________________________");
              //await testBackward();
              //print("_____________________________________________________________________________________________");
              //await runTransformer();
              //print("_____________________________________________________________________________________________");
              //await testResultTransfer();
              //print("_____________________________________________________________________________________________");
              //await testSpeeds();
              await runSimple();

            } catch (e) {
              print("❌ Fehler bei der Initialisierung: $e");
            }
          },
          child: const Text('Initialize GPU Engine'),
        ),
      ),
    );
  }

  Future<bool> runSimple() async {
    await GPUEngine.initialize(target: target);

    CommandBuffer tape = CommandBuffer();

    GPUTensor<Vector> VecA = GPUTensor([1.1, 2.2, 3.3]);
    GPUTensor<Vector> VecB = GPUTensor([4.4, 5.5, 6.6]);
    GPUTensor<Vector> VecC = GPUTensor([1.0, 0.1, -1.0]);

    GPUTensor<Vector> VecD = addVectorGPU(VecA, VecB, tape);
    GPUTensor<Vector> VecE = elementWiseMultiplyGPU(VecC, VecD, tape);

    GPUEngine.run(tape.bytes());

    VecE.toCpu();
    print("Result: ${VecE.value}");
    VecE.printGraph();
    TapeDecoder(tape.bytes()).decode();
    return true;
  }

  Future<bool> runTransformer() async {
    await GPUEngine.initialize(debug: false, target: target);
    Random random = Random();

    int numHeads = 8;
    int runsPerTest = 50;

    print(
      '====================================================================================================',
    );
    print(
      '              GPU TRANSFORMER ENCODER BLOCK INFERENCE BENCHMARK (FORWARD ONLY)                      ',
    );
    print(
      '====================================================================================================',
    );
    print(
      'Heads: $numHeads | Feed-Forward Ratio: 4x dModel | Inference Runs/Test: $runsPerTest',
    );
    print(
      '----------------------------------------------------------------------------------------------------',
    );
    print(
      ' SeqLen | dModel | Compile(ms) | Latency(ms) | Throughput (GB/s) | Compute (TFLOPs)',
    );
    print(
      '----------------------------------------------------------------------------------------------------',
    );

    List<int> sequences = <int>[32, 64, 128, 512];
    List<int> dimensions = <int>[32, 64, 128, 256];

    for (int s = 0; s < sequences.length; s = s + 1) {
      int seqLength = sequences[s];

      for (int d = 0; d < dimensions.length; d = d + 1) {
        int dModel = dimensions[d];
        int dff = dModel * 4; // Standard Transformer configuration

        // 1. Generate Input Matrix (Batch = 1)
        List<List<double>> hInput = <List<double>>[];
        for (int i = 0; i < seqLength; i = i + 1) {
          List<double> row = <double>[];
          for (int j = 0; j < dModel; j = j + 1) {
            row.add((random.nextDouble() * 2.0) - 1.0);
          }
          hInput.add(row);
        }

        GPUTensor<Matrix> input = GPUTensor<Matrix>(hInput);

        // 2. Build Transformer Block
        TransformerEncoderBlockTapeLayer transformerBlock =
            TransformerEncoderBlockTapeLayer(dModel, numHeads, dff);
        transformerBlock.build(input);

        Stopwatch compileSw = Stopwatch();
        compileSw.start();

        // ===================================================================
        // FORWARD TAPE (INFERENCE ONLY)
        // ===================================================================
        CommandBuffer fTape = CommandBuffer();
        List<GPUTensor> intermediates = <GPUTensor>[];

        GPUTensor<Matrix> output =
            transformerBlock.forward(input, fTape, intermediates)
                as GPUTensor<Matrix>;

        Uint8List forwardBytes = fTape.bytes();

        compileSw.stop();

        // ===================================================================
        // EXECUTION LOOP & METRIC CALCULATIONS
        // ===================================================================

        double seqD = seqLength.toDouble();
        double modD = dModel.toDouble();
        double ffD = dff.toDouble();

        // FLOPs purely for the forward pass
        // MHA (Proj + Out): 8 * Seq * D^2
        // MHA (Attention):  4 * Seq^2 * D
        // FFN (W1 + W2):    4 * Seq * D * FF
        double totalFlopsStep =
            (8.0 * seqD * modD * modD) +
            (4.0 * seqD * seqD * modD) +
            (4.0 * seqD * modD * ffD);

        // Memory Traffic (Bytes Read/Written to VRAM)
        // Weights: ~4D^2 (MHA) + 2*D*FF (FFN) -> * 4 bytes
        double weightBytes = (4.0 * modD * modD + 2.0 * modD * ffD) * 4.0;
        // Activations: Rough estimate of intermediate reads/writes per step
        double actBytes =
            ((20.0 * seqD * modD) + (5.0 * seqD * ffD) + (4.0 * seqD * seqD)) *
            4.0;
        double totalBytesStep = weightBytes + actBytes;

        Stopwatch runSw = Stopwatch();

        // Warmup (Push weights into VRAM caches)
        GPUEngine.run(forwardBytes);

        // Inference Loop
        runSw.start();
        for (int run = 1; run <= runsPerTest; run = run + 1) {
          GPUEngine.run(forwardBytes);
        }
        runSw.stop();

        double avgRunSec =
            (runSw.elapsedMicroseconds / 1000000.0) / runsPerTest;
        double avgRunMs = avgRunSec * 1000.0;

        double tflops = (totalFlopsStep / avgRunSec) / 1000000000000.0;
        double gbps = (totalBytesStep / avgRunSec) / 1000000000.0;

        String sSeq = seqLength.toString().padRight(6);
        String sDim = dModel.toString().padRight(6);
        String sComp = compileSw.elapsedMilliseconds.toString().padRight(11);
        String sAvg = avgRunMs.toStringAsFixed(2).padRight(11);
        String sGbps = gbps.toStringAsFixed(2).padRight(17);
        String sTflops = tflops.toStringAsFixed(4).padRight(16);

        print(' $sSeq | $sDim | $sComp | $sAvg | $sGbps | $sTflops');

        // Free Memory
        transformerBlock.free();
        input.free();
        output.free();
        for (int i = 0; i < intermediates.length; i = i + 1) {
          intermediates[i].free();
        }
      }
    }

    print(
      '----------------------------------------------------------------------------------------------------',
    );
    print('Benchmark Complete.');
    return true;

  }

  Future<bool> testResultTransfer() async {
    await GPUEngine.initialize(debug: false, target: target);
    GPUTensor<Vec> a = GPUTensor<Vec>([
      1.0,
      2.0,
      3.0,
      4.0,
      5.0,
      6.0,
      7.0,
      8.0,
      9.0,
      10.0,
    ]);
    GPUTensor<Vec> b = GPUTensor<Vec>([
      10.0,
      9.0,
      8.0,
      7.0,
      6.0,
      5.0,
      4.0,
      3.0,
      2.0,
      1.0,
    ]);

    CommandBuffer tape = CommandBuffer();
    GPUTensor<Scalar> c = dotProductGPU(a, b, tape);
    GPUTensor<Scalar> d = l2NormGPU(a, tape);
    GPUTensor<Scalar> e = euclideanDistanceGPU(a, b, tape);
    GPUTensor<Scalar> f = cosineSimilarityGPU(a, b, tape);
    GPUTensor<Scalar> g = maeLossGPU(a, b, tape);
    GPUEngine.run(tape.bytes());
    a.toCpu();
    b.toCpu();
    c.toCpu();
    d.toCpu();
    e.toCpu();
    f.toCpu();
    g.toCpu();
    print("Original Tensors: ${a.value}, ${b.value}");
    print("Dot Product: ${c.value}");
    print("L2 Norm: ${d.value}");
    print("Euclidean Distance: ${e.value}");
    print("Cosine Similarity: ${f.value}");
    print("MAE Loss: ${g.value}");
    return true;

  }

  Future<bool> testLearning() async {
    print("🚀 Initialisiere GPU Engine...");
    // 1. Engine starten (passe das Target an, falls du auf Android/Web bist)
    await GPUEngine.initialize(debug: false, target: this.target); //

    print("📦 Allokiere Tensoren im VRAM...");
    // 2. Tensoren anlegen
    // Shape: [1, 2] bedeutet 1 Sample mit 2 Features
    var input = GPUTensor<Matrix>.empty([1, 2]); //
    var target = GPUTensor<Matrix>.empty([1, 1]); //

    // Gewichte & Bias direkt im VRAM randomisiert initialisieren
    var weights = GPUTensor<Matrix>.randomUniform([2, 1], 1.0); //[cite: 2]
    var bias = GPUTensor<Vector>.randomUniform([1], 1.0); //[cite: 2]

    print("📼 Zeichne Computational Graph (Tape) auf...");
    // 3. CommandBuffer erstellen
    var tape = CommandBuffer(); //[cite: 5]

    // --- FORWARD PASS ---
    // input * weights
    var step1 = matMulGPU(input, weights, tape); //[cite: 3]
    // + bias
    var output = addBiasToMatMulOutGPU(step1, bias, tape); //[cite: 3]
    // Loss berechnen (Mean Squared Error)
    var loss = mseMatrixGPU(output, target, tape); //[cite: 3]

    // --- BACKWARD PASS ---
    // Vorherige Gradienten im Graphen nullen
    loss.zeroGraphGrads(tape); //[cite: 2]
    // Backpropagation auslösen (füllt den ersten Gradienten mit 1.0)
    loss.backward(tape, fillOnes: true); //[cite: 2]

    // --- OPTIMIZER STEP ---
    // Gewichte und Bias via Stochastic Gradient Descent anpassen
    sgdUpdateGPU(weights, 0.01, tape); //[cite: 3]
    sgdUpdateGPU(bias, 0.01, tape); //[cite: 3]

    // Tape zu Bytes kompilieren
    Uint8List compiledTape = tape.bytes(); //[cite: 5]

    print("🔥 Starte Training auf der Engine...");
    // 4. Test-Trainings-Loop feuern
    for (int epoch = 1; epoch <= 50; epoch++) {
      // a) Daten von der Dart-CPU in den VRAM pushen
      input.pushData([0.5, 0.8]); //[cite: 2]
      target.pushData([1.0]); //[cite: 2]

      // b) Das komplette kompilierte Tape auf der C++ Engine ausführen
      GPUEngine.run(compiledTape); //[cite: 1]

      // c) Den berechneten Loss-Wert zurück in den RAM holen, um ihn anzuzeigen
      loss.toCpu(); //[cite: 2]
      print("Epoche $epoch - Loss: ${loss.value}"); //[cite: 2]
    }

    print("🧹 Räume VRAM auf...");
    // 5. Sauberes Memory Management
    input.free(); //[cite: 2]
    target.free(); //[cite: 2]
    weights.free(); //[cite: 2]
    bias.free(); //[cite: 2]
    step1.free(); //[cite: 2]
    output.free(); //[cite: 2]
    loss.free(); //[cite: 2]

    GPUEngine.dispose(); //[cite: 1]
    print("✅ Test erfolgreich abgeschlossen!");
    return true;

  }

  Future<bool> testMath() async {
    print("🚀 Starte massive GPU Math-Validierung...");
    await GPUEngine.initialize(
      debug: false,
      target: this.target,
    ); //[cite: 1]

    // ======================================================================
    // 1. BASIC MATH (Element-wise)
    // ======================================================================
    print("\n--- 1. BASIC MATH (Vectors) ---");
    {
      var vA = GPUTensor<Vector>.empty([2]); //[cite: 2]
      var vB = GPUTensor<Vector>.empty([2]); //[cite: 2]
      vA.pushData([4.0, 9.0]); //[cite: 2]
      vB.pushData([2.0, 3.0]); //[cite: 2]

      var tape = CommandBuffer(); //[cite: 5]
      var tAdd = addGPU<Vector>(vA, vB, tape); //[cite: 3]
      var tSub = subtractGPU<Vector>(vA, vB, tape); //[cite: 3]
      var tMul = multiplyGPU<Vector>(vA, vB, tape); //[cite: 3]
      var tDiv = divideGPU<Vector>(vA, vB, tape); //[cite: 3]
      var tExp = vectorExpGPU(vA, tape); //[cite: 3]
      var tAbs = absGPU<Vector>(
        GPUTensor<Vector>([-4.0, -9.0]),
        tape,
      ); //[cite: 2, 3]
      var tSqrt = sqrtGPU<Vector>(vA, tape); //[cite: 3]
      var tLog = logGPU<Vector>(vA, tape); //[cite: 3]
      var tPow = powGPU<Vector>(vA, 2.0, tape); //[cite: 3]
      var tClamp = clampGPU<Vector>(vA, 0.0, 5.0, tape); //[cite: 3]

      GPUEngine.run(tape.bytes()); //[cite: 1]

      tAdd.toCpu();
      tSub.toCpu();
      tMul.toCpu();
      tDiv.toCpu(); //[cite: 2]
      tExp.toCpu();
      tAbs.toCpu();
      tSqrt.toCpu();
      tLog.toCpu();
      tPow.toCpu();
      tClamp.toCpu(); //[cite: 2]

      print("Inputs: vA=[4, 9], vB=[2, 3]");
      print("ADD:    ${tAdd.value}");
      print("SUB:    ${tSub.value}");
      print("MUL:    ${tMul.value}");
      print("DIV:    ${tDiv.value}");
      print("EXP(vA):${tExp.value}");
      print("ABS([-4, -9]): ${tAbs.value}");
      print("SQRT(vA):      ${tSqrt.value}");
      print("LOG(vA):       ${tLog.value}");
      print("POW(vA, 2):    ${tPow.value}");
      print("CLAMP(vA,0,5): ${tClamp.value}");

      vA.free();
      vB.free();
      tAdd.free();
      tSub.free();
      tMul.free();
      tDiv.free(); //[cite: 2]
      tExp.free();
      tAbs.free();
      tSqrt.free();
      tLog.free();
      tPow.free();
      tClamp.free(); //[cite: 2]
    }

    // ======================================================================
    // 2. LINEAR ALGEBRA & MATRIX OPS
    // ======================================================================
    print("\n--- 2. LINEAR ALGEBRA ---");
    {
      print("Make GPUTensors");
      var mA = GPUTensor<Matrix>.empty([2, 2]); //[cite: 2]
      var mB = GPUTensor<Matrix>.empty([2, 2]); //[cite: 2]
      var vC = GPUTensor<Vector>.empty([2]); //[cite: 2]

      print("Push Data to GPU");
      mA.pushData([1.0, 2.0, 3.0, 4.0]); // [[1, 2], [3, 4]] //[cite: 2]
      mB.pushData([2.0, 0.0, 1.0, 2.0]); // [[2, 0], [1, 2]] //[cite: 2]
      vC.pushData([10.0, 20.0]); //[cite: 2]

      print("Tape");
      var tape = CommandBuffer(); //[cite: 5]
      print("tMatMul");
      var tMatMul = matMulGPU(mA, mB, tape); //[cite: 3]
      print("tMatVec");
      var tMatVec = matVecMulGPU(mA, vC, tape); //[cite: 3]
      print("tTrans");
      var tTrans = transposeGPU(mA, tape); //[cite: 3]
      print("tAddBi");
      var tAddBi = addBiasToMatMulOutGPU(mA, vC, tape); //[cite: 3]
      print("tScale");
      var tScale = scaleMatrixGPU(mA, 10.0, tape); //[cite: 3]
      print("tAddSca");
      var tAddSca = addScalarMatrixGPU(
        mA,
        GPUTensor<Scalar>(5.0),
        tape,
      ); //[cite: 2, 3]

      print("Run tape");
      GPUEngine.run(tape.bytes()); //[cite: 1]

      print("Push to CPU");
      tMatMul.toCpu();
      tMatVec.toCpu();
      tTrans.toCpu();
      tAddBi.toCpu();
      tScale.toCpu();
      tAddSca.toCpu(); //[cite: 2]

      print("mA=[[1,2],[3,4]], mB=[[2,0],[1,2]], vC=[10,20]");
      print("MATMUL(mA, mB):   ${tMatMul.value}");
      print("MATVEC(mA, vC):   ${tMatVec.value}");
      print("TRANSPOSE(mA):    ${tTrans.value}");
      print("ADDBIAS(mA, vC):  ${tAddBi.value}");
      print("SCALE(mA, 10):    ${tScale.value}");
      print("ADD_SCALAR(mA,5): ${tAddSca.value}");

      mA.free();
      mB.free();
      vC.free();
      tMatMul.free();
      tMatVec.free();
      tTrans.free();
      tAddBi.free();
      tScale.free();
      tAddSca.free(); //[cite: 2]
    }

    // ======================================================================
    // 3. ACTIVATIONS
    // ======================================================================
    print("\n--- 3. ACTIVATIONS ---");
    {
      var vA = GPUTensor<Vector>.empty([3]); //[cite: 2]
      vA.pushData([-1.0, 0.0, 1.0]); //[cite: 2]

      var mA = GPUTensor<Matrix>.empty([1, 2]); //[cite: 2]
      mA.pushData([0.0, 1.0]); //[cite: 2]

      var tape = CommandBuffer(); //[cite: 5]
      var tRelu = reluGPU(vA, tape); //[cite: 3]
      var tSig = sigmoidGPU(vA, tape); //[cite: 3]
      var tTanh = vectorTanhGPU(vA, tape); //[cite: 3]
      var tGelu = geluGPU(vA, tape); //[cite: 3]
      var tSoftmax = softmaxMatrixGPU(mA, tape); //[cite: 3]

      GPUEngine.run(tape.bytes()); //[cite: 1]

      tRelu.toCpu();
      tSig.toCpu();
      tTanh.toCpu();
      tGelu.toCpu();
      tSoftmax.toCpu(); //[cite: 2]

      print("Inputs: vA=[-1, 0, 1], mA=[[0, 1]]");
      print("RELU(vA):    ${tRelu.value}");
      print("SIGMOID(vA): ${tSig.value}");
      print("TANH(vA):    ${tTanh.value}");
      print("GELU(vA):    ${tGelu.value}");
      print("SOFTMAX(mA): ${tSoftmax.value}");

      vA.free();
      mA.free();
      tRelu.free();
      tSig.free();
      tTanh.free();
      tGelu.free();
      tSoftmax.free(); //[cite: 2]
    }

    // ======================================================================
    // 4. REDUCTIONS & STATS
    // ======================================================================
    print("\n--- 4. REDUCTIONS & STATS ---");
    {
      var vA = GPUTensor<Vector>.empty([3]); //[cite: 2]
      var vB = GPUTensor<Vector>.empty([3]); //[cite: 2]
      var mA = GPUTensor<Matrix>.empty([2, 2]); //[cite: 2]

      vA.pushData([1.0, 2.0, 3.0]); //[cite: 2]
      vB.pushData([4.0, 5.0, 6.0]); //[cite: 2]
      mA.pushData([1.0, 2.0, 3.0, 4.0]); // [[1, 2], [3, 4]] //[cite: 2]

      var tape = CommandBuffer(); //[cite: 5]
      var tSumVec = sumGPU(vA, tape); //[cite: 3]
      var tSumMat = sumMatrixGPU(mA, tape); //[cite: 3]
      var tSumCol = sumReduceColumnsGPU(mA, tape); //[cite: 3]
      var tSumRow = sumReduceRowsGPU(mA, tape); //[cite: 3]
      var tDot = dotProductGPU(vA, vB, tape); //[cite: 3]
      var tL2 = l2NormGPU(
        GPUTensor<Vector>([3.0, 4.0]),
        tape,
      ); // Sollte 5 ergeben //[cite: 2, 3]
      var tEuc = euclideanDistanceGPU(vA, vB, tape); //[cite: 3]
      var tCos = cosineSimilarityGPU(vA, vB, tape); //[cite: 3]

      GPUEngine.run(tape.bytes()); //[cite: 1]

      tSumVec.toCpu();
      tSumMat.toCpu();
      tSumCol.toCpu();
      tSumRow.toCpu(); //[cite: 2]
      tDot.toCpu();
      tL2.toCpu();
      tEuc.toCpu();
      tCos.toCpu(); //[cite: 2]

      print("Inputs: vA=[1,2,3], vB=[4,5,6], mA=[[1,2],[3,4]]");
      print("SUM(vA):       ${tSumVec.value}");
      print("SUM(mA):       ${tSumMat.value}");
      print("SUM_COLS(mA):  ${tSumCol.value}");
      print("SUM_ROWS(mA):  ${tSumRow.value}");
      print("DOT(vA, vB):   ${tDot.value}");
      print("L2NORM([3,4]): ${tL2.value}");
      print("EUC_DIST(vA, vB): ${tEuc.value}");
      print("COS_SIM(vA, vB):  ${tCos.value}");

      vA.free();
      vB.free();
      mA.free();
      tSumVec.free();
      tSumMat.free();
      tSumCol.free();
      tSumRow.free();
      tDot.free();
      tL2.free();
      tEuc.free();
      tCos.free(); //[cite: 2]
    }

    // ======================================================================
    // 5. LOSS FUNCTIONS
    // ======================================================================
    print("\n--- 5. LOSS FUNCTIONS ---");
    {
      var vPred = GPUTensor<Vector>.empty([2]); //[cite: 2]
      var vTarg = GPUTensor<Vector>.empty([2]); //[cite: 2]

      vPred.pushData([0.8, 0.2]); //[cite: 2]
      vTarg.pushData([1.0, 0.0]); //[cite: 2]

      var tape = CommandBuffer(); //[cite: 5]
      var tBce = binaryCrossEntropyGPU<Vector>(vPred, vTarg, tape); //[cite: 3]
      var tMse = mseGPU(vPred, vTarg, tape); //[cite: 3]
      var tMae = maeLossGPU(vPred, vTarg, tape); //[cite: 3]

      GPUEngine.run(tape.bytes()); //[cite: 1]
      tBce.toCpu();
      tMse.toCpu();
      tMae.toCpu(); //[cite: 2]

      print("Inputs: Pred=[0.8, 0.2], Targ=[1.0, 0.0]");
      print("BCE_LOSS: ${tBce.value}");
      print("MSE_LOSS: ${tMse.value}");
      print("MAE_LOSS: ${tMae.value}");

      vPred.free();
      vTarg.free();
      tBce.free();
      tMse.free();
      tMae.free(); //[cite: 2]
    }

    // ======================================================================
    // 6. SHAPE / TENSOR MANIPULATION
    // ======================================================================
    print("\n--- 6. SHAPE & TENSOR MANIPULATION ---");
    {
      var vA = GPUTensor<Vector>.empty([4]); //[cite: 2]
      vA.pushData([1.0, 2.0, 3.0, 4.0]); //[cite: 2]

      var mA = GPUTensor<Matrix>.empty([2, 2]); //[cite: 2]
      mA.pushData([1.0, 2.0, 3.0, 4.0]); //[cite: 2]
      var mB = GPUTensor<Matrix>.empty([2, 2]); //[cite: 2]
      mB.pushData([5.0, 6.0, 7.0, 8.0]); //[cite: 2]

      var tape = CommandBuffer(); //[cite: 5]
      var tReshape = reshapeVectorToMatrixGPU(vA, 2, 2, tape); //[cite: 3]
      var tSliceCol = sliceColumnGPU(mA, 0, 1, tape); //[cite: 3]
      var tSelRow = selectRowGPU(mA, 1, tape); //[cite: 3]
      var tConcat = concatenateGPU(
        vA,
        GPUTensor<Vector>([5.0, 6.0]),
        tape,
      ); //[cite: 2, 3]
      var tStack = stackMatricesGPU([mA, mB], tape); //[cite: 3]
      var tPad = padMatrixGPU(mA, 1, tape); //[cite: 3]

      GPUEngine.run(tape.bytes()); //[cite: 1]

      tReshape.toCpu();
      tSliceCol.toCpu();
      tSelRow.toCpu();
      tConcat.toCpu();
      tStack.toCpu();
      tPad.toCpu(); //[cite: 2]

      print("RESHAPE(v[1,2,3,4] -> 2x2): ${tReshape.value}");
      print("SLICE_COL(mA, 0, 1):        ${tSliceCol.value}");
      print("SELECT_ROW(mA, 1):          ${tSelRow.value}");
      print("CONCAT_VEC(vA, [5,6]):      ${tConcat.value}");
      print("STACK_MAT(mA, mB):          ${tStack.value}");
      print("PAD_MAT(mA, 1):             ${tPad.value}");

      vA.free();
      mA.free();
      mB.free();
      tReshape.free();
      tSliceCol.free();
      tSelRow.free();
      tConcat.free();
      tStack.free();
      tPad.free(); //[cite: 2]
    }

    // ======================================================================
    // 7. ADVANCED LAYERS (Conv, Pool)
    // ======================================================================
    print("\n--- 7. ADVANCED LAYERS ---");
    {
      var mIn = GPUTensor<Matrix>.empty([3, 3]); //[cite: 2]
      // 3x3 Matrix voller 1er
      mIn.pushData([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]); //[cite: 2]

      var mKernel = GPUTensor<Matrix>.empty([2, 2]); //[cite: 2]
      // 2x2 Kernel voller 1er
      mKernel.pushData([1.0, 1.0, 1.0, 1.0]); //[cite: 2]

      var tape = CommandBuffer(); //[cite: 5]
      // Simple 2D Convolution
      var tConv = conv2dSimpleGPU(mIn, mKernel, tape); //[cite: 3]
      // MaxPool2D (2x2 pool, stride 1)
      var tPool = maxPool2dGPU(mIn, 2, 1, tape); //[cite: 3]

      GPUEngine.run(tape.bytes()); //[cite: 1]

      tConv.toCpu();
      tPool.toCpu(); //[cite: 2]

      print("Inputs: 3x3 Matrix (ones), 2x2 Kernel (ones)");
      print("CONV2D_SIMPLE: ${tConv.value} (Erwartet 2x2 mit Wert 4.0)");
      print("MAXPOOL_2D:    ${tPool.value}");

      mIn.free();
      mKernel.free();
      tConv.free();
      tPool.free(); //[cite: 2]
    }

    print("\n🧹 Aufräumen...");
    GPUEngine.dispose(); //[cite: 1]
    print("✅ Alle Funktionen gefeuert und validiert!");
    return true;

  }

  Future<bool> testLinear() async {
    print("🚀 Initialisiere chirurgischen Linear-Algebra-Debugger...");
    await GPUEngine.initialize(
      debug: false,
      target: target,
    ); //[cite: 1]

    // Basis-Tensoren anlegen
    var mA = GPUTensor<Matrix>.empty([2, 2]); //[cite: 2]
    var mB = GPUTensor<Matrix>.empty([2, 2]); //[cite: 2]
    var vC = GPUTensor<Vector>.empty([2]); //[cite: 2]
    var sD = GPUTensor<Scalar>(
      5.0,
    ); // Scalar für addScalarMatrixGPU //[cite: 2]

    mA.pushData([1.0, 2.0, 3.0, 4.0]); //[cite: 2]
    mB.pushData([2.0, 0.0, 1.0, 2.0]); //[cite: 2]
    vC.pushData([10.0, 20.0]); //[cite: 2]

    print("\n--- TEST 1: MATMUL ---");
    var tape1 = CommandBuffer(); //[cite: 5]
    var tMatMul = matMulGPU(mA, mB, tape1); //[cite: 3]
    print("Feuere MatMul...");
    GPUEngine.run(tape1.bytes()); //[cite: 1]
    tMatMul.toCpu(); //[cite: 2]
    print("✅ MATMUL OK: ${tMatMul.value}");

    print("\n--- TEST 2: MATVEC ---");
    var tape2 = CommandBuffer(); //[cite: 5]
    var tMatVec = matVecMulGPU(mA, vC, tape2); //[cite: 3]
    print("Feuere MatVec...");
    GPUEngine.run(tape2.bytes()); //[cite: 1]
    tMatVec.toCpu(); //[cite: 2]
    print("✅ MATVEC OK: ${tMatVec.value}");

    print("\n--- TEST 3: TRANSPOSE ---");
    var tape3 = CommandBuffer(); //[cite: 5]
    var tTrans = transposeGPU(mA, tape3); //[cite: 3]
    print("Feuere Transpose...");
    GPUEngine.run(tape3.bytes()); //[cite: 1]
    tTrans.toCpu(); //[cite: 2]
    print("✅ TRANSPOSE OK: ${tTrans.value}");

    print("\n--- TEST 4: SCALE MATRIX ---");
    var tape4 = CommandBuffer(); //[cite: 5]
    var tScale = scaleMatrixGPU(mA, 10.0, tape4); //[cite: 3]
    print("Feuere Scale Matrix...");
    GPUEngine.run(tape4.bytes()); //[cite: 1]
    tScale.toCpu(); //[cite: 2]
    print("✅ SCALE MATRIX OK: ${tScale.value}");

    print("\n--- TEST 5: ADD BIAS TO MATMUL (Nutzt OP_BROADCAST_ADD) ---");
    var tape5 = CommandBuffer(); //[cite: 5]
    var tAddBi = addBiasToMatMulOutGPU(mA, vC, tape5); //[cite: 3]
    print("Feuere Add Bias...");
    GPUEngine.run(tape5.bytes()); //[cite: 1]
    tAddBi.toCpu(); //[cite: 2]
    print("✅ ADD BIAS OK: ${tAddBi.value}");

    print("\n--- TEST 6: ADD SCALAR MATRIX (Nutzt OP_BROADCAST_ADD) ---");
    var tape6 = CommandBuffer(); //[cite: 5]
    var tAddSca = addScalarMatrixGPU(mA, sD, tape6); //[cite: 3]
    print("Feuere Add Scalar Matrix...");
    GPUEngine.run(tape6.bytes()); //[cite: 1]
    tAddSca.toCpu(); //[cite: 2]
    print("✅ ADD SCALAR MATRIX OK: ${tAddSca.value}");

    print("\n🧹 Räume auf...");
    mA.free();
    mB.free();
    vC.free();
    sD.free(); //[cite: 2]
    tMatMul.free();
    tMatVec.free();
    tTrans.free();
    tScale.free();
    tAddBi.free();
    tAddSca.free(); //[cite: 2]
    GPUEngine.dispose(); //[cite: 1]

    print("🎉 Alle Tests überlebt!");
    return true;

  }

  Future<bool> testBackward() async{
    print("🚀 Starte massive BACKWARD (Gradient) Validierung...");
    await GPUEngine.initialize(debug: false, target: target); //[cite: 1]

    // =========================================================
    // 1. BASIC MATH BACKWARDS
    // =========================================================
    print("\n--- 1. BASIC MATH (ADD, SUB, MUL, DIV) ---");
    {
      var tape = CommandBuffer(); //[cite: 5]

      // ADD: Ableitung von (A + B) nach A ist 1, nach B ist 1
      var aAdd = GPUTensor<Vector>.empty([2]); aAdd.pushData([2.0, 3.0]); //[cite: 2]
      var bAdd = GPUTensor<Vector>.empty([2]); bAdd.pushData([4.0, 5.0]); //[cite: 2]
      var outAdd = addGPU<Vector>(aAdd, bAdd, tape); //[cite: 3]
      outAdd.backward(tape, fillOnes: true); //[cite: 2]

      // SUB: Ableitung von (A - B) nach A ist 1, nach B ist -1
      var aSub = GPUTensor<Vector>.empty([2]); aSub.pushData([2.0, 3.0]); //[cite: 2]
      var bSub = GPUTensor<Vector>.empty([2]); bSub.pushData([4.0, 5.0]); //[cite: 2]
      var outSub = subtractGPU<Vector>(aSub, bSub, tape); //[cite: 3]
      outSub.backward(tape, fillOnes: true); //[cite: 2]

      // MUL: Ableitung von (A * B) nach A ist B, nach B ist A
      var aMul = GPUTensor<Vector>.empty([2]); aMul.pushData([2.0, 3.0]); //[cite: 2]
      var bMul = GPUTensor<Vector>.empty([2]); bMul.pushData([4.0, 5.0]); //[cite: 2]
      var outMul = multiplyGPU<Vector>(aMul, bMul, tape); //[cite: 3]
      outMul.backward(tape, fillOnes: true); //[cite: 2]

      // DIV: Ableitung von (A / B) nach A ist 1/B, nach B ist -A/B^2
      var aDiv = GPUTensor<Vector>.empty([2]); aDiv.pushData([2.0, 3.0]); //[cite: 2]
      var bDiv = GPUTensor<Vector>.empty([2]); bDiv.pushData([4.0, 5.0]); //[cite: 2]
      var outDiv = divideGPU<Vector>(aDiv, bDiv, tape); //[cite: 3]
      outDiv.backward(tape, fillOnes: true); //[cite: 2]

      GPUEngine.run(tape.bytes()); //[cite: 1]

      // Gradienten (.grad) zusammen mit den Daten in den RAM holen
      aAdd.toCpu(); bAdd.toCpu(); //[cite: 2]
      aSub.toCpu(); bSub.toCpu(); //[cite: 2]
      aMul.toCpu(); bMul.toCpu(); //[cite: 2]
      aDiv.toCpu(); bDiv.toCpu(); //[cite: 2]

      print("Inputs: A=[2,3], B=[4,5]");
      print("ADD Grad A: ${aAdd.gradValue} (Erwartet: [1.0, 1.0])"); //[cite: 2]
      print("ADD Grad B: ${bAdd.gradValue} (Erwartet: [1.0, 1.0])"); //[cite: 2]
      print("SUB Grad A: ${aSub.gradValue} (Erwartet: [1.0, 1.0])"); //[cite: 2]
      print("SUB Grad B: ${bSub.gradValue} (Erwartet: [-1.0, -1.0])"); //[cite: 2]
      print("MUL Grad A: ${aMul.gradValue} (Erwartet: [4.0, 5.0])"); //[cite: 2]
      print("MUL Grad B: ${bMul.gradValue} (Erwartet: [2.0, 3.0])"); //[cite: 2]
      print("DIV Grad A: ${aDiv.gradValue} (Erwartet: [0.25, 0.2])"); //[cite: 2]
      print("DIV Grad B: ${bDiv.gradValue} (Erwartet: [-0.125, -0.12])"); //[cite: 2]

      aAdd.free(); bAdd.free(); outAdd.free(); //[cite: 2]
      aSub.free(); bSub.free(); outSub.free(); //[cite: 2]
      aMul.free(); bMul.free(); outMul.free(); //[cite: 2]
      aDiv.free(); bDiv.free(); outDiv.free(); //[cite: 2]
    }

    // =========================================================
    // 2. UNARY & ACTIVATIONS
    // =========================================================
    print("\n--- 2. UNARY & ACTIVATIONS ---");
    {
      var tape = CommandBuffer(); //[cite: 5]

      // POW: Ableitung von x^2 ist 2*x
      var aPow = GPUTensor<Vector>.empty([2]); aPow.pushData([2.0, 4.0]); //[cite: 2]
      var outPow = powGPU<Vector>(aPow, 2.0, tape); //[cite: 3]
      outPow.backward(tape, fillOnes: true); //[cite: 2]

      // LOG: Ableitung von ln(x) ist 1/x
      var aLog = GPUTensor<Vector>.empty([2]); aLog.pushData([2.0, 4.0]); //[cite: 2]
      var outLog = logGPU<Vector>(aLog, tape); //[cite: 3]
      outLog.backward(tape, fillOnes: true); //[cite: 2]

      // RELU: Ableitung von max(0, x) ist 1 wenn x > 0, sonst 0
      var aRelu = GPUTensor<Vector>.empty([2]); aRelu.pushData([2.0, -1.0]); //[cite: 2]
      var outRelu = reluGPU(aRelu, tape); //[cite: 3]
      outRelu.backward(tape, fillOnes: true); //[cite: 2]

      GPUEngine.run(tape.bytes()); //[cite: 1]
      aPow.toCpu(); aLog.toCpu(); aRelu.toCpu(); //[cite: 2]

      print("POW(A=[2,4], 2) Grad: ${aPow.gradValue} (Erwartet: [4.0, 8.0])"); //[cite: 2]
      print("LOG(A=[2,4]) Grad:    ${aLog.gradValue} (Erwartet: [0.5, 0.25])"); //[cite: 2]
      print("RELU(A=[2,-1]) Grad:  ${aRelu.gradValue} (Erwartet: [1.0, 0.0])"); //[cite: 2]

      aPow.free(); outPow.free(); //[cite: 2]
      aLog.free(); outLog.free(); //[cite: 2]
      aRelu.free(); outRelu.free(); //[cite: 2]
    }

    // =========================================================
    // 3. MATMUL & LINEAR ALGEBRA
    // =========================================================
    print("\n--- 3. MATMUL & LINEAR ALGEBRA ---");
    {
      var tape = CommandBuffer(); //[cite: 5]

      // C = A * B. Grad(A) = Grad(C) * B^T. Grad(B) = A^T * Grad(C)
      var mA = GPUTensor<Matrix>.empty([1, 2]); mA.pushData([1.0, 2.0]); //[cite: 2]
      var mB = GPUTensor<Matrix>.empty([2, 1]); mB.pushData([3.0, 4.0]); //[cite: 2]
      var outMatMul = matMulGPU(mA, mB, tape); //[cite: 3]
      outMatMul.backward(tape, fillOnes: true); //[cite: 2]

      GPUEngine.run(tape.bytes()); //[cite: 1]
      mA.toCpu(); mB.toCpu(); //[cite: 2]

      print("MATMUL A=[[1,2]], B=[[3],[4]]");
      print("Grad A: ${mA.gradValue} (Erwartet: [[3.0, 4.0]])"); //[cite: 2]
      print("Grad B: ${mB.gradValue} (Erwartet: [[1.0], [2.0]])"); //[cite: 2]

      mA.free(); mB.free(); outMatMul.free(); //[cite: 2]
    }

    // =========================================================
    // 4. LOSSES & REDUCTIONS
    // =========================================================
    print("\n--- 4. LOSSES & REDUCTIONS ---");
    {
      var tape = CommandBuffer(); //[cite: 5]

      // MSE Loss: (Pred - Target) * 2 / N
      var vPred = GPUTensor<Vector>.empty([2]); vPred.pushData([0.8, 0.2]); //[cite: 2]
      var vTarg = GPUTensor<Vector>.empty([2]); vTarg.pushData([1.0, 0.0]); //[cite: 2]
      var outMse = mseGPU(vPred, vTarg, tape); //[cite: 3]
      outMse.backward(tape, fillOnes: true); //[cite: 2]

      // Sum Reduction: Gradient verteilt sich gleichmäßig als 1.0
      var vSum = GPUTensor<Vector>.empty([3]); vSum.pushData([1.0, 2.0, 3.0]); //[cite: 2]
      var outSum = sumGPU(vSum, tape); //[cite: 3]
      outSum.backward(tape, fillOnes: true); //[cite: 2]

      GPUEngine.run(tape.bytes()); //[cite: 1]
      vPred.toCpu(); vSum.toCpu(); //[cite: 2]

      print("MSE Pred=[0.8, 0.2], Targ=[1.0, 0.0]");
      print("Grad Pred: ${vPred.gradValue} (Erwartet: [-0.2, 0.2])"); //[cite: 2]

      print("SUM V=[1,2,3]");
      print("Grad V:    ${vSum.gradValue} (Erwartet: [1.0, 1.0, 1.0])"); //[cite: 2]

      vPred.free(); vTarg.free(); outMse.free(); //[cite: 2]
      vSum.free(); outSum.free(); //[cite: 2]
    }

    // =========================================================
    // 5. CONVOLUTION 2D
    // =========================================================
    print("\n--- 5. CONVOLUTION 2D ---");
    {
      var tape = CommandBuffer(); //[cite: 5]

      // Faltung eines 3x3 Inputs mit einem 2x2 Kernel.
      // Der Gradient des Kernels ist die Summe der überlappenden Fenster.
      var mIn = GPUTensor<Matrix>.empty([3, 3]); //[cite: 2]
      mIn.pushData([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]); //[cite: 2]
      var mKer = GPUTensor<Matrix>.empty([2, 2]); //[cite: 2]
      mKer.pushData([1.0, 1.0, 1.0, 1.0]); //[cite: 2]

      var outConv = conv2dSimpleGPU(mIn, mKer, tape); //[cite: 3]
      outConv.backward(tape, fillOnes: true); //[cite: 2]

      GPUEngine.run(tape.bytes()); //[cite: 1]
      mKer.toCpu(); //[cite: 2]

      print("CONV2D In=3x3(1..9), Ker=2x2(1s)");
      print("Grad Kernel: ${mKer.gradValue} (Erwartet: [[12.0, 16.0], [24.0, 28.0]])"); //[cite: 2]

      mIn.free(); mKer.free(); outConv.free(); //[cite: 2]
    }

    print("\n🧹 Aufräumen...");
    GPUEngine.dispose(); //[cite: 1]
    print("✅ Backward Validierung abgeschlossen!");
    return true;

  }

  Future<bool> testSpeeds()async{
    print("🚀 Starte TFLOP & Overhead Benchmark...");
    // Target anpassen (z.B. Target.cuda oder Target.android_x86_64)
    await GPUEngine.initialize(debug: false, target: target); //

    // ====================================================================
    // TEST 1: FFI & DISPATCH OVERHEAD
    // ====================================================================
    print("\n=======================================================");
    print("⏱️ TEST 1: ENGINE DISPATCH OVERHEAD");
    print("=======================================================");

    // Ein winziger Tensor (1 Element), um die GPU-Rechenzeit gegen 0 zu drücken
    var tinyVec = GPUTensor<Vector>.empty([1]); //[cite: 2]
    var tapeOverhead = CommandBuffer(); //[cite: 5]

    // Nur ein simpler Fill-Befehl
    tapeOverhead.putInt(OP_FILL); //[cite: 2, 5]
    tapeOverhead.putString(tinyVec.id); //[cite: 2, 5]
    tapeOverhead.putFloat(1.0); //[cite: 2, 5]

    Uint8List compiledOverhead = tapeOverhead.bytes(); //[cite: 5]

    // Warmup (Weckt die Engine und füllt Caches)
    for (int i = 0; i < 100; i++) {
      GPUEngine.run(compiledOverhead); //[cite: 1]
    }

    int overheadIterations = 10000;
    Stopwatch swOverhead = Stopwatch()..start();
    for (int i = 0; i < overheadIterations; i++) {
      GPUEngine.run(compiledOverhead); //[cite: 1]
    }
    swOverhead.stop();

    double totalMs = swOverhead.elapsedMicroseconds / 1000.0;
    double msPerDispatch = totalMs / overheadIterations;

    print("Befehle gesendet: $overheadIterations");
    print("Gesamtzeit:       ${totalMs.toStringAsFixed(2)} ms");
    print("Overhead pro Run: ${(msPerDispatch * 1000).toStringAsFixed(2)} Mikrosekunden (µs) pro Dispatch");

    tinyVec.free(); //[cite: 2]

    // ====================================================================
    // TEST 2: MAXIMAL TFLOPS (Matrix Multiplication)
    // ====================================================================
    print("\n=======================================================");
    print("🔥 TEST 2: MAXIMAL TFLOPS (MATRIX MULTIPLICATION)");
    print("=======================================================");

    // Matrix-Dimension: 4096 x 4096
    // Belegt ca. 3x 67 MB VRAM (A, B, und Out) = ~201 MB total
    int M = 512*2;
    print("Matrix Größe:      ${M}x$M Elemente");

    var matA = GPUTensor<Matrix>.empty([M, M]); //[cite: 2]
    var matB = GPUTensor<Matrix>.empty([M, M]); //[cite: 2]

    // Wir füllen sie auf der GPU, damit wir keine Zeit mit CPU->GPU Transfers verlieren
    var setupTape = CommandBuffer(); //[cite: 5]
    setupTape.putInt(OP_FILL); setupTape.putString(matA.id); setupTape.putFloat(1.0); //[cite: 2, 5]
    setupTape.putInt(OP_FILL); setupTape.putString(matB.id); setupTape.putFloat(1.0); //[cite: 2, 5]
    GPUEngine.run(setupTape.bytes()); //[cite: 1]

    var tflopTape = CommandBuffer(); //[cite: 5]
    // Das ist unsere schwere Last
    var matOut = matMulGPU(matA, matB, tflopTape); //[cite: 3]
    Uint8List compiledTflop = tflopTape.bytes(); //[cite: 5]

    // Formel für MatMul Flops: 2 * M * K * N
    // Da M = K = N = 4096:
    double flopsPerRun = 2.0 * M * M * M;
    double tflopsPerRun = flopsPerRun / 1e12; // In TeraFLOPs umrechnen

    // Warmup
    GPUEngine.run(compiledTflop); //[cite: 1]

    int tflopIterations = 20;
    print("Starte $tflopIterations Durchläufe...");

    Stopwatch swTflop = Stopwatch()..start();
    for (int i = 0; i < tflopIterations; i++) {
      GPUEngine.run(compiledTflop); //[cite: 1]
    }
    swTflop.stop();

    double seconds = swTflop.elapsedMicroseconds / 1000000.0;
    double totalTflops = tflopsPerRun * tflopIterations;
    double tflopsPerSecond = totalTflops / seconds;
    double gflopsPerSecond = tflopsPerSecond * 1000.0;

    print("Gesamtzeit:        ${seconds.toStringAsFixed(3)} s");
    print("Berechnete TFLOPs: ${totalTflops.toStringAsFixed(3)} TFLOPs total");
    print("-------------------------------------------------------");
    print("Leistung (GFLOP/s): ${gflopsPerSecond.toStringAsFixed(2)} GFLOP/s");
    print("Leistung (TFLOP/s): ${tflopsPerSecond.toStringAsFixed(4)} TFLOP/s");
    print("=======================================================\n");

    matA.free(); matB.free(); matOut.free(); //[cite: 2]
    GPUEngine.dispose(); //[cite: 1]
    return true;
  }

  Future<bool> testLearning2() async {
    print("🚀 Initialisiere GPU Engine für Forward-Pass-Test...");
    await GPUEngine.initialize(debug: false, target:target);

    SNetworkGPU net = SNetworkGPU();
    net.add(DenseTL(8));
    net.add(ReLULayerMatrixTapeLayer());
    net.add(DenseTL(1));
    net.add(SigmoidMatrixTL());

    // Kompilieren (allokiert VRAM und bereitet das Forward-Tape vor)
    print("⚙️ Kompiliere Graphen...");
    net.compile([4, 2], [4, 1], 0.001, useAdam: true);

    // Unser Test-Batch
    List<double> xBatch = [
      0.0, 0.0,
      0.0, 1.0,
      1.0, 0.0,
      1.0, 1.0
    ];

    print("🔮 Führe Forward-Pass (predict) aus...");
    // predict() schiebt xBatch in den VRAM, triggert fTape und holt das Ergebnis in den RAM
    var prediction = net.predict(xBatch);

    print("\n📊 Rohe Vorhersagen (Untrainiert, basierend auf Zufallsgewichten):");
    print(prediction);

    // Optional: Wir können auch direkt prüfen, ob die Gewichte ausgelesen werden können
    print("\n⚖️ Lade initiale Gewichte aus Layer 1 (DenseTL) herunter...");
    var initialWeights = net.layers[0].getWeights();
    print(initialWeights['weights']);

    print("\n🧹 Aufräumen...");
    net.free();
    GPUEngine.dispose();

    return true;
  }
  Future<bool> testBackwardPass() async {
    print("🚀 Initialisiere GPU Engine für Backward-Pass-Test...");
    await GPUEngine.initialize(debug: false, target: target);

    SNetworkGPU net = SNetworkGPU();
    net.add(DenseTL(8));
    net.add(ReLULayerMatrixTapeLayer());
    net.add(DenseTL(1));
    net.add(SigmoidMatrixTL());

    // Wir nutzen eine sehr hohe Lernrate (0.5), damit wir den Sprung der Gewichte deutlich sehen
    net.compile([4, 2], [4, 1], 0.5, useAdam: true);

    List<double> xBatch = [
      0.0, 0.0,
      0.0, 1.0,
      1.0, 0.0,
      1.0, 1.0
    ];
    List<double> yBatch = [0.0, 1.0, 1.0, 0.0];

    // 1. Gewichte VOR dem Gradienten-Update lesen
    print("\n⚖️ Initiale Gewichte in Layer 1 (Zeile 1):");
    var wBefore = net.layers[0].getWeights()['weights'] as List;
    print(wBefore[0]);

    // 2. Ein einziger Durchlauf (Forward -> Backward -> Adam Update)
    print("\n🔮 Führe Backpropagation aus...");
    double loss = net.trainStep(xBatch, yBatch);
    print("📊 Initialer Loss: $loss");

    // 3. Gewichte NACH dem Gradienten-Update lesen
    print("\n📉 Gewichte NACH dem Update (Zeile 1):");
    var wAfter = net.layers[0].getWeights()['weights'] as List;
    print(wAfter[0]);

    // 4. Prüfen, ob sich etwas bewegt hat
    bool didLearn = false;
    for (int i = 0; i < (wBefore[0] as List).length; i++) {
      if (wBefore[0][i] != wAfter[0][i]) {
        didLearn = true;
        break;
      }
    }

    print(didLearn ? "✅ Gradienten fließen! Gewichte wurden erfolgreich geupdatet." : "❌ FEHLER: Gewichte sind eingefroren (Gradient ist 0.0).");

    net.free();
    GPUEngine.dispose();
    return true;
  }
  Future<bool> testShortTraining() async {
    print("🚀 Initialisiere GPU Engine für Kurz-Training...");
    await GPUEngine.initialize(debug: false, target: target);

    SNetworkGPU net = SNetworkGPU();
    net.add(DenseTL(16)); // 16 Neuronen, um das 0.69-Minimum sicher zu knacken
    net.add(ReLULayerMatrixTapeLayer());
    net.add(DenseTL(1));
    net.add(SigmoidMatrixTL());

    // Kompilieren mit Adam und einer guten Lernrate
    net.compile([4, 2], [4, 1], 0.05, useAdam: true);

    List<double> xBatch = [
      0.0, 0.0,
      0.0, 1.0,
      1.0, 0.0,
      1.0, 1.0
    ];
    List<double> yBatch = [0.0, 1.0, 1.0, 0.0];

    print("\n🔮 Starte Training für 300 Epochen...");

    for (int epoch = 1; epoch <= 300; epoch++) {
      double loss = net.trainStep(xBatch, yBatch);

      if (epoch % 50 == 0 || epoch == 1) {
        print("Epoch ${epoch.toString().padLeft(3)} | Loss: ${loss.toStringAsFixed(6)}");
      }
    }

    print("\n📊 Vorhersagen NACH dem Training (Ziel: 0, 1, 1, 0):");
    var prediction = net.predict(xBatch);

    // Etwas schöner formatiert ausgeben
    List<List<double>> preds = prediction as List<List<double>>;
    print("Input [0,0] -> ${preds[0][0].toStringAsFixed(4)} (Ziel: 0.0)");
    print("Input [0,1] -> ${preds[1][0].toStringAsFixed(4)} (Ziel: 1.0)");
    print("Input [1,0] -> ${preds[2][0].toStringAsFixed(4)} (Ziel: 1.0)");
    print("Input [1,1] -> ${preds[3][0].toStringAsFixed(4)} (Ziel: 0.0)");

    print("\n🧹 Aufräumen...");
    net.free();
    GPUEngine.dispose();
    return true;
  }
  Future<bool> testConvergence() async {
    print("🚀 Initialisiere GPU Engine für Konvergenz-Test...");
    await GPUEngine.initialize(debug: false, target: target);

    SNetworkGPU net = SNetworkGPU();
    net.add(DenseTL(16));
    net.add(ReLULayerMatrixTapeLayer());
    net.add(DenseTL(1));
    net.add(SigmoidMatrixTL());

    net.compile([4, 2], [4, 1], 0.05, useAdam: true);

    List<double> xBatch = [
      0.0, 0.0,
      0.0, 1.0,
      1.0, 0.0,
      1.0, 1.0
    ];
    List<double> yBatch = [0.0, 1.0, 1.0, 0.0];

    print("\n🔍 Beobachte Vorhersagen und Loss über 50 Epochen...");

    for (int epoch = 1; epoch <= 50; epoch++) {
      // 1. Trainieren (Forward, Backward, Optimize)
      double loss = net.trainStep(xBatch, yBatch);

      // 2. Aktuellen Stand der Vorhersagen abholen
      var prediction = net.predict(xBatch);
      List<List<double>> preds = prediction as List<List<double>>;

      // 3. Ein einzelnes Gewicht auslesen, um die Bewegung zu tracken
      var weights = net.layers[0].getWeights()['weights'] as List;
      double sampleWeight = weights[0][0];

      // Alle 10 Epochen (und beim ersten Mal) loggen
      if (epoch % 10 == 0 || epoch == 1) {
        print("\n--- Epoche ${epoch.toString().padLeft(2)} ---");
        print("Loss:      ${loss.toStringAsFixed(6)}");
        print("Gewicht 0: ${sampleWeight.toStringAsFixed(6)}");
        print("Preds:     [${preds[0][0].toStringAsFixed(4)}, ${preds[1][0].toStringAsFixed(4)}, ${preds[2][0].toStringAsFixed(4)}, ${preds[3][0].toStringAsFixed(4)}]");
      }
    }

    print("\n🧹 Aufräumen...");
    net.free();
    GPUEngine.dispose();
    return true;
  }

  Future<bool> testTrainingAndPrintWeights() async {
    print("🚀 Initialisiere GPU Engine...");
    await GPUEngine.initialize(debug: false, target: target);

    SNetworkGPU net = SNetworkGPU();

    net.add(DenseTL(16));

    // ✅ DER LEBENSRETTER: Sigmoid stirbt niemals!
    // Es liefert auch bei negativen Werten immer einen kleinen Gradienten zurück,
    // sodass Adam weiterarbeiten kann und das Netzwerk nicht einfriert.
    net.add(SigmoidMatrixTL());

    net.add(DenseTL(1));
    net.add(SigmoidMatrixTL());

    // Wir können die Lernrate jetzt mutig auf 0.05 setzen, da Sigmoid robuster ist
    net.compile([4, 2], [4, 1], 0.05, useAdam: true);

    List<double> xBatch = [
      0.0, 0.0,
      0.0, 1.0,
      1.0, 0.0,
      1.0, 1.0
    ];
    List<double> yBatch = [0.0, 1.0, 1.0, 0.0];

    print("\n🔥 Starte Training für 2000 Epochen...");

    for (int epoch = 1; epoch <= 2000; epoch++) {
      double loss = net.trainStep(xBatch, yBatch);

      if (epoch % 200 == 0 || epoch == 1) {
        print("Epoch ${epoch.toString().padLeft(4)} | Loss: ${loss.toStringAsFixed(6)}");
      }
    }

    print("\n🧠 Vorhersagen nach 2000 Epochen (Ziel: 0, 1, 1, 0):");
    var prediction = net.predict(xBatch);
    List<List<double>> preds = prediction as List<List<double>>;
    print("Input [0,0] -> ${preds[0][0].toStringAsFixed(4)}");
    print("Input [0,1] -> ${preds[1][0].toStringAsFixed(4)}");
    print("Input [1,0] -> ${preds[2][0].toStringAsFixed(4)}");
    print("Input [1,1] -> ${preds[3][0].toStringAsFixed(4)}");

    print("\n🧹 Aufräumen...");
    net.free();
    GPUEngine.dispose();
    return true;
  }
  Future<bool> debugAllGradients() async {
    print("🚀 Initialisiere GPU Engine für GRADIENTEN-RÖNTGEN...");
    await GPUEngine.initialize(debug: false, target: target);

    SNetworkGPU net = SNetworkGPU();
    net.add(DenseTL(4)); // 4 Neuronen = Übersichtlicher Output!
    net.add(ReLULayerMatrixTapeLayer());
    net.add(DenseTL(1));
    net.add(SigmoidMatrixTL());

    // 🛑 WICHTIG: SGD mit Lernrate 1.0 !
    // Dadurch gilt: Gradient = Gewicht_Vorher - Gewicht_Nachher
    net.compile([4, 2], [4, 1], 1.0, useAdam: false);

    List<double> xBatch = [
      0.0, 0.0,
      0.0, 1.0,
      1.0, 0.0,
      1.0, 1.0
    ];
    List<double> yBatch = [0.0, 1.0, 1.0, 0.0];

    // 1. Snapshot: Gewichte VOR dem Forward-Pass aus dem VRAM holen
    var l1Before = net.layers[0].getWeights()['weights'] as List;
    var l2Before = net.layers[2].getWeights()['weights'] as List;

    // 2. Aktion: EIN einziger Durchlauf (Forward + Backward + Optimize)
    print("\n🔥 Führe exakt 1 Backpropagation-Schritt aus...");
    double loss = net.trainStep(xBatch, yBatch);
    print("📊 Berechneter Loss: $loss");

    // 3. Snapshot: Gewichte NACH dem Update aus dem VRAM holen
    var l1After = net.layers[0].getWeights()['weights'] as List;
    var l2After = net.layers[2].getWeights()['weights'] as List;

    // 4. Analyse: Berechnen und Printen!
    print("\n" + "="*60);
    print("🔍 GRADIENTEN-ANALYSE (LAYER 1 - Hidden Layer)");
    print("="*60);
    for (int i = 0; i < l1Before.length; i++) {
      List<double> rowBefore = (l1Before[i] as List).cast<double>();
      List<double> rowAfter = (l1After[i] as List).cast<double>();
      print("Neuron $i:");
      for (int j = 0; j < rowBefore.length; j++) {
        double delta = rowAfter[j] - rowBefore[j];
        double grad = -delta; // W_alt - W_neu
        String marker = (grad == 0.0) ? " 💀 DEAD" : " ✅ ALIVE";
        print("  Weight $j | Vorher: ${rowBefore[j].toStringAsFixed(6).padLeft(9)} | Nachher: ${rowAfter[j].toStringAsFixed(6).padLeft(9)} | Gradient: ${grad.toStringAsFixed(8)}$marker");
      }
    }

    print("\n" + "="*60);
    print("🔍 GRADIENTEN-ANALYSE (LAYER 2 - Output Layer)");
    print("="*60);
    for (int i = 0; i < l2Before.length; i++) {
      List<double> rowBefore = (l2Before[i] as List).cast<double>();
      List<double> rowAfter = (l2After[i] as List).cast<double>();
      print("Output-Verbindung Zeile $i:");
      for (int j = 0; j < rowBefore.length; j++) {
        double delta = rowAfter[j] - rowBefore[j];
        double grad = -delta; // W_alt - W_neu
        String marker = (grad == 0.0) ? " 💀 DEAD" : " ✅ ALIVE";
        print("  Weight $j | Vorher: ${rowBefore[j].toStringAsFixed(6).padLeft(9)} | Nachher: ${rowAfter[j].toStringAsFixed(6).padLeft(9)} | Gradient: ${grad.toStringAsFixed(8)}$marker");
      }
    }

    print("\n🧹 Aufräumen...");
    net.free();
    GPUEngine.dispose();
    return true;
  }
  Future<bool> extractRawGradients() async {
    print("🚀 Initialisiere GPU Engine für ROH-GRADIENTEN-EXTRAKTION...");
    await GPUEngine.initialize(debug: false, target: target);

    SNetworkGPU net = SNetworkGPU();
    net.add(DenseTL(4));
    net.add(SigmoidMatrixTL());
    net.add(DenseTL(1));
    net.add(SigmoidMatrixTL());

    net.compile([4, 2], [4, 1], 1.0, useAdam: false);

    List<double> xBatch = [
      0.0, 0.0,
      0.0, 1.0,
      1.0, 0.0,
      1.0, 1.0
    ];
    List<double> yBatch = [0.0, 1.0, 1.0, 0.0];

    net.inputRef.pushData(xBatch);
    net.targetRef.pushData(yBatch);

    print("\n🔥 Führe Forward und Backward Pass aus (OHNE Optimizer)...");
    GPUEngine.run(net.zTape);
    GPUEngine.run(net.fTape);
    GPUEngine.run(net.bTape);

    print("\n" + "="*60);
    print("🔍 ROH-GRADIENTEN DIREKT AUS DEM C++ VRAM (via toCpu())");
    print("="*60);

    for (int i = 0; i < net.allParams.length; i++) {
      GPUTensor param = net.allParams[i];

      // ✅ KORREKTUR: Wir nutzen sauber die integrierte toCpu() Methode der Tensor-Klasse[cite: 3]
      param.toCpu();

      // Sobald toCpu() aufgerufen wurde, ist die .grad Liste des Tensors direkt befüllt[cite: 3]
      var gradData = param.grad;

      print("\n--- Parameter $i (Shape: ${param.shape}, ID: ${param.id}) ---");
      print("Rohe Gradienten im Speicher:");
      print(gradData);
    }

    print("\n🧹 Aufräumen...");
    net.free();
    GPUEngine.dispose();
    return true;
  }


}
*/

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  /// Adjust for your architecture
  await GPUEngine.initialize(target: Target.cuda);
  runApp(MaterialApp(home: BenchmarkScreen()));
}

class BenchmarkScreen extends StatefulWidget {
  @override
  State<BenchmarkScreen> createState() => _BenchmarkScreenState();
}

class _BenchmarkScreenState extends State<BenchmarkScreen> {
  String status = 'Ready to benchmark';
  bool isRunning = false;

  void runBenchmarkSuite() async {
    setState(() {
      isRunning = true;
      status = 'Running benchmarks...';
    });

    await Future<void>.delayed(Duration(milliseconds: 100));

    // 1. Training and Static Prediction
    print('--- Test 1: Training and Static Prediction ---');
    GPUTensor<Matrix> trainInput1 = GPUTensor<Matrix>(<List<double>>[
      <double>[1.0, 1.0],
      <double>[2.0, 2.0],
    ]);

    GPUTensor<Matrix> trainTarget1 = GPUTensor<Matrix>(<List<double>>[
      <double>[2.0, 2.0],
      <double>[4.0, 4.0],
    ]);

    SeqModel<Matrix, Matrix> model1 = SeqModel<Matrix, Matrix>(
      <TapeLayer>[DenseTL(2)],
      trainInput1,
      target: trainTarget1,
      lossFunction: mseMatrixGPU,
      optimizerBuilder: (List<GPUTensor> params) {
        return SGDGPU(params, 0.01);
      },
    );

    model1.compile();

    int epochs1 = 1000;
    for (int i = 0; i < epochs1; i = i + 1) {
      model1.runTraining();
    }

    model1.loss?.toCpu();
    print('Final Training Loss: ${model1.loss?.value}');

    GPUTensor<Matrix> inferInput1 = GPUTensor<Matrix>(<List<double>>[
      <double>[3.0, 3.0],
    ]);

    GPUTensor<Matrix> inferResult1 = model1.predict(inferInput1);

    model1.runForward();
    inferResult1.toCpu();
    print('Prediction for [3.0, 3.0]: ${inferResult1.value}');

    List<double> newInferData = <double>[4.0, 4.0];
    model1.runForward(inputData: newInferData);
    inferResult1.toCpu();
    print('Prediction for [4.0, 4.0]: ${inferResult1.value}');

    model1.free();

    // 2. GPU Engine Benchmark - Deep Network Static Unrolling
    print('--- Test 2: Deep Network Static Unrolling ---');
    int batchSize2 = 1024;
    int inputFeatures2 = 64 * 64;
    int outputFeatures2 = 16;

    Random rnd2 = Random(42);

    List<List<double>> rawInput2 = <List<double>>[];
    List<List<double>> rawTarget2 = <List<double>>[];

    for (int i = 0; i < batchSize2; i = i + 1) {
      List<double> inRow = <double>[];
      for (int j = 0; j < inputFeatures2; j = j + 1) {
        inRow.add(rnd2.nextDouble());
      }
      rawInput2.add(inRow);

      List<double> outRow = <double>[];
      for (int j = 0; j < outputFeatures2; j = j + 1) {
        outRow.add(rnd2.nextDouble());
      }
      rawTarget2.add(outRow);
    }

    GPUTensor<Matrix> trainInput2 = GPUTensor<Matrix>(rawInput2);
    GPUTensor<Matrix> trainTarget2 = GPUTensor<Matrix>(rawTarget2);

    SeqModel<Matrix, Matrix> model2 = SeqModel<Matrix, Matrix>(
      <TapeLayer>[
        DenseTL(128),
        GeluLayerMatrixTL(),
        DenseTL(64),
        GeluLayerMatrixTL(),
        DenseTL(outputFeatures2),
      ],
      trainInput2,
      target: trainTarget2,
      lossFunction: mseMatrixGPU,
      optimizerBuilder: (List<GPUTensor> params) {
        return AdamGPU(params, 0.001);
      },
    );

    Stopwatch timer2 = Stopwatch();

    print('Compiling static tapes...');
    timer2.start();
    model2.compile();
    timer2.stop();
    print('Compilation took: ${timer2.elapsedMilliseconds} ms');

    print(
      'Starting high-speed training loop (20 seconds max) for Batch Size $batchSize2...',
    );
    timer2.reset();
    timer2.start();

    int actualEpochs2 = 0;

    while (timer2.elapsedMilliseconds < 20000) {
      model2.runTraining();
      actualEpochs2 = actualEpochs2 + 1;
    }

    timer2.stop();
    model2.loss?.toCpu();

    double seconds2 = timer2.elapsedMilliseconds / 1000.0;
    double epochsPerSec2 = actualEpochs2 / seconds2;

    double dense1Flops2 = batchSize2 * 128 * inputFeatures2 * 2.0;
    double dense2Flops2 = batchSize2 * 64 * 128 * 2.0;
    double dense3Flops2 = batchSize2 * outputFeatures2 * 64 * 2.0;

    double forwardFlops2 = dense1Flops2 + dense2Flops2 + dense3Flops2;
    double totalFlopsPerEpoch2 = forwardFlops2 * 3.0;

    double totalFlopsAchieved2 = totalFlopsPerEpoch2 * actualEpochs2;
    double flopsPerSecond2 = totalFlopsAchieved2 / seconds2;

    double gflops2 = flopsPerSecond2 / 1000000000.0;
    double tflops2 = gflops2 / 1000.0;

    print('Final Training Loss: ${model2.loss?.value}');
    print(
      'Completed $actualEpochs2 epochs in ${seconds2.toStringAsFixed(2)} seconds',
    );
    print('Engine Performance: ${epochsPerSec2.toStringAsFixed(2)} Epochs/sec');
    print(
      'Compute Performance: ${gflops2.toStringAsFixed(2)} GFLOPS (${tflops2.toStringAsFixed(4)} TFLOPS)',
    );

    model2.free();

    // 3. GPU Engine Benchmark - Pure MatMul Isolated
    print('--- Test 3: Pure MatMul Isolated ---');
    int m3 = 2048;
    int k3 = 2048;
    int n3 = 2048;

    Random rnd3 = Random(42);

    List<List<double>> rawA3 = <List<double>>[];
    for (int i = 0; i < m3; i = i + 1) {
      List<double> row = <double>[];
      for (int j = 0; j < k3; j = j + 1) {
        row.add(rnd3.nextDouble());
      }
      rawA3.add(row);
    }

    List<List<double>> rawB3 = <List<double>>[];
    for (int i = 0; i < k3; i = i + 1) {
      List<double> row = <double>[];
      for (int j = 0; j < n3; j = j + 1) {
        row.add(rnd3.nextDouble());
      }
      rawB3.add(row);
    }

    GPUTensor<Matrix> tensorA3 = GPUTensor<Matrix>(rawA3);
    GPUTensor<Matrix> tensorB3 = GPUTensor<Matrix>(rawB3);
    GPUTensor<Matrix> tensorC3 = GPUTensor<Matrix>.empty(<int>[m3, n3]);

    CommandBuffer tape3 = CommandBuffer();
    matMulGPU(tensorA3, tensorB3, tape3, outTensor: tensorC3);

    Stopwatch timer3 = Stopwatch();
    print('Starting high-speed pure MatMul loop (20 seconds max)...');
    timer3.start();

    int actualSteps3 = 0;

    while (timer3.elapsedMilliseconds < 20000) {
      GPUEngine.run(tape3.bytes());
      actualSteps3 = actualSteps3 + 1;
    }

    timer3.stop();
    double seconds3 = timer3.elapsedMilliseconds / 1000.0;

    double flopsPerStep3 = 2.0 * m3 * k3 * n3;
    double totalFlopsAchieved3 = flopsPerStep3 * actualSteps3;
    double flopsPerSecond3 = totalFlopsAchieved3 / seconds3;

    double gflops3 = flopsPerSecond3 / 1000000000.0;
    double tflops3 = gflops3 / 1000.0;

    print(
      'Completed $actualSteps3 MatMul steps in ${seconds3.toStringAsFixed(2)} seconds',
    );
    print(
      'Compute Performance: ${gflops3.toStringAsFixed(2)} GFLOPS (${tflops3.toStringAsFixed(4)} TFLOPS)',
    );

    tensorA3.free();
    tensorB3.free();
    tensorC3.free();

    // 4. GPU Engine Benchmark - Absolute VRAM Saturation MatMul
    print('--- Test 4: Scaled VRAM Saturation MatMul ---');
    int m4 = 2048;
    int k4 = 2048;
    int n4 = 2048;

    GPUTensor<Matrix> tensorA4 = GPUTensor<Matrix>.empty(<int>[m4, k4]);
    GPUTensor<Matrix> tensorB4 = GPUTensor<Matrix>.empty(<int>[k4, n4]);
    GPUTensor<Matrix> tensorC4 = GPUTensor<Matrix>.empty(<int>[m4, n4]);

    CommandBuffer tape4 = CommandBuffer();
    matMulGPU(tensorA4, tensorB4, tape4, outTensor: tensorC4);

    int steps4 = 5;

    print('Starting VRAM saturation test for $steps4 steps...');
    Stopwatch timer4 = Stopwatch();
    timer4.start();

    for (int i = 0; i < steps4; i = i + 1) {
      GPUEngine.run(tape4.bytes());
    }

    timer4.stop();
    double seconds4 = timer4.elapsedMilliseconds / 1000.0;

    double flopsPerStep4 = 2.0 * m4 * k4 * n4;
    double totalFlopsAchieved4 = flopsPerStep4 * steps4;
    double flopsPerSecond4 = totalFlopsAchieved4 / seconds4;

    double gflops4 = flopsPerSecond4 / 1000000000.0;
    double tflops4 = gflops4 / 1000.0;

    print(
      'Completed $steps4 massive MatMul steps in ${seconds4.toStringAsFixed(2)} seconds',
    );
    print(
      'Compute Performance: ${gflops4.toStringAsFixed(2)} GFLOPS (${tflops4.toStringAsFixed(4)} TFLOPS)',
    );

    tensorA4.free();
    tensorB4.free();
    tensorC4.free();

    setState(() {
      isRunning = false;
      status = 'All benchmarks completed. Check logcat/console.';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('GPU Engine Benchmark')),
      body: Center(
        child: Padding(
          padding: EdgeInsets.all(24.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                status,
                textAlign: TextAlign.center,
                style: TextStyle(fontSize: 18.0),
              ),
              SizedBox(height: 32.0),
              ElevatedButton(
                onPressed: isRunning ? null : runBenchmarkSuite,
                child: Padding(
                  padding: EdgeInsets.symmetric(
                    horizontal: 24.0,
                    vertical: 12.0,
                  ),
                  child: Text(
                    isRunning ? 'Running...' : 'Run Benchmarks',
                    style: TextStyle(fontSize: 16.0),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
4
likes
0
points
589
downloads

Publisher

verified publishernomoproject.dev

Weekly Downloads

Bring native GPU-accelerated machine learning to Flutter. Train models, build transformers, and run local inference without Python or cloud APIs.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

ffi, flutter, flutter_web_plugins, plugin_platform_interface, web

More

Packages that depend on flutter_ml

Packages that implement flutter_ml